From deed096e783fca21b1773fa84c28be139a3c57ad Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 30 Aug 2026 13:42:31 +0200 Subject: add: parse conditional expressions --- src/main.c | 62 +++++++++++++++++++++++++++++++---------------------- src/parser/ast.c | 16 ++++++++++++-- src/parser/ast.h | 10 +++++++++ src/parser/parser.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ tests/parser_test.c | 20 +++++++++++++++++ 5 files changed, 134 insertions(+), 28 deletions(-) diff --git a/src/main.c b/src/main.c index b40f90e..63f87fd 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,41 @@ static void print_expr(struct Expr* expr) } } +static void print_statement(struct Statement* statement, const char* indent) +{ + switch (statement->kind) + { + case STATEMENT_ASSIGN: + { + struct AssignStatement assign = statement->assign; + printf("%s%s%.*s %.*s ", indent, + assign.target_deref ? "^" : "", + (int)assign.target.length, assign.target.start, + (int)assign.op.length, assign.op.start); + print_expr(assign.value); + printf("\n"); + break; + } + case STATEMENT_LABEL: + printf("%s%.*s:\n", indent, (int)statement->label.name.length, statement->label.name.start); + break; + case STATEMENT_GOTO: + printf("%sgoto %.*s\n", indent, (int)statement->jump.label.length, statement->jump.label.start); + break; + case STATEMENT_SYSCALL: + printf("%ssyscall\n", indent); + break; + case STATEMENT_IF: + printf("%sif ", indent); + print_expr(statement->branch.left); + printf(" %.*s ", (int)statement->branch.comparison.length, statement->branch.comparison.start); + print_expr(statement->branch.right); + printf("\n"); + print_statement(statement->branch.body, " "); + break; + } +} + int main(int argc, char** argv) { struct Args args; @@ -79,32 +114,7 @@ int main(int argc, char** argv) printf(")\n"); for (size_t s = 0; s < proc.body_count; s += 1) - { - struct Statement statement = proc.body[s]; - switch (statement.kind) - { - case STATEMENT_ASSIGN: - { - struct AssignStatement assign = statement.assign; - printf(" %s%.*s %.*s ", - assign.target_deref ? "^" : "", - (int)assign.target.length, assign.target.start, - (int)assign.op.length, assign.op.start); - print_expr(assign.value); - printf("\n"); - break; - } - case STATEMENT_LABEL: - printf(" %.*s:\n", (int)statement.label.name.length, statement.label.name.start); - break; - case STATEMENT_GOTO: - printf(" goto %.*s\n", (int)statement.jump.label.length, statement.jump.label.start); - break; - case STATEMENT_SYSCALL: - printf(" syscall\n"); - break; - } - } + print_statement(&proc.body[s], " "); } free_program(&program); diff --git a/src/parser/ast.c b/src/parser/ast.c index 97cdb6d..9bdf4d1 100644 --- a/src/parser/ast.c +++ b/src/parser/ast.c @@ -25,8 +25,20 @@ void free_expr(struct Expr* expr) static void free_statement(struct Statement* statement) { - if (statement->kind == STATEMENT_ASSIGN) - free_expr(statement->assign.value); + switch (statement->kind) + { + case STATEMENT_ASSIGN: + free_expr(statement->assign.value); + break; + case STATEMENT_IF: + free_expr(statement->branch.left); + free_expr(statement->branch.right); + free_statement(statement->branch.body); + free(statement->branch.body); + break; + default: + break; + } } void free_proc(struct ProcDecl* proc) diff --git a/src/parser/ast.h b/src/parser/ast.h index 199d802..e85e72f 100644 --- a/src/parser/ast.h +++ b/src/parser/ast.h @@ -65,6 +65,7 @@ enum StatementKind STATEMENT_LABEL, STATEMENT_GOTO, STATEMENT_SYSCALL, + STATEMENT_IF, }; struct AssignStatement @@ -85,6 +86,14 @@ struct GotoStatement struct Token label; }; +struct IfStatement +{ + struct Expr* left; + struct Token comparison; + struct Expr* right; + struct Statement* body; +}; + struct Statement { enum StatementKind kind; @@ -93,6 +102,7 @@ struct Statement struct AssignStatement assign; struct LabelStatement label; struct GotoStatement jump; + struct IfStatement branch; }; }; diff --git a/src/parser/parser.c b/src/parser/parser.c index 5431bfd..2ae52f2 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -206,8 +206,62 @@ static struct Expr* parse_expression(struct Parser* parser) return parse_binary(parser, parse_multiplicative, TOKEN_PLUS, TOKEN_MINUS); } +static bool is_compare_op(enum TokenType type) +{ + return type == TOKEN_EQUAL_EQUAL + || type == TOKEN_BANG_EQUAL + || type == TOKEN_LESS + || type == TOKEN_LESS_EQUAL + || type == TOKEN_GREATER + || type == TOKEN_GREATER_EQUAL; +} + +static bool parse_statement(struct Parser* parser, struct Statement* out); + +static bool parse_if(struct Parser* parser, struct Statement* out) +{ + struct Expr* left = parse_expression(parser); + if (left == NULL) + return false; + + if (!is_compare_op(parser->current.type)) + { + error_at(parser, parser->current, "expected a comparison operator"); + free_expr(left); + return false; + } + advance_parser(parser); + struct Token comparison = parser->previous; + + struct Expr* right = parse_expression(parser); + if (right == NULL) + { + free_expr(left); + return false; + } + + struct Statement* body = malloc(sizeof(struct Statement)); + if (!parse_statement(parser, body)) + { + free(body); + free_expr(left); + free_expr(right); + return false; + } + + out->kind = STATEMENT_IF; + out->branch.left = left; + out->branch.comparison = comparison; + out->branch.right = right; + out->branch.body = body; + return true; +} + static bool parse_statement(struct Parser* parser, struct Statement* out) { + if (match_token(parser, TOKEN_IF)) + return parse_if(parser, out); + if (match_token(parser, TOKEN_SYSCALL)) { out->kind = STATEMENT_SYSCALL; diff --git a/tests/parser_test.c b/tests/parser_test.c index a0013a7..8856f4c 100644 --- a/tests/parser_test.c +++ b/tests/parser_test.c @@ -131,6 +131,25 @@ static void test_parse_expressions(struct TestContext* context) free_program(&program); } +static void test_parse_if(struct TestContext* context) +{ + struct Lexer lexer = create_lexer("proc main\n{\nif rax != 0\ngoto loop\n}\n"); + struct Program program; + + check(context, parse_program(&lexer, &program)); + check(context, program.procs[0].body_count == 1); + + struct Statement branch = program.procs[0].body[0]; + check(context, branch.kind == STATEMENT_IF); + check(context, primary_is(branch.branch.left, "rax")); + check(context, text_is(branch.branch.comparison, "!=")); + check(context, primary_is(branch.branch.right, "0")); + check(context, branch.branch.body->kind == STATEMENT_GOTO); + check(context, text_is(branch.branch.body->jump.label, "loop")); + + free_program(&program); +} + static void test_parse_errors(struct TestContext* context) { struct Program program; @@ -156,5 +175,6 @@ void run_parser_tests(struct TestContext* context) test_parse_proc_body(context); test_parse_simple_statements(context); test_parse_expressions(context); + test_parse_if(context); test_parse_errors(context); } -- cgit v1.3