From: Brendan Hansen Date: Mon, 22 Jun 2020 15:51:02 +0000 (-0500) Subject: Added proper symbol resolution X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=8d01ea5fcf183ac6adccb97a4355d4a437ac0bad;p=onyx.git Added proper symbol resolution Compiler is now in a working state --- diff --git a/Makefile b/Makefile index 54dc1cbb..bf09e282 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ OBJ_FILES=\ build/onyxlex.o \ build/onyxparser.o \ + build/onyxsempass.o \ build/onyxmsgs.o \ build/onyxutils.o \ build/onyxwasm.o \ diff --git a/include/onyxparser.h b/include/onyxparser.h index 081e2adc..39becb23 100644 --- a/include/onyxparser.h +++ b/include/onyxparser.h @@ -22,12 +22,9 @@ typedef struct OnyxParser { OnyxToken *prev_token; OnyxToken *curr_token; - // NOTE: A table of the current identifiers in the current scope. - // If the identifier doesn't at the time of parsing, it is an error. - // Cleared at the end of a block. - bh_table(OnyxAstNode*) identifiers; - OnyxAstNodeScope *curr_scope; - + // NOTE: Identifiers currently is only used to resolve type names + // at parse time, since these are the only symbols we know. + bh_table(OnyxAstNode *) identifiers; OnyxMessages *msgs; bh_allocator allocator; @@ -42,6 +39,7 @@ typedef enum OnyxAstNodeKind { ONYX_AST_NODE_KIND_BLOCK, ONYX_AST_NODE_KIND_SCOPE, ONYX_AST_NODE_KIND_LOCAL, + ONYX_AST_NODE_KIND_SYMBOL, ONYX_AST_NODE_KIND_ADD, ONYX_AST_NODE_KIND_MINUS, @@ -119,7 +117,6 @@ struct OnyxAstNodeNumLit { OnyxTypeInfo *type; OnyxAstNode *next; union { i32 i; i64 l; f32 f; f64 d; } value; - ptr unused; }; struct OnyxAstNodeLocal { @@ -127,20 +124,17 @@ struct OnyxAstNodeLocal { u32 flags; OnyxToken *token; OnyxTypeInfo *type; + OnyxAstNode *next; OnyxAstNodeLocal *prev_local; - OnyxAstNode *shadowed; - ptr unused; }; -// NOTE: Needs to have shadowed in the same position as OnyxAstNodeLocal struct OnyxAstNodeParam { OnyxAstNodeKind kind; u32 flags; OnyxToken *token; // Symbol name i.e. 'a', 'b' OnyxTypeInfo *type; OnyxAstNodeParam *next; - OnyxAstNode *shadowed; - ptr unused; + OnyxAstNodeLocal *prev_local; }; struct OnyxAstNodeScope { @@ -150,7 +144,6 @@ struct OnyxAstNodeScope { OnyxTypeInfo *type; // NOTE: UNUSED OnyxAstNodeScope *prev_scope; OnyxAstNodeLocal *last_local; - OnyxAstNode *unused; }; struct OnyxAstNodeBlock { diff --git a/include/onyxsempass.h b/include/onyxsempass.h new file mode 100644 index 00000000..fc5f4eca --- /dev/null +++ b/include/onyxsempass.h @@ -0,0 +1,29 @@ +#ifndef ONYXSEMPASS_H +#define ONYXSEMPASS_H + +#include "bh.h" + +#include "onyxlex.h" +#include "onyxparser.h" +#include "onyxmsgs.h" + +typedef struct SemPassSymbol { + OnyxAstNode *node; + struct SemPassSymbol *shadowed; +} SemPassSymbol; + +typedef struct OnyxSemPassState { + // NOTE: Adding node_allocator in case we need + // to make any more node in the tree + bh_allocator allocator, node_allocator; + OnyxMessages *msgs; + + OnyxAstNodeScope* curr_scope; + + bh_table(SemPassSymbol *) symbols; +} OnyxSemPassState; + +OnyxSemPassState onyx_sempass_create(bh_allocator alloc, bh_allocator node_alloc, OnyxMessages* msgs); +void onyx_sempass(OnyxSemPassState* state, OnyxAstNode* root_node); + +#endif diff --git a/onyx b/onyx index cfd3f20a..8cec77eb 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/minimal.onyx b/progs/minimal.onyx index b6610b33..38b1d12e 100644 --- a/progs/minimal.onyx +++ b/progs/minimal.onyx @@ -3,6 +3,33 @@ print :: foreign "host" "print" proc (value i32) --- print_float :: foreign "host" "print" proc (value f32) --- print_if :: foreign "host" "print" proc (i i32, f f32) --- +export main :: proc { + output := do_stuff(); + + if output == -66 { + new_output := abs(output) * 2; + print(new_output); + + } elseif output == -67 { + new_output := 10; + print(1234); + + } else { + print(-1); + } + + print(output); + print_float(float_test()); + + print_if(output, float_test()); +} + +factorial :: proc (n i32) -> i32 { + if n <= 1 { return 1; } + + return n * factorial(n - 1); +} + foo :: proc -> i32 { return 10; } @@ -26,6 +53,11 @@ diff_square :: proc (a i32, b i32) -> i32 { c := a - b; // Mutable d :: a + b; // Constant + { + c := a * 2; + d := (b + a) * 2; + } + return c * d; } @@ -38,23 +70,3 @@ do_stuff :: proc -> i32 { float_test :: proc -> f32 { return 3.14159f; } - -export main :: proc { - output := do_stuff(); - - if output == -66 { - new_output :: abs(output) * 2; - print(new_output); - - } elseif output == -67 { - print(1234); - - } else { - print(-1); - } - - print(output); - print_float(float_test()); - - print_if(output, float_test()); -} diff --git a/progs/new_minimal.onyx b/progs/new_minimal.onyx new file mode 100644 index 00000000..cbb7fe06 --- /dev/null +++ b/progs/new_minimal.onyx @@ -0,0 +1,13 @@ +print :: foreign "host" "print" proc (value i32) --- + +foo :: proc -> i32 { + a := 2 + 4; + a = 5; + bar(a + 1); + print(bar(a)); + return a; +} + +bar :: proc (val i32) -> i32 { + return val + 1; +} diff --git a/src/onyx.c b/src/onyx.c index 2a57a80e..788be323 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -5,6 +5,7 @@ #include "onyxlex.h" #include "onyxmsgs.h" #include "onyxparser.h" +#include "onyxsempass.h" #include "onyxutils.h" #include "onyxwasm.h" @@ -49,27 +50,39 @@ int main(int argc, char *argv[]) { OnyxParser parser = onyx_parser_create(ast_alloc, &tokenizer, &msgs); OnyxAstNode* program = onyx_parse(&parser); + bh_printf("BEFORE SYMBOL_RESOLUTION: "); + onyx_ast_print(program, 1); + + bh_arena sp_arena; + bh_arena_init(&sp_arena, alloc, 16 * 1024); + bh_allocator sp_alloc = bh_arena_allocator(&sp_arena); + + OnyxSemPassState sp_state = onyx_sempass_create(sp_alloc, ast_alloc, &msgs); + onyx_sempass(&sp_state, program); + // NOTE: if there are errors, assume the parse tree was generated wrong, // even if it may have still been generated correctly. if (onyx_message_has_errors(&msgs)) { onyx_message_print(&msgs); goto main_exit; } else { - onyx_ast_print(program, 0); + bh_printf("\n\nAFTER SYMBOL RESOLUTION: "); + onyx_ast_print(program, 1); bh_printf("\nNo errors.\n"); } // NOTE: 4th: Generate a WASM module from the parse tree and // write it to a file. - OnyxWasmModule wasm_mod = onyx_wasm_generate_module(alloc, program); - - bh_file out_file; - bh_file_create(&out_file, "out.wasm"); - onyx_wasm_module_write_to_file(&wasm_mod, out_file); - bh_file_close(&out_file); - - onyx_wasm_module_free(&wasm_mod); +// OnyxWasmModule wasm_mod = onyx_wasm_generate_module(alloc, program); +// +// bh_file out_file; +// bh_file_create(&out_file, "out.wasm"); +// onyx_wasm_module_write_to_file(&wasm_mod, out_file); +// bh_file_close(&out_file); +// +// onyx_wasm_module_free(&wasm_mod); main_exit: // NOTE: Cleanup, since C doesn't have defer + bh_arena_free(&sp_arena); bh_arena_free(&msg_arena); bh_arena_free(&ast_arena); onyx_parser_free(&parser); diff --git a/src/onyxparser.c b/src/onyxparser.c index 0d0146ec..01dc0fec 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -12,6 +12,7 @@ static const char* ast_node_names[] = { "BLOCK", "SCOPE", "LOCAL", + "SYMBOL", "ADD", "MINUS", @@ -80,7 +81,7 @@ static OnyxAstNode* parse_expression(OnyxParser* parser); static OnyxAstNodeIf* parse_if_stmt(OnyxParser* parser); static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret); static OnyxAstNode* parse_return_statement(OnyxParser* parser); -static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function); +static OnyxAstNodeBlock* parse_block(OnyxParser* parser); static OnyxAstNode* parse_statement(OnyxParser* parser); static OnyxTypeInfo* parse_type(OnyxParser* parser); static OnyxAstNodeParam* parse_function_params(OnyxParser* parser); @@ -134,66 +135,6 @@ static OnyxToken* expect(OnyxParser* parser, OnyxTokenType token_type) { return token; } -static OnyxAstNodeScope* enter_scope(OnyxParser* parser) { - OnyxAstNodeScope* scope = (OnyxAstNodeScope*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SCOPE); - scope->prev_scope = parser->curr_scope; - parser->curr_scope = scope; - return scope; -} - -static OnyxAstNodeScope* leave_scope(OnyxParser* parser) { - // NOTE: Can't leave a scope if there is no scope - assert(parser->curr_scope != NULL); - - for (OnyxAstNodeLocal *walker = parser->curr_scope->last_local; walker != NULL; walker = walker->prev_local) { - remove_identifier(parser, (OnyxAstNode *) walker); - } - - parser->curr_scope = parser->curr_scope->prev_scope; - return parser->curr_scope; -} - -static OnyxAstNode* lookup_identifier(OnyxParser* parser, OnyxToken* token) { - OnyxAstNode* ident = NULL; - - onyx_token_null_toggle(*token); - if (bh_table_has(OnyxAstNode*, parser->identifiers, token->token)) { - ident = bh_table_get(OnyxAstNode*, parser->identifiers, token->token); - } - onyx_token_null_toggle(*token); - - return ident; -} - -static void insert_identifier(OnyxParser* parser, OnyxAstNode* ident, b32 is_local) { - OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident; - if (is_local) { - OnyxAstNodeScope* scope = parser->curr_scope; - local->prev_local = scope->last_local; - scope->last_local = local; - } - - onyx_token_null_toggle(*local->token); - if (bh_table_has(OnyxAstNode*, parser->identifiers, local->token->token)) { - local->shadowed = bh_table_get(OnyxAstNode*, parser->identifiers, local->token->token); - } - - bh_table_put(OnyxAstNodeLocal*, parser->identifiers, local->token->token, local); - onyx_token_null_toggle(*local->token); -} - -static void remove_identifier(OnyxParser* parser, OnyxAstNode* ident) { - OnyxAstNodeLocal* local = (OnyxAstNodeLocal *) ident; - - onyx_token_null_toggle(*local->token); - if (local->shadowed) { - bh_table_put(OnyxAstNode*, parser->identifiers, local->token->token, local->shadowed); - } else { - bh_table_delete(OnyxAstNode*, parser->identifiers, local->token->token); - } - onyx_token_null_toggle(*local->token); -} - static OnyxAstNodeNumLit* parse_numeric_literal(OnyxParser* parser) { OnyxAstNodeNumLit* lit_node = (OnyxAstNodeNumLit *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LITERAL); lit_node->token = expect(parser, TOKEN_TYPE_LITERAL_NUMERIC); @@ -260,16 +201,8 @@ static OnyxAstNode* parse_factor(OnyxParser* parser) { case TOKEN_TYPE_SYMBOL: { OnyxToken* sym_token = expect(parser, TOKEN_TYPE_SYMBOL); - OnyxAstNode* sym_node = lookup_identifier(parser, sym_token); - if (sym_node == NULL) { - onyx_token_null_toggle(*sym_token); - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL, - sym_token->pos, sym_token->token); - onyx_token_null_toggle(*sym_token); - - return sym_node; - } + OnyxAstNode* sym_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SYMBOL); + sym_node->token = sym_token; if (parser->curr_token->type != TOKEN_TYPE_OPEN_PAREN) { return sym_node; @@ -406,16 +339,6 @@ static OnyxAstNode* parse_expression(OnyxParser* parser) { bin_op->type = parse_type(parser); } else { right = parse_factor(parser); - if (left->type != right->type) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_BINOP_MISMATCH_TYPE, - bin_op_tok->pos, - left->type->name, right->type->name); - - find_token(parser, TOKEN_TYPE_SYM_SEMICOLON); - return &error_node; - } - bin_op->right = right; bin_op->type = right->type; @@ -434,25 +357,25 @@ static OnyxAstNodeIf* parse_if_stmt(OnyxParser* parser) { expect(parser, TOKEN_TYPE_KEYWORD_IF); OnyxAstNode* cond = parse_expression(parser); - OnyxAstNode* true_block = (OnyxAstNode *) parse_block(parser, 0); + OnyxAstNode* true_block = (OnyxAstNode *) parse_block(parser); OnyxAstNodeIf* if_node = (OnyxAstNodeIf *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_IF); OnyxAstNodeIf* root_if = if_node; if_node->cond = cond; if (true_block != NULL) - if_node->true_block = true_block->as_block.body; + if_node->true_block = true_block; while (parser->curr_token->type == TOKEN_TYPE_KEYWORD_ELSEIF) { parser_next_token(parser); OnyxAstNodeIf* elseif_node = (OnyxAstNodeIf *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_IF); cond = parse_expression(parser); - true_block = (OnyxAstNode *) parse_block(parser, 0); + true_block = (OnyxAstNode *) parse_block(parser); elseif_node->cond = cond; if (true_block != NULL) - elseif_node->true_block = true_block->as_block.body; + elseif_node->true_block = true_block; if_node->false_block = (OnyxAstNode *) elseif_node; if_node = elseif_node; @@ -461,9 +384,9 @@ static OnyxAstNodeIf* parse_if_stmt(OnyxParser* parser) { if (parser->curr_token->type == TOKEN_TYPE_KEYWORD_ELSE) { parser_next_token(parser); - OnyxAstNode* false_block = (OnyxAstNode *) parse_block(parser, 0); + OnyxAstNode* false_block = (OnyxAstNode *) parse_block(parser); if (false_block != NULL) - if_node->false_block = false_block->as_block.body; + if_node->false_block = false_block; } return root_if; @@ -490,9 +413,8 @@ static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret) { OnyxAstNodeLocal* local = (OnyxAstNodeLocal*) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_LOCAL); local->token = symbol; local->type = type; - local->flags |= ONYX_AST_FLAG_LVAL; - - insert_identifier(parser, (OnyxAstNode *) local, 1); + local->flags |= ONYX_AST_FLAG_LVAL; // NOTE: DELETE + *ret = (OnyxAstNode *) local; if (parser->curr_token->type == TOKEN_TYPE_SYM_EQUALS || parser->curr_token->type == TOKEN_TYPE_SYM_COLON) { if (parser->curr_token->type == TOKEN_TYPE_SYM_COLON) { @@ -500,6 +422,7 @@ static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret) { } OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT); + local->next = assignment; assignment->token = parser->curr_token; parser_next_token(parser); @@ -514,18 +437,10 @@ static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret) { return 1; } assignment->right = expr; - assignment->left = (OnyxAstNode*) local; - if (local->type->is_known && local->type != expr->type) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_ASSIGNMENT_TYPE_MISMATCH, - assignment->token->pos, - local->type->name, expr->type->name); - return 1; - } else if (!local->type->is_known) { - local->type = expr->type; - } - *ret = assignment; + OnyxAstNode* left_symbol = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SYMBOL); + left_symbol->token = symbol; + assignment->left = left_symbol; } return 1; } @@ -535,39 +450,15 @@ static b32 parse_symbol_statement(OnyxParser* parser, OnyxAstNode** ret) { { parser_next_token(parser); - OnyxAstNode* lval = lookup_identifier(parser, symbol); - - if (lval != NULL && lval->flags & ONYX_AST_FLAG_LVAL && (lval->flags & ONYX_AST_FLAG_CONST) == 0) { - OnyxAstNode* rval = parse_expression(parser); - OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT); - assignment->right = rval; - assignment->left = lval; - *ret = assignment; - return 1; - } - - onyx_token_null_toggle(*symbol); - if (lval == NULL) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL, - symbol->pos, symbol->token); - } - - else if ((lval->flags & ONYX_AST_FLAG_LVAL) == 0) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_NOT_LVAL, - symbol->pos, symbol->token); - } - - else if (lval->flags & ONYX_AST_FLAG_CONST) { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_ASSIGN_CONST, - symbol->pos, symbol->token); - } - onyx_token_null_toggle(*symbol); + OnyxAstNode* lval = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SYMBOL); + lval->token = symbol; - find_token(parser, TOKEN_TYPE_SYM_SEMICOLON); - return 1; + OnyxAstNode* rval = parse_expression(parser); + OnyxAstNode* assignment = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_ASSIGNMENT); + assignment->right = rval; + assignment->left = lval; + *ret = assignment; + return 1; } default: @@ -608,7 +499,7 @@ static OnyxAstNode* parse_statement(OnyxParser* parser) { case TOKEN_TYPE_OPEN_BRACE: needs_semicolon = 0; - retval = (OnyxAstNode *) parse_block(parser, 0); + retval = (OnyxAstNode *) parse_block(parser); break; case TOKEN_TYPE_SYMBOL: @@ -649,7 +540,7 @@ static OnyxAstNode* parse_statement(OnyxParser* parser) { return retval; } -static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function) { +static OnyxAstNodeBlock* parse_block(OnyxParser* parser) { // --- is for an empty block if (parser->curr_token->type == TOKEN_TYPE_SYM_MINUS) { expect(parser, TOKEN_TYPE_SYM_MINUS); @@ -661,10 +552,8 @@ static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function expect(parser, TOKEN_TYPE_OPEN_BRACE); OnyxAstNodeBlock* block = (OnyxAstNodeBlock *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_BLOCK); - if (belongs_to_function) { - OnyxAstNodeScope* scope = enter_scope(parser); - block->scope = scope; - } + OnyxAstNodeScope* scope = (OnyxAstNodeScope *) onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_SCOPE); + block->scope = scope; OnyxAstNode** next = &block->body; OnyxAstNode* stmt = NULL; @@ -673,16 +562,14 @@ static OnyxAstNodeBlock* parse_block(OnyxParser* parser, b32 belongs_to_function if (stmt != NULL && stmt->kind != ONYX_AST_NODE_KIND_ERROR) { *next = stmt; + + while (stmt->next != NULL) stmt = stmt->next; next = &stmt->next; } } expect(parser, TOKEN_TYPE_CLOSE_BRACE); - if (belongs_to_function) { - leave_scope(parser); - } - return block; } @@ -762,15 +649,7 @@ static OnyxAstNodeFuncDef* parse_function_definition(OnyxParser* parser) { func_def->return_type = &builtin_types[ONYX_TYPE_INFO_KIND_VOID]; } - for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) { - insert_identifier(parser, (OnyxAstNode *) p, 0); - } - - func_def->body = parse_block(parser, 1); - - for (OnyxAstNodeParam* p = func_def->params; p != NULL; p = p->next) { - remove_identifier(parser, (OnyxAstNode *) p); - } + func_def->body = parse_block(parser); return func_def; } @@ -801,26 +680,6 @@ static OnyxAstNode* parse_top_level_symbol(OnyxParser* parser) { } } -static b32 define_function(OnyxParser* parser, OnyxAstNodeFuncDef* func) { - onyx_token_null_toggle(*func->token); - - if (!bh_table_has(OnyxAstNode *, parser->identifiers, func->token->token)) { - bh_table_put(OnyxAstNode *, parser->identifiers, func->token->token, func); - } else { - onyx_message_add(parser->msgs, - ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION, - func->token->pos, - func->token->token); - - // NOTE: I really wish C had defer... - onyx_token_null_toggle(*func->token); - return 0; - } - - onyx_token_null_toggle(*func->token); - return 1; -} - static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) { switch (parser->curr_token->type) { case TOKEN_TYPE_KEYWORD_USE: @@ -855,21 +714,12 @@ static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) { OnyxAstNode* node = parse_top_level_symbol(parser); if (node->kind == ONYX_AST_NODE_KIND_FUNCDEF) { node->token = symbol; - - if (!define_function(parser, &node->as_funcdef)) { - return NULL; - } } if (node->kind == ONYX_AST_NODE_KIND_FOREIGN) { OnyxAstNodeForeign* foreign = &node->as_foreign; foreign->import->token = symbol; - if (foreign->import->kind == ONYX_AST_NODE_KIND_FUNCDEF) { - if (!define_function(parser, &foreign->import->as_funcdef)) { - return NULL; - } - } } return node; @@ -921,7 +771,6 @@ OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, Onyx parser.curr_token = tokenizer->tokens; parser.prev_token = NULL; parser.msgs = msgs; - parser.curr_scope = NULL; return parser; } @@ -941,6 +790,8 @@ OnyxAstNode* onyx_parse(OnyxParser *parser) { // Building a linked list of statements down the "next" chain if (curr_stmt != NULL && curr_stmt != &error_node) { *prev_stmt = curr_stmt; + + while (curr_stmt->next != NULL) curr_stmt = curr_stmt->next; prev_stmt = &curr_stmt->next; } } diff --git a/src/onyxsempass.c b/src/onyxsempass.c new file mode 100644 index 00000000..233646fd --- /dev/null +++ b/src/onyxsempass.c @@ -0,0 +1,316 @@ +#define BH_DEBUG +#include "onyxsempass.h" + +OnyxSemPassState onyx_sempass_create(bh_allocator alloc, bh_allocator node_alloc, OnyxMessages* msgs) { + OnyxSemPassState state = { + .allocator = alloc, + .node_allocator = node_alloc, + + .msgs = msgs, + + .curr_scope = NULL, + .symbols = NULL, + }; + + bh_table_init(bh_heap_allocator(), state.symbols, 61); + + return state; +} + +static void symbol_introduce(OnyxSemPassState* state, OnyxAstNode* symbol); +static void symbol_remove(OnyxSemPassState* state, OnyxAstNode* symbol); +static OnyxAstNode* symbol_resolve(OnyxSemPassState* state, OnyxAstNode* symbol); +static void scope_enter(OnyxSemPassState* state, OnyxAstNodeScope* scope); +static OnyxAstNodeScope* scope_leave(OnyxSemPassState* state); +static b32 define_function(OnyxSemPassState* state, OnyxAstNodeFuncDef* func); +static void symres_local(OnyxSemPassState* state, OnyxAstNodeLocal** local); +static void symres_call(OnyxSemPassState* state, OnyxAstNode* call); +static void symres_expression(OnyxSemPassState* state, OnyxAstNode** expr); +static void symres_assignment(OnyxSemPassState* state, OnyxAstNode* assign); +static void symres_return(OnyxSemPassState* state, OnyxAstNode* ret); +static void symres_if(OnyxSemPassState* state, OnyxAstNodeIf* ifnode); +static void symres_statement_chain(OnyxSemPassState* state, OnyxAstNode* walker, OnyxAstNode** trailer); +static b32 symres_statement(OnyxSemPassState* state, OnyxAstNode* stmt); +static void symres_block(OnyxSemPassState* state, OnyxAstNodeBlock* block); +static void symres_function_definition(OnyxSemPassState* state, OnyxAstNodeFuncDef* func); + +static void symbol_introduce(OnyxSemPassState* state, OnyxAstNode* symbol) { + onyx_token_null_toggle(*symbol->token); + + SemPassSymbol* sp_sym = (SemPassSymbol *) bh_alloc_item(state->allocator, SemPassSymbol); + sp_sym->node = symbol; + sp_sym->shadowed = NULL; + + if (bh_table_has(SemPassSymbol *, state->symbols, symbol->token->token)) { + sp_sym->shadowed = bh_table_get(SemPassSymbol *, state->symbols, symbol->token->token); + } + + bh_table_put(SemPassSymbol *, state->symbols, symbol->token->token, sp_sym); + + if (symbol->kind == ONYX_AST_NODE_KIND_LOCAL) { + OnyxAstNodeLocal* local = &symbol->as_local; + local->prev_local = state->curr_scope->last_local; + state->curr_scope->last_local = local; + } + + onyx_token_null_toggle(*symbol->token); +} + +static void symbol_remove(OnyxSemPassState* state, OnyxAstNode* symbol) { + onyx_token_null_toggle(*symbol->token); + + SemPassSymbol* sp_sym = bh_table_get(SemPassSymbol *, state->symbols, symbol->token->token); + + if (sp_sym->shadowed) { + bh_table_put(SemPassSymbol *, state->symbols, symbol->token->token, sp_sym->shadowed); + } else { + bh_table_delete(SemPassSymbol *, state->symbols, symbol->token->token); + } + + onyx_token_null_toggle(*symbol->token); +} + +static OnyxAstNode* symbol_resolve(OnyxSemPassState* state, OnyxAstNode* symbol) { + onyx_token_null_toggle(*symbol->token); + + if (!bh_table_has(SemPassSymbol *, state->symbols, symbol->token->token)) { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL, + symbol->token->pos, + symbol->token->token); + + onyx_token_null_toggle(*symbol->token); + return NULL; + } + + SemPassSymbol* sp_sym = bh_table_get(SemPassSymbol *, state->symbols, symbol->token->token); + + onyx_token_null_toggle(*symbol->token); + return sp_sym->node; +} + +static void scope_enter(OnyxSemPassState* state, OnyxAstNodeScope* scope) { + scope->prev_scope = state->curr_scope; + state->curr_scope = scope; +} + +static OnyxAstNodeScope* scope_leave(OnyxSemPassState* state) { + // NOTE: Can't leave a scope if there is no scope + assert(state->curr_scope != NULL); + + for (OnyxAstNodeLocal *walker = state->curr_scope->last_local; walker != NULL; walker = walker->prev_local) { + symbol_remove(state, (OnyxAstNode *) walker); + } + + state->curr_scope = state->curr_scope->prev_scope; + return state->curr_scope; +} + +static b32 define_function(OnyxSemPassState* state, OnyxAstNodeFuncDef* func) { + onyx_token_null_toggle(*func->token); + + // NOTE: If the function hasn't already been defined + if (!bh_table_has(SemPassSymbol *, state->symbols, func->token->token)) { + SemPassSymbol* sp_sym = bh_alloc_item(state->allocator, SemPassSymbol); + sp_sym->node = (OnyxAstNode *) func; + sp_sym->shadowed = NULL; + bh_table_put(SemPassSymbol *, state->symbols, func->token->token, sp_sym); + } else { + onyx_message_add(state->msgs, + ONYX_MESSAGE_TYPE_FUNCTION_REDEFINITION, + func->token->pos, + func->token->token); + + // NOTE: I really wish C had defer... + onyx_token_null_toggle(*func->token); + return 0; + } + + onyx_token_null_toggle(*func->token); + return 1; +} + +static void symres_local(OnyxSemPassState* state, OnyxAstNodeLocal** local) { + symbol_introduce(state, (OnyxAstNode *) *local); +} + +static void symres_call(OnyxSemPassState* state, OnyxAstNode* stmt) { + OnyxAstNodeCall* call = &stmt->as_call; + + OnyxAstNode* callee = symbol_resolve(state, call->callee); + if (callee) call->callee = callee; + else DEBUG_HERE; + + symres_statement_chain(state, call->arguments, &call->arguments); +} + +static void symres_expression(OnyxSemPassState* state, OnyxAstNode** expr) { + switch ((*expr)->kind) { + case ONYX_AST_NODE_KIND_ADD: + case ONYX_AST_NODE_KIND_MINUS: + case ONYX_AST_NODE_KIND_MULTIPLY: + case ONYX_AST_NODE_KIND_DIVIDE: + case ONYX_AST_NODE_KIND_MODULUS: + case ONYX_AST_NODE_KIND_EQUAL: + case ONYX_AST_NODE_KIND_NOT_EQUAL: + case ONYX_AST_NODE_KIND_LESS: + case ONYX_AST_NODE_KIND_LESS_EQUAL: + case ONYX_AST_NODE_KIND_GREATER: + case ONYX_AST_NODE_KIND_GREATER_EQUAL: + symres_expression(state, &(*expr)->left); + symres_expression(state, &(*expr)->right); + break; + + case ONYX_AST_NODE_KIND_NEGATE: + symres_expression(state, &(*expr)->left); + break; + + case ONYX_AST_NODE_KIND_CAST: + if ((*expr)->type == NULL) { + DEBUG_HERE; + return; + } + symres_expression(state, &(*expr)->left); + break; + + case ONYX_AST_NODE_KIND_CALL: symres_call(state, *expr); break; + + case ONYX_AST_NODE_KIND_BLOCK: symres_block(state, &(*expr)->as_block); + + case ONYX_AST_NODE_KIND_SYMBOL: + *expr = symbol_resolve(state, *expr); + break; + + // NOTE: This is a good case, since it means the symbol is already resolved + case ONYX_AST_NODE_KIND_LOCAL: break; + + case ONYX_AST_NODE_KIND_LITERAL: break; + + default: + DEBUG_HERE; + break; + } +} + +static void symres_assignment(OnyxSemPassState* state, OnyxAstNode* assign) { + OnyxAstNode* lval = symbol_resolve(state, assign->left); + if (lval == NULL) return; + assign->left = lval; + + symres_expression(state, &assign->right); +} + +static void symres_return(OnyxSemPassState* state, OnyxAstNode* ret) { + if (ret->left) + symres_expression(state, &ret->left); +} + +static void symres_if(OnyxSemPassState* state, OnyxAstNodeIf* ifnode) { + symres_expression(state, &ifnode->cond); + if (ifnode->true_block) { + if (ifnode->true_block->kind == ONYX_AST_NODE_KIND_BLOCK) + symres_block(state, &ifnode->true_block->as_block); + + else if (ifnode->true_block->kind == ONYX_AST_NODE_KIND_IF) + symres_if(state, &ifnode->true_block->as_if); + + else DEBUG_HERE; + } + + if (ifnode->false_block) { + if (ifnode->false_block->kind == ONYX_AST_NODE_KIND_BLOCK) + symres_block(state, &ifnode->false_block->as_block); + + else if (ifnode->false_block->kind == ONYX_AST_NODE_KIND_IF) + symres_if(state, &ifnode->false_block->as_if); + + else DEBUG_HERE; + } +} + +// NOTE: Returns 1 if the statment should be removed +static b32 symres_statement(OnyxSemPassState* state, OnyxAstNode* stmt) { + switch (stmt->kind) { + case ONYX_AST_NODE_KIND_LOCAL: symres_local(state, (OnyxAstNodeLocal **) &stmt); return 1; + case ONYX_AST_NODE_KIND_ASSIGNMENT: symres_assignment(state, stmt); return 0; + case ONYX_AST_NODE_KIND_RETURN: symres_return(state, stmt); return 0; + case ONYX_AST_NODE_KIND_IF: symres_if(state, &stmt->as_if); return 0; + case ONYX_AST_NODE_KIND_CALL: symres_call(state, stmt); return 0; + case ONYX_AST_NODE_KIND_ARGUMENT: symres_expression(state, (OnyxAstNode **) &stmt->left); return 0; + + default: return 0; + } +} + +static void symres_statement_chain(OnyxSemPassState* state, OnyxAstNode* walker, OnyxAstNode** trailer) { + while (walker) { + if (symres_statement(state, walker)) { + *trailer = walker->next; + + OnyxAstNode* tmp = walker->next; + walker->next = NULL; + walker = tmp; + } else { + trailer = &walker; + walker = walker->next; + } + } +} + +static void symres_block(OnyxSemPassState* state, OnyxAstNodeBlock* block) { + scope_enter(state, block->scope); + if (block->body) + symres_statement_chain(state, block->body, &block->body); + scope_leave(state); +} + +static void symres_function_definition(OnyxSemPassState* state, OnyxAstNodeFuncDef* func) { + forll(OnyxAstNodeParam, param, func->params, next) { + symbol_introduce(state, (OnyxAstNode *) param); + } + + symres_block(state, func->body); + + forll(OnyxAstNodeParam, param, func->params, next) { + symbol_remove(state, (OnyxAstNode *) param); + } +} + +void onyx_sempass(OnyxSemPassState* state, OnyxAstNode* root_node) { + OnyxAstNode* walker = root_node; + while (walker) { + switch (walker->kind) { + case ONYX_AST_NODE_KIND_FUNCDEF: + if (!define_function(state, &walker->as_funcdef)) return; + break; + + case ONYX_AST_NODE_KIND_FOREIGN: + if (walker->as_foreign.import->kind == ONYX_AST_NODE_KIND_FUNCDEF) { + if (!define_function(state, &walker->as_foreign.import->as_funcdef)) return; + } + break; + + default: break; + } + + walker = walker->next; + } + + bh_printf("\n\n"); + bh_table_each_start(SemPassSymbol*, state->symbols) { + bh_printf("%s -> %l\n", key, (u64) value); + } bh_table_each_end; + + // NOTE: First, resolve all symbols + walker = root_node; + while (walker) { + switch (walker->kind) { + case ONYX_AST_NODE_KIND_FUNCDEF: + symres_function_definition(state, &walker->as_funcdef); + break; + default: break; + } + + walker = walker->next; + } +} diff --git a/src/onyxutils.c b/src/onyxutils.c index 732be585..2964b344 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -88,10 +88,20 @@ void onyx_ast_print(OnyxAstNode* node, i32 indent) { if (local->prev_local && indent == 0) { bh_printf(", "); onyx_ast_print((OnyxAstNode *) local->prev_local, 0); - } + } else if (local->next && indent != 0) { + onyx_ast_print(local->next, indent); + } break; } + case ONYX_AST_NODE_KIND_SYMBOL: { + bh_printf("%b", node->token->token, node->token->length); + if (node->next) { + onyx_ast_print(node->next, indent); + } + break; + } + case ONYX_AST_NODE_KIND_RETURN: { if (node->left) { onyx_ast_print(node->left, indent + 1); @@ -119,7 +129,13 @@ void onyx_ast_print(OnyxAstNode* node, i32 indent) { case ONYX_AST_NODE_KIND_CALL: { OnyxAstNodeCall* call = &node->as_call; - bh_printf("%b", call->callee->token->token, call->callee->token->length); + if (call->callee) { + if (call->callee->kind == ONYX_AST_NODE_KIND_FUNCDEF) { + bh_printf("function: %b", call->callee->token->token, call->callee->token->length); + } else { + onyx_ast_print(call->callee, indent + 1); + } + } onyx_ast_print(call->arguments, indent + 1); if (call->next) { onyx_ast_print(call->next, indent);