OBJ_FILES=\
build/onyxlex.o \
build/onyxparser.o \
+ build/onyxsempass.o \
build/onyxmsgs.o \
build/onyxutils.o \
build/onyxwasm.o \
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;
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,
OnyxTypeInfo *type;
OnyxAstNode *next;
union { i32 i; i64 l; f32 f; f64 d; } value;
- ptr unused;
};
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 {
OnyxTypeInfo *type; // NOTE: UNUSED
OnyxAstNodeScope *prev_scope;
OnyxAstNodeLocal *last_local;
- OnyxAstNode *unused;
};
struct OnyxAstNodeBlock {
--- /dev/null
+#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
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;
}
c := a - b; // Mutable
d :: a + b; // Constant
+ {
+ c := a * 2;
+ d := (b + a) * 2;
+ }
+
return c * d;
}
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());
-}
--- /dev/null
+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;
+}
#include "onyxlex.h"
#include "onyxmsgs.h"
#include "onyxparser.h"
+#include "onyxsempass.h"
#include "onyxutils.h"
#include "onyxwasm.h"
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);
"BLOCK",
"SCOPE",
"LOCAL",
+ "SYMBOL",
"ADD",
"MINUS",
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);
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);
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;
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;
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;
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;
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) {
}
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);
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;
}
{
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:
case TOKEN_TYPE_OPEN_BRACE:
needs_semicolon = 0;
- retval = (OnyxAstNode *) parse_block(parser, 0);
+ retval = (OnyxAstNode *) parse_block(parser);
break;
case TOKEN_TYPE_SYMBOL:
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);
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;
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;
}
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;
}
}
}
-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:
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;
parser.curr_token = tokenizer->tokens;
parser.prev_token = NULL;
parser.msgs = msgs;
- parser.curr_scope = NULL;
return 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;
}
}
--- /dev/null
+#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;
+ }
+}
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);
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);