aboutsummaryrefslogtreecommitdiff
path: root/src/parser.c
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-09 05:43:06 +0200
committerhachem <im@hachem.wtf>2026-09-09 05:43:06 +0200
commitbf3bd19f79936fa519a5285701763da58935592a (patch)
tree9f854e50405a6fabd28d783232360c2c0b26e2f9 /src/parser.c
parent8b76d35b0a045e0a3277be422513b938ef33dcce (diff)
refactor: split code gen into arch seam
Diffstat (limited to 'src/parser.c')
-rw-r--r--src/parser.c101
1 files changed, 98 insertions, 3 deletions
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;
}