diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-24 15:03:27 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-24 15:03:27 +0200 |
commit | c9d686a1a6f49f8c8c4aa698c14448d961a0f024 (patch) | |
tree | 57a7a94b1dd39b2d5158ff9e81e1e72bc7e80525 /src/main.c | |
parent | 6fbe06eda046bd3b261e2ba84e1f728ecdf55a47 (diff) | |
download | bdl-c9d686a1a6f49f8c8c4aa698c14448d961a0f024.tar.gz bdl-c9d686a1a6f49f8c8c4aa698c14448d961a0f024.zip |
Add more graph viz for function maps and types
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 193 |
1 files changed, 174 insertions, 19 deletions
@@ -63,8 +63,15 @@ typedef struct Symbol { | |||
63 | struct SymbolMap *fields; | 63 | struct SymbolMap *fields; |
64 | } Symbol; | 64 | } Symbol; |
65 | 65 | ||
66 | typedef struct Fun { | ||
67 | Str name; | ||
68 | Str param_type; | ||
69 | Str return_type; | ||
70 | } Fun; | ||
71 | |||
66 | MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) | 72 | MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) |
67 | MAPDEF(TypeMap, typemap, Str, Type, str_hash, str_eq) | 73 | MAPDEF(TypeMap, typemap, Str, Type, str_hash, str_eq) |
74 | MAPDEF(FunMap, funmap, Str, Fun, str_hash, str_eq) | ||
68 | 75 | ||
69 | typedef struct Scope { | 76 | typedef struct Scope { |
70 | sz id; | 77 | sz id; |
@@ -74,13 +81,17 @@ typedef struct Scope { | |||
74 | } Scope; | 81 | } Scope; |
75 | 82 | ||
76 | typedef struct TypeScope { | 83 | typedef struct TypeScope { |
84 | sz id; | ||
85 | Str name; | ||
77 | TypeMap *types; | 86 | TypeMap *types; |
87 | FunMap *funcs; | ||
78 | struct TypeScope *parent; | 88 | struct TypeScope *parent; |
79 | } TypeScope; | 89 | } TypeScope; |
80 | 90 | ||
81 | typedef struct Analyzer { | 91 | typedef struct Analyzer { |
82 | Arena *storage; | 92 | Arena *storage; |
83 | Str file_name; | 93 | Str file_name; |
94 | sz typescope_gen; | ||
84 | sz scope_gen; | 95 | sz scope_gen; |
85 | Scope **scopes; | 96 | Scope **scopes; |
86 | TypeScope **types; | 97 | TypeScope **types; |
@@ -103,6 +114,7 @@ TypeScope * | |||
103 | typescope_alloc(Analyzer *a, TypeScope *parent) { | 114 | typescope_alloc(Analyzer *a, TypeScope *parent) { |
104 | TypeScope *scope = arena_calloc(sizeof(TypeScope), a->storage); | 115 | TypeScope *scope = arena_calloc(sizeof(TypeScope), a->storage); |
105 | scope->parent = parent; | 116 | scope->parent = parent; |
117 | scope->id = a->typescope_gen++; | ||
106 | array_push(a->types, scope, a->storage); | 118 | array_push(a->types, scope, a->storage); |
107 | return scope; | 119 | return scope; |
108 | } | 120 | } |
@@ -139,25 +151,35 @@ graph_scope(Scope *scope, Arena a) { | |||
139 | SymbolMapIter iter = symmap_iterator(scope->symbols, &a); | 151 | SymbolMapIter iter = symmap_iterator(scope->symbols, &a); |
140 | SymbolMap *sym = symmap_next(&iter, &a); | 152 | SymbolMap *sym = symmap_next(&iter, &a); |
141 | print( | 153 | print( |
142 | "%d[shape=\"none\" label=<\ | 154 | "%d[shape=\"none\" label=<<TABLE ALIGN=\"left\" STYLE=\"rounded\" " |
143 | <TABLE ALIGN=\"left\" STYLE=\"rounded\" BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"6\" COLUMNS=\"*\">\ | 155 | "BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"6\" " |
144 | <TR style=\"rounded\" ><TD COLSPAN=\"3\">ID: %d</TD></TR>", | 156 | "COLUMNS=\"*\">" |
157 | "<TR style=\"rounded\"><TD COLSPAN=\"3\">ID: %d</TD></TR>", | ||
145 | scope->id, scope->id); | 158 | scope->id, scope->id); |
146 | print( | 159 | print( |
147 | "<TR ><TD ALIGN=\"left\" > NAME </TD><TD ALIGN=\"left\" > KIND " | 160 | "<TR >" |
148 | "</TD><TD ALIGN=\"left\" > TYPE </TD></TR>"); | 161 | "<TD ALIGN=\"left\" > NAME </TD>" |
162 | "<TD ALIGN=\"left\" > KIND </TD>" | ||
163 | "<TD ALIGN=\"left\" > TYPE </TD>" | ||
164 | "</TR>"); | ||
149 | while (sym) { | 165 | while (sym) { |
150 | Str type_name = cstr("nil"); | 166 | Str type_name = cstr("nil"); |
151 | print( | 167 | print( |
152 | "<TR><TD ALIGN=\"left\"> %s </TD><TD ALIGN=\"left\"> %s </TD><TD " | 168 | "<TR>" |
153 | "ALIGN=\"left\"> %s </TD></TR>", | 169 | "<TD ALIGN=\"left\"> %s </TD>" |
170 | "<TD ALIGN=\"left\"> %s </TD>" | ||
171 | "<TD ALIGN=\"left\"> %s </TD>" | ||
172 | "</TR>", | ||
154 | sym->val.name, sym_kind_str[sym->val.kind], type_name); | 173 | sym->val.name, sym_kind_str[sym->val.kind], type_name); |
155 | SymbolMapIter field_iter = symmap_iterator(sym->val.fields, &a); | 174 | SymbolMapIter field_iter = symmap_iterator(sym->val.fields, &a); |
156 | SymbolMap *field = symmap_next(&field_iter, &a); | 175 | SymbolMap *field = symmap_next(&field_iter, &a); |
157 | while (field) { | 176 | while (field) { |
158 | print( | 177 | print( |
159 | "<TR><TD ALIGN=\"left\"> %s.%s </TD><TD ALIGN=\"left\"> %s " | 178 | "<TR>" |
160 | "</TD></TR>", | 179 | "<TD ALIGN=\"left\"> %s.%s </TD>" |
180 | "<TD ALIGN=\"left\"> %s " | ||
181 | "</TD>" | ||
182 | "</TR>", | ||
161 | sym->val.name, field->val.name, sym_kind_str[field->val.kind]); | 183 | sym->val.name, field->val.name, sym_kind_str[field->val.kind]); |
162 | field = symmap_next(&field_iter, &a); | 184 | field = symmap_next(&field_iter, &a); |
163 | } | 185 | } |
@@ -176,6 +198,85 @@ graph_scope(Scope *scope, Arena a) { | |||
176 | } | 198 | } |
177 | 199 | ||
178 | void | 200 | void |
201 | graph_typescope(TypeScope *scope, Arena a) { | ||
202 | if (!scope->types) { | ||
203 | return; | ||
204 | } | ||
205 | TypeMapIter iter = typemap_iterator(scope->types, &a); | ||
206 | TypeMap *type = typemap_next(&iter, &a); | ||
207 | print( | ||
208 | "%d[shape=\"none\" label=<<TABLE ALIGN=\"left\" STYLE=\"rounded\" " | ||
209 | "BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"6\" " | ||
210 | "COLUMNS=\"*\">", | ||
211 | scope->id); | ||
212 | print( | ||
213 | "<TR >" | ||
214 | "<TD ALIGN=\"left\" > NAME </TD>" | ||
215 | "<TD ALIGN=\"left\" > TYPE </TD>" | ||
216 | "</TR>"); | ||
217 | while (type) { | ||
218 | print( | ||
219 | "<TR>" | ||
220 | "<TD ALIGN=\"left\"> %s </TD>" | ||
221 | "<TD ALIGN=\"left\"> %s</TD>" | ||
222 | "</TR>", | ||
223 | type->key, type->val); | ||
224 | type = typemap_next(&iter, &a); | ||
225 | } | ||
226 | println("</TABLE>>];"); | ||
227 | |||
228 | sz this_id = scope->id; | ||
229 | while (scope->parent) { | ||
230 | if (scope->parent->types) { | ||
231 | println("%d:e->%d:w;", this_id, scope->parent->id); | ||
232 | break; | ||
233 | } else { | ||
234 | scope = scope->parent; | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | |||
239 | void | ||
240 | graph_functions(TypeScope *scope, Arena a) { | ||
241 | if (!scope->funcs) { | ||
242 | return; | ||
243 | } | ||
244 | FunMapIter iter = funmap_iterator(scope->funcs, &a); | ||
245 | FunMap *func = funmap_next(&iter, &a); | ||
246 | print( | ||
247 | "fun_%d[shape=\"none\" label=<<TABLE ALIGN=\"left\" STYLE=\"rounded\" " | ||
248 | "BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"6\" " | ||
249 | "COLUMNS=\"*\">", | ||
250 | scope->id); | ||
251 | print( | ||
252 | "<TR >" | ||
253 | "<TD ALIGN=\"left\" > NAME </TD>" | ||
254 | "<TD ALIGN=\"left\" > PARAMS </TD>" | ||
255 | "<TD ALIGN=\"left\" > RETURN </TD>" | ||
256 | "</TR>"); | ||
257 | while (func) { | ||
258 | print( | ||
259 | "<TR>" | ||
260 | "<TD PORT=\"%s\" ALIGN=\"left\" > %s </TD>" | ||
261 | "<TD ALIGN=\"left\" > %s </TD>" | ||
262 | "<TD ALIGN=\"left\" > %s </TD>" | ||
263 | "</TR>", | ||
264 | func->val.name, func->val.name, func->val.param_type, func->val.return_type); | ||
265 | func = funmap_next(&iter, &a); | ||
266 | } | ||
267 | println("</TABLE>>];"); | ||
268 | sz this_id = scope->id; | ||
269 | while (scope->parent) { | ||
270 | if (scope->parent->types) { | ||
271 | println("fun_%d:e->fun_%d:%s:w;", this_id, scope->parent->id, scope->name); | ||
272 | break; | ||
273 | } else { | ||
274 | scope = scope->parent; | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | |||
279 | void | ||
179 | graph_symbols(Scope **scopes, Arena a) { | 280 | graph_symbols(Scope **scopes, Arena a) { |
180 | if (scopes == NULL) return; | 281 | if (scopes == NULL) return; |
181 | println("digraph symbols {"); | 282 | println("digraph symbols {"); |
@@ -197,6 +298,28 @@ graph_symbols(Scope **scopes, Arena a) { | |||
197 | } | 298 | } |
198 | 299 | ||
199 | void | 300 | void |
301 | graph_types(TypeScope **scopes, Arena a) { | ||
302 | if (scopes == NULL) return; | ||
303 | println("digraph symbols {"); | ||
304 | println("rankdir=LR;"); | ||
305 | println("ranksep=\"0.95 equally\";"); | ||
306 | println("nodesep=\"0.5 equally\";"); | ||
307 | println("overlap=scale;"); | ||
308 | println("bgcolor=\"transparent\";"); | ||
309 | for (sz i = 0; i < array_size(scopes); i++) { | ||
310 | TypeScope *scope = scopes[i]; | ||
311 | if (!scope) { | ||
312 | continue; | ||
313 | } | ||
314 | println("subgraph %d {", i); | ||
315 | graph_typescope(scope, a); | ||
316 | graph_functions(scope, a); | ||
317 | println("}"); | ||
318 | } | ||
319 | println("}"); | ||
320 | } | ||
321 | |||
322 | void | ||
200 | emit_semantic_error(Analyzer *a, Node *n, Str msg) { | 323 | emit_semantic_error(Analyzer *a, Node *n, Str msg) { |
201 | eprintln("%s:%d:%d: error: %s", a->file_name, n->line, n->col, msg); | 324 | eprintln("%s:%d:%d: error: %s", a->file_name, n->line, n->col, msg); |
202 | } | 325 | } |
@@ -219,7 +342,9 @@ typecheck_returns(Analyzer *a, Node *node, Str expected) { | |||
219 | case NODE_RETURN: { | 342 | case NODE_RETURN: { |
220 | bool err = !str_eq(node->type, expected); | 343 | bool err = !str_eq(node->type, expected); |
221 | if (err) { | 344 | if (err) { |
222 | emit_semantic_error(a, node, cstr("mismatched return types")); | 345 | eprintln( |
346 | "%s:%d:%d: error: mismatched return type %s, expected %s", | ||
347 | a->file_name, node->line, node->col, node->type, expected); | ||
223 | } | 348 | } |
224 | } break; | 349 | } break; |
225 | case NODE_BLOCK: { | 350 | case NODE_BLOCK: { |
@@ -242,7 +367,25 @@ typecheck_returns(Analyzer *a, Node *node, Str expected) { | |||
242 | typecheck_returns(a, node->var_val, expected); | 367 | typecheck_returns(a, node->var_val, expected); |
243 | } | 368 | } |
244 | } break; | 369 | } break; |
245 | default: { | 370 | case NODE_ADD: |
371 | case NODE_SUB: | ||
372 | case NODE_DIV: | ||
373 | case NODE_MUL: | ||
374 | case NODE_MOD: | ||
375 | case NODE_NOT: | ||
376 | case NODE_AND: | ||
377 | case NODE_OR: | ||
378 | case NODE_EQ: | ||
379 | case NODE_NEQ: | ||
380 | case NODE_LT: | ||
381 | case NODE_GT: | ||
382 | case NODE_LE: | ||
383 | case NODE_GE: | ||
384 | case NODE_BITNOT: | ||
385 | case NODE_BITAND: | ||
386 | case NODE_BITOR: | ||
387 | case NODE_BITLSHIFT: | ||
388 | case NODE_BITRSHIFT: { | ||
246 | if (node->left) { | 389 | if (node->left) { |
247 | typecheck_returns(a, node->left, expected); | 390 | typecheck_returns(a, node->left, expected); |
248 | } | 391 | } |
@@ -250,6 +393,7 @@ typecheck_returns(Analyzer *a, Node *node, Str expected) { | |||
250 | typecheck_returns(a, node->right, expected); | 393 | typecheck_returns(a, node->right, expected); |
251 | } | 394 | } |
252 | } break; | 395 | } break; |
396 | default: break; | ||
253 | } | 397 | } |
254 | } | 398 | } |
255 | 399 | ||
@@ -543,7 +687,7 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
543 | return node->type; | 687 | return node->type; |
544 | } break; | 688 | } break; |
545 | case NODE_RETURN: { | 689 | case NODE_RETURN: { |
546 | Type ret_type = cstr("("); | 690 | Type ret_type = cstr(""); |
547 | for (sz i = 0; i < array_size(node->elements); i++) { | 691 | for (sz i = 0; i < array_size(node->elements); i++) { |
548 | Node *expr = node->elements[i]; | 692 | Node *expr = node->elements[i]; |
549 | Type type = type_inference(a, expr, scope); | 693 | Type type = type_inference(a, expr, scope); |
@@ -552,7 +696,6 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
552 | ret_type = str_concat(ret_type, cstr(","), a->storage); | 696 | ret_type = str_concat(ret_type, cstr(","), a->storage); |
553 | } | 697 | } |
554 | } | 698 | } |
555 | ret_type = str_concat(ret_type, cstr(")"), a->storage); | ||
556 | node->type = ret_type; | 699 | node->type = ret_type; |
557 | return node->type; | 700 | return node->type; |
558 | } break; | 701 | } break; |
@@ -576,8 +719,9 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
576 | // } | 719 | // } |
577 | 720 | ||
578 | node->type = cstr("nil"); | 721 | node->type = cstr("nil"); |
722 | TypeScope *prev_scope = scope; | ||
579 | scope = typescope_alloc(a, scope); | 723 | scope = typescope_alloc(a, scope); |
580 | Type param_type = cstr("("); | 724 | Type param_type = cstr(""); |
581 | for (sz i = 0; i < array_size(node->func_params); i++) { | 725 | for (sz i = 0; i < array_size(node->func_params); i++) { |
582 | Node *param = node->func_params[i]; | 726 | Node *param = node->func_params[i]; |
583 | Str symbol = param->param_name->value.str; | 727 | Str symbol = param->param_name->value.str; |
@@ -591,10 +735,9 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
591 | param_type = str_concat(param_type, cstr(","), a->storage); | 735 | param_type = str_concat(param_type, cstr(","), a->storage); |
592 | } | 736 | } |
593 | } | 737 | } |
594 | param_type = str_concat(param_type, cstr(")"), a->storage); | ||
595 | node->fun_params = param_type; | 738 | node->fun_params = param_type; |
596 | 739 | ||
597 | Type ret_type = cstr("("); | 740 | Type ret_type = cstr(""); |
598 | for (sz i = 0; i < array_size(node->func_ret); i++) { | 741 | for (sz i = 0; i < array_size(node->func_ret); i++) { |
599 | Node *expr = node->func_ret[i]; | 742 | Node *expr = node->func_ret[i]; |
600 | Type type = type_inference(a, expr, scope); | 743 | Type type = type_inference(a, expr, scope); |
@@ -603,9 +746,17 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
603 | ret_type = str_concat(ret_type, cstr(","), a->storage); | 746 | ret_type = str_concat(ret_type, cstr(","), a->storage); |
604 | } | 747 | } |
605 | } | 748 | } |
606 | ret_type = str_concat(ret_type, cstr(")"), a->storage); | ||
607 | node->fun_return = ret_type; | 749 | node->fun_return = ret_type; |
608 | 750 | ||
751 | Str symbol = node->func_name->value.str; | ||
752 | // TODO: check it was not defined before. | ||
753 | scope->name = symbol; | ||
754 | funmap_insert(&prev_scope->funcs, symbol, | ||
755 | (Fun){.name = symbol, | ||
756 | .param_type = param_type, | ||
757 | .return_type = ret_type}, | ||
758 | a->storage); | ||
759 | |||
609 | if (node->func_body->kind == NODE_BLOCK) { | 760 | if (node->func_body->kind == NODE_BLOCK) { |
610 | Type type; | 761 | Type type; |
611 | for (sz i = 0; i < array_size(node->func_body->elements); i++) { | 762 | for (sz i = 0; i < array_size(node->func_body->elements); i++) { |
@@ -619,7 +770,10 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
619 | 770 | ||
620 | // Ensure main body return matches the prototype. | 771 | // Ensure main body return matches the prototype. |
621 | if (!str_eq(node->func_body->type, ret_type)) { | 772 | if (!str_eq(node->func_body->type, ret_type)) { |
622 | emit_semantic_error(a, node, cstr("mismatched return types")); | 773 | eprintln( |
774 | "%s:%d:%d: error: mismatched return type %s, expected %s", | ||
775 | a->file_name, node->line, node->col, node->func_body->type, | ||
776 | ret_type); | ||
623 | } | 777 | } |
624 | 778 | ||
625 | // Ensure ALL return statements match the function prototype. | 779 | // Ensure ALL return statements match the function prototype. |
@@ -1017,7 +1171,8 @@ process_file(Str path) { | |||
1017 | 1171 | ||
1018 | // Printing symbol tables. | 1172 | // Printing symbol tables. |
1019 | if (mode == PRINT_SYMTABLES) { | 1173 | if (mode == PRINT_SYMTABLES) { |
1020 | graph_symbols(analyzer.scopes, lexer_arena); | 1174 | // graph_symbols(analyzer.scopes, lexer_arena); |
1175 | graph_types(analyzer.types, lexer_arena); | ||
1021 | } | 1176 | } |
1022 | if (mode == PRINT_SEMANTIC) { | 1177 | if (mode == PRINT_SEMANTIC) { |
1023 | graph_ast(parser.nodes); | 1178 | graph_ast(parser.nodes); |