From bf3bd19f79936fa519a5285701763da58935592a Mon Sep 17 00:00:00 2001 From: hachem Date: Wed, 9 Sep 2026 05:43:06 +0200 Subject: refactor: split code gen into arch seam --- src/parser.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-) (limited to 'src/parser.c') diff --git a/src/parser.c b/src/parser.c index ae7c1b3..5379114 100644 --- a/src/parser.c +++ b/src/parser.c @@ -586,6 +586,59 @@ static bool parse_while(struct Parser* parser, struct Statement* out) return true; } +static bool token_starts_operand(enum TokenType type) +{ + return type == TOKEN_IDENTIFIER || type == TOKEN_INTEGER || type == TOKEN_FLOAT + || type == TOKEN_CHAR || type == TOKEN_CARET || type == TOKEN_MINUS; +} + +static bool parse_instruction(struct Parser* parser, struct Token mnemonic, struct Statement* out) +{ + struct Expr** operands = NULL; + size_t count = 0; + size_t capacity = 0; + + // assembly is line-oriented: operands share the mnemonic's line, and a bare + // mnemonic like `hlt` is just followed by the next statement + if (parser->current.line == mnemonic.line && token_starts_operand(parser->current.type)) + { + do + { + struct Expr* operand = parse_expression(parser); + if (operand == NULL) + goto error; + + if (count == capacity) + { + capacity = capacity < 4 ? 4 : capacity * 2; + struct Expr** grown = realloc(operands, capacity * sizeof(struct Expr*)); + if (grown == NULL) + { + free_expr(operand); + goto error; + } + operands = grown; + } + operands[count] = operand; + count += 1; + } + while (match_token(parser, TOKEN_COMMA)); + } + + out->kind = STATEMENT_INSTRUCTION; + out->instruction.mnemonic = mnemonic; + out->instruction.operands = operands; + out->instruction.operand_count = count; + out->instruction.operand_capacity = capacity; + return true; + +error: + for (size_t i = 0; i < count; i += 1) + free_expr(operands[i]); + free(operands); + return false; +} + static bool parse_statement(struct Parser* parser, struct Statement* out) { if (match_token(parser, TOKEN_IF)) @@ -652,6 +705,9 @@ static bool parse_statement(struct Parser* parser, struct Statement* out) return true; } + if (!deref && !is_assign_op(parser->current.type)) + return parse_instruction(parser, name, out); + if (!is_assign_op(parser->current.type)) { error_at(parser, parser->current, "expected an assignment operator"); @@ -729,6 +785,18 @@ static bool parse_directive(struct Parser* parser, struct Program* program) return false; struct Token key = parser->previous; + // bare directive with no value, e.g. [boot] + if (match_token(parser, TOKEN_RIGHT_BRACKET)) + { + if (token_text_is(key, "boot")) + { + program->config.boot = true; + return true; + } + error_at(parser, key, "unknown directive"); + return false; + } + if (!consume(parser, TOKEN_COLON, "expected ':' after directive name")) return false; @@ -745,12 +813,39 @@ static bool parse_directive(struct Parser* parser, struct Program* program) if (token_text_is(key, "bits")) { - if (value.type != TOKEN_INTEGER || (!token_text_is(value, "64") && !token_text_is(value, "32"))) + if (value.type != TOKEN_INTEGER + || (!token_text_is(value, "64") && !token_text_is(value, "32") && !token_text_is(value, "16"))) + { + error_at(parser, value, "bits must be 16, 32 or 64"); + return false; + } + program->config.bits = token_text_is(value, "64") ? 64 : token_text_is(value, "32") ? 32 : 16; + return true; + } + + if (token_text_is(key, "format")) + { + if (value.type == TOKEN_IDENTIFIER && (token_text_is(value, "elf") || token_text_is(value, "elf64"))) + program->config.format = OUTPUT_ELF; + else if (value.type == TOKEN_IDENTIFIER && (token_text_is(value, "bin") || token_text_is(value, "binary"))) + program->config.format = OUTPUT_BIN; + else + { + error_at(parser, value, "format must be elf or bin"); + return false; + } + return true; + } + + if (token_text_is(key, "org")) + { + if (value.type != TOKEN_INTEGER) { - error_at(parser, value, "bits must be 32 or 64"); + error_at(parser, value, "org must be an integer address"); return false; } - program->config.bits = token_text_is(value, "64") ? 64 : 32; + program->config.has_org = true; + program->config.org = value; return true; } -- cgit v1.3