Added proper symbol resolution
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 22 Jun 2020 15:51:02 +0000 (10:51 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 22 Jun 2020 15:51:02 +0000 (10:51 -0500)
Compiler is now in a working state

Makefile
include/onyxparser.h
include/onyxsempass.h [new file with mode: 0644]
onyx
progs/minimal.onyx
progs/new_minimal.onyx [new file with mode: 0644]
src/onyx.c
src/onyxparser.c
src/onyxsempass.c [new file with mode: 0644]
src/onyxutils.c

index 54dc1cbb2b15e348eebae6b4f6c27eea58914bea..bf09e2824c66a4909f37eb69c11540b380d91c43 100644 (file)
--- 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 \
index 081e2adc3a174faed054ceff14d8100643663526..39becb233bfd54839edcdec565f3076d32cbf989 100644 (file)
@@ -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 (file)
index 0000000..fc5f4ec
--- /dev/null
@@ -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 cfd3f20a370ecf787b69acd09c5917471cdf4918..8cec77ebea577bbd812aee7adc52b376c672fb05 100755 (executable)
Binary files a/onyx and b/onyx differ
index b6610b33e23836674e5cbf5ff62ab6a1210b0792..38b1d12e52f7eb5432d6228080d2a51156ea8897 100644 (file)
@@ -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 (file)
index 0000000..cbb7fe0
--- /dev/null
@@ -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;
+}
index 2a57a80e3c912279ffaa6625137049f7e58b6346..788be3238caf3dc52c4e12264dd0ecec1cd9dffe 100644 (file)
@@ -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);
index 0d0146ecddbfd450c359b059b78c720a0323600a..01dc0fec3b921317697dc50218347413b27dee4b 100644 (file)
@@ -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 (file)
index 0000000..233646f
--- /dev/null
@@ -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;
+    }
+}
index 732be5859934d4910709b0042d4a70356bc71f6d..2964b344d850e48681e60da9fed880bf45bcece5 100644 (file)
@@ -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);