code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(173 lines)
1 // Logic for parsing various suffixes that can go after an expression
3 #include <stdbool.h>
5 #include "../ast.h"
6 #include "../stdlib/print.h"
7 #include "../stdlib/util.h"
8 #include "context.h"
9 #include "errors.h"
10 #include "expressions.h"
11 #include "utils.h"
13 ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs) {
14 if (!lhs) return NULL;
15 const char *pos = lhs->end;
16 whitespace(ctx, &pos);
17 if (!match(&pos, ".")) return NULL;
18 if (*pos == '.') return NULL;
19 whitespace(ctx, &pos);
20 bool dollar = match(&pos, "$");
21 const char *field = get_id(&pos);
22 if (!field) return NULL;
23 if (dollar) field = String("$", field);
24 return NewAST(ctx->file, lhs->start, pos, FieldAccess, .fielded = lhs, .field = field);
27 ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs) {
28 if (!lhs) return NULL;
29 const char *pos = lhs->end;
30 if (match(&pos, "!")) return NewAST(ctx->file, lhs->start, pos, NonOptional, .value = lhs);
31 else return NULL;
34 ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs) {
35 if (!lhs) return NULL;
36 const char *start = lhs->start;
37 const char *pos = lhs->end;
38 if (!match(&pos, "[")) return NULL;
39 whitespace(ctx, &pos);
40 ast_t *index = optional(ctx, &pos, parse_extended_expr);
41 whitespace(ctx, &pos);
42 expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this index");
43 return NewAST(ctx->file, start, pos, Index, .indexed = lhs, .index = index);
46 ast_t *parse_comprehension_suffix(parse_ctx_t *ctx, ast_t *expr) {
47 // <expr> "for" [<index>,]<var> "in" <iter> ["if" <condition> | "unless" <condition>]
48 if (!expr) return NULL;
49 const char *start = expr->start;
50 const char *pos = expr->end;
51 whitespace(ctx, &pos);
52 if (!match_word(&pos, "for")) return NULL;
54 ast_list_t *vars = NULL;
55 for (;;) {
56 ast_t *var = optional(ctx, &pos, parse_var);
57 if (var) vars = new (ast_list_t, .ast = var, .next = vars);
59 spaces(&pos);
60 if (!match(&pos, ",")) break;
62 REVERSE_LIST(vars);
64 expect_str(ctx, start, &pos, "in", "I expected an 'in' for this 'for'");
65 ast_t *iter = expect(ctx, start, &pos, parse_expr, "I expected an iterable value for this 'for'");
66 const char *next_pos = pos;
67 whitespace(ctx, &next_pos);
68 ast_t *filter = NULL;
69 if (match_word(&next_pos, "if")) {
70 pos = next_pos;
71 filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'");
72 } else if (match_word(&next_pos, "unless")) {
73 pos = next_pos;
74 filter = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'");
75 filter = WrapAST(filter, Not, filter);
77 return NewAST(ctx->file, start, pos, Comprehension, .expr = expr, .vars = vars, .iter = iter, .filter = filter);
80 ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt) {
81 // <statement> "if" <condition> | <statement> "unless" <condition>
82 if (!stmt) return stmt;
83 const char *start = stmt->start;
84 const char *pos = stmt->end;
85 if (match_word(&pos, "if")) {
86 ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'if'");
87 return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt);
88 } else if (match_word(&pos, "unless")) {
89 ast_t *condition = expect(ctx, pos - 2, &pos, parse_expr, "I expected a condition for this 'unless'");
90 condition = WrapAST(condition, Not, condition);
91 return NewAST(ctx->file, start, pos, If, .condition = condition, .body = stmt);
92 } else {
93 return stmt;
97 ast_t *parse_method_call_suffix(parse_ctx_t *ctx, ast_t *self) {
98 if (!self) return NULL;
100 const char *start = self->start;
101 const char *pos = self->end;
103 if (!match(&pos, ".")) return NULL;
104 if (*pos == ' ') return NULL;
105 const char *fn = get_id(&pos);
106 if (!fn) return NULL;
107 spaces(&pos);
108 if (!match(&pos, "(")) return NULL;
109 whitespace(ctx, &pos);
111 arg_ast_t *args = NULL;
112 for (;;) {
113 const char *arg_start = pos;
114 const char *name = get_id(&pos);
115 whitespace(ctx, &pos);
116 if (!name || !match(&pos, "=")) {
117 name = NULL;
118 pos = arg_start;
121 ast_t *arg = optional(ctx, &pos, parse_expr);
122 if (!arg) {
123 if (name) parser_err(ctx, arg_start, pos, "I expected an argument here");
124 break;
126 args = new (arg_ast_t, .start = arg_start, .end = arg->end, .name = name, .value = arg, .next = args);
127 if (!match_separator(ctx, &pos)) break;
129 REVERSE_LIST(args);
131 whitespace(ctx, &pos);
133 if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed");
135 return NewAST(ctx->file, start, pos, MethodCall, .self = self, .name = fn, .args = args);
138 ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn) {
139 if (!fn) return NULL;
141 const char *start = fn->start;
142 const char *pos = fn->end;
144 if (!match(&pos, "(")) return NULL;
146 whitespace(ctx, &pos);
148 arg_ast_t *args = NULL;
149 for (;;) {
150 const char *arg_start = pos;
151 const char *name = get_id(&pos);
152 whitespace(ctx, &pos);
153 if (!name || !match(&pos, "=")) {
154 name = NULL;
155 pos = arg_start;
158 ast_t *arg = optional(ctx, &pos, parse_expr);
159 if (!arg) {
160 if (name) parser_err(ctx, arg_start, pos, "I expected an argument here");
161 break;
163 args = new (arg_ast_t, .name = name, .value = arg, .next = args);
164 if (!match_separator(ctx, &pos)) break;
167 whitespace(ctx, &pos);
169 if (!match(&pos, ")")) parser_err(ctx, start, pos, "This parenthesis is unclosed");
171 REVERSE_LIST(args);
172 return NewAST(ctx->file, start, pos, FunctionCall, .fn = fn, .args = args);