diff options
author | Himbeer <himbeer@disroot.org> | 2025-05-08 15:10:34 +0200 |
---|---|---|
committer | Himbeer <himbeer@disroot.org> | 2025-05-08 15:10:34 +0200 |
commit | af5c04c06b32727def8be04f0f73a09c9d5d8972 (patch) | |
tree | 6ec2da723e9ad00fcea31bc32aa610dd67299769 | |
parent | 299a8632fbe235cf2eacbeb91b4c1538ec0427c3 (diff) |
-rw-r--r-- | src/parse.c | 290 |
1 files changed, 289 insertions, 1 deletions
diff --git a/src/parse.c b/src/parse.c index 656edde..88ebe3e 100644 --- a/src/parse.c +++ b/src/parse.c @@ -847,6 +847,294 @@ parse_union(struct lexer *lexer, struct ast_union *un) } static int +parse_local_s(struct lexer *lexer, struct ast_local *lv) +{ + lv->loc = lex_loc(lexer); + + if (!match(lexer, T_LET)) { + return 0; + } + + struct token nametok; + if (lex(lexer, &nametok) != T_NAME) { + error(nametok.loc, "expected local variable name after 'let'"); + } + lv->name = nametok.info.str; + + if (!parse_type(lexer, &lv->type)) { + error(lex_loc(lexer), "expected local variable type after its name"); + } + + lv->init = NULL; + if (match(lexer, T_ASSIGN)) { + lv->init = must_malloc(sizeof(struct expr)); + if (!parse_expr(lexer, lv->init)) { + error(lex_loc(lexer), "expected initialization expression after '='"); + } + } + + return 1; +} + +static int +parse_assign_s(struct lexer *lexer, struct ast_assign *a, enum stmt *kind) +{ + if (match(lexer, T_ASSIGN)) { + *kind = S_ASSIGN; + } else if (match(lexer, T_ADDASSIGN)) { + *kind = S_ADDASSIGN; + } else if (match(lexer, T_SUBASSIGN)) { + *kind = S_SUBASSIGN; + } else if (match(lexer, T_MULASSIGN)) { + *kind = S_MULASSIGN; + } else if (match(lexer, T_DIVASSIGN)) { + *kind = S_DIVASSIGN; + } else if (match(lexer, T_MODASSIGN)) { + *kind = S_REMASSIGN; + } else if (match(lexer, T_BANDASSIGN)) { + *kind = S_BANDASSIGN; + } else if (match(lexer, T_BORASSIGN)) { + *kind = S_BORASSIGN; + } else if (match(lexer, T_BXORASSIGN)) { + *kind = S_BXORASSIGN; + } else { + return 0; + } + + if (!parse_expr(lexer, &a->rhs)) { + error(lex_loc(lexer), "expected expression on right-hand side of assignment"); + } + + return 1; +} + +static int +parse_crement_s(struct lexer *lexer, enum stmt *kind) +{ + if (match(lexer, T_INCR)) { + *kind = S_INCR; + } else if (match(lexer, T_DECR)) { + *kind = S_DECR; + } else { + return 0; + } + + return 1; +} + +static int +parse_else(struct lexer *lexer, struct ast_else *alt) +{ + alt->loc = lex_loc(lexer); + + if (!match(lexer, T_ELSE)) { + return 0; + } + + alt->action.conditional = must_malloc(sizeof(struct ast_if)); + if (parse_if(lexer, alt->action.conditional)) { + alt->cond = 1; + return 1; + } + free(alt->action.conditional); + + alt->cond = 0; + + if (!match(lexer, T_LBRACE)) { + error(lex_loc(lexer), "expected 'if' or '{' after 'else'"); + } + + alt->action.body.stmts = must_calloc(1, sizeof(struct ast_stmt)); + alt->action.body.stmtsz = 1; + alt->action.body.stmtlen = 0; + + struct ast_stmt stmt; + while (parse_stmt(lexer, &stmt, 0)) { + if (alt->action.body.stmtlen >= alt->action.body.stmtsz) { + alt->action.body.stmtsz *= 2; + size_t newsz = alt->action.body.stmtsz * sizeof(struct ast_stmt); + alt->action.body.stmts = must_realloc(alt->action.body.stmts, newsz); + } + alt->action.body.stmts[alt->action.body.stmtlen++] = stmt; + } + + if (!match(lexer, T_RBRACE)) { + error(lex_loc(lexer), "expected '}' to close else body"); + } + + return 1; +} + +static int +parse_if(struct lexer *lexer, struct ast_if *cnd) +{ + cnd->loc = lex_loc(lexer); + + if (!match(lexer, T_IF)) { + return 0; + } + + if (!parse_expr(lexer, &cnd->cond)) { + error(lex_loc(lexer), "expected condition expression after 'if'"); + } + + if (!match(lexer, T_LBRACE)) { + error(lex_loc(lexer), "expected '{' after if condition"); + } + + cnd->stmts = must_calloc(1, sizeof(struct ast_stmt)); + cnd->stmtsz = 1; + cnd->stmtlen = 0; + + struct ast_stmt stmt; + while (parse_stmt(lexer, &stmt, 0)) { + if (cnd->stmtlen >= cnd->stmtsz) { + cnd->stmtsz *= 2; + size_t newsz = cnd->stmtsz * sizeof(struct ast_stmt); + cnd->stmts = must_realloc(cnd->stmts, newsz); + } + cnd->stmts[cnd->stmtlen++] = stmt; + } + + if (!match(lexer, T_RBRACE)) { + error(lex_loc(lexer), "expected '}' to close if body"); + } + + cnd->alt = must_malloc(sizeof(struct ast_else)); + if (!parse_else(lexer, cnd->alt)) { + free(cnd->alt); + cnd->alt = NULL; + } + + return 1; +} + +static int +parse_for(struct lexer *lexer, struct ast_for *loop) +{ + loop->loc = lex_loc(lexer); + + if (!match(lexer, T_FOR)) { + return 0; + } + + loop->init = must_malloc(sizeof(struct ast_stmt)); + if (!parse_stmt(lexer, loop->init, 1)) { + free(loop->init); + loop->init = NULL; + } else if (loop->init->kind >= S_IF) { + error(loop->init->loc, "for loop initializer must not be an if statement, for loop, return or expression"); + } + + if (!match(lexer, T_SEMICOLON)) { + error(lex_loc(lexer), "expected ';' before loop condition"); + } + + if (!parse_expr(lexer, &loop->cond)) { + error(lex_loc(lexer), "expected loop condition expression"); + } + + if (!match(lexer, T_SEMICOLON)) { + error(lex_loc(lexer), "expected ';' after loop condition"); + } + + loop->step = must_malloc(sizeof(struct ast_stmt)); + if (!parse_stmt(lexer, loop->step, 1)) { + free(loop->step); + loop->step = NULL; + } else if (loop->step->kind >= S_IF) { + error(loop->step->loc, "for loop step must not be an if statement, for loop, return or expression"); + } + + if (!match(lexer, T_LBRACE)) { + error(lex_loc(lexer), "expected '{'"); + } + + loop->stmts = must_calloc(1, sizeof(struct ast_stmt)); + loop->stmtsz = 1; + loop->stmtlen = 0; + + struct ast_stmt stmt; + while (parse_stmt(lexer, &stmt, 0)) { + if (loop->stmtlen >= loop->stmtsz) { + loop->stmtsz *= 2; + size_t newsz = loop->stmtsz * sizeof(struct ast_stmt); + loop->stmts = must_realloc(loop->stmts, newsz); + } + loop->stmts[loop->stmtlen++] = stmt; + } + + if (!match(lexer, T_RBRACE)) { + error(lex_loc(lexer), "expected '}' to close for loop body"); + } + + return 1; +} + +static int +parse_return_s(struct lexer *lexer, struct ast_return *ret) +{ + ret->loc = lex_loc(lexer); + + if (!match(lexer, T_RETURN)) { + return 0; + } + + ret->value = must_malloc(sizeof(struct expr)); + if (!parse_expr(lexer, ret->value)) { + free(ret->value); + ret->value = NULL; + } + + return 1; +} + +static int +parse_stmt_exprbased(struct lexer *lexer, struct ast_stmt *stmt) +{ + struct expr e; + if (!parse_expr(lexer, &e)) { + return 0; + } + + if (parse_assign_s(lexer, &stmt->stmt.assign, &stmt->kind)) { + stmt->stmt.assign.lhs = e; + } else if (parse_crement_s(lexer, &stmt->kind)) { + stmt->stmt.e = e; + } else { + stmt->kind = S_EXPR; + stmt->stmt.e = e; + } + + return 1; +} + +static int +parse_stmt(struct lexer *lexer, struct ast_stmt *stmt, int nosemi) +{ + stmt->loc = lex_loc(lexer); + + if (parse_local_s(lexer, &stmt->stmt.localvar)) { + stmt->kind = S_LOCALVAR; + } else if (parse_if(lexer, &stmt->stmt.conditional)) { + stmt->kind = S_IF; + } else if (parse_for(lexer, &stmt->stmt.loop)) { + stmt->kind = S_FOR; + } else if (parse_return_s(lexer, &stmt->stmt.ret)) { + stmt->kind = S_RETURN; + } else if (!parse_stmt_exprbased(lexer, stmt)) { + return 0; + } + + int needsemi = stmt->kind != S_IF && stmt->kind != S_FOR; + if (needsemi && !nosemi && !match(lexer, T_SEMICOLON)) { + error(lex_loc(lexer), "expected semicolon after statement"); + } + + return 1; +} + +static int parse_func(struct lexer *lexer, struct ast_func *fn) { fn->loc = lex_loc(lexer); @@ -905,7 +1193,7 @@ parse_func(struct lexer *lexer, struct ast_func *fn) fn->stmtlen = 0; struct ast_stmt stmt; - while (parse_stmt(lexer, &stmt)) { + while (parse_stmt(lexer, &stmt, 0)) { if (fn->stmtlen >= fn->stmtsz) { fn->stmtsz *= 2; size_t newsz = fn->stmtsz * sizeof(struct ast_stmt); |