code / tomo

Lines41.3K C23.7K Markdown9.7K YAML5.0K Tomo2.3K
7 others 763
Python231 Shell230 make212 INI47 Text21 SVG16 Lua6
(185 lines)
1 // Logic for parsing functions
3 #include <gc.h>
4 #include <stdarg.h>
5 #include <stdbool.h>
6 #include <string.h>
8 #include <unictype.h>
9 #include <uniname.h>
11 #include "../ast.h"
12 #include "../formatter/utils.h"
13 #include "../stdlib/datatypes.h"
14 #include "../stdlib/text.h"
15 #include "../stdlib/util.h"
16 #include "context.h"
17 #include "controlflow.h"
18 #include "errors.h"
19 #include "expressions.h"
20 #include "functions.h"
21 #include "types.h"
22 #include "utils.h"
24 arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) {
25 const char *comment_start = *pos;
26 arg_ast_t *args = NULL;
27 for (;;) {
28 const char *batch_start = *pos;
29 ast_t *default_val = NULL;
30 type_ast_t *type = NULL;
32 typedef struct name_list_s {
33 const char *start, *end;
34 const char *name, *alias;
35 Text_t comment;
36 struct name_list_s *next;
37 } name_list_t;
39 name_list_t *names = NULL;
40 for (;;) {
41 whitespace(ctx, pos);
42 const char *name = get_id(pos);
43 if (!name) break;
44 const char *name_start = *pos;
45 whitespace(ctx, pos);
47 const char *alias = NULL;
48 if (match(pos, "|")) {
49 whitespace(ctx, pos);
50 alias = get_id(pos);
51 if (!alias) parser_err(ctx, *pos, *pos, "I expected an argument alias after `|`");
54 Text_t comments = EMPTY_TEXT;
55 for (OptionalText_t com;
56 (com = next_comment(ctx->comments, &comment_start, name_start)).tag != TEXT_NONE;) {
57 if (comments.length > 0) comments = Texts(comments, " ");
58 comments = Texts(comments, Text$trim(Text$without_prefix(com, Text("#")), Text(" \t"), true, true));
61 if (match(pos, ":")) {
62 type = expect(ctx, *pos, pos, parse_type, "I expected a type here");
63 whitespace(ctx, pos);
64 if (match(pos, "=")) default_val = expect(ctx, *pos, pos, parse_term, "I expected a value here");
65 names = new (name_list_t, .start = name_start, .end = *pos, .name = name, .alias = alias, .next = names,
66 .comment = comments);
67 break;
68 } else if (strncmp(*pos, "==", 2) != 0 && match(pos, "=")) {
69 default_val = expect(ctx, *pos, pos, parse_term, "I expected a value here");
70 names = new (name_list_t, .start = name_start, .end = *pos, .name = name, .alias = alias, .next = names,
71 .comment = comments);
72 break;
73 } else if (name) {
74 names = new (name_list_t, .start = name_start, .end = *pos, .name = name, .alias = alias, .next = names,
75 .comment = comments);
76 spaces(pos);
77 if (!match(pos, ",")) break;
78 } else {
79 break;
82 if (!names) break;
83 if (!default_val && !type)
84 parser_err(ctx, batch_start, *pos,
85 "I expected a ':' and type, or '=' and a default value after this parameter (", names->name,
86 ")");
88 REVERSE_LIST(names);
89 for (; names; names = names->next)
90 args = new (arg_ast_t, .start = names->start, .end = names->end, .name = names->name, .alias = names->alias,
91 .comment = names->comment, .type = type, .value = default_val, .next = args);
93 if (!match_separator(ctx, pos)) break;
96 REVERSE_LIST(args);
97 return args;
100 ast_t *parse_func_def(parse_ctx_t *ctx, const char *pos) {
101 const char *start = pos;
102 if (!match_word(&pos, "func")) return NULL;
104 ast_t *name = optional(ctx, &pos, parse_var);
105 if (!name) return NULL;
107 spaces(&pos);
109 expect_str(ctx, start, &pos, "(", "I expected a parenthesis for this function's arguments");
111 arg_ast_t *args = parse_args(ctx, &pos);
112 spaces(&pos);
113 type_ast_t *ret_type = match(&pos, "->") ? optional(ctx, &pos, parse_type) : NULL;
114 whitespace(ctx, &pos);
115 bool is_inline = false;
116 ast_t *cache_ast = NULL;
117 for (bool specials = match(&pos, ";"); specials; specials = match_separator(ctx, &pos)) {
118 const char *flag_start = pos;
119 if (match_word(&pos, "inline")) {
120 is_inline = true;
121 } else if (match_word(&pos, "cached")) {
122 if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str = "-1");
123 } else if (match_word(&pos, "cache_size")) {
124 whitespace(ctx, &pos);
125 if (!match(&pos, "=")) parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'");
126 whitespace(ctx, &pos);
127 cache_ast = expect(ctx, start, &pos, parse_expr, "I expected a maximum size for the cache");
130 expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function definition");
132 ast_t *body = expect(ctx, start, &pos, parse_block, "This function needs a body block");
133 return NewAST(ctx->file, start, pos, FunctionDef, .name = name, .args = args, .ret_type = ret_type, .body = body,
134 .cache = cache_ast, .is_inline = is_inline);
137 ast_t *parse_convert_def(parse_ctx_t *ctx, const char *pos) {
138 const char *start = pos;
139 if (!match_word(&pos, "convert")) return NULL;
141 spaces(&pos);
143 if (!match(&pos, "(")) return NULL;
145 arg_ast_t *args = parse_args(ctx, &pos);
146 spaces(&pos);
147 type_ast_t *ret_type = match(&pos, "->") ? optional(ctx, &pos, parse_type) : NULL;
148 whitespace(ctx, &pos);
149 bool is_inline = false;
150 ast_t *cache_ast = NULL;
151 for (bool specials = match(&pos, ";"); specials; specials = match_separator(ctx, &pos)) {
152 const char *flag_start = pos;
153 if (match_word(&pos, "inline")) {
154 is_inline = true;
155 } else if (match_word(&pos, "cached")) {
156 if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str = "-1");
157 } else if (match_word(&pos, "cache_size")) {
158 whitespace(ctx, &pos);
159 if (!match(&pos, "=")) parser_err(ctx, flag_start, pos, "I expected a value for 'cache_size'");
160 whitespace(ctx, &pos);
161 cache_ast = expect(ctx, start, &pos, parse_expr, "I expected a maximum size for the cache");
164 expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function definition");
166 ast_t *body = expect(ctx, start, &pos, parse_block, "This function needs a body block");
167 return NewAST(ctx->file, start, pos, ConvertDef, .args = args, .ret_type = ret_type, .body = body,
168 .cache = cache_ast, .is_inline = is_inline);
171 ast_t *parse_lambda(parse_ctx_t *ctx, const char *pos) {
172 const char *start = pos;
173 if (!match_word(&pos, "func")) return NULL;
174 spaces(&pos);
175 if (!match(&pos, "(")) return NULL;
176 arg_ast_t *args = parse_args(ctx, &pos);
177 spaces(&pos);
178 type_ast_t *ret = match(&pos, "->") ? optional(ctx, &pos, parse_type) : NULL;
179 spaces(&pos);
180 expect_closing(ctx, &pos, ")", "I was expecting a ')' to finish this anonymous function's arguments");
181 ast_t *body = optional(ctx, &pos, parse_block);
182 if (!body) body = NewAST(ctx->file, pos, pos, Block, .statements = NULL);
183 return NewAST(ctx->file, start, pos, Lambda, .id = ctx->next_lambda_id++, .args = args, .ret_type = ret,
184 .body = body);