/* * Statement parsing routines for the bootstrap compiler. * * Copyright © 2025 Samuel Lidén Borell * * SPDX-License-Identifier: EUPL-1.2+ */ #include #include #include "compiler.h" #include "token.h" static int current_stmt_id = 0; static struct Stmt *current_loop = NULL; static struct Var **nextptr_vardecl = NULL; static enum Token parse_stmt_block(struct Stmt **stmt_out); void parse_func_body(void) { struct Stmt **stmt_ptr = ¤t_func->code; struct Section **section_ptr = ¤t_func->section_first; current_stmt_id = 0; assert(current_func->vardecls == NULL); nextptr_vardecl = ¤t_func->vardecls; for (;;) { enum Token t = parse_stmt_block(stmt_ptr); if (t == T_KW_end) { assert(current_loop == NULL); return; } else if (t == T_KW_section) { struct LexemeInfo li; struct Section *section; expect(&li, T_LowerIdent, "Expected lowercase section name"); section = (struct Section *)tree_insert_str( ¤t_func->section_by_name, li.string, li.len, NULL, sizeof(struct Section)); if (section->ident.node.is_defined) { error("Duplicate section name"); } section->ident.node.is_defined = 1; *section_ptr = section; section_ptr = §ion->next; section->next = NULL; section->code = NULL; stmt_ptr = §ion->code; expect_next_line(); } else { error(tokenizer_line_is_indented() ? "Too many `end`s" : "Expected `end` or `section xxx` after code block"); } } } static struct Stmt *new_stmt(void) { struct Stmt *s = malloc(sizeof(struct Stmt)); NO_NULL(s); s->next = NULL; s->id = current_stmt_id++; return s; } static enum Token parse_stmt_block(struct Stmt **stmt_out) { for (;;) { struct Stmt *s; struct LexemeInfo li; enum Token t, endt; t = tokenize(&li); switch ((int)t) { case T_EOL: expect_next_line(); continue; case T_KW_case: case T_KW_default: case T_KW_else: case T_KW_elif: case T_KW_end: case T_KW_loopempty: case T_KW_loopend: case T_KW_section: /* End of statement block */ return t; } s = new_stmt(); *stmt_out = s; s->line = current_line; switch ((int)t) { case T_KW_assert: s->kind = S_ASSERT; s->u.expr = parse_expr(); break; case T_KW_break: s->kind = S_BREAK; if (!current_loop) { error("`break` must be inside a loop"); } s->u.break_.loop = current_loop; break; case T_KW_continue: s->kind = S_CONTINUE; if (!current_loop) { error("`continue` must be inside a loop"); } s->u.break_.loop = current_loop; break; case T_KW_for: { struct Var *var; s->kind = S_FOR; /* TODO loop var should go out of scope after the loop */ var = parse_var(¤t_func->vars, VAR_DECL_ONLY, nextptr_vardecl); var->is_funcparam = 0; nextptr_vardecl = &var->next; s->u.for_.var = var; expect(&li, T_KW_in, "Expected `in` keyword in `for` statement"); s->u.for_.loop.cond = parse_expr(); goto loopbody; } case T_KW_if: { struct StmtElif **elif_next; s->kind = S_IF; /* true-block */ s->u.if_.cond = parse_expr(); expect_next_line(); endt = parse_stmt_block(&s->u.if_.true_); /* elif-blocks */ s->u.if_.elifs = NULL; elif_next = &s->u.if_.elifs; while (endt == T_KW_elif) { struct StmtElif *elif = malloc(sizeof(struct StmtElif)); NO_NULL(elif); elif->cond = parse_expr(); expect_next_line(); endt = parse_stmt_block(&elif->body); elif->next = NULL; *elif_next = elif; elif_next = &elif->next; } /* false-block */ if (endt == T_KW_else) { expect_next_line(); endt = parse_stmt_block(&s->u.if_.false_); } if (endt != T_KW_end) { error("Expected `end` after `if` block"); } break; } case T_KW_return: t = tokenize(&li); unread_token(); if (t == T_EOL) { s->kind = S_RETURN_NOVALUE; if (current_func->returns != NULL) { error("`return` without value in function with returns"); } } else { s->kind = S_RETURN_VALUE; if (current_func->returns == NULL) { error("`return` with value in function without returns"); } assert(current_func->num_returns >= 1); s->u.expr = parse_expr(); } break; case T_KW_switch: { struct Case **nextptr; s->kind = S_SWITCH; s->u.switch_.cond = parse_expr(); s->u.switch_.default_ = NULL; s->u.switch_.cases = NULL; nextptr = &s->u.switch_.cases; expect_next_line(); t = tokenize(&li); for (;;) { if (t == T_KW_case) { struct Case *case_ = malloc(sizeof(struct Case)); NO_NULL(case_); case_->value = parse_expr(); expect_next_line(); t = parse_stmt_block(&case_->block); case_->next = NULL; *nextptr = case_; nextptr = &case_->next; } else if (t == T_KW_default) { expect_next_line(); t = parse_stmt_block(&s->u.switch_.default_); if (t != T_KW_end) { error("Expected `end` after `default` block"); } } else if (t == T_KW_end) { break; } else if (t == T_EOL) { /* Empty / comment-only lines are allowed */ expect_next_line(); t = tokenize(&li); } else { error("Expected `case`, `default` or `end` in `switch` block"); } } break; } case T_KW_while: { struct Stmt *outer_loop; s->kind = S_WHILE; s->u.loop.cond = parse_expr(); loopbody: s->u.loop.loopempty = NULL; s->u.loop.loopend = NULL; expect_next_line(); outer_loop = current_loop; current_loop = s; endt = parse_stmt_block(&s->u.loop.body); current_loop = outer_loop; /* loopempty block: Reached if loop is never executed */ if (endt == T_KW_loopempty) { expect_next_line(); endt = parse_stmt_block(&s->u.loop.loopempty); } /* TODO consider renaming this: - finish? - complete? */ /* loopend block: Reached if loop is not exited (or if empty) */ if (endt == T_KW_loopend) { expect_next_line(); endt = parse_stmt_block(&s->u.loop.loopend); } if (endt != T_KW_end) { const char *msg; if (endt == T_KW_loopempty) { msg="`loopempty` must come before `loopend`"; } else if (s->u.loop.loopend) { msg="Expected `end` after loop"; } else if (s->u.loop.loopempty) { msg="Expected `end` or `finish` after loop"; } else { msg="Expected `end`, `loopempty` or `loopend` after loop"; } error(msg); } break; } TOKEN_CASES_QUALIFIERS case T_KW_bool: case T_KW_byte: case T_KW_int: case T_KW_long: case T_UpperIdent: { /* Variable definition */ struct Var *var; s->kind = S_VARDECL; /* TODO "unassign" vars when they go out of scope (maybe allow re-assignment in another scope, but only with exactly the same type) */ unread_token(); var = parse_var(¤t_func->vars, VAR_ALLOW_INITVAL, nextptr_vardecl); NO_NULL(var); var->is_funcparam = 0; nextptr_vardecl = &var->next; s->u.var = var; break; } case T_LowerIdent: /* Function call or assignment expression */ unread_token(); s->kind = S_EXPR; s->u.expr = parse_expr(); break; default: error("Unexpected token at start of statement"); } expect_next_line(); stmt_out = &s->next; } }