From 056e2f927dabbf16c1f0a1628dd973b66c51d791 Mon Sep 17 00:00:00 2001 From: Samuel Huang Date: Wed, 8 Nov 2023 12:42:56 -0500 Subject: [PATCH] Introduce resolver * Improve bminor.c readability * Improve readability of Makefile * Improve error handling in scanner and parser * Fix assignment expression parsing issue * Fix an issue in decl_print() * Modify printer to directly print to stdout * Change printer_test() in runtest.sh to use diff * Add .tmp for testing output to .gitignore * Create test cases for resolver * Add test for resolver in runtest.sh * Create symbol.c and symbol.h * Create scope.c and scope.h * Create resolver.c and resolver.h * Implement decl_resolve() * Implement stmt_resolve() * Implement param_list_resolve() * Implement expr_resolve() * Add rules for resolver in Makefile * Add --resolve option in bminor.c * Fix no prototype warning in hash_table.c --- .gitignore | 3 +- Makefile | 64 +++++++++++++------- bminor.c | 32 +++++----- decl.c | 49 +++++++++++++--- decl.h | 1 + expr.c | 24 ++++++++ expr.h | 4 ++ grammar.y | 6 +- hash_table.c | 8 +-- lex.yy.l | 16 ++++- param_list.c | 22 ++++++- param_list.h | 3 +- parser.c | 15 ++++- printer.c | 116 ------------------------------------- printer.h | 1 - resolver.c | 10 ++++ resolver.h | 3 + runtest.sh | 18 +++--- scope.c | 64 ++++++++++++++++++++ scope.h | 27 +++++++++ stmt.c | 39 ++++++++++++- stmt.h | 2 +- symbol.c | 12 ++++ symbol.h | 13 +++-- test/printer/good11.bminor | 6 ++ test/resolver/bad0.bminor | 5 ++ test/resolver/bad1.bminor | 10 ++++ test/resolver/bad2.bminor | 10 ++++ test/resolver/bad3.bminor | 17 ++++++ test/resolver/bad4.bminor | 13 +++++ test/resolver/bad5.bminor | 16 +++++ test/resolver/bad6.bminor | 16 +++++ test/resolver/bad7.bminor | 21 +++++++ test/resolver/bad8.bminor | 21 +++++++ test/resolver/bad9.bminor | 18 ++++++ test/resolver/good0.bminor | 12 ++++ test/resolver/good1.bminor | 9 +++ test/resolver/good2.bminor | 9 +++ test/resolver/good3.bminor | 16 +++++ test/resolver/good4.bminor | 13 +++++ test/resolver/good5.bminor | 16 +++++ test/resolver/good6.bminor | 16 +++++ test/resolver/good7.bminor | 21 +++++++ test/resolver/good8.bminor | 21 +++++++ test/resolver/good9.bminor | 16 +++++ 45 files changed, 658 insertions(+), 196 deletions(-) create mode 100644 resolver.c create mode 100644 resolver.h create mode 100644 scope.c create mode 100644 scope.h create mode 100644 symbol.c create mode 100644 test/printer/good11.bminor create mode 100644 test/resolver/bad0.bminor create mode 100644 test/resolver/bad1.bminor create mode 100644 test/resolver/bad2.bminor create mode 100644 test/resolver/bad3.bminor create mode 100644 test/resolver/bad4.bminor create mode 100644 test/resolver/bad5.bminor create mode 100644 test/resolver/bad6.bminor create mode 100644 test/resolver/bad7.bminor create mode 100644 test/resolver/bad8.bminor create mode 100644 test/resolver/bad9.bminor create mode 100644 test/resolver/good0.bminor create mode 100644 test/resolver/good1.bminor create mode 100644 test/resolver/good2.bminor create mode 100644 test/resolver/good3.bminor create mode 100644 test/resolver/good4.bminor create mode 100644 test/resolver/good5.bminor create mode 100644 test/resolver/good6.bminor create mode 100644 test/resolver/good7.bminor create mode 100644 test/resolver/good8.bminor create mode 100644 test/resolver/good9.bminor diff --git a/.gitignore b/.gitignore index 155d5ff..d32e376 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ token.h grammar.tab.c grammar.output -test/**/*.out \ No newline at end of file +test/**/*.out +test/**/*.tmp diff --git a/Makefile b/Makefile index 8994218..2e7736c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,9 @@ -bminor: bminor.c encoder.o scanner.o lex.yy.o parser.o grammar.tab.o printer.o decl.o stmt.o expr.o type.o param_list.o +bminor: bminor.c \ + encoder.o \ + scanner.o lex.yy.o \ + parser.o grammar.tab.o \ + printer.o decl.o stmt.o expr.o type.o param_list.o \ + resolver.o symbol.o scope.o hash_table.o gcc $^ -o $@ encoder.o: encoder.c encoder.h @@ -6,43 +11,55 @@ encoder.o: encoder.c encoder.h # Scanner lex.yy.c: lex.yy.l - flex $^ + flex $< lex.yy.o: lex.yy.c - gcc -c $^ -o $@ + gcc -c $< -o $@ scanner.o: scanner.c token.h gcc -c $< -o $@ # Parser -parser.o: parser.c - gcc -c $^ -o $@ +parser.o: parser.c printer.h + gcc -c $< -o $@ grammar.tab.c token.h: grammar.y lex.yy.c - #bison --defines=token.h $< - bison -v --defines=token.h $< + bison --defines=token.h $< grammar.tab.o: grammar.tab.c gcc -c $^ -o $@ # Printer -printer.o: printer.c - gcc -c $^ -o $@ +printer.o: printer.c printer.h + gcc -c $< -o $@ -decl.o: decl.c - gcc -c $^ -o $@ +decl.o: decl.c decl.h + gcc -c $< -o $@ -stmt.o: stmt.c - gcc -c $^ -o $@ +stmt.o: stmt.c stmt.h + gcc -c $< -o $@ -expr.o: expr.c - gcc -c $^ -o $@ +expr.o: expr.c expr.h + gcc -c $< -o $@ -type.o: type.c - gcc -c $^ -o $@ +type.o: type.c type.h + gcc -c $< -o $@ -param_list.o: param_list.c - gcc -c $^ -o $@ +param_list.o: param_list.c param_list.h + gcc -c $< -o $@ + +# Resolver +resolver.o: resolver.c resolver.h + gcc -c $< -o $@ + +symbol.o: symbol.c symbol.h + gcc -c $< -o $@ + +scope.o: scope.c scope.h + gcc -c $< -o $@ + +hash_table.o: hash_table.c hash_table.h + gcc -c $< -o $@ # Tests test-encoder: @@ -57,9 +74,16 @@ test-parser: test-printer: ./runtest.sh printer +test-resolver: + ./runtest.sh resolver + +# Cleanup +clean-test: + rm -f ./test/*/*.bminor.out + clean: + rm -f ./test/*/*.bminor.out rm -f lex.yy.c rm -f token.h grammar.tab.c grammar.output rm -f *.o - rm -f ./test/*/*.bminor.out rm -f bminor diff --git a/bminor.c b/bminor.c index 3d801b7..a41a669 100644 --- a/bminor.c +++ b/bminor.c @@ -6,6 +6,7 @@ #include "scanner.h" #include "parser.h" #include "printer.h" +#include "resolver.h" void usage(int exit_code) { @@ -21,12 +22,10 @@ int main(int argc, char* argv[]) { case 1: usage(EXIT_FAILURE); - break; case 2: if (strcmp(argv[1], "--help") == 0) usage(EXIT_SUCCESS); else usage(EXIT_FAILURE); - break; case 3: option = argv[1]; filename = argv[2]; @@ -34,7 +33,6 @@ int main(int argc, char* argv[]) default: fprintf(stderr, "Too many arguments.\n"); usage(EXIT_FAILURE); - break; } // Open input file @@ -47,31 +45,27 @@ int main(int argc, char* argv[]) // Perform the requested operation if (strcmp(option, "--encode") == 0) - { - if (decode(fp) == 0) - return EXIT_SUCCESS; - } + return decode(fp) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; else if (strcmp(option, "--scan") == 0) - { - if (scan(fp) == 0) - return EXIT_SUCCESS; - } + return scan(fp) == 0 ? EXIT_SUCCESS : EXIT_FAILURE; else if (strcmp(option, "--parse") == 0) + return parse(fp) != NULL ? EXIT_SUCCESS : EXIT_FAILURE; + else if (strcmp(option, "--print") == 0) { - if (parse(fp) != NULL) - return EXIT_SUCCESS; + struct decl* d = parse(fp); + if (d == NULL) return EXIT_FAILURE; + print(d); + return EXIT_SUCCESS; } - else if (strcmp(option, "--print") == 0) + else if (strcmp(option, "--resolve") == 0) { - return print_and_compare(filename, fp); + struct decl* d = parse(fp); + if (d == NULL) return EXIT_FAILURE; + return resolve(d); } else { fprintf(stderr, "Unknown option '%s'\n", option); usage(EXIT_FAILURE); } - - //Error message - fprintf(stderr, "Failed to %s file %s\n", option + 2, filename); - return EXIT_FAILURE; } diff --git a/decl.c b/decl.c index e8f9c86..96f2d15 100644 --- a/decl.c +++ b/decl.c @@ -2,6 +2,8 @@ #include #include "decl.h" #include "type.h" +#include "symbol.h" +#include "scope.h" struct decl* decl_create(char* name, struct type* type, struct expr* value, struct stmt* code, struct decl* next) { @@ -34,9 +36,7 @@ void decl_print(struct decl* d, int indent) printf("}"); } else - { expr_print(d->value); - } } if (d->code) @@ -45,14 +45,49 @@ void decl_print(struct decl* d, int indent) stmt_print(d->code, indent + 1); for (i = 0; i < indent; i++) printf("\t"); - printf("}"); + printf("}\n"); + } + else + printf(";\n"); + + decl_print(d->next, indent); +} + +void decl_resolve(struct decl* d) +{ + if (!d) return; + + if (scope_lookup_current(d->name)) + { + fprintf(stderr, "error: symbol '%s' already declared\n", d->name); + res_errors++; + } + symbol_t kind; + int which; + if (cur_scope->level > 0) + { + kind = SYMBOL_LOCAL; + which = cur_local++; } else { - // Functions don't end with semicolon - printf(";"); + kind = SYMBOL_GLOBAL; + which = cur_global++; } + d->symbol = symbol_create(kind, d->type, d->name, which); + scope_bind(d->name, d->symbol); - printf("\n"); - decl_print(d->next, indent); + if (d->type->kind == TYPE_ARRAY) + expr_resolve(d->type->size); + + expr_resolve(d->value); + if (d->code) + { + scope_enter(); + param_list_resolve(d->type->params); + stmt_resolve(d->code); + scope_exit(); + } + + decl_resolve(d->next); } \ No newline at end of file diff --git a/decl.h b/decl.h index b7b5f0b..60be736 100755 --- a/decl.h +++ b/decl.h @@ -17,5 +17,6 @@ struct decl struct decl* decl_create(char* name, struct type* type, struct expr* value, struct stmt* code, struct decl* next); void decl_print(struct decl* d, int indent); +void decl_resolve(struct decl* d); #endif diff --git a/expr.c b/expr.c index 378d504..27b7b54 100644 --- a/expr.c +++ b/expr.c @@ -2,6 +2,7 @@ #include #include "encoder.h" #include "expr.h" +#include "scope.h" /* Creating binary nodes by default */ @@ -291,3 +292,26 @@ void expr_print(const struct expr* e) break; } } + +/* Resolving the expression nodes */ + +void expr_resolve(struct expr* e) +{ + if (!e) return; + if (e->kind == EXPR_NAME) + { + e->symbol = scope_lookup(e->name); + if (e->symbol) + resolve_msg(e->symbol); + else + { + printf("Resolve Error | Undefined symbol '%s'\n", e->name); + res_errors++; + } + } + else + { + expr_resolve(e->left); + expr_resolve(e->right); + } +} diff --git a/expr.h b/expr.h index e0afaf6..773467c 100755 --- a/expr.h +++ b/expr.h @@ -62,6 +62,7 @@ struct expr const char* string_literal; }; +/* Creating non-leaf nodes */ struct expr* expr_create(expr_t kind, struct expr* left, struct expr* right); struct expr* expr_create_unary(expr_t kind, struct expr* operand); @@ -76,4 +77,7 @@ struct expr* expr_create_string_literal(const char* str); /* Printing the expressions */ void expr_print(const struct expr* e); +/* Resolving the expressions */ +void expr_resolve(struct expr* e); + #endif diff --git a/grammar.y b/grammar.y index 54bac93..6523199 100644 --- a/grammar.y +++ b/grammar.y @@ -8,12 +8,14 @@ extern int yylineno; extern char *yytext; void yyerror(const char *s) { - fprintf(stderr, "Error: %s at line %d near '%s'\n", s, yylineno, yytext); + fprintf(stderr, "Parse Error @ %d | %s\n", yylineno, s); } struct decl *root = NULL; %} +%error-verbose + /* Keywords */ %token TOKEN_ARRAY %token TOKEN_AUTO @@ -216,7 +218,7 @@ opt_expr : /* epsilon */ { $$ = NULL; } expr : expr1 ; -expr1 : lval TOKEN_ASSIGN expr1 { expr_create(EXPR_ASSIGN, $1, $3); } +expr1 : lval TOKEN_ASSIGN expr1 { $$ = expr_create(EXPR_ASSIGN, $1, $3); } | expr2 ; diff --git a/hash_table.c b/hash_table.c index 99a1649..9ad2663 100644 --- a/hash_table.c +++ b/hash_table.c @@ -309,15 +309,13 @@ acceptable. Do NOT use for cryptographic purposes. -------------------------------------------------------------------- */ -static ub4 jenkins_hash(k, length, initval) - register const ub1 *k; /* the key */ - register ub4 length; /* the length of the key */ - register ub4 initval; /* the previous hash, or an arbitrary value */ +static ub4 jenkins_hash(const ub1 *k, ub4 length, ub4 initval) { register ub4 a, b, c, len; /* Set up the internal state */ len = length; a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ - c = initval; /* the previous hash value *//*---------------------------------------- handle most of the key */ + c = initval; /* the previous hash value */ + /*---------------------------------------- handle most of the key */ while(len >= 12) { a += (k[0] + ((ub4) k[1] << 8) + ((ub4) k[2] << 16) + ((ub4) k[3] << 24)); b += (k[4] + ((ub4) k[5] << 8) + ((ub4) k[6] << 16) + ((ub4) k[7] << 24)); diff --git a/lex.yy.l b/lex.yy.l index 7faa2ff..98181c8 100644 --- a/lex.yy.l +++ b/lex.yy.l @@ -7,6 +7,8 @@ static int returned_eof = 0; %} +%option yylineno + %% /* Comments */ @@ -87,7 +89,11 @@ while { printf("WHILE\n"); return TOKEN_WHILE; } yylval.c = s[0]; return TOKEN_CHAR_LITERAL; } else { - printf("ERROR: Invalid character literal: %s\n", yytext); + fprintf(stderr, "Scan Error @ %d | Invalid character literal ", yylineno); + if (*yytext == '\'') + fprintf(stderr, "%s\n", yytext); + else + fprintf(stderr, "'%s'\n", yytext); return TOKEN_ERROR; } } @@ -135,7 +141,11 @@ while { printf("WHILE\n"); return TOKEN_WHILE; } yylval.s = strdup(s); return TOKEN_STRING_LITERAL; } else { - printf("ERROR: Invalid string literal: %s\n", yytext); + fprintf(stderr, "Scan Error @ %d | invalid string literal ", yylineno); + if (*yytext == '\'') + fprintf(stderr, "%s\n", yytext); + else + fprintf(stderr, "'%s'\n", yytext); return TOKEN_ERROR; } } @@ -154,7 +164,7 @@ while { printf("WHILE\n"); return TOKEN_WHILE; } } . { - printf("ERROR: Invalid character %s\n", yytext); + fprintf(stderr, "Scan Error @ %d | invalid character '%s'\n", yylineno, yytext); return TOKEN_ERROR; } %% diff --git a/param_list.c b/param_list.c index 274429b..c7f2a07 100644 --- a/param_list.c +++ b/param_list.c @@ -1,6 +1,7 @@ #include #include #include "param_list.h" +#include "scope.h" struct param_list* param_list_create(char* name, struct type* type, struct param_list* next) { @@ -21,4 +22,23 @@ void param_list_print(struct param_list* p) printf(", "); param_list_print(p->next); } -} \ No newline at end of file +} + +void param_list_resolve(struct param_list* p) +{ + if (!p) + { + cur_param = 0; + return; + } + if (scope_lookup_current(p->name)) + { + fprintf(stderr, "Resolve Error | duplicate parameter '%s'\n", p->name); + res_errors++; + } + struct symbol* s = symbol_create(SYMBOL_PARAM, p->type, p->name, cur_param++); + scope_bind(p->name, s); + if (p->type->kind == TYPE_ARRAY) + expr_resolve(p->type->size); + param_list_resolve(p->next); +} diff --git a/param_list.h b/param_list.h index 67f8ffb..4037241 100755 --- a/param_list.h +++ b/param_list.h @@ -11,6 +11,7 @@ struct param_list }; struct param_list* param_list_create(char* name, struct type* type, struct param_list* next); -void param_list_print(struct param_list* a); +void param_list_print(struct param_list* p); +void param_list_resolve(struct param_list* p); #endif diff --git a/parser.c b/parser.c index d3389be..afb3043 100644 --- a/parser.c +++ b/parser.c @@ -1,15 +1,26 @@ #include +#include #include "parser.h" extern int yyparse(); -extern void yyrestart(FILE*); +extern FILE* yyin; extern int yydebug; extern struct decl* root; struct decl* parse(FILE* fp) { yydebug = 0; - yyrestart(fp); + yyin = fp; + + fflush(stdout); + int original = dup(fileno(stdout)); + freopen("/dev/null", "w", stdout); // Suppress output + yyparse(); + + fflush(stdout); + dup2(original, fileno(stdout)); + close(original); + return root; } diff --git a/printer.c b/printer.c index 215d83f..9c7e442 100644 --- a/printer.c +++ b/printer.c @@ -1,122 +1,6 @@ -#include -#include -#include -#include #include "printer.h" -#include "parser.h" void print(struct decl* d) { decl_print(d, 0); } - -int fprint(struct decl* d, FILE* fp) -{ - int fd = fileno(fp); - int saved_stdout = dup(STDOUT_FILENO); - if (dup2(fd, STDOUT_FILENO) == -1) - { - fprintf(stderr, "Failed to redirect stdout to file\n"); - return 1; - } - print(d); - dup2(saved_stdout, STDOUT_FILENO); - close(saved_stdout); - return 0; -} - -int print_and_compare(char* filename, FILE* fp) -{ - // FIXME: "syntax error at line 1 near ''" when printing the second file - // TODO: Suppress output from scanner and printer - // Parse the input file - struct decl* d1 = parse(fp); - if (d1 == NULL) - { - printf("Failed to parse file %s before printing\n", filename); - return EXIT_FAILURE; - } - printf("\n======== Printing started ========\n\n"); - print(d1); - - // Construct filenames - char* ext_out = ".out"; - char* ext_tmp = ".tmp"; - char* output = malloc(strlen(filename) + strlen(ext_out) + 1); - char* temp = malloc(strlen(filename) + strlen(ext_tmp) + 1); - if (output == NULL || temp == NULL) - { - fprintf(stderr, "Failed to allocate memory for output filename\n"); - return EXIT_FAILURE; - } - strcpy(output, filename); - strcat(output, ext_out); - strcpy(temp, filename); - strcat(temp, ext_tmp); - - // Open output file for printing - FILE* fp_out = fopen(output, "w+"); - if (fp_out == NULL) - { - fprintf(stderr, "Failed to open file %s for printing\n", output); - return EXIT_FAILURE; - } - - // Print the AST to the output file - if (fprint(d1, fp_out)) - { - fprintf(stderr, "Failed to print file %s\n", filename); - fclose(fp_out); - return EXIT_FAILURE; - } - else - { - printf("Successfully printed to file %s\n", output); - } - - // Parse the output file - struct decl* d2 = parse(fp_out); - fclose(fp_out); - if (d2 == NULL) - { - fprintf(stderr, "Failed to parse file %s after printing\n", output); - return EXIT_FAILURE; - } - - // Open temp file for printing - FILE* fp_tmp = fopen(temp, "w"); - if (fp_tmp == NULL) - { - fprintf(stderr, "Failed to open file %s for printing\n", temp); - return EXIT_FAILURE; - } - - // Print the AST to the temp file - if (fprint(d2, fp_tmp)) - { - fprintf(stderr, "Failed to print to file %s\n", temp); - return EXIT_FAILURE; - } - else - { - printf("Successfully printed to file %s\n", temp); - } - - // Compare the output and temp files - char* command = malloc(strlen(output) + strlen(temp) + 7); - sprintf(command, "diff %s %s", output, temp); - int ret = system(command); - switch (ret) - { - case -1: - fprintf(stderr, "Failed to execute diff command\n"); - return EXIT_FAILURE; - case 0: - printf("Successfully printed and verified\n"); - remove(temp); - return EXIT_SUCCESS; - default: - fprintf(stderr, "Successfully printed but failed to verify\n"); - return EXIT_FAILURE; - } -} diff --git a/printer.h b/printer.h index 49aa7a0..e283961 100644 --- a/printer.h +++ b/printer.h @@ -1,4 +1,3 @@ #include "decl.h" void print(struct decl* d); -int print_and_compare(char* filename, FILE* fp); diff --git a/resolver.c b/resolver.c new file mode 100644 index 0000000..465edd7 --- /dev/null +++ b/resolver.c @@ -0,0 +1,10 @@ +#include "resolver.h" +#include "scope.h" + +int resolve(struct decl* root) +{ + scope_enter(); + decl_resolve(root); + scope_exit(); + return res_errors; +} diff --git a/resolver.h b/resolver.h new file mode 100644 index 0000000..e82dad0 --- /dev/null +++ b/resolver.h @@ -0,0 +1,3 @@ +#include "decl.h" + +int resolve(struct decl* root); diff --git a/runtest.sh b/runtest.sh index b41acc2..7421073 100755 --- a/runtest.sh +++ b/runtest.sh @@ -11,13 +11,15 @@ module="$1" printer_test () { for testfile in ./test/printer/good*.bminor; do - if bminor --print "$testfile" 2>/dev/null; then + if bminor --print "$testfile" > "$testfile.out" && + bminor --print "$testfile.out" > "$testfile.tmp" && + diff "$testfile.out" "$testfile.tmp" > /dev/null; then echo "$testfile success (as expected)" + rm "$testfile.tmp" else echo "$testfile failure (INCORRECT)" fi done - exit 0 } case $module in @@ -32,6 +34,10 @@ case $module in ;; "printer") printer_test + exit + ;; + "resolver") + command="resolve" ;; *) echo "Unknown module: $module" @@ -47,14 +53,6 @@ for testfile in ./test/"$module"/good*.bminor; do fi done -for testfile in ./test/"$module"/good*.bminor; do - if bminor --"$command" "$testfile" > "$testfile.out"; then - echo "$testfile success (as expected)" - else - echo "$testfile failure (INCORRECT)" - fi -done - for testfile in ./test/"$module"/bad*.bminor; do if bminor --"$command" "$testfile" > "$testfile.out"; then echo "$testfile success (INCORRECT)" diff --git a/scope.c b/scope.c new file mode 100644 index 0000000..4a10eb1 --- /dev/null +++ b/scope.c @@ -0,0 +1,64 @@ +#include +#include +#include "scope.h" + +int res_errors = 0; +struct scope* cur_scope = NULL; +int cur_global, cur_param, cur_local = 0; + +void scope_enter() +{ + struct scope* s = malloc(sizeof(*s)); + s->parent = cur_scope; + s->table = hash_table_create(0, 0); + s->level = cur_scope ? cur_scope->level + 1 : 0; + cur_scope = s; +} + +void scope_exit() +{ + if (!cur_scope) return; + hash_table_delete(cur_scope->table); + struct scope* tmp = cur_scope; + cur_scope = cur_scope->parent; + free(tmp); +} + +void scope_bind(const char* name, struct symbol* s) +{ + hash_table_insert(cur_scope->table, name, s); +} + +struct symbol* scope_lookup(const char* name) +{ + struct scope* tmp = cur_scope; + while (tmp) + { + struct symbol* s = hash_table_lookup(tmp->table, name); + if (s) return s; + tmp = tmp->parent; + } + return NULL; +} + +struct symbol* scope_lookup_current(const char* name) +{ + return hash_table_lookup(cur_scope->table, name); +} + +void resolve_msg(struct symbol* s) +{ + printf("%s -> ", s->name); + switch (s->kind) + { + case SYMBOL_LOCAL: + printf("local @ %d\n", s->which); + break; + case SYMBOL_PARAM: + printf("param @ %d\n", s->which); + break; + case SYMBOL_GLOBAL: + printf("global @ %s\n", s->name); + break; + } +} diff --git a/scope.h b/scope.h new file mode 100644 index 0000000..322216b --- /dev/null +++ b/scope.h @@ -0,0 +1,27 @@ +#ifndef _SCOPE_H_ +#define _SCOPE_H_ + +#include "symbol.h" +#include "hash_table.h" + +struct scope +{ + int level; + struct scope* parent; + struct hash_table* table; +}; + +extern int res_errors; +extern struct scope* cur_scope; +extern int cur_global; +extern int cur_param; +extern int cur_local; + +void scope_enter(); +void scope_exit(); +void scope_bind(const char* name, struct symbol* s); +struct symbol* scope_lookup(const char* name); +struct symbol* scope_lookup_current(const char* name); +void resolve_msg(struct symbol* s); + +#endif diff --git a/stmt.c b/stmt.c index 682aff2..08dd44f 100644 --- a/stmt.c +++ b/stmt.c @@ -1,6 +1,7 @@ #include #include #include "stmt.h" +#include "scope.h" struct stmt* stmt_create( stmt_t kind, @@ -105,7 +106,6 @@ void stmt_print(const struct stmt* s, int indent) { case STMT_DECL: decl_print(s->decl, indent); - printf(";\n"); break; case STMT_EXPR: stmt_print_indent(indent); @@ -146,3 +146,40 @@ void stmt_print(const struct stmt* s, int indent) if (s->next) stmt_print(s->next, indent); } + +void stmt_resolve(struct stmt* s) +{ + if (!s) return; + switch (s->kind) + { + case STMT_DECL: + decl_resolve(s->decl); + break; + case STMT_EXPR: + expr_resolve(s->expr); + break; + case STMT_IF_ELSE: + expr_resolve(s->expr); + stmt_resolve(s->body); + stmt_resolve(s->else_body); + break; + case STMT_FOR: + expr_resolve(s->init_expr); + expr_resolve(s->expr); + expr_resolve(s->next_expr); + stmt_resolve(s->body); + break; + case STMT_PRINT: + expr_resolve(s->expr); + break; + case STMT_RETURN: + expr_resolve(s->expr); + break; + case STMT_BLOCK: + scope_enter(); + stmt_resolve(s->body); + scope_exit(); + break; + } + stmt_resolve(s->next); +} \ No newline at end of file diff --git a/stmt.h b/stmt.h index dcb89a1..7f130b5 100755 --- a/stmt.h +++ b/stmt.h @@ -38,7 +38,7 @@ struct stmt* stmt_create( ); struct stmt* stmt_create_empty(stmt_t kind); - void stmt_print(const struct stmt* s, int indent); +void stmt_resolve(struct stmt* s); #endif diff --git a/symbol.c b/symbol.c new file mode 100644 index 0000000..f0e4bc2 --- /dev/null +++ b/symbol.c @@ -0,0 +1,12 @@ +#include +#include "symbol.h" + +struct symbol* symbol_create(symbol_t kind, struct type* type, char* name, int which) +{ + struct symbol* s = malloc(sizeof(*s)); + s->kind = kind; + s->type = type; + s->name = name; + s->which = which; + return s; +} diff --git a/symbol.h b/symbol.h index 63498df..a893859 100755 --- a/symbol.h +++ b/symbol.h @@ -1,22 +1,23 @@ - #ifndef SYMBOL_H #define SYMBOL_H #include "type.h" -typedef enum { +typedef enum +{ SYMBOL_LOCAL, SYMBOL_PARAM, SYMBOL_GLOBAL } symbol_t; -struct symbol { +struct symbol +{ symbol_t kind; - struct type *type; - char *name; + struct type* type; + char* name; int which; }; -struct symbol * symbol_create( symbol_t kind, struct type *type, char *name ); +struct symbol* symbol_create(symbol_t kind, struct type* type, char* name, int which); #endif diff --git a/test/printer/good11.bminor b/test/printer/good11.bminor new file mode 100644 index 0000000..5879e59 --- /dev/null +++ b/test/printer/good11.bminor @@ -0,0 +1,6 @@ +/* Function */ +main : function void () = { + x: integer = 0; + y: integer = x * 2; + a: array [2] integer = {1 + 1, x / y}; +} diff --git a/test/resolver/bad0.bminor b/test/resolver/bad0.bminor new file mode 100644 index 0000000..5b4d4a4 --- /dev/null +++ b/test/resolver/bad0.bminor @@ -0,0 +1,5 @@ +/* Undeclared variable at top level */ +a: integer; +c: integer = a; +d: integer = b; +e: array [a] array [b] integer; diff --git a/test/resolver/bad1.bminor b/test/resolver/bad1.bminor new file mode 100644 index 0000000..8485520 --- /dev/null +++ b/test/resolver/bad1.bminor @@ -0,0 +1,10 @@ +/* Undeclared function */ +a: function void () = { + return; +} + +b: function integer () = { + a(); + b(); + c(); +} diff --git a/test/resolver/bad2.bminor b/test/resolver/bad2.bminor new file mode 100644 index 0000000..bf99c09 --- /dev/null +++ b/test/resolver/bad2.bminor @@ -0,0 +1,10 @@ +/* Undefined parameter */ + +a: function void (arg1: integer, arg2: integer) = { + a: integer = 2; + b: float = a; + c: array [a] boolean = {true, false}; + arg1 = arg2; + a + b + c; + return arg3; +} diff --git a/test/resolver/bad3.bminor b/test/resolver/bad3.bminor new file mode 100644 index 0000000..1b0c89a --- /dev/null +++ b/test/resolver/bad3.bminor @@ -0,0 +1,17 @@ +/* Mixed declarations */ + +a: integer; +b: float; +c: string; +d: boolean; + +func: function void (arg: integer); + +main: function void () = { + func(a, e); + f: float; + g: string; + h: boolean; + a + b + c + d + f + g + h; + return i + j + k; +} diff --git a/test/resolver/bad4.bminor b/test/resolver/bad4.bminor new file mode 100644 index 0000000..3f2bd56 --- /dev/null +++ b/test/resolver/bad4.bminor @@ -0,0 +1,13 @@ +/* Multi-levels */ + +var: integer; + +main: function void (var: integer) = { + { + var: integer; + main(var0); + } + var++; +} + +new: integer = var; \ No newline at end of file diff --git a/test/resolver/bad5.bminor b/test/resolver/bad5.bminor new file mode 100644 index 0000000..fce8e94 --- /dev/null +++ b/test/resolver/bad5.bminor @@ -0,0 +1,16 @@ +/* Conditions */ + +var: integer; + +main: function void () = { + if (new == 0) + new++; + else + new--; + + new: boolean; + if (new) + new++; + else + new--; +} diff --git a/test/resolver/bad6.bminor b/test/resolver/bad6.bminor new file mode 100644 index 0000000..457c271 --- /dev/null +++ b/test/resolver/bad6.bminor @@ -0,0 +1,16 @@ +/* Loop */ + +var: integer; + +main: function void () = { + if (new == 0) + new++; + else + new--; + + new: boolean; + if (new) + new++; + else + new--; +} diff --git a/test/resolver/bad7.bminor b/test/resolver/bad7.bminor new file mode 100644 index 0000000..ef1a266 --- /dev/null +++ b/test/resolver/bad7.bminor @@ -0,0 +1,21 @@ +/* Finding max in array */ + +// SIZE: integer = 10; + +find_max: function integer (arr: array [] integer, size: integer) = { + max: integer = arr[0]; + // i: integer; + for (i = 1; i < size; i++) { + if (arr[i] > max) { + max = arr[i]; + } + } + return max; +} + +main: function integer () = { + numbers: array [SIZE] integer = {0, 3, 5, 7, 9, 2, 4, 6, 8, 1}; + max: integer = find_max(numbers, SIZE); + print "The maximum number is ", max, "\n"; + return 0; +} diff --git a/test/resolver/bad8.bminor b/test/resolver/bad8.bminor new file mode 100644 index 0000000..20c0c6d --- /dev/null +++ b/test/resolver/bad8.bminor @@ -0,0 +1,21 @@ +/* Sum of even numbers in an array */ + +SIZE: integer = 10; + +sum_even: function integer (arr: array [] integer, size: integer) = { + // sum: integer = 0; + i: integer; + for (i = 0; i < size; i++) { + if (arr[i] % 2 == 0) { + sum = sum + arr[i]; + } + } + return sum; +} + +main: function integer () = { + numbers: array [SIZE] integer = {0, 3, 5, 7, 9, 2, 4, 6, 8, 1}; + // even_sum: integer = sum_even(numbers, SIZE); + print "The sum of even numbers is ", even_sum, "\n"; + return 0; +} diff --git a/test/resolver/bad9.bminor b/test/resolver/bad9.bminor new file mode 100644 index 0000000..dae0119 --- /dev/null +++ b/test/resolver/bad9.bminor @@ -0,0 +1,18 @@ +/* Factorial of a number using recursion */ + +/* +factorial: function integer (n: integer) = { + if (n <= 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +*/ + +main: function integer () = { + num: integer = 5; + result: integer = factorial(num); + print "The factorial of ", num, " is ", result, "\n"; + return 0; +} diff --git a/test/resolver/good0.bminor b/test/resolver/good0.bminor new file mode 100644 index 0000000..59ccccf --- /dev/null +++ b/test/resolver/good0.bminor @@ -0,0 +1,12 @@ +/* Variable declarations */ +a: integer; +b: integer = a; +c: integer = a + b; +d: float = c + 45.67; +e: boolean = false; +f: char = 'q'; +g: string = "hello bminor\n"; +h: array [g] integer; +i: array [2] boolean = {true, false}; +j: array [2] array [d] boolean; +k: array [2] integer = {i + j, 1 / 3}; diff --git a/test/resolver/good1.bminor b/test/resolver/good1.bminor new file mode 100644 index 0000000..847a013 --- /dev/null +++ b/test/resolver/good1.bminor @@ -0,0 +1,9 @@ +/* Function declarations */ +a: function void () = { + return; +} + +b: function integer () = { + a(); + b(); +} diff --git a/test/resolver/good2.bminor b/test/resolver/good2.bminor new file mode 100644 index 0000000..4addf58 --- /dev/null +++ b/test/resolver/good2.bminor @@ -0,0 +1,9 @@ +/* Declarations within function */ + +a: function void (arg1: integer, arg2: integer) = { + a: integer = 2; + b: float = a; + c: array [a] boolean = {true, false}; + arg1 = arg2; + a + b + c; +} diff --git a/test/resolver/good3.bminor b/test/resolver/good3.bminor new file mode 100644 index 0000000..0facc39 --- /dev/null +++ b/test/resolver/good3.bminor @@ -0,0 +1,16 @@ +/* Mixed declarations */ + +a: integer; +b: float; +c: string; +d: boolean; + +func: function void (arg: integer); + +main: function void () = { + func(a); + f: float; + g: string; + h: boolean; + a + b + c + d + f + g + h; +} diff --git a/test/resolver/good4.bminor b/test/resolver/good4.bminor new file mode 100644 index 0000000..4e61be9 --- /dev/null +++ b/test/resolver/good4.bminor @@ -0,0 +1,13 @@ +/* Multi-levels */ + +var: integer; + +main: function void (var: integer) = { + { + var: integer; + main(var); + } + var++; +} + +new: integer = var; \ No newline at end of file diff --git a/test/resolver/good5.bminor b/test/resolver/good5.bminor new file mode 100644 index 0000000..de58aa2 --- /dev/null +++ b/test/resolver/good5.bminor @@ -0,0 +1,16 @@ +/* Conditions */ + +var: integer; + +main: function void () = { + if (var == 0) + var++; + else + var--; + + new: boolean; + if (new) + new++; + else + new--; +} diff --git a/test/resolver/good6.bminor b/test/resolver/good6.bminor new file mode 100644 index 0000000..ede7e74 --- /dev/null +++ b/test/resolver/good6.bminor @@ -0,0 +1,16 @@ +/* Loop */ + +var: integer; + +main: function void () = { + if (var == 0) + var++; + else + var--; + + new: boolean; + if (new) + new++; + else + new--; +} diff --git a/test/resolver/good7.bminor b/test/resolver/good7.bminor new file mode 100644 index 0000000..6c0b443 --- /dev/null +++ b/test/resolver/good7.bminor @@ -0,0 +1,21 @@ +/* Finding max in array */ + +SIZE: integer = 10; + +find_max: function integer (arr: array [] integer, size: integer) = { + max: integer = arr[0]; + i: integer; + for (i = 1; i < size; i++) { + if (arr[i] > max) { + max = arr[i]; + } + } + return max; +} + +main: function integer () = { + numbers: array [SIZE] integer = {0, 3, 5, 7, 9, 2, 4, 6, 8, 1}; + max: integer = find_max(numbers, SIZE); + print "The maximum number is ", max, "\n"; + return 0; +} diff --git a/test/resolver/good8.bminor b/test/resolver/good8.bminor new file mode 100644 index 0000000..e9f3949 --- /dev/null +++ b/test/resolver/good8.bminor @@ -0,0 +1,21 @@ +/* Sum of even numbers in an array */ + +SIZE: integer = 10; + +sum_even: function integer (arr: array [] integer, size: integer) = { + sum: integer = 0; + i: integer; + for (i = 0; i < size; i++) { + if (arr[i] % 2 == 0) { + sum = sum + arr[i]; + } + } + return sum; +} + +main: function integer () = { + numbers: array [SIZE] integer = {0, 3, 5, 7, 9, 2, 4, 6, 8, 1}; + even_sum: integer = sum_even(numbers, SIZE); + print "The sum of even numbers is ", even_sum, "\n"; + return 0; +} diff --git a/test/resolver/good9.bminor b/test/resolver/good9.bminor new file mode 100644 index 0000000..6857d9f --- /dev/null +++ b/test/resolver/good9.bminor @@ -0,0 +1,16 @@ +/* Factorial of a number using recursion */ + +factorial: function integer (n: integer) = { + if (n <= 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} + +main: function integer () = { + num: integer = 5; + result: integer = factorial(num); + print "The factorial of ", num, " is ", result, "\n"; + return 0; +}