diff options
author | Bad Diode <bd@badd10de.dev> | 2022-04-19 10:41:32 -0300 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2022-04-19 10:41:32 -0300 |
commit | c69929cc501203829082b810bafe75a22947e00c (patch) | |
tree | 46dceba9e657a424626ab97f510b49d3b2b889cf | |
parent | 7cf1451ab52586ed9c4eeae1b1ec3b4ebaa83393 (diff) | |
download | bdl-c69929cc501203829082b810bafe75a22947e00c.tar.gz bdl-c69929cc501203829082b810bafe75a22947e00c.zip |
Add viz for symbol tables
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | src/main.c | 11 | ||||
-rw-r--r-- | src/semantic.c | 44 | ||||
-rw-r--r-- | src/viz.c | 60 |
4 files changed, 97 insertions, 21 deletions
@@ -63,6 +63,9 @@ viz_par: $(BIN) | |||
63 | viz_sem: $(BIN) | 63 | viz_sem: $(BIN) |
64 | $(BIN) -ps example.bdl | dot -Nfontname="Iosevka Term" -Tpng > viz.png | 64 | $(BIN) -ps example.bdl | dot -Nfontname="Iosevka Term" -Tpng > viz.png |
65 | 65 | ||
66 | viz_sym: $(BIN) | ||
67 | $(BIN) -pt example.bdl | dot -Nfontname="Iosevka Term" -Tpng > viz.png | ||
68 | |||
66 | # Remove build directory. | 69 | # Remove build directory. |
67 | clean: | 70 | clean: |
68 | rm -rf $(BUILD_DIR) | 71 | rm -rf $(BUILD_DIR) |
@@ -17,6 +17,7 @@ typedef enum ExecMode { | |||
17 | PRINT_LEX, | 17 | PRINT_LEX, |
18 | PRINT_PARSE, | 18 | PRINT_PARSE, |
19 | PRINT_SEMANTIC, | 19 | PRINT_SEMANTIC, |
20 | PRINT_SYMTABLES, | ||
20 | } ExecMode; | 21 | } ExecMode; |
21 | 22 | ||
22 | static ExecMode mode = RUN_NORMAL; | 23 | static ExecMode mode = RUN_NORMAL; |
@@ -46,12 +47,18 @@ process_source(const StringView *source, const char *file_name) { | |||
46 | check_errors(file_name); | 47 | check_errors(file_name); |
47 | if (mode == PRINT_PARSE) { | 48 | if (mode == PRINT_PARSE) { |
48 | viz_ast(roots); | 49 | viz_ast(roots); |
50 | return; | ||
49 | } | 51 | } |
50 | 52 | ||
51 | // Symbol table generation and type checking. | 53 | // Symbol table generation and type checking. |
52 | ParseTree *parse_tree = semantic_analysis(roots); | 54 | ParseTree *parse_tree = semantic_analysis(roots); |
53 | if (mode == PRINT_SEMANTIC) { | 55 | if (mode == PRINT_SEMANTIC) { |
54 | viz_ast(parse_tree->roots); | 56 | viz_ast(parse_tree->roots); |
57 | return; | ||
58 | } | ||
59 | if (mode == PRINT_SYMTABLES) { | ||
60 | viz_symtables(parse_tree->scopes); | ||
61 | return; | ||
55 | } | 62 | } |
56 | } | 63 | } |
57 | 64 | ||
@@ -116,7 +123,7 @@ print_usage(void) { | |||
116 | printf("Usage: %s [options] <filename filename ...>\n", BIN_NAME); | 123 | printf("Usage: %s [options] <filename filename ...>\n", BIN_NAME); |
117 | printf("\n"); | 124 | printf("\n"); |
118 | printf("\t-h \t\tShow usage.\n"); | 125 | printf("\t-h \t\tShow usage.\n"); |
119 | printf("\t-p [l | p | s]\tPrint mode for [l]exing, [p]arsing or [s]emantic analysis\n"); | 126 | printf("\t-p [l|p|s|t]\tPrint mode for [l]exing, [p]arsing, [s]emantic analysis, symbol [t]ables\n"); |
120 | printf("\n"); | 127 | printf("\n"); |
121 | } | 128 | } |
122 | 129 | ||
@@ -138,6 +145,8 @@ main(int argc, char *argv[]) { | |||
138 | mode = PRINT_PARSE; | 145 | mode = PRINT_PARSE; |
139 | } else if (optarg[0] == 's' && optarg[1] == '\0') { | 146 | } else if (optarg[0] == 's' && optarg[1] == '\0') { |
140 | mode = PRINT_SEMANTIC; | 147 | mode = PRINT_SEMANTIC; |
148 | } else if (optarg[0] == 't' && optarg[1] == '\0') { | ||
149 | mode = PRINT_SYMTABLES; | ||
141 | } else { | 150 | } else { |
142 | print_usage(); | 151 | print_usage(); |
143 | return EXIT_FAILURE; | 152 | return EXIT_FAILURE; |
diff --git a/src/semantic.c b/src/semantic.c index 06958b9..6c9f290 100644 --- a/src/semantic.c +++ b/src/semantic.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "hashtable.h" | 1 | #include "hashtable.h" |
2 | 2 | ||
3 | typedef struct Scope { | 3 | typedef struct Scope { |
4 | size_t id; | ||
4 | struct Scope *parent; | 5 | struct Scope *parent; |
5 | HashTable *symbols; | 6 | HashTable *symbols; |
6 | HashTable *types; | 7 | HashTable *types; |
@@ -8,8 +9,7 @@ typedef struct Scope { | |||
8 | 9 | ||
9 | typedef struct ParseTree { | 10 | typedef struct ParseTree { |
10 | Root *roots; | 11 | Root *roots; |
11 | Scope *global_scope; | 12 | Scope **scopes; |
12 | Scope *current_scope; | ||
13 | } ParseTree; | 13 | } ParseTree; |
14 | 14 | ||
15 | typedef struct Type { | 15 | typedef struct Type { |
@@ -70,6 +70,7 @@ typedef struct Symbol { | |||
70 | }; | 70 | }; |
71 | } Symbol; | 71 | } Symbol; |
72 | 72 | ||
73 | static size_t scope_gen_id = 0; | ||
73 | 74 | ||
74 | Symbol * | 75 | Symbol * |
75 | alloc_symval(Node *name, SymbolType type) { | 76 | alloc_symval(Node *name, SymbolType type) { |
@@ -110,6 +111,7 @@ bool type_eq(void *a, void *b) { | |||
110 | Scope * | 111 | Scope * |
111 | alloc_scope(Scope *parent) { | 112 | alloc_scope(Scope *parent) { |
112 | Scope *scope = malloc(sizeof(Scope)); | 113 | Scope *scope = malloc(sizeof(Scope)); |
114 | scope->id = scope_gen_id++; | ||
113 | scope->parent = parent; | 115 | scope->parent = parent; |
114 | scope->symbols = ht_init(sym_hash, sym_eq); | 116 | scope->symbols = ht_init(sym_hash, sym_eq); |
115 | scope->types = ht_init(type_hash, type_eq); | 117 | scope->types = ht_init(type_hash, type_eq); |
@@ -216,7 +218,7 @@ find_symbol(Scope *scope, Node *node) { | |||
216 | } | 218 | } |
217 | 219 | ||
218 | bool | 220 | bool |
219 | resolve_type(Scope *scope, Node *node) { | 221 | resolve_type(ParseTree *ast, Scope *scope, Node *node) { |
220 | if (node->expr_type != NULL) { | 222 | if (node->expr_type != NULL) { |
221 | return true; | 223 | return true; |
222 | } | 224 | } |
@@ -224,7 +226,7 @@ resolve_type(Scope *scope, Node *node) { | |||
224 | case NODE_BUILTIN: { | 226 | case NODE_BUILTIN: { |
225 | for (size_t i = 0; i < array_size(node->builtin.args); ++i) { | 227 | for (size_t i = 0; i < array_size(node->builtin.args); ++i) { |
226 | Node *arg = node->builtin.args[i]; | 228 | Node *arg = node->builtin.args[i]; |
227 | if (!resolve_type(scope, arg)) { | 229 | if (!resolve_type(ast, scope, arg)) { |
228 | return false; | 230 | return false; |
229 | } | 231 | } |
230 | } | 232 | } |
@@ -311,6 +313,7 @@ resolve_type(Scope *scope, Node *node) { | |||
311 | case NODE_FUN: { | 313 | case NODE_FUN: { |
312 | // Fill up new scope with parameters | 314 | // Fill up new scope with parameters |
313 | scope = alloc_scope(scope); | 315 | scope = alloc_scope(scope); |
316 | array_push(ast->scopes, scope); | ||
314 | 317 | ||
315 | // Parameters. | 318 | // Parameters. |
316 | for (size_t i = 0; i < array_size(node->fun.param_names); ++i) { | 319 | for (size_t i = 0; i < array_size(node->fun.param_names); ++i) { |
@@ -328,14 +331,14 @@ resolve_type(Scope *scope, Node *node) { | |||
328 | if (body->type == NODE_BLOCK) { | 331 | if (body->type == NODE_BLOCK) { |
329 | for (size_t i = 0; i < array_size(body->block.expr); ++i) { | 332 | for (size_t i = 0; i < array_size(body->block.expr); ++i) { |
330 | Node *expr = body->block.expr[i]; | 333 | Node *expr = body->block.expr[i]; |
331 | if (!resolve_type(scope, expr)) { | 334 | if (!resolve_type(ast, scope, expr)) { |
332 | return false; | 335 | return false; |
333 | } | 336 | } |
334 | } | 337 | } |
335 | Node *last_expr = body->block.expr[array_size(body->block.expr) - 1]; | 338 | Node *last_expr = body->block.expr[array_size(body->block.expr) - 1]; |
336 | node->expr_type = last_expr->expr_type; | 339 | node->expr_type = last_expr->expr_type; |
337 | } else { | 340 | } else { |
338 | if (!resolve_type(scope, body)) { | 341 | if (!resolve_type(ast, scope, body)) { |
339 | return false; | 342 | return false; |
340 | } | 343 | } |
341 | } | 344 | } |
@@ -350,9 +353,10 @@ resolve_type(Scope *scope, Node *node) { | |||
350 | } break; | 353 | } break; |
351 | case NODE_BLOCK: { | 354 | case NODE_BLOCK: { |
352 | scope = alloc_scope(scope); | 355 | scope = alloc_scope(scope); |
356 | array_push(ast->scopes, scope); | ||
353 | for (size_t i = 0; i < array_size(node->block.expr); ++i) { | 357 | for (size_t i = 0; i < array_size(node->block.expr); ++i) { |
354 | Node *expr = node->block.expr[i]; | 358 | Node *expr = node->block.expr[i]; |
355 | if (!resolve_type(scope, expr)) { | 359 | if (!resolve_type(ast, scope, expr)) { |
356 | return false; | 360 | return false; |
357 | } | 361 | } |
358 | } | 362 | } |
@@ -360,16 +364,16 @@ resolve_type(Scope *scope, Node *node) { | |||
360 | node->expr_type = last_expr->expr_type; | 364 | node->expr_type = last_expr->expr_type; |
361 | } break; | 365 | } break; |
362 | case NODE_IF: { | 366 | case NODE_IF: { |
363 | if (!resolve_type(scope, node->ifexpr.cond)) { | 367 | if (!resolve_type(ast, scope, node->ifexpr.cond)) { |
364 | return false; | 368 | return false; |
365 | } | 369 | } |
366 | if (!resolve_type(scope, node->ifexpr.expr_true)) { | 370 | if (!resolve_type(ast, scope, node->ifexpr.expr_true)) { |
367 | return false; | 371 | return false; |
368 | } | 372 | } |
369 | Type *type_true = node->ifexpr.expr_true->expr_type; | 373 | Type *type_true = node->ifexpr.expr_true->expr_type; |
370 | node->expr_type = type_true; | 374 | node->expr_type = type_true; |
371 | if (node->ifexpr.expr_false != NULL) { | 375 | if (node->ifexpr.expr_false != NULL) { |
372 | if (!resolve_type(scope, node->ifexpr.expr_false)) { | 376 | if (!resolve_type(ast, scope, node->ifexpr.expr_false)) { |
373 | return false; | 377 | return false; |
374 | } | 378 | } |
375 | } | 379 | } |
@@ -396,10 +400,10 @@ resolve_type(Scope *scope, Node *node) { | |||
396 | } break; | 400 | } break; |
397 | case NODE_SET: { | 401 | case NODE_SET: { |
398 | node->expr_type = &default_types[TYPE_VOID]; | 402 | node->expr_type = &default_types[TYPE_VOID]; |
399 | if (!resolve_type(scope, node->set.symbol)) { | 403 | if (!resolve_type(ast, scope, node->set.symbol)) { |
400 | return false; | 404 | return false; |
401 | } | 405 | } |
402 | if (!resolve_type(scope, node->set.value)) { | 406 | if (!resolve_type(ast, scope, node->set.value)) { |
403 | return false; | 407 | return false; |
404 | } | 408 | } |
405 | Node *symbol = node->set.symbol; | 409 | Node *symbol = node->set.symbol; |
@@ -426,7 +430,7 @@ resolve_type(Scope *scope, Node *node) { | |||
426 | 430 | ||
427 | node->expr_type = &default_types[TYPE_VOID]; | 431 | node->expr_type = &default_types[TYPE_VOID]; |
428 | // TODO: type inference from right side when not annotated? | 432 | // TODO: type inference from right side when not annotated? |
429 | if (!resolve_type(scope, node->def.value)) { | 433 | if (!resolve_type(ast, scope, node->def.value)) { |
430 | return false; | 434 | return false; |
431 | } | 435 | } |
432 | Node *symbol = node->def.symbol; | 436 | Node *symbol = node->def.symbol; |
@@ -455,7 +459,7 @@ resolve_type(Scope *scope, Node *node) { | |||
455 | } break; | 459 | } break; |
456 | case NODE_FUNCALL: { | 460 | case NODE_FUNCALL: { |
457 | Symbol *val = find_symbol(scope, node->funcall.name); | 461 | Symbol *val = find_symbol(scope, node->funcall.name); |
458 | if (!resolve_type(scope, node->funcall.name)) { | 462 | if (!resolve_type(ast, scope, node->funcall.name)) { |
459 | return false; | 463 | return false; |
460 | } | 464 | } |
461 | if (val->type != SYMBOL_FUN) { | 465 | if (val->type != SYMBOL_FUN) { |
@@ -470,7 +474,7 @@ resolve_type(Scope *scope, Node *node) { | |||
470 | node->expr_type = node->funcall.name->expr_type; | 474 | node->expr_type = node->funcall.name->expr_type; |
471 | for (size_t i = 0; i < array_size(node->funcall.args); ++i) { | 475 | for (size_t i = 0; i < array_size(node->funcall.args); ++i) { |
472 | Node *arg = node->funcall.args[i]; | 476 | Node *arg = node->funcall.args[i]; |
473 | if (!resolve_type(scope, arg)) { | 477 | if (!resolve_type(ast, scope, arg)) { |
474 | return false; | 478 | return false; |
475 | } | 479 | } |
476 | Node *expected = val->fun.param_types[i]; | 480 | Node *expected = val->fun.param_types[i]; |
@@ -490,18 +494,18 @@ ParseTree * | |||
490 | semantic_analysis(Root *roots) { | 494 | semantic_analysis(Root *roots) { |
491 | ParseTree *parse_tree = malloc(sizeof(ParseTree)); | 495 | ParseTree *parse_tree = malloc(sizeof(ParseTree)); |
492 | parse_tree->roots = roots; | 496 | parse_tree->roots = roots; |
493 | parse_tree->global_scope = alloc_scope(NULL); | 497 | array_init(parse_tree->scopes, 0); |
494 | parse_tree->current_scope = parse_tree->global_scope; | 498 | Scope *scope = alloc_scope(NULL); |
499 | array_push(parse_tree->scopes, scope); | ||
495 | 500 | ||
496 | // Fill global scope with default types. | 501 | // Fill global scope with default types. |
497 | HashTable *types = parse_tree->global_scope->types; | 502 | HashTable *types = scope->types; |
498 | for (size_t i = 0; i < sizeof(default_types)/sizeof(Type); ++i) { | 503 | for (size_t i = 0; i < sizeof(default_types)/sizeof(Type); ++i) { |
499 | Type *type = &default_types[i]; | 504 | Type *type = &default_types[i]; |
500 | ht_insert(types, &type->name, type); | 505 | ht_insert(types, &type->name, type); |
501 | } | 506 | } |
502 | 507 | ||
503 | // Fill up global function symbols. | 508 | // Fill up global function symbols. |
504 | Scope *scope = parse_tree->global_scope; | ||
505 | for (size_t i = 0; i < array_size(parse_tree->roots); ++i) { | 509 | for (size_t i = 0; i < array_size(parse_tree->roots); ++i) { |
506 | Node *root = parse_tree->roots[i]; | 510 | Node *root = parse_tree->roots[i]; |
507 | if (root->type == NODE_FUN) { | 511 | if (root->type == NODE_FUN) { |
@@ -518,7 +522,7 @@ semantic_analysis(Root *roots) { | |||
518 | for (size_t i = 0; i < array_size(parse_tree->roots); ++i) { | 522 | for (size_t i = 0; i < array_size(parse_tree->roots); ++i) { |
519 | // Fill up symbol tables in proper scope and resolve type of expression | 523 | // Fill up symbol tables in proper scope and resolve type of expression |
520 | // for all elements. | 524 | // for all elements. |
521 | if (!resolve_type(scope, parse_tree->roots[i])) { | 525 | if (!resolve_type(parse_tree, scope, parse_tree->roots[i])) { |
522 | return NULL; | 526 | return NULL; |
523 | } | 527 | } |
524 | } | 528 | } |
@@ -170,3 +170,63 @@ viz_ast(Root *roots) { | |||
170 | printf("}\n"); | 170 | printf("}\n"); |
171 | } | 171 | } |
172 | 172 | ||
173 | void | ||
174 | viz_symtables(Scope **scopes) { | ||
175 | printf("digraph symtables {\n"); | ||
176 | printf("rankdir=RL;\n"); | ||
177 | printf("ranksep=\"0.95 equally\";\n"); | ||
178 | printf("nodesep=\"0.5 equally\";\n"); | ||
179 | printf("overlap=scale;\n"); | ||
180 | for (size_t i = 0; i < array_size(scopes); ++i) { | ||
181 | Scope *scope = scopes[i]; | ||
182 | if (array_size(scope->symbols->pairs) == 0) { | ||
183 | continue; | ||
184 | } | ||
185 | printf("%zu [shape=none,label=<", scope->id); | ||
186 | printf("<table border=\"1\" cellborder=\"0\">\n"); | ||
187 | printf("<tr><td>NAME</td><td>KIND</td><td>TYPE</td></tr>"); | ||
188 | for (size_t j = 0; j < array_cap(scope->symbols->pairs); ++j) { | ||
189 | HashTablePair pair = scope->symbols->pairs[j]; | ||
190 | if (pair.key == NULL) { | ||
191 | continue; | ||
192 | } | ||
193 | Symbol *sym = pair.value; | ||
194 | printf("<tr>"); | ||
195 | printf("<td>"); | ||
196 | print_node(sym->name); | ||
197 | printf("</td>"); | ||
198 | switch (sym->type) { | ||
199 | case SYMBOL_VAR: { | ||
200 | printf("<td>var</td>"); | ||
201 | printf("<td>"); | ||
202 | print_node(sym->var.type); | ||
203 | printf("</td>"); | ||
204 | } break; | ||
205 | case SYMBOL_FUN: { | ||
206 | printf("<td>fun</td>"); | ||
207 | printf("<td>"); | ||
208 | printf("("); | ||
209 | size_t n_params = array_size(sym->fun.param_types); | ||
210 | for (size_t k = 0; k < n_params; ++k) { | ||
211 | print_node(sym->fun.param_types[k]); | ||
212 | if (k < n_params - 1) { | ||
213 | printf(" "); | ||
214 | } | ||
215 | } | ||
216 | printf(")"); | ||
217 | printf(":"); | ||
218 | print_node(sym->fun.return_type); | ||
219 | printf("</td>"); | ||
220 | } break; | ||
221 | } | ||
222 | printf("</tr>\n"); | ||
223 | } | ||
224 | printf("</table>\n"); | ||
225 | printf(">];\n"); | ||
226 | |||
227 | if (scope->parent != NULL) { | ||
228 | printf("%zu -> %zu\n", scope->id, scope->parent->id); | ||
229 | } | ||
230 | } | ||
231 | printf("}\n"); | ||
232 | } | ||