#include #include #include "parser/parser.h" #include "tests.h" static bool text_is(struct Token token, const char* text) { size_t length = strlen(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"); struct Program program; check(context, parse_program(&lexer, &program)); check(context, program.const_count == 2); check(context, text_is(program.consts[0].name, "A")); check(context, text_is(program.consts[0].value, "1")); check(context, text_is(program.consts[1].name, "B")); check(context, text_is(program.consts[1].value, "60")); free_program(&program); } static void test_parse_data(struct TestContext* context) { struct Lexer lexer = create_lexer("data msg = \"hi\"\n"); struct Program program; check(context, parse_program(&lexer, &program)); check(context, program.data_count == 1); check(context, text_is(program.data_decls[0].name, "msg")); check(context, text_is(program.data_decls[0].value, "\"hi\"")); free_program(&program); } static void test_parse_proc_params(struct TestContext* context) { struct Lexer lexer = create_lexer("proc f(a: rdi, b: rsi)\n{\n}\n"); struct Program program; check(context, parse_program(&lexer, &program)); check(context, program.proc_count == 1); check(context, text_is(program.procs[0].name, "f")); check(context, program.procs[0].param_count == 2); check(context, text_is(program.procs[0].params[0].name, "a")); check(context, text_is(program.procs[0].params[0].reg, "rdi")); check(context, text_is(program.procs[0].params[1].name, "b")); check(context, text_is(program.procs[0].params[1].reg, "rsi")); free_program(&program); } static void test_parse_proc_body(struct TestContext* context) { struct Lexer lexer = create_lexer("proc main\n{\nrax = 1\nrcx -= 1\n^rsi = rdx\n}\n"); struct Program program; check(context, parse_program(&lexer, &program)); check(context, program.proc_count == 1); check(context, program.procs[0].body_count == 3); struct Statement first = program.procs[0].body[0]; check(context, first.kind == STATEMENT_ASSIGN); check(context, !first.assign.target_deref); check(context, text_is(first.assign.target, "rax")); check(context, text_is(first.assign.op, "=")); check(context, primary_is(first.assign.value, "1")); struct Statement second = program.procs[0].body[1]; check(context, text_is(second.assign.target, "rcx")); check(context, text_is(second.assign.op, "-=")); struct Statement third = program.procs[0].body[2]; check(context, third.assign.target_deref); check(context, text_is(third.assign.target, "rsi")); check(context, primary_is(third.assign.value, "rdx")); free_program(&program); } static void test_parse_simple_statements(struct TestContext* context) { struct Lexer lexer = create_lexer("proc main\n{\nloop:\nsyscall\ngoto loop\n}\n"); struct Program program; check(context, parse_program(&lexer, &program)); check(context, program.procs[0].body_count == 3); struct Statement label = program.procs[0].body[0]; check(context, label.kind == STATEMENT_LABEL); check(context, text_is(label.label.name, "loop")); check(context, program.procs[0].body[1].kind == STATEMENT_SYSCALL); struct Statement jump = program.procs[0].body[2]; check(context, jump.kind == STATEMENT_GOTO); check(context, text_is(jump.jump.label, "loop")); 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_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; struct Lexer missing_name = create_lexer("const = 1\n"); check(context, !parse_program(&missing_name, &program)); free_program(&program); struct Lexer wrong_value = create_lexer("data x = 5\n"); check(context, !parse_program(&wrong_value, &program)); free_program(&program); struct Lexer unterminated = create_lexer("proc main\n{\nrax = 1\n"); check(context, !parse_program(&unterminated, &program)); free_program(&program); } void run_parser_tests(struct TestContext* context) { test_parse_consts(context); test_parse_data(context); test_parse_proc_params(context); test_parse_proc_body(context); test_parse_simple_statements(context); test_parse_expressions(context); test_parse_if(context); test_parse_errors(context); }