diff options
| author | hachem <im@hachem.wtf> | 2026-08-30 13:24:29 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-30 13:24:29 +0200 |
| commit | 9e979a49b041a9d0e8ba6f8fee50bd11520169af (patch) | |
| tree | f0e5cc0047cfc9c6b11a83175a4e07b7b0917ea7 | |
| parent | 53946c982cb3290a25f7c8529ef1399f0635ef37 (diff) | |
add: parse expressions
| -rw-r--r-- | src/main.c | 26 | ||||
| -rw-r--r-- | src/parser/ast.c | 41 | ||||
| -rw-r--r-- | src/parser/ast.h | 41 | ||||
| -rw-r--r-- | src/parser/parser.c | 96 | ||||
| -rw-r--r-- | tests/parser_test.c | 32 |
5 files changed, 213 insertions, 23 deletions
@@ -6,6 +6,25 @@ #include "lexer/lexer.h" #include "parser/parser.h" +static void print_expr(struct Expr* expr) +{ + switch (expr->kind) + { + case EXPR_PRIMARY: + printf("%.*s", (int)expr->primary.token.length, expr->primary.token.start); + break; + case EXPR_BINARY: + print_expr(expr->binary.left); + printf(" %.*s ", (int)expr->binary.op.length, expr->binary.op.start); + print_expr(expr->binary.right); + break; + case EXPR_MEMBER: + print_expr(expr->member.object); + printf(".%.*s", (int)expr->member.member.length, expr->member.member.start); + break; + } +} + int main(int argc, char** argv) { struct Args args; @@ -67,11 +86,12 @@ int main(int argc, char** argv) case STATEMENT_ASSIGN: { struct AssignStatement assign = statement.assign; - printf(" %s%.*s %.*s %.*s\n", + printf(" %s%.*s %.*s ", assign.target_deref ? "^" : "", (int)assign.target.length, assign.target.start, - (int)assign.op.length, assign.op.start, - (int)assign.value.length, assign.value.start); + (int)assign.op.length, assign.op.start); + print_expr(assign.value); + printf("\n"); break; } case STATEMENT_LABEL: diff --git a/src/parser/ast.c b/src/parser/ast.c index 4f10915..97cdb6d 100644 --- a/src/parser/ast.c +++ b/src/parser/ast.c @@ -2,6 +2,42 @@ #include "parser/ast.h" +void free_expr(struct Expr* expr) +{ + if (expr == NULL) + return; + + switch (expr->kind) + { + case EXPR_PRIMARY: + break; + case EXPR_BINARY: + free_expr(expr->binary.left); + free_expr(expr->binary.right); + break; + case EXPR_MEMBER: + free_expr(expr->member.object); + break; + } + + free(expr); +} + +static void free_statement(struct Statement* statement) +{ + if (statement->kind == STATEMENT_ASSIGN) + free_expr(statement->assign.value); +} + +void free_proc(struct ProcDecl* proc) +{ + free(proc->params); + + for (size_t i = 0; i < proc->body_count; i += 1) + free_statement(&proc->body[i]); + free(proc->body); +} + struct Program create_program(void) { struct Program program; @@ -23,10 +59,7 @@ void free_program(struct Program* program) free(program->data_decls); for (size_t i = 0; i < program->proc_count; i += 1) - { - free(program->procs[i].params); - free(program->procs[i].body); - } + free_proc(&program->procs[i]); free(program->procs); program->consts = NULL; diff --git a/src/parser/ast.h b/src/parser/ast.h index 4604e1a..199d802 100644 --- a/src/parser/ast.h +++ b/src/parser/ast.h @@ -23,6 +23,42 @@ struct Param struct Token reg; }; +enum ExprKind +{ + EXPR_PRIMARY, + EXPR_BINARY, + EXPR_MEMBER, +}; + +struct PrimaryExpr +{ + struct Token token; +}; + +struct BinaryExpr +{ + struct Expr* left; + struct Token op; + struct Expr* right; +}; + +struct MemberExpr +{ + struct Expr* object; + struct Token member; +}; + +struct Expr +{ + enum ExprKind kind; + union + { + struct PrimaryExpr primary; + struct BinaryExpr binary; + struct MemberExpr member; + }; +}; + enum StatementKind { STATEMENT_ASSIGN, @@ -36,7 +72,7 @@ struct AssignStatement bool target_deref; struct Token target; struct Token op; - struct Token value; + struct Expr* value; }; struct LabelStatement @@ -93,6 +129,9 @@ void add_const(struct Program* program, struct ConstDecl decl); void add_data(struct Program* program, struct DataDecl decl); struct ProcDecl create_proc(void); +void free_proc(struct ProcDecl* proc); void add_param(struct ProcDecl* proc, struct Param param); void add_statement(struct ProcDecl* proc, struct Statement statement); void add_proc(struct Program* program, struct ProcDecl decl); + +void free_expr(struct Expr* expr); diff --git a/src/parser/parser.c b/src/parser/parser.c index a125b7d..5431bfd 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -123,17 +123,87 @@ static bool is_assign_op(enum TokenType type) || type == TOKEN_SLASH_EQUAL; } -static bool parse_value(struct Parser* parser, struct Token* out) +static struct Expr* alloc_expr(enum ExprKind kind) +{ + struct Expr* expr = malloc(sizeof(struct Expr)); + expr->kind = kind; + return expr; +} + +static struct Expr* parse_primary(struct Parser* parser) { if (check(parser, TOKEN_IDENTIFIER) || check(parser, TOKEN_INTEGER) || check(parser, TOKEN_CHAR)) { advance_parser(parser); - *out = parser->previous; - return true; + + struct Expr* expr = alloc_expr(EXPR_PRIMARY); + expr->primary.token = parser->previous; + return expr; } - error_at(parser, parser->current, "expected a value"); - return false; + error_at(parser, parser->current, "expected an expression"); + return NULL; +} + +static struct Expr* parse_postfix(struct Parser* parser) +{ + struct Expr* expr = parse_primary(parser); + if (expr == NULL) + return NULL; + + while (match_token(parser, TOKEN_DOT)) + { + if (!consume(parser, TOKEN_IDENTIFIER, "expected member name after '.'")) + { + free_expr(expr); + return NULL; + } + + struct Expr* member = alloc_expr(EXPR_MEMBER); + member->member.object = expr; + member->member.member = parser->previous; + expr = member; + } + + return expr; +} + +static struct Expr* parse_binary(struct Parser* parser, struct Expr* (*operand)(struct Parser*), enum TokenType a, enum TokenType b) +{ + struct Expr* left = operand(parser); + if (left == NULL) + return NULL; + + while (check(parser, a) || check(parser, b)) + { + advance_parser(parser); + struct Token op = parser->previous; + + struct Expr* right = operand(parser); + if (right == NULL) + { + free_expr(left); + return NULL; + } + + struct Expr* binary = alloc_expr(EXPR_BINARY); + binary->binary.left = left; + binary->binary.op = op; + binary->binary.right = right; + left = binary; + } + + return left; +} + +static struct Expr* parse_multiplicative(struct Parser* parser) +{ + return parse_binary(parser, parse_postfix, TOKEN_STAR, TOKEN_SLASH); +} + +static struct Expr* parse_expression(struct Parser* parser) +{ + return parse_binary(parser, parse_multiplicative, TOKEN_PLUS, TOKEN_MINUS); } static bool parse_statement(struct Parser* parser, struct Statement* out) @@ -173,17 +243,18 @@ static bool parse_statement(struct Parser* parser, struct Statement* out) return false; } - struct AssignStatement assign; - assign.target_deref = deref; - assign.target = name; advance_parser(parser); - assign.op = parser->previous; + struct Token op = parser->previous; - if (!parse_value(parser, &assign.value)) + struct Expr* value = parse_expression(parser); + if (value == NULL) return false; out->kind = STATEMENT_ASSIGN; - out->assign = assign; + out->assign.target_deref = deref; + out->assign.target = name; + out->assign.op = op; + out->assign.value = value; return true; } @@ -226,8 +297,7 @@ static bool parse_proc(struct Parser* parser, struct Program* program) return true; error: - free(decl.params); - free(decl.body); + free_proc(&decl); return false; } diff --git a/tests/parser_test.c b/tests/parser_test.c index ea18add..a0013a7 100644 --- a/tests/parser_test.c +++ b/tests/parser_test.c @@ -10,6 +10,11 @@ static bool text_is(struct Token token, const char* text) return token.length == length && memcmp(token.start, text, length) == 0; } +static bool primary_is(struct Expr* expr, const char* text) +{ + return expr->kind == EXPR_PRIMARY && text_is(expr->primary.token, text); +} + static void test_parse_consts(struct TestContext* context) { struct Lexer lexer = create_lexer("const A = 1\nconst B = 60\n"); @@ -69,7 +74,7 @@ static void test_parse_proc_body(struct TestContext* context) check(context, !first.assign.target_deref); check(context, text_is(first.assign.target, "rax")); check(context, text_is(first.assign.op, "=")); - check(context, text_is(first.assign.value, "1")); + check(context, primary_is(first.assign.value, "1")); struct Statement second = program.procs[0].body[1]; check(context, text_is(second.assign.target, "rcx")); @@ -78,7 +83,7 @@ static void test_parse_proc_body(struct TestContext* context) struct Statement third = program.procs[0].body[2]; check(context, third.assign.target_deref); check(context, text_is(third.assign.target, "rsi")); - check(context, text_is(third.assign.value, "rdx")); + check(context, primary_is(third.assign.value, "rdx")); free_program(&program); } @@ -104,6 +109,28 @@ static void test_parse_simple_statements(struct TestContext* context) free_program(&program); } +static void test_parse_expressions(struct TestContext* context) +{ + struct Lexer lexer = create_lexer("proc main\n{\nrsi = buffer + 31\nrdx = message.len\n}\n"); + struct Program program; + + check(context, parse_program(&lexer, &program)); + check(context, program.procs[0].body_count == 2); + + struct Expr* sum = program.procs[0].body[0].assign.value; + check(context, sum->kind == EXPR_BINARY); + check(context, text_is(sum->binary.op, "+")); + check(context, primary_is(sum->binary.left, "buffer")); + check(context, primary_is(sum->binary.right, "31")); + + struct Expr* member = program.procs[0].body[1].assign.value; + check(context, member->kind == EXPR_MEMBER); + check(context, primary_is(member->member.object, "message")); + check(context, text_is(member->member.member, "len")); + + free_program(&program); +} + static void test_parse_errors(struct TestContext* context) { struct Program program; @@ -128,5 +155,6 @@ void run_parser_tests(struct TestContext* context) test_parse_proc_params(context); test_parse_proc_body(context); test_parse_simple_statements(context); + test_parse_expressions(context); test_parse_errors(context); } |
