code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(156 lines)
1 // Logic for parsing statements
3 #include <gc.h>
4 #include <stdbool.h>
6 #include "../ast.h"
7 #include "../stdlib/util.h"
8 #include "context.h"
9 #include "errors.h"
10 #include "expressions.h"
11 #include "files.h"
12 #include "statements.h"
13 #include "suffixes.h"
14 #include "types.h"
15 #include "utils.h"
17 ast_t *parse_declaration(parse_ctx_t *ctx, const char *pos) {
18 const char *start = pos;
19 ast_t *var = parse_var(ctx, pos);
20 if (!var) return NULL;
21 pos = var->end;
22 spaces(&pos);
23 if (!match(&pos, ":")) return NULL;
24 spaces(&pos);
25 type_ast_t *type = optional(ctx, &pos, parse_type);
26 spaces(&pos);
27 ast_t *val = NULL;
28 if (match(&pos, "=")) {
29 val = optional(ctx, &pos, parse_extended_expr);
30 if (!val) {
31 if (optional(ctx, &pos, parse_use))
32 parser_err(ctx, start, pos, "'use' statements are only allowed at the top level of a file");
33 else parser_err(ctx, pos, eol(pos), "This is not a valid expression");
36 return NewAST(ctx->file, start, pos, Declare, .var = var, .type = type, .value = val);
39 ast_t *parse_assignment(parse_ctx_t *ctx, const char *pos) {
40 const char *start = pos;
41 ast_list_t *targets = NULL;
42 for (;;) {
43 ast_t *lhs = optional(ctx, &pos, parse_term);
44 if (!lhs) break;
45 targets = new (ast_list_t, .ast = lhs, .next = targets);
46 spaces(&pos);
47 if (!match(&pos, ",")) break;
48 whitespace(ctx, &pos);
51 if (!targets) return NULL;
53 spaces(&pos);
54 if (!match(&pos, "=")) return NULL;
55 if (match(&pos, "=")) return NULL; // == comparison
57 ast_list_t *values = NULL;
58 for (;;) {
59 ast_t *rhs = optional(ctx, &pos, parse_extended_expr);
60 if (!rhs) break;
61 values = new (ast_list_t, .ast = rhs, .next = values);
62 spaces(&pos);
63 if (!match(&pos, ",")) break;
64 whitespace(ctx, &pos);
67 REVERSE_LIST(targets);
68 REVERSE_LIST(values);
70 return NewAST(ctx->file, start, pos, Assign, .targets = targets, .values = values);
73 ast_t *parse_update(parse_ctx_t *ctx, const char *pos) {
74 const char *start = pos;
75 ast_t *lhs = optional(ctx, &pos, parse_expr);
76 if (!lhs) return NULL;
77 spaces(&pos);
78 ast_e op;
79 if (match(&pos, "+=")) op = PlusUpdate;
80 else if (match(&pos, "++=")) op = ConcatUpdate;
81 else if (match(&pos, "-=")) op = MinusUpdate;
82 else if (match(&pos, "*=")) op = MultiplyUpdate;
83 else if (match(&pos, "/=")) op = DivideUpdate;
84 else if (match(&pos, "^=")) op = PowerUpdate;
85 else if (match(&pos, "<<=")) op = LeftShiftUpdate;
86 else if (match(&pos, "<<<=")) op = UnsignedLeftShiftUpdate;
87 else if (match(&pos, ">>=")) op = RightShiftUpdate;
88 else if (match(&pos, ">>>=")) op = UnsignedRightShiftUpdate;
89 else if (match(&pos, "and=")) op = AndUpdate;
90 else if (match(&pos, "or=")) op = OrUpdate;
91 else if (match(&pos, "xor=")) op = XorUpdate;
92 else return NULL;
93 ast_t *rhs = expect(ctx, start, &pos, parse_extended_expr, "I expected an expression here");
94 return new (ast_t, .file = ctx->file, .start = start, .end = pos, .tag = op, .__data.PlusUpdate.lhs = lhs,
95 .__data.PlusUpdate.rhs = rhs);
98 ast_t *parse_debug_log(parse_ctx_t *ctx, const char *pos) {
99 const char *start = pos;
100 if (!match(&pos, ">>")) return NULL;
101 spaces(&pos);
102 ast_t *first_value =
103 expect(ctx, start, &pos, parse_statement, "I couldn't parse the expression for this debugging message");
104 ast_list_t *values = new (ast_list_t, .ast = first_value, .next = NULL);
105 spaces(&pos);
106 while (match(&pos, ",")) {
107 spaces(&pos);
108 ast_t *value = optional(ctx, &pos, parse_statement);
109 if (!value) break;
110 values = new (ast_list_t, .ast = value, .next = values);
111 spaces(&pos);
113 REVERSE_LIST(values);
114 // whitespace(ctx, &pos);
115 return NewAST(ctx->file, start, pos, DebugLog, .values = values);
118 ast_t *parse_assert(parse_ctx_t *ctx, const char *pos) {
119 const char *start = pos;
120 if (!match_word(&pos, "assert")) return NULL;
121 spaces(&pos);
122 ast_t *expr = expect(ctx, start, &pos, parse_extended_expr, "I couldn't parse the expression for this assert");
123 spaces(&pos);
124 ast_t *message = NULL;
125 if (match(&pos, ",")) {
126 whitespace(ctx, &pos);
127 message = expect(ctx, start, &pos, parse_extended_expr, "I couldn't parse the error message for this assert");
128 } else {
129 pos = expr->end;
131 return NewAST(ctx->file, start, pos, Assert, .expr = expr, .message = message);
134 ast_t *parse_statement(parse_ctx_t *ctx, const char *pos) {
135 ast_t *stmt = NULL;
136 if ((stmt = parse_declaration(ctx, pos)) || (stmt = parse_debug_log(ctx, pos)) || (stmt = parse_assert(ctx, pos)))
137 return stmt;
139 if (!(false || (stmt = parse_update(ctx, pos)) || (stmt = parse_assignment(ctx, pos))))
140 stmt = parse_extended_expr(ctx, pos);
142 for (bool progress = (stmt != NULL); progress;) {
143 ast_t *new_stmt;
144 progress = false;
145 if (stmt->tag == Var) {
146 progress = (false || (new_stmt = parse_method_call_suffix(ctx, stmt))
147 || (new_stmt = parse_fncall_suffix(ctx, stmt)));
148 } else if (stmt->tag == FunctionCall) {
149 new_stmt = parse_optional_conditional_suffix(ctx, stmt);
150 progress = (new_stmt != stmt);
153 if (progress) stmt = new_stmt;
155 return stmt;