-Memory design:\r
- - Pointers will work very similar to how they do in C\r
- - A pointer is a u32\r
- - Pointers will be notated:\r
- ^u32 <- Pointer to u32\r
-\r
- - Pointer operations will be:\r
- * will take the address of a value\r
- - This operation will not be defined well for a while\r
- - You can't take the address of a local since it doesn't exist in memory\r
-\r
- << will take the value out of a pointer\r
-\r
- Example use:\r
- {{{\r
- ptr: ^i32 = 0; // Address starting at 0\r
- ptr_ptr := ^ptr;\r
- }}}\r
+Memory design:
+ - Pointers will work very similar to how they do in C
+ - A pointer is a u32
+ - Pointers will be notated:
+ ^u32 <- Pointer to u32
+
+ - Pointer operations will be:
+ * will take the address of a value
+ - This operation will not be defined well for a while
+ - You can't take the address of a local since it doesn't exist in memory
+
+ << will take the value out of a pointer
+
+ Example use:
+ {{{
+ ptr: ^i32 = 0; // Address starting at 0
+ ptr_ptr := ^ptr;
+ }}}
+
+
+
+
+
+Treating top level declarations differently:
+ Currently, top level declarations are treated special, as they would correspond to
+ the structure of the WASM that would be generated. For example,
+
+ inc :: proc (a: i32) -> i32 { return a + 1; }
+
+ would be turned into an AstFunction node with a token of 'inc', and,
+
+ global :: 5
+
+ would be turned into a AstGlobal node with a token of 'global'.
+
+ The problem I have with this approach is it creates an inconsistency when thinking
+ about what is going on in the various stages in the compiler.
+
+ A better approach would be to have a AstBinding node, that represents a binding
+ from a symbol, stored on the token member, to another Ast Node. The node definition
+ would be:
+
+ struct AstBinding { AstTyped base; AstNode* node; }
+
+ For a function definition such as 'inc' above, the node structure would look like:
+
+ AstBinding (inc)
+ .node -> AstFunction
+ .params -> AstLocal (a) -> NULL
+ .body -> ...
+
+ This way, in symbol resolution, the top level bindings are added to the table and
+ there are no special cases.
+
+ Other nuances:
+
+ global :: 5 - This would replace all instances of 'global' with the integer constant
+ 5. This would not make a global in WASM.
+
+ global := 5 - This would work as expected but will make generating the WASM more difficult.
+ This feature may wait to come back.
+
+ global :: i32 - This would work as a type alias. 'global' would have the type node as it's 'node'
+
+ print :: proc #foriegn "host" "print" (...) ---
+
+
+Explicit overriden functions:
+ Considered syntax:
+
+ foo_i32 :: proc (val: i32) -> i32 ---
+ foo_i64 :: proc (val: i64) -> i64 ---
+ foo_f32 :: proc (val: f32) -> f32 ---
+ foo_f64 :: proc (val: f64) -> f64 ---
+
+ foo :: proc #overload {
+ foo_i32, foo_i64, foo_f32, foo_f64
+ }
+
+ foo(10); // calls foo_i32
+ foo(2.0f); // calls foo_f32
+
+
+ min_f32 :: proc #intrinsic (a: f32, b:f32) -> f32 ---
+ min_f64 :: proc #intrinsic (a: f64, b:f64) -> f64 ---
+
+ min_i32 :: proc (a: i32, b: i32) -> i32 {
+ least := a;
+ if b < a { least = b; }
+
+ return least;
+ }
+
+ min_i64 :: proc (a: i64, b: i64) -> i64 {
+ least := a;
+ if b < a { least = b; }
+
+ return least;
+ }
+
+ min :: proc #overload { min_i32, min_i64, min_f32, min_f64 }
+
+ min(2, 5);
+ min(4.5, 10.4);
typedef struct AstAssign AstAssign;
typedef struct AstNumLit AstNumLit;
typedef struct AstLocal AstLocal;
-typedef struct AstLocalGroup AstLocalGroup;
typedef struct AstReturn AstReturn;
-typedef struct AstBlock AstBlock;
-typedef struct AstIf AstIf;
-typedef struct AstWhile AstWhile;
-typedef struct AstFunction AstFunction;
-typedef struct AstForeign AstForeign;
-typedef struct AstGlobal AstGlobal;
typedef struct AstCall AstCall;
typedef struct AstIntrinsicCall AstIntrinsicCall;
typedef struct AstArgument AstArgument;
-typedef struct AstUse AstUse;
+
+typedef struct AstBlock AstBlock;
+typedef struct AstIf AstIf;
+typedef struct AstWhile AstWhile;
+typedef struct AstLocalGroup AstLocalGroup;
typedef struct AstType AstType;
typedef struct AstBasicType AstBasicType;
typedef struct AstPointerType AstPointerType;
+typedef struct AstFunctionType AstFunctionType;
+
+typedef struct AstBinding AstBinding;
+typedef struct AstFunction AstFunction;
+typedef struct AstForeign AstForeign;
+typedef struct AstGlobal AstGlobal;
+typedef struct AstUse AstUse;
typedef enum AstKind {
Ast_Kind_Error,
Ast_Kind_Program,
Ast_Kind_Use,
+ Ast_Kind_Binding,
Ast_Kind_Function,
Ast_Kind_Foreign,
Ast_Kind_Block,
Ast_Kind_Unary_Op,
Ast_Kind_Binary_Op,
+ Ast_Kind_Type_Start,
Ast_Kind_Type,
Ast_Kind_Basic_Type,
Ast_Kind_Pointer_Type,
+ Ast_Kind_Function_Type,
+ Ast_Kind_Type_End,
Ast_Kind_Literal,
Ast_Kind_Param,
// Function flags
Ast_Flag_Inline = BH_BIT(8),
Ast_Flag_Intrinsic = BH_BIT(9),
+ Ast_Flag_Foreign = BH_BIT(10),
} AstFlags;
typedef enum UnaryOp {
// Expression Nodes
struct AstBinOp { AstTyped base; BinaryOp operation; AstTyped *left, *right; };
struct AstUnaryOp { AstTyped base; UnaryOp operation; AstTyped *expr; };
-struct AstAssign { AstNode base; AstTyped* lval; AstTyped* expr; };
+struct AstAssign { AstNode base; AstTyped* lval; AstTyped* expr; };
struct AstNumLit { AstTyped base; union { i32 i; i64 l; f32 f; f64 d; } value; };
struct AstLocal { AstTyped base; AstLocal *prev_local; };
-struct AstReturn { AstNode base; AstTyped* expr; };
+struct AstReturn { AstNode base; AstTyped* expr; };
struct AstCall { AstTyped base; AstArgument *arguments; AstNode *callee; };
struct AstArgument { AstTyped base; AstTyped *value; };
struct AstType { AstKind kind; u32 flags; char* name; };
struct AstBasicType { AstType base; Type* type; };
struct AstPointerType { AstType base; AstType* elem; };
+struct AstFunctionType { AstType base; bh_arr(AstType *) params; AstType* results; };
// Top level nodes
-struct AstFunction { AstTyped base; AstBlock *body; AstLocal *params; };
-struct AstForeign { AstNode base; OnyxToken *mod_token, *name_token; AstNode *import; };
+struct AstBinding { AstTyped base; AstNode* node; };
+struct AstForeign { AstNode base; OnyxToken *mod_token, *name_token; AstNode *import; };
struct AstGlobal { AstTyped base; AstTyped *initial_value; };
-struct AstUse { AstNode base; OnyxToken *filename; };
+struct AstUse { AstNode base; OnyxToken *filename; };
+struct AstFunction {
+ AstTyped base;
+
+ AstBlock *body;
+ AstLocal *params;
+
+ // NOTE: Used when a function is exported with a specific name
+ char* exported_name;
+
+ // NOTE: Used when the function is declared as foreign
+ OnyxToken* foreign_module;
+ OnyxToken* foreign_name;
+};
typedef enum OnyxIntrinsic {
ONYX_INTRINSIC_UNDEFINED,
struct AstIntrinsicCall { AstTyped base; AstArgument *arguments; OnyxIntrinsic intrinsic; };
typedef struct OnyxProgram {
- bh_arr(AstGlobal *) globals;
+ bh_arr(AstBinding *) top_level_bindings;
+ bh_arr(AstNode *) nodes_to_process;
+
bh_arr(AstFunction *) functions;
- bh_arr(AstForeign *) foreigns;
} OnyxProgram;
#include "onyxmsgs.h"
#include "onyxastnodes.h"
+typedef struct ParseResults {
+ // NOTE: The allocator used to make the arrays below
+ bh_allocator allocator;
+
+ bh_arr(AstUse *) uses;
+ bh_arr(AstBinding *) bindings;
+
+ // NOTE: Contains all the nodes that will need some processing (symbol resolution, type checking)
+ bh_arr(AstNode *) nodes_to_process;
+} ParseResults;
+
typedef struct OnyxParser {
- OnyxTokenizer *tokenizer; // NOTE: not used since all tokens are lexed before parsing starts
+ bh_allocator allocator;
+
+ // NOTE: not used since all tokens are lexed before parsing starts
+ OnyxTokenizer *tokenizer;
OnyxToken *prev_token;
OnyxToken *curr_token;
OnyxMessages *msgs;
- bh_allocator allocator;
+ ParseResults results;
} OnyxParser;
const char* onyx_ast_node_kind_string(AstKind kind);
void* onyx_ast_node_new(bh_allocator alloc, i32 size, AstKind kind);
OnyxParser onyx_parser_create(bh_allocator alloc, OnyxTokenizer *tokenizer, OnyxMessages* msgs);
void onyx_parser_free(OnyxParser* parser);
-bh_arr(AstNode *) onyx_parse(OnyxParser *parser);
+ParseResults onyx_parse(OnyxParser *parser);
#endif // #ifndef ONYXPARSER_H
let s:cpo_save = &cpo
set cpo&vim
-syn keyword onyxKeyword struct proc use export foreign
+syn keyword onyxKeyword struct proc use
syn keyword onyxKeyword if elseif else
-syn keyword onyxKeyword for while loop return do
+syn keyword onyxKeyword for while do
syn keyword onyxKeyword break continue return
syn keyword onyxKeyword as
p := 0 as ^i32;
}
-print :: foreign "host" "print" proc (val: i32) ---
+print :: proc
+ #foreign "host" "print"
+ (val: i32) ---
test :: proc (a: bool) -> bool {
return !a;
return or_i32(a, b);
}
-export main :: proc {
+// THOUGHT: This could be a WASM global that is immutable and it
+// would be accessed using global.get
+// OR
+// This could be immediately substituted in the expression tree. i.e.
+// 4 + global => 4 + (5 * 2 + 6)
+global :: 5 * 2 + 6
+
+// WASM globals would be declared as such:
+// wasm_global :: global i32
+
+main :: proc #export {
a := 16;
print(clz_i32(a));
+ print(4 + global);
+
b := 1 + foo(2);
print(b);
return 8675309 + something_else(n) + global_value;
}
-export fib :: proc (n: i32) -> i32 {
+fib :: proc #export (n: i32) -> i32 {
if n == 0 { return 1; }
if n == 1 { return 1; }
return a;
}
-export factorial :: proc (n: i32) -> i32 {
+factorial :: proc #export (n: i32) -> i32 {
if n <= 1 { return 1; }
f := 1;
-print_bool :: foreign "host" "print" proc (value: bool) ---
-print_i32 :: foreign "host" "print" proc (value: i32) ---
-print_f32 :: foreign "host" "print" proc (value: f32) ---
-print_i64 :: foreign "host" "print" proc (value: i64) ---
-print_f64 :: foreign "host" "print" proc (value: f64) ---
+print_bool :: proc #foreign "host" "print" (value: bool) ---
+print_i32 :: proc #foreign "host" "print" (value: i32) ---
+print_f32 :: proc #foreign "host" "print" (value: f32) ---
+print_i64 :: proc #foreign "host" "print" (value: i64) ---
+print_f64 :: proc #foreign "host" "print" (value: f64) ---
return 100 * n + global_value;
}
-global_value := 100
-
-export in_unit_circle :: proc (x: f32, y: f32) -> bool {
+in_unit_circle :: proc #export (x: f32, y: f32) -> bool {
return (x * x) + (y * y) < 1.0f;
}
+echo :: proc (n: i32) -> i32 {
+ print_i32(n);
+ return n;
+}
+
+global_value :: echo(fib(4) * 2);
+
+local_brute :: proc {
+
+ a := 123;
+ b := 123.0f;
+ c := 123.0;
+
+ {
+ a := 5.0f;
+ b := 12.3;
+
+ print_f32(a);
+ print_f64(b);
+ }
+
+ print_i32(a);
+ print_f32(b);
+ print_f64(c);
+}
+
+main3 :: proc #export {
+ local_brute();
+}
+
+
// This is the entry point
-export main2 :: proc {
+main2 :: proc #export {
i := 0;
while i < 10 {
- res :: fib(i);
+ res :: clz_i32(fib(i));
print_i32(res);
i += 1;
}
// z : f32;
// }
-export main :: proc {
+main :: proc #export {
print_i32(clz_i32(16));
print_f32(sqrt_f32(2.0f));
print_bool(in_unit_circle(0.5f, 0.5f));
big_num := fib(factorial(4));
- something :: other_value(0);
- global_value = 1000;
- something_else :: other_value(0);
+ something :: other_value(0);
+ something_else :: other_value(1);
condition := big_num < something;
bh_arr_free(opts->files);
}
-static bh_arr(AstNode *) parse_source_file(CompilerState* compiler_state, bh_file_contents* file_contents) {
+static ParseResults parse_source_file(CompilerState* compiler_state, bh_file_contents* file_contents) {
// NOTE: Maybe don't want to recreate the tokenizer and parser for every file
if (compiler_state->options->verbose_output)
bh_printf("[Lexing] %s\n", file_contents->filename);
bh_table_put(bh_file_contents, compiler_state->loaded_files, (char *) filename, fc);
fc = bh_table_get(bh_file_contents, compiler_state->loaded_files, (char *) filename);
- bh_arr(AstNode *) top_nodes = parse_source_file(compiler_state, &fc);
+ ParseResults results = parse_source_file(compiler_state, &fc);
- bh_arr(AstUse *) uses = NULL;
-
- bh_arr_each(AstNode *, node, top_nodes) {
- switch ((*node)->kind) {
- case Ast_Kind_Use:
- bh_arr_push(uses, (AstUse *) *node);
- break;
-
- case Ast_Kind_Global:
- bh_arr_push(compiler_state->program.globals, (AstGlobal *) (*node));
- break;
-
- case Ast_Kind_Foreign:
- bh_arr_push(compiler_state->program.foreigns, (AstForeign *) (*node));
- break;
-
- case Ast_Kind_Function:
- bh_arr_push(compiler_state->program.functions, (AstFunction *) (*node));
- break;
-
- default:
- assert(("Invalid top level node", 0));
- break;
- }
- }
-
- bh_arr_each(AstUse *, use_node, uses) {
+ bh_arr_each(AstUse *, use_node, results.uses) {
char* formatted_name = bh_aprintf(
global_heap_allocator,
"%b.onyx",
bh_arr_push(compiler_state->queued_files, formatted_name);
}
- bh_arr_free(uses);
+ bh_arr_each(AstBinding *, binding_node, results.bindings)
+ bh_arr_push(compiler_state->program.top_level_bindings, *binding_node);
+
+ bh_arr_each(AstNode *, node, results.nodes_to_process)
+ bh_arr_push(compiler_state->program.nodes_to_process, *node);
if (onyx_message_has_errors(&compiler_state->msgs)) {
return ONYX_COMPILER_PROGRESS_FAILED_PARSE;
static void compiler_state_init(CompilerState* compiler_state, OnyxCompileOptions* opts) {
compiler_state->options = opts;
- bh_arr_new(global_heap_allocator, compiler_state->program.foreigns, 4);
- bh_arr_new(global_heap_allocator, compiler_state->program.globals, 4);
+ bh_arr_new(global_heap_allocator, compiler_state->program.top_level_bindings, 4);
bh_arr_new(global_heap_allocator, compiler_state->program.functions, 4);
bh_arena_init(&compiler_state->msg_arena, opts->allocator, 4096);
OnyxCompileOptions compile_opts = compile_opts_parse(global_heap_allocator, argc, argv);
CompilerState compile_state = {
.program = {
- .foreigns = NULL,
- .globals = NULL,
- .functions = NULL
+ .top_level_bindings = NULL,
+ .nodes_to_process = NULL,
+
+ .functions = NULL,
},
.wasm_mod = { 0 }
};
#define BH_DEBUG
#include "onyxsempass.h"
-static void check_function(OnyxSemPassState* state, AstFunction* func);
-static void check_block(OnyxSemPassState* state, AstBlock* block);
-static void check_statement_chain(OnyxSemPassState* state, AstNode* start);
-static void check_statement(OnyxSemPassState* state, AstNode* stmt);
-static void check_assignment(OnyxSemPassState* state, AstAssign* assign);
-static void check_return(OnyxSemPassState* state, AstReturn* retnode);
-static void check_if(OnyxSemPassState* state, AstIf* ifnode);
-static void check_while(OnyxSemPassState* state, AstWhile* whilenode);
-static void check_call(OnyxSemPassState* state, AstCall* call);
-static void check_binaryop(OnyxSemPassState* state, AstBinaryOp* binop);
-static void check_expression(OnyxSemPassState* state, AstTyped* expr);
-static void check_global(OnyxSemPassState* state, AstGlobal* global);
-
-static void check_assignment(OnyxSemPassState* state, AstAssign* assign) {
+static b32 check_function(OnyxSemPassState* state, AstFunction* func);
+static b32 check_block(OnyxSemPassState* state, AstBlock* block);
+static b32 check_statement_chain(OnyxSemPassState* state, AstNode* start);
+static b32 check_statement(OnyxSemPassState* state, AstNode* stmt);
+static b32 check_assignment(OnyxSemPassState* state, AstAssign* assign);
+static b32 check_return(OnyxSemPassState* state, AstReturn* retnode);
+static b32 check_if(OnyxSemPassState* state, AstIf* ifnode);
+static b32 check_while(OnyxSemPassState* state, AstWhile* whilenode);
+static b32 check_call(OnyxSemPassState* state, AstCall* call);
+static b32 check_binaryop(OnyxSemPassState* state, AstBinaryOp* binop);
+static b32 check_expression(OnyxSemPassState* state, AstTyped* expr);
+static b32 check_global(OnyxSemPassState* state, AstGlobal* global);
+
+static b32 check_assignment(OnyxSemPassState* state, AstAssign* assign) {
if (assign->lval->kind == Ast_Kind_Symbol) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL,
assign->lval->token->pos,
assign->lval->token->text, assign->lval->token->length);
- return;
+ return 1;
}
if ((assign->lval->flags & Ast_Flag_Const) != 0 && assign->lval->type != NULL) {
ONYX_MESSAGE_TYPE_ASSIGN_CONST,
assign->base.token->pos,
assign->lval->token->text, assign->lval->token->length);
- return;
+ return 1;
}
if ((assign->lval->flags & Ast_Flag_Lval) == 0) {
ONYX_MESSAGE_TYPE_NOT_LVAL,
assign->base.token->pos,
assign->lval->token->text, assign->lval->token->length);
- return;
+ return 1;
}
if (assign->lval->type == NULL) {
assign->lval->type = type_build_from_ast(state->node_allocator, assign->lval->type_node);
}
- check_expression(state, assign->expr);
+ if (check_expression(state, assign->expr)) return 1;
if (assign->lval->type == NULL) {
assign->lval->type = assign->expr->type;
assign->base.token->pos,
type_get_name(assign->lval->type),
type_get_name(assign->expr->type));
- return;
+ return 1;
}
}
+
+ return 0;
}
-static void check_return(OnyxSemPassState* state, AstReturn* retnode) {
+static b32 check_return(OnyxSemPassState* state, AstReturn* retnode) {
if (retnode->expr) {
- check_expression(state, retnode->expr);
+ if (check_expression(state, retnode->expr)) return 1;
if (!types_are_compatible(retnode->expr->type, state->expected_return_type)) {
onyx_message_add(state->msgs,
retnode->expr->token->pos,
type_get_name(retnode->expr->type),
type_get_name(state->expected_return_type));
+ return 1;
}
} else {
if (state->expected_return_type->Basic.size > 0) {
ONYX_MESSAGE_TYPE_LITERAL,
retnode->base.token->pos,
"returning from non-void function without value");
+ return 1;
}
}
+
+ return 0;
}
-static void check_if(OnyxSemPassState* state, AstIf* ifnode) {
- check_expression(state, ifnode->cond);
+static b32 check_if(OnyxSemPassState* state, AstIf* ifnode) {
+ if (check_expression(state, ifnode->cond)) return 1;
if (ifnode->cond->type == NULL
|| ifnode->cond->type->kind != Type_Kind_Basic
ONYX_MESSAGE_TYPE_LITERAL,
ifnode->cond->token->pos,
"expected boolean type for condition");
- return;
+ return 1;
}
- if (ifnode->true_block.as_if) check_statement(state, (AstNode *) ifnode->true_block.as_block);
- if (ifnode->false_block.as_if) check_statement(state, (AstNode *) ifnode->false_block.as_block);
+ if (ifnode->true_block.as_if) if (check_statement(state, (AstNode *) ifnode->true_block.as_block)) return 1;
+ if (ifnode->false_block.as_if) if (check_statement(state, (AstNode *) ifnode->false_block.as_block)) return 1;
+
+ return 0;
}
-static void check_while(OnyxSemPassState* state, AstWhile* whilenode) {
- check_expression(state, whilenode->cond);
+static b32 check_while(OnyxSemPassState* state, AstWhile* whilenode) {
+ if (check_expression(state, whilenode->cond)) return 1;
if (whilenode->cond->type == NULL
|| whilenode->cond->type->kind != Type_Kind_Basic
ONYX_MESSAGE_TYPE_LITERAL,
whilenode->cond->token->pos,
"expected boolean type for condition");
- return;
+ return 1;
}
- check_block(state, whilenode->body);
+ return check_block(state, whilenode->body);
}
-static void check_call(OnyxSemPassState* state, AstCall* call) {
+static b32 check_call(OnyxSemPassState* state, AstCall* call) {
AstFunction* callee = (AstFunction *) call->callee;
if (callee->base.kind == Ast_Kind_Symbol) {
ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL,
callee->base.token->pos,
callee->base.token->text, callee->base.token->length);
- return;
+ return 1;
}
if (callee->base.kind != Ast_Kind_Function) {
ONYX_MESSAGE_TYPE_CALL_NON_FUNCTION,
call->base.token->pos,
callee->base.token->text, callee->base.token->length);
- return;
+ return 1;
}
// NOTE: If we calling an intrinsic function, translate the
i32 arg_pos = 0;
while (formal_param != NULL && actual_param != NULL) {
- check_expression(state, (AstTyped *) actual_param);
+ if (check_expression(state, (AstTyped *) actual_param)) return 1;
if (formal_param->base.type == NULL) {
formal_param->base.type = type_build_from_ast(state->node_allocator, formal_param->base.type_node);
type_get_name(formal_param->base.type),
arg_pos,
type_get_name(actual_param->base.type));
- return;
+ return 1;
}
arg_pos++;
ONYX_MESSAGE_TYPE_LITERAL,
call->base.token->pos,
"too few arguments to function call");
- return;
+ return 1;
}
if (formal_param == NULL && actual_param != NULL) {
ONYX_MESSAGE_TYPE_LITERAL,
call->base.token->pos,
"too many arguments to function call");
- return;
+ return 1;
}
+
+ return 0;
}
-static void check_binaryop(OnyxSemPassState* state, AstBinaryOp* binop) {
- check_expression(state, binop->left);
- check_expression(state, binop->right);
+static b32 check_binaryop(OnyxSemPassState* state, AstBinaryOp* binop) {
+ if (check_expression(state, binop->left)) return 1;
+ if (check_expression(state, binop->right)) return 1;
if (binop->left->type == NULL) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE,
binop->base.token->pos,
NULL, 0);
- return;
+ return 1;
}
if (binop->right->type == NULL) {
ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE,
binop->base.token->pos,
NULL, 0);
- return;
+ return 1;
}
if (type_is_pointer(binop->left->type)
ONYX_MESSAGE_TYPE_LITERAL,
binop->base.token->pos,
"binary operations are not supported for pointers (yet).");
- return;
+ return 1;
}
if (!types_are_compatible(binop->left->type, binop->right->type)) {
binop->base.token->pos,
type_get_name(binop->left->type),
type_get_name(binop->right->type));
- return;
+ return 1;
}
if (binop->operation >= Binary_Op_Equal
} else {
binop->base.type = binop->left->type;
}
+
+ return 0;
}
-static void check_expression(OnyxSemPassState* state, AstTyped* expr) {
+static b32 check_expression(OnyxSemPassState* state, AstTyped* expr) {
+ if (expr->kind > Ast_Kind_Type_Start && expr->kind < Ast_Kind_Type_End) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_LITERAL,
+ (OnyxFilePos) { 0 },
+ "type used as part of an expression");
+ return 1;
+ }
+
if (expr->type == NULL) {
expr->type = type_build_from_ast(state->node_allocator, expr->type_node);
}
+ i32 retval = 0;
switch (expr->kind) {
- case Ast_Kind_Binary_Op:
- check_binaryop(state, (AstBinaryOp *) expr);
- break;
+ case Ast_Kind_Binary_Op: retval = check_binaryop(state, (AstBinaryOp *) expr); break;
case Ast_Kind_Unary_Op:
- check_expression(state, ((AstUnaryOp *) expr)->expr);
+ retval = check_expression(state, ((AstUnaryOp *) expr)->expr);
if (((AstUnaryOp *) expr)->operation != Unary_Op_Cast) {
expr->type = ((AstUnaryOp *) expr)->expr->type;
}
break;
- case Ast_Kind_Call:
- check_call(state, (AstCall *) expr);
- break;
-
- case Ast_Kind_Block:
- check_block(state, (AstBlock *) expr);
- break;
+ case Ast_Kind_Call: retval = check_call(state, (AstCall *) expr); break;
+ case Ast_Kind_Block: retval = check_block(state, (AstBlock *) expr); break;
case Ast_Kind_Symbol:
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_UNRESOLVED_SYMBOL,
expr->token->pos,
expr->token->text, expr->token->length);
+ retval = 1;
break;
case Ast_Kind_Local:
ONYX_MESSAGE_TYPE_LITERAL,
expr->token->pos,
"local variable with unknown type");
+ retval = 1;
}
break;
ONYX_MESSAGE_TYPE_LITERAL,
expr->token->pos,
"global with unknown type");
+ retval = 1;
}
break;
case Ast_Kind_Argument:
- check_expression(state, ((AstArgument *) expr)->value);
+ retval = check_expression(state, ((AstArgument *) expr)->value);
expr->type = ((AstArgument *) expr)->value->type;
break;
break;
default:
+ retval = 1;
DEBUG_HERE;
break;
}
+
+ return retval;
}
-static void check_global(OnyxSemPassState* state, AstGlobal* global) {
+static b32 check_global(OnyxSemPassState* state, AstGlobal* global) {
if (global->initial_value) {
- check_expression(state, global->initial_value);
+ if (check_expression(state, global->initial_value)) return 1;
if (global->base.type == NULL) {
global->base.type = type_build_from_ast(state->node_allocator, global->base.type_node);
global->base.token->text, global->base.token->length,
type_get_name(global->base.type),
type_get_name(global->initial_value->type));
- return;
+ return 1;
}
} else {
if (global->initial_value->type)
ONYX_MESSAGE_TYPE_LITERAL,
global->base.token->pos,
"global variable with unknown type");
+ return 1;
}
+
+ return 0;
}
-static void check_statement(OnyxSemPassState* state, AstNode* stmt) {
+static b32 check_statement(OnyxSemPassState* state, AstNode* stmt) {
switch (stmt->kind) {
- case Ast_Kind_Assignment: check_assignment(state, (AstAssign *) stmt); break;
- case Ast_Kind_Return: check_return(state, (AstReturn *) stmt); break;
- case Ast_Kind_If: check_if(state, (AstIf *) stmt); break;
- case Ast_Kind_While: check_while(state, (AstWhile *) stmt); break;
- case Ast_Kind_Call: check_call(state, (AstCall *) stmt); break;
- case Ast_Kind_Block: check_block(state, (AstBlock *) stmt); break;
-
- default: break;
+ case Ast_Kind_Assignment: return check_assignment(state, (AstAssign *) stmt);
+ case Ast_Kind_Return: return check_return(state, (AstReturn *) stmt);
+ case Ast_Kind_If: return check_if(state, (AstIf *) stmt);
+ case Ast_Kind_While: return check_while(state, (AstWhile *) stmt);
+ case Ast_Kind_Call: return check_call(state, (AstCall *) stmt);
+ case Ast_Kind_Block: return check_block(state, (AstBlock *) stmt);
+
+ default: return 0;
}
}
-static void check_statement_chain(OnyxSemPassState* state, AstNode* start) {
+static b32 check_statement_chain(OnyxSemPassState* state, AstNode* start) {
while (start) {
- check_statement(state, start);
+ if (check_statement(state, start)) return 1;
start = start->next;
}
+
+ return 0;
}
-static void check_block(OnyxSemPassState* state, AstBlock* block) {
- check_statement_chain(state, block->body);
+static b32 check_block(OnyxSemPassState* state, AstBlock* block) {
+ if (check_statement_chain(state, block->body)) return 1;
forll(AstLocal, local, block->locals->last_local, prev_local) {
if (local->base.type == NULL) {
ONYX_MESSAGE_TYPE_UNRESOLVED_TYPE,
local->base.token->pos,
local->base.token->text, local->base.token->length);
- return;
+ return 1;
}
}
+
+ return 0;
}
-static void check_function(OnyxSemPassState* state, AstFunction* func) {
+static b32 check_function(OnyxSemPassState* state, AstFunction* func) {
for (AstLocal *param = func->params; param != NULL; param = (AstLocal *) param->base.next) {
if (param->base.type == NULL) {
param->base.type = type_build_from_ast(state->node_allocator, param->base.type_node);
ONYX_MESSAGE_TYPE_LITERAL,
param->base.token->pos,
"function parameter types must be known");
- return;
+ return 1;
}
if (param->base.type->Basic.size == 0) {
ONYX_MESSAGE_TYPE_LITERAL,
param->base.token->pos,
"function parameters must have non-void types");
- return;
+ return 1;
}
}
+ // NOTE: Acutally the return type
if (func->base.type == NULL) {
func->base.type = type_build_from_ast(state->node_allocator, func->base.type_node);
}
+ if ((func->base.flags & Ast_Flag_Exported) != 0) {
+ if ((func->base.flags & Ast_Flag_Foreign) != 0) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_LITERAL,
+ func->base.token->pos,
+ "exporting a foreign function");
+ return 1;
+ }
+
+ if ((func->base.flags & Ast_Flag_Intrinsic) != 0) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_LITERAL,
+ func->base.token->pos,
+ "exporting a intrinsic function");
+ return 1;
+ }
+
+ if ((func->base.flags & Ast_Flag_Inline) != 0) {
+ onyx_message_add(state->msgs,
+ ONYX_MESSAGE_TYPE_LITERAL,
+ func->base.token->pos,
+ "exporting a inlined function");
+ return 1;
+ }
+ }
+
state->expected_return_type = func->base.type;
if (func->body) {
- check_block(state, func->body);
+ return check_block(state, func->body);
+ }
+
+ return 0;
+}
+
+static b32 check_node(OnyxSemPassState* state, AstNode* node) {
+ switch (node->kind) {
+ case Ast_Kind_Function: return check_function(state, (AstFunction *) node);
+ case Ast_Kind_Block: return check_block(state, (AstBlock *) node);
+ case Ast_Kind_Assignment: return check_assignment(state, (AstAssign *) node);
+ case Ast_Kind_Return: return check_return(state, (AstReturn *) node);
+ case Ast_Kind_If: return check_if(state, (AstIf *) node);
+ case Ast_Kind_While: return check_while(state, (AstWhile *) node);
+ case Ast_Kind_Call: return check_call(state, (AstCall *) node);
+ case Ast_Kind_Binary_Op: return check_binaryop(state, (AstBinaryOp *) node);
+ default: return check_expression(state, (AstTyped *) node);
}
}
void onyx_type_check(OnyxSemPassState* state, OnyxProgram* program) {
- bh_arr_each(AstForeign *, foreign, program->foreigns)
- if ((*foreign)->import->kind == Ast_Kind_Function)
- check_function(state, (AstFunction *) (*foreign)->import);
+ // bh_arr_each(AstForeign *, foreign, program->foreigns)
+ // if ((*foreign)->import->kind == Ast_Kind_Function)
+ // check_function(state, (AstFunction *) (*foreign)->import);
- bh_arr_each(AstGlobal *, global, program->globals)
- check_global(state, *global);
+ // bh_arr_each(AstGlobal *, global, program->globals)
+ // check_global(state, *global);
- bh_arr_each(AstFunction *, function, program->functions)
- check_function(state, *function);
+ // bh_arr_each(AstFunction *, function, program->functions)
+ // check_function(state, *function);
+
+ bh_arr_each(AstNode *, node, program->nodes_to_process) {
+ check_node(state, *node);
+
+ if ((*node)->kind == Ast_Kind_Function) {
+ bh_arr_push(program->functions, (AstFunction *) *node);
+ }
+ }
}
}
LITERAL_TOKEN("struct", 1, Token_Type_Keyword_Struct);
- LITERAL_TOKEN("export", 1, Token_Type_Keyword_Export);
+// LITERAL_TOKEN("export", 1, Token_Type_Keyword_Export);
LITERAL_TOKEN("use", 1, Token_Type_Keyword_Use);
LITERAL_TOKEN("if", 1, Token_Type_Keyword_If);
LITERAL_TOKEN("elseif", 1, Token_Type_Keyword_Elseif);
LITERAL_TOKEN("else", 1, Token_Type_Keyword_Else);
- LITERAL_TOKEN("foreign", 1, Token_Type_Keyword_Foreign);
+ // LITERAL_TOKEN("foreign", 1, Token_Type_Keyword_Foreign);
LITERAL_TOKEN("return", 1, Token_Type_Keyword_Return);
LITERAL_TOKEN("proc", 1, Token_Type_Keyword_Proc);
LITERAL_TOKEN("as", 1, Token_Type_Keyword_Cast);
static void parser_prev_token(OnyxParser* parser);
static b32 is_terminating_token(TokenType token_type);
static OnyxToken* expect(OnyxParser* parser, TokenType token_type);
-static AstNumLit* parse_numeric_literal(OnyxParser* parser);
-static AstTyped* parse_factor(OnyxParser* parser);
-static AstTyped* parse_expression(OnyxParser* parser);
-static AstIf* parse_if_stmt(OnyxParser* parser);
-static AstWhile* parse_while_stmt(OnyxParser* parser);
-static b32 parse_symbol_statement(OnyxParser* parser, AstNode** ret);
-static AstReturn* parse_return_statement(OnyxParser* parser);
-static AstBlock* parse_block(OnyxParser* parser);
-static AstNode* parse_statement(OnyxParser* parser);
-static AstType* parse_type(OnyxParser* parser);
-static AstLocal* parse_function_params(OnyxParser* parser);
+
+static AstNumLit* parse_numeric_literal(OnyxParser* parser);
+static AstTyped* parse_factor(OnyxParser* parser);
+static AstTyped* parse_expression(OnyxParser* parser);
+static AstIf* parse_if_stmt(OnyxParser* parser);
+static AstWhile* parse_while_stmt(OnyxParser* parser);
+static b32 parse_symbol_statement(OnyxParser* parser, AstNode** ret);
+static AstReturn* parse_return_statement(OnyxParser* parser);
+static AstBlock* parse_block(OnyxParser* parser);
+static AstNode* parse_statement(OnyxParser* parser);
+static AstType* parse_type(OnyxParser* parser);
+static AstLocal* parse_function_params(OnyxParser* parser);
static AstFunction* parse_function_definition(OnyxParser* parser);
-static AstNode* parse_top_level_statement(OnyxParser* parser);
+static AstNode* parse_top_level_statement(OnyxParser* parser);
static void parser_next_token(OnyxParser* parser) {
parser->prev_token = parser->curr_token;
break;
}
+ case Token_Type_Keyword_Proc:
+ {
+ AstFunction* func_node = parse_function_definition(parser);
+
+ bh_arr_push(parser->results.nodes_to_process, (AstNode *) func_node);
+
+ retval = (AstTyped *) func_node;
+ break;
+ }
+
default:
onyx_message_add(parser->msgs,
ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
func_def->base.flags |= Ast_Flag_Inline;
}
+ else if (parse_possible_directive(parser, "foreign")) {
+ func_def->foreign_module = expect(parser, Token_Type_Literal_String);
+ func_def->foreign_name = expect(parser, Token_Type_Literal_String);
+
+ func_def->base.flags |= Ast_Flag_Foreign;
+ }
+
+ else if (parse_possible_directive(parser, "export")) {
+ func_def->base.flags |= Ast_Flag_Exported;
+
+ if (parser->curr_token->type == Token_Type_Literal_String) {
+ OnyxToken* str_token = expect(parser, Token_Type_Literal_String);
+ func_def->exported_name =
+ bh_aprintf(global_heap_allocator,
+ "%b",
+ str_token->text,
+ str_token->length);
+ }
+ }
+
else {
OnyxToken* directive_token = expect(parser, '#');
OnyxToken* symbol_token = expect(parser, Token_Type_Symbol);
return func_def;
}
-static AstNode* parse_foreign(OnyxParser* parser) {
- expect(parser, Token_Type_Keyword_Foreign);
-
- AstForeign* foreign = make_node(AstForeign, Ast_Kind_Foreign);
- foreign->mod_token = expect(parser, Token_Type_Literal_String);
- foreign->name_token = expect(parser, Token_Type_Literal_String);
-
- if (parser->curr_token->type == Token_Type_Keyword_Proc) {
- foreign->import = (AstNode *) parse_function_definition(parser);
-
- } else {
- AstType* type = parse_type(parser);
-
- AstGlobal* global = make_node(AstGlobal, Ast_Kind_Global);
- global->base.type_node = type;
- global->base.flags |= Ast_Flag_Lval;
-
- foreign->import = (AstNode *) global;
- }
-
- return (AstNode *) foreign;
-}
-
-static AstNode* parse_top_level_constant_symbol(OnyxParser* parser) {
- if (parser->curr_token->type == Token_Type_Keyword_Proc) {
- return (AstNode *) parse_function_definition(parser);
-
- } else if (parser->curr_token->type == Token_Type_Keyword_Struct) {
- // Handle struct case
- assert(0);
-
- } else if (parser->curr_token->type == Token_Type_Keyword_Foreign) {
- return (AstNode *) parse_foreign(parser);
-
- } else {
- // Global constant with initial value
- AstGlobal* global = make_node(AstGlobal, Ast_Kind_Global);
- global->initial_value = parse_expression(parser);
- global->base.flags |= Ast_Flag_Const;
- global->base.flags |= Ast_Flag_Lval;
- global->base.flags |= Ast_Flag_Comptime;
-
- return (AstNode *) global;
- }
+// static AstNode* parse_foreign(OnyxParser* parser) {
+// expect(parser, Token_Type_Keyword_Foreign);
+//
+// AstForeign* foreign = make_node(AstForeign, Ast_Kind_Foreign);
+// foreign->mod_token = expect(parser, Token_Type_Literal_String);
+// foreign->name_token = expect(parser, Token_Type_Literal_String);
+//
+// if (parser->curr_token->type == Token_Type_Keyword_Proc) {
+// foreign->import = (AstNode *) parse_function_definition(parser);
+//
+// } else {
+// AstType* type = parse_type(parser);
+//
+// AstGlobal* global = make_node(AstGlobal, Ast_Kind_Global);
+// global->base.type_node = type;
+// global->base.flags |= Ast_Flag_Lval;
+//
+// foreign->import = (AstNode *) global;
+// }
+//
+// return (AstNode *) foreign;
+// }
+
+static AstTyped* parse_top_level_constant_symbol(OnyxParser* parser) {
+// if (parser->curr_token->type == Token_Type_Keyword_Proc) {
+// return (AstNode *) parse_function_definition(parser);
+//
+// } else if (parser->curr_token->type == Token_Type_Keyword_Struct) {
+// // Handle struct case
+// assert(0);
+//
+// } else if (parser->curr_token->type == Token_Type_Keyword_Foreign) {
+// return (AstNode *) parse_foreign(parser);
+//
+// } else {
+// // Global constant with initial value
+// AstGlobal* global = make_node(AstGlobal, Ast_Kind_Global);
+// global->initial_value = parse_expression(parser);
+// global->base.flags |= Ast_Flag_Const;
+// global->base.flags |= Ast_Flag_Lval;
+// global->base.flags |= Ast_Flag_Comptime;
+//
+// return (AstNode *) global;
+// }
+
+ return parse_expression(parser);
}
static AstNode* parse_top_level_statement(OnyxParser* parser) {
return (AstNode *) use_node;
}
- case Token_Type_Keyword_Export:
- {
- expect(parser, Token_Type_Keyword_Export);
- if (parser->curr_token->type != Token_Type_Symbol) {
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_EXPECTED_TOKEN,
- parser->curr_token->pos,
- onyx_get_token_type_name(Token_Type_Symbol),
- onyx_get_token_type_name(parser->curr_token->type));
- break;
- }
-
- AstNode* top_level_decl = parse_top_level_statement(parser);
- top_level_decl->flags |= Ast_Flag_Exported;
- return top_level_decl;
- }
-
case Token_Type_Symbol:
{
OnyxToken* symbol = parser->curr_token;
parser_next_token(parser);
+ expect(parser, ':');
expect(parser, ':');
- AstType* type = NULL;
-
- if (parser->curr_token->type == Token_Type_Symbol) {
- type = parse_type(parser);
- }
-
- if (parser->curr_token->type == ':') {
- parser_next_token(parser);
-
- AstNode* node = parse_top_level_constant_symbol(parser);
-
- if (node->kind == Ast_Kind_Global) {
- ((AstGlobal *) node)->base.type_node = type;
- }
-
- if (node->kind == Ast_Kind_Foreign) {
- ((AstForeign *) node)->import->token = symbol;
-
- } else {
- node->token = symbol;
- }
-
- return node;
-
- } else if (parser->curr_token->type == '=') {
- parser_next_token(parser);
-
- AstGlobal* global = make_node(AstGlobal, Ast_Kind_Global);
- global->base.token = symbol;
- global->base.flags |= Ast_Flag_Lval;
- global->initial_value = parse_expression(parser);
- global->base.type_node = type;
-
- return (AstNode *) global;
+ AstTyped* node = parse_top_level_constant_symbol(parser);
+ if (node->kind == Ast_Kind_Function) {
+ node->token = symbol;
} else {
- onyx_token_null_toggle(parser->curr_token);
- onyx_message_add(parser->msgs,
- ONYX_MESSAGE_TYPE_UNEXPECTED_TOKEN,
- parser->curr_token->pos,
- parser->curr_token->text);
- onyx_token_null_toggle(parser->curr_token);
+ // HACK
+ bh_arr_push(parser->results.nodes_to_process, (AstNode *) node);
}
- return &error_node;
+ AstBinding* binding = make_node(AstBinding, Ast_Kind_Binding);
+ binding->base.token = symbol;
+ binding->node = (AstNode *) node;
+
+ return (AstNode *) binding;
}
default: break;
parser.allocator = alloc;
parser.tokenizer = tokenizer;
parser.curr_token = tokenizer->tokens;
- parser.prev_token = NULL; parser.msgs = msgs;
+ parser.prev_token = NULL;
+ parser.msgs = msgs;
+
+ parser.results = (ParseResults) {
+ .allocator = global_heap_allocator,
+
+ .uses = NULL,
+ .bindings = NULL,
+ .nodes_to_process = NULL,
+ };
+
+ bh_arr_new(parser.results.allocator, parser.results.uses, 4);
+ bh_arr_new(parser.results.allocator, parser.results.bindings, 4);
+ bh_arr_new(parser.results.allocator, parser.results.nodes_to_process, 4);
+
return parser;
}
void onyx_parser_free(OnyxParser* parser) {
}
-bh_arr(AstNode *) onyx_parse(OnyxParser *parser) {
- bh_arr(AstNode *) top_level_nodes = NULL;
- bh_arr_new(global_heap_allocator, top_level_nodes, 4);
-
+ParseResults onyx_parse(OnyxParser *parser) {
while (parser->curr_token->type != Token_Type_End_Stream) {
AstNode* curr_stmt = parse_top_level_statement(parser);
- // Building a linked list of statements down the "next" chain
if (curr_stmt != NULL && curr_stmt != &error_node) {
while (curr_stmt != NULL) {
- bh_arr_push(top_level_nodes, curr_stmt);
+
+ switch (curr_stmt->kind) {
+ case Ast_Kind_Use: bh_arr_push(parser->results.uses, (AstUse *) curr_stmt); break;
+ case Ast_Kind_Binding: bh_arr_push(parser->results.bindings, (AstBinding *) curr_stmt); break;
+ default: assert(("Invalid top level node", 0));
+ }
+
curr_stmt = curr_stmt->next;
}
}
}
- return top_level_nodes;
+ return parser->results;
}
// WASM, this function may not be needed. It brings all of the locals
// defined in sub-scopes up to the function-block level. This is a
// requirement of WASM, but not of other targets.
-static void collapse_scopes(OnyxProgram* program) {
- bh_arr(AstBlock*) traversal_queue = NULL;
- bh_arr_new(global_scratch_allocator, traversal_queue, 4);
- bh_arr_set_length(traversal_queue, 0);
-
- bh_arr_each(AstFunction *, func, program->functions) {
- AstLocalGroup* top_locals = (*func)->body->locals;
-
- bh_arr_push(traversal_queue, (*func)->body);
- while (!bh_arr_is_empty(traversal_queue)) {
- AstBlock* block = traversal_queue[0];
-
- if (block->base.kind == Ast_Kind_If) {
- AstIf* if_node = (AstIf *) block;
- if (if_node->true_block.as_block != NULL)
- bh_arr_push(traversal_queue, if_node->true_block.as_block);
-
- if (if_node->false_block.as_block != NULL)
- bh_arr_push(traversal_queue, if_node->false_block.as_block);
-
- } else {
-
- if (block->locals != top_locals && block->locals->last_local != NULL) {
- AstLocal* last_local = block->locals->last_local;
- while (last_local && last_local->prev_local != NULL) last_local = last_local->prev_local;
-
- last_local->prev_local = top_locals->last_local;
- top_locals->last_local = block->locals->last_local;
- block->locals->last_local = NULL;
- }
-
- AstNode* walker = block->body;
- while (walker) {
- if (walker->kind == Ast_Kind_Block) {
- bh_arr_push(traversal_queue, (AstBlock *) walker);
-
- } else if (walker->kind == Ast_Kind_While) {
- bh_arr_push(traversal_queue, ((AstWhile *) walker)->body);
-
- } else if (walker->kind == Ast_Kind_If) {
- if (((AstIf *) walker)->true_block.as_block != NULL)
- bh_arr_push(traversal_queue, ((AstIf *) walker)->true_block.as_block);
-
- if (((AstIf *) walker)->false_block.as_block != NULL)
- bh_arr_push(traversal_queue, ((AstIf *) walker)->false_block.as_block);
- }
-
- walker = walker->next;
- }
- }
-
- bh_arr_deleten(traversal_queue, 0, 1);
- }
- }
-}
+// static void collapse_scopes(OnyxProgram* program) {
+// bh_arr(AstBlock*) traversal_queue = NULL;
+// bh_arr_new(global_scratch_allocator, traversal_queue, 4);
+// bh_arr_set_length(traversal_queue, 0);
+//
+// bh_arr_each(AstFunction *, func, program->functions) {
+// AstLocalGroup* top_locals = (*func)->body->locals;
+//
+// bh_arr_push(traversal_queue, (*func)->body);
+// while (!bh_arr_is_empty(traversal_queue)) {
+// AstBlock* block = traversal_queue[0];
+//
+// if (block->base.kind == Ast_Kind_If) {
+// AstIf* if_node = (AstIf *) block;
+// if (if_node->true_block.as_block != NULL)
+// bh_arr_push(traversal_queue, if_node->true_block.as_block);
+//
+// if (if_node->false_block.as_block != NULL)
+// bh_arr_push(traversal_queue, if_node->false_block.as_block);
+//
+// } else {
+//
+// if (block->locals != top_locals && block->locals->last_local != NULL) {
+// AstLocal* last_local = block->locals->last_local;
+// while (last_local && last_local->prev_local != NULL) last_local = last_local->prev_local;
+//
+// last_local->prev_local = top_locals->last_local;
+// top_locals->last_local = block->locals->last_local;
+// block->locals->last_local = NULL;
+// }
+//
+// AstNode* walker = block->body;
+// while (walker) {
+// if (walker->kind == Ast_Kind_Block) {
+// bh_arr_push(traversal_queue, (AstBlock *) walker);
+//
+// } else if (walker->kind == Ast_Kind_While) {
+// bh_arr_push(traversal_queue, ((AstWhile *) walker)->body);
+//
+// } else if (walker->kind == Ast_Kind_If) {
+// if (((AstIf *) walker)->true_block.as_block != NULL)
+// bh_arr_push(traversal_queue, ((AstIf *) walker)->true_block.as_block);
+//
+// if (((AstIf *) walker)->false_block.as_block != NULL)
+// bh_arr_push(traversal_queue, ((AstIf *) walker)->false_block.as_block);
+// }
+//
+// walker = walker->next;
+// }
+// }
+//
+// bh_arr_deleten(traversal_queue, 0, 1);
+// }
+// }
+// }
void onyx_sempass(OnyxSemPassState* state, OnyxProgram* program) {
onyx_resolve_symbols(state, program);
onyx_type_check(state, program);
if (onyx_message_has_errors(state->msgs)) return;
- collapse_scopes(program);
+ // collapse_scopes(program);
}
#define BH_DEBUG
#include "onyxsempass.h"
-static void symbol_introduce(OnyxSemPassState* state, AstNode* symbol);
+static void symbol_introduce(OnyxSemPassState* state, OnyxToken* tkn, AstNode* symbol);
static void symbol_basic_type_introduce(OnyxSemPassState* state, AstBasicType* basic_type);
-static b32 symbol_unique_introduce(OnyxSemPassState* state, AstNode* symbol);
-static void symbol_remove(OnyxSemPassState* state, AstNode* symbol);
-static AstNode* symbol_resolve(OnyxSemPassState* state, AstNode* symbol);
+static b32 symbol_unique_introduce(OnyxSemPassState* state, OnyxToken* tkn, AstNode* symbol);
+static void symbol_remove(OnyxSemPassState* state, OnyxToken* tkn);
+static AstNode* symbol_resolve(OnyxSemPassState* state, OnyxToken* tkn);
static void local_group_enter(OnyxSemPassState* state, AstLocalGroup* local_group);
static void local_group_leave(OnyxSemPassState* state);
+
static void symres_local(OnyxSemPassState* state, AstLocal** local);
static void symres_call(OnyxSemPassState* state, AstCall* call);
static void symres_expression(OnyxSemPassState* state, AstTyped** expr);
static void symres_if(OnyxSemPassState* state, AstIf* ifnode);
static void symres_while(OnyxSemPassState* state, AstWhile* whilenode);
static void symres_statement_chain(OnyxSemPassState* state, AstNode* walker, AstNode** trailer);
-static b32 symres_statement(OnyxSemPassState* state, AstNode* stmt);
+static b32 symres_statement(OnyxSemPassState* state, AstNode* stmt);
static void symres_block(OnyxSemPassState* state, AstBlock* block);
static void symres_function(OnyxSemPassState* state, AstFunction* func);
static AstType* symres_type(OnyxSemPassState* state, AstType* type);
-static void symbol_introduce(OnyxSemPassState* state, AstNode* symbol) {
- onyx_token_null_toggle(symbol->token);
+static void symbol_introduce(OnyxSemPassState* state, OnyxToken* tkn, AstNode* symbol) {
+ onyx_token_null_toggle(tkn);
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->text)) {
- sp_sym->shadowed = bh_table_get(SemPassSymbol *, state->symbols, symbol->token->text);
+ if (bh_table_has(SemPassSymbol *, state->symbols, tkn->text)) {
+ sp_sym->shadowed = bh_table_get(SemPassSymbol *, state->symbols, tkn->text);
}
- bh_table_put(SemPassSymbol *, state->symbols, symbol->token->text, sp_sym);
+ bh_table_put(SemPassSymbol *, state->symbols, tkn->text, sp_sym);
if (symbol->kind == Ast_Kind_Local) {
AstLocal* local = (AstLocal *) symbol;
state->curr_local_group->last_local = local;
}
- onyx_token_null_toggle(symbol->token);
+ onyx_token_null_toggle(tkn);
}
-static void symbol_remove(OnyxSemPassState* state, AstNode* symbol) {
- onyx_token_null_toggle(symbol->token);
+static void symbol_remove(OnyxSemPassState* state, OnyxToken* tkn) {
+ onyx_token_null_toggle(tkn);
- SemPassSymbol* sp_sym = bh_table_get(SemPassSymbol *, state->symbols, symbol->token->text);
+ SemPassSymbol* sp_sym = bh_table_get(SemPassSymbol *, state->symbols, tkn->text);
if (sp_sym->shadowed) {
- bh_table_put(SemPassSymbol *, state->symbols, symbol->token->text, sp_sym->shadowed);
+ bh_table_put(SemPassSymbol *, state->symbols, tkn->text, sp_sym->shadowed);
} else {
- bh_table_delete(SemPassSymbol *, state->symbols, symbol->token->text);
+ bh_table_delete(SemPassSymbol *, state->symbols, tkn->text);
}
- onyx_token_null_toggle(symbol->token);
+ onyx_token_null_toggle(tkn);
}
-static AstNode* symbol_resolve(OnyxSemPassState* state, AstNode* symbol) {
- onyx_token_null_toggle(symbol->token);
+static AstNode* symbol_resolve(OnyxSemPassState* state, OnyxToken* tkn) {
+ onyx_token_null_toggle(tkn);
- if (!bh_table_has(SemPassSymbol *, state->symbols, symbol->token->text)) {
+ if (!bh_table_has(SemPassSymbol *, state->symbols, tkn->text)) {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_UNKNOWN_SYMBOL,
- symbol->token->pos,
- symbol->token->text);
+ tkn->pos,
+ tkn->text);
- onyx_token_null_toggle(symbol->token);
- return symbol;
+ onyx_token_null_toggle(tkn);
+ return NULL;
}
- SemPassSymbol* sp_sym = bh_table_get(SemPassSymbol *, state->symbols, symbol->token->text);
+ SemPassSymbol* sp_sym = bh_table_get(SemPassSymbol *, state->symbols, tkn->text);
- onyx_token_null_toggle(symbol->token);
+ onyx_token_null_toggle(tkn);
return sp_sym->node;
}
assert(state->curr_local_group != NULL);
for (AstLocal *walker = state->curr_local_group->last_local; walker != NULL; walker = walker->prev_local) {
- symbol_remove(state, (AstNode *) walker);
+ symbol_remove(state, walker->base.token);
}
state->curr_local_group = state->curr_local_group->prev_group;
bh_table_put(SemPassSymbol *, state->symbols, basic_type->base.name, sp_sym);
}
-static b32 symbol_unique_introduce(OnyxSemPassState* state, AstNode* symbol) {
- onyx_token_null_toggle(symbol->token);
+static b32 symbol_unique_introduce(OnyxSemPassState* state, OnyxToken* tkn, AstNode* symbol) {
+ onyx_token_null_toggle(tkn);
// NOTE: If the function hasn't already been defined
- if (!bh_table_has(SemPassSymbol *, state->symbols, symbol->token->text)) {
+ if (!bh_table_has(SemPassSymbol *, state->symbols, tkn->text)) {
SemPassSymbol* sp_sym = bh_alloc_item(state->allocator, SemPassSymbol);
sp_sym->node = symbol;
sp_sym->shadowed = NULL;
- bh_table_put(SemPassSymbol *, state->symbols, symbol->token->text, sp_sym);
+ bh_table_put(SemPassSymbol *, state->symbols, tkn->text, sp_sym);
} else {
onyx_message_add(state->msgs,
ONYX_MESSAGE_TYPE_CONFLICTING_GLOBALS,
- symbol->token->pos,
- symbol->token->text);
+ tkn->pos,
+ tkn->text);
// NOTE: I really wish C had defer...
- onyx_token_null_toggle(symbol->token);
+ onyx_token_null_toggle(tkn);
return 0;
}
- onyx_token_null_toggle(symbol->token);
+ onyx_token_null_toggle(tkn);
return 1;
}
if (type == NULL) return NULL;
if (type->kind == Ast_Kind_Symbol) {
- return (AstType *) symbol_resolve(state, (AstNode *) type);
+ return (AstType *) symbol_resolve(state, ((AstNode *) type)->token);
}
// NOTE: Already resolved
static void symres_local(OnyxSemPassState* state, AstLocal** local) {
(*local)->base.type_node = symres_type(state, (*local)->base.type_node);
- symbol_introduce(state, (AstNode *) *local);
+ symbol_introduce(state, (*local)->base.token, (AstNode *) *local);
}
static void symres_call(OnyxSemPassState* state, AstCall* call) {
- AstNode* callee = symbol_resolve(state, call->callee);
+ AstNode* callee = symbol_resolve(state, call->callee->token);
if (callee) call->callee = callee;
else DEBUG_HERE;
case Ast_Kind_Block: symres_block(state, (AstBlock *) *expr); break;
case Ast_Kind_Symbol:
- *expr = (AstTyped *) symbol_resolve(state, (AstNode *) *expr);
+ *expr = (AstTyped *) symbol_resolve(state, ((AstNode *) *expr)->token);
break;
// NOTE: This is a good case, since it means the symbol is already resolved
}
static void symres_assignment(OnyxSemPassState* state, AstAssign* assign) {
- AstTyped* lval = (AstTyped *) symbol_resolve(state, (AstNode *) assign->lval);
+ AstTyped* lval = (AstTyped *) symbol_resolve(state, assign->lval->token);
if (lval == NULL) return;
assign->lval = lval;
for (AstLocal *param = func->params; param != NULL; param = (AstLocal *) param->base.next) {
param->base.type_node = symres_type(state, param->base.type_node);
- symbol_introduce(state, (AstNode *) param);
+ symbol_introduce(state, param->base.token, (AstNode *) param);
}
if (func->base.type_node != NULL) {
symres_block(state, func->body);
for (AstLocal *param = func->params; param != NULL; param = (AstLocal *) param->base.next) {
- symbol_remove(state, (AstNode *) param);
+ symbol_remove(state, param->base.token);
+ }
+}
+
+static void symres_top_node(OnyxSemPassState* state, AstNode** node) {
+ switch ((*node)->kind) {
+ case Ast_Kind_Call:
+ case Ast_Kind_Unary_Op:
+ case Ast_Kind_Binary_Op:
+ case Ast_Kind_Literal:
+ case Ast_Kind_Symbol:
+ symres_expression(state, (AstTyped **) node);
+ break;
+
+ case Ast_Kind_Function:
+ symres_function(state, (AstFunction *) *node);
+ break;
+
+ default:
+ DEBUG_HERE;
+ break;
}
}
symbol_basic_type_introduce(state, &basic_type_rawptr);
// NOTE: Introduce all global symbols
- bh_arr_each(AstGlobal *, global, program->globals)
- if (!symbol_unique_introduce(state, (AstNode *) *global)) return;
+ // bh_arr_each(AstGlobal *, global, program->globals)
+ // if (!symbol_unique_introduce(state, (AstNode *) *global)) return;
- bh_arr_each(AstFunction *, function, program->functions)
- if (!symbol_unique_introduce(state, (AstNode *) *function)) return;
+ // bh_arr_each(AstFunction *, function, program->functions)
+ // if (!symbol_unique_introduce(state, (AstNode *) *function)) return;
- bh_arr_each(AstForeign *, foreign, program->foreigns) {
- AstKind import_kind = (*foreign)->import->kind;
+ // bh_arr_each(AstForeign *, foreign, program->foreigns) {
+ // AstKind import_kind = (*foreign)->import->kind;
- if (import_kind == Ast_Kind_Function || import_kind == Ast_Kind_Global)
- if (!symbol_unique_introduce(state, (*foreign)->import)) return;
- }
+ // if (import_kind == Ast_Kind_Function || import_kind == Ast_Kind_Global)
+ // if (!symbol_unique_introduce(state, (*foreign)->import)) return;
+ // }
- // NOTE: Then, resolve all symbols in all functions
- bh_arr_each(AstForeign *, foreign, program->foreigns) {
- if ((*foreign)->import->kind == Ast_Kind_Function) {
- symres_function(state, (AstFunction *) (*foreign)->import);
- }
- }
+ // // NOTE: Then, resolve all symbols in all functions
+ // bh_arr_each(AstForeign *, foreign, program->foreigns) {
+ // if ((*foreign)->import->kind == Ast_Kind_Function) {
+ // symres_function(state, (AstFunction *) (*foreign)->import);
+ // }
+ // }
+
+ bh_arr_each(AstBinding *, binding, program->top_level_bindings)
+ if (!symbol_unique_introduce(state, (*binding)->base.token, (*binding)->node)) return;
- bh_arr_each(AstFunction *, function, program->functions)
- symres_function(state, *function);
+ bh_arr_each(AstNode *, node, program->nodes_to_process)
+ symres_top_node(state, node);
}
"PROGRAM",
"USE",
+ "BINDING",
"FUNCTION",
"FOREIGN",
"BLOCK",
"UN_OP",
"BIN_OP",
+ "TYPE_START (BAD)"
"TYPE",
+ "POINTER_TYPE",
+ "FUNCTION_TYPE",
+ "TYPE_END (BAD)"
+
"LITERAL",
"PARAM",
"ARGUMENT",
#define WI(instr) bh_arr_push(code, ((WasmInstruction){ instr, 0x00 }));
#define WID(instr, data) bh_arr_push(code, ((WasmInstruction){ instr, data }));
-
-static void compile_function_body(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstFunction* fd);
-static void compile_block(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstBlock* block);
-static void compile_statement(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNode* stmt);
-static void compile_assign_lval(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstTyped* lval);
-static void compile_assignment(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstAssign* assign);
-static void compile_if(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstIf* if_node);
-static void compile_while(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstWhile* while_node);
-static void compile_binop(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstBinaryOp* binop);
-static void compile_unaryop(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstUnaryOp* unop);
-static void compile_call(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstCall* call);
-static void compile_intrinsic_call(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstIntrinsicCall* call);
-static void compile_expression(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstTyped* expr);
-static void compile_cast(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstUnaryOp* cast);
-static void compile_return(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstReturn* ret);
-
-static void compile_function_body(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstFunction* fd) {
+#define COMPILE_FUNC(kind, ...) static void compile_ ## kind (OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, __VA_ARGS__)
+
+COMPILE_FUNC(function_body, AstFunction* fd);
+COMPILE_FUNC(block, AstBlock* block);
+COMPILE_FUNC(statement, AstNode* stmt);
+COMPILE_FUNC(assign_lval, AstTyped* lval);
+COMPILE_FUNC(assignment, AstAssign* assign);
+COMPILE_FUNC(if, AstIf* if_node);
+COMPILE_FUNC(while, AstWhile* while_node);
+COMPILE_FUNC(binop, AstBinaryOp* binop);
+COMPILE_FUNC(unaryop, AstUnaryOp* unop);
+COMPILE_FUNC(call, AstCall* call);
+COMPILE_FUNC(intrinsic_call, AstIntrinsicCall* call);
+COMPILE_FUNC(expression, AstTyped* expr);
+COMPILE_FUNC(cast, AstUnaryOp* cast);
+COMPILE_FUNC(return, AstReturn* ret);
+
+COMPILE_FUNC(function_body, AstFunction* fd) {
if (fd->body == NULL) return;
bh_arr(WasmInstruction) code = *pcode;
*pcode = code;
}
-static void compile_block(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstBlock* block) {
+COMPILE_FUNC(block, AstBlock* block) {
bh_arr(WasmInstruction) code = *pcode;
bh_arr_push(mod->structured_jump_target, 1);
*pcode = code;
}
-static void compile_structured_jump(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, b32 jump_backward) {
+COMPILE_FUNC(structured_jump, b32 jump_backward) {
bh_arr(WasmInstruction) code = *pcode;
i32 labelidx = 0;
*pcode = code;
}
-static void compile_statement(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstNode* stmt) {
+COMPILE_FUNC(statement, AstNode* stmt) {
bh_arr(WasmInstruction) code = *pcode;
switch (stmt->kind) {
*pcode = code;
}
-static void compile_assign_lval(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstTyped* lval) {
+COMPILE_FUNC(assign_lval, AstTyped* lval) {
bh_arr(WasmInstruction) code = *pcode;
if (lval->kind == Ast_Kind_Local || lval->kind == Ast_Kind_Param) {
*pcode = code;
}
-static void compile_if(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstIf* if_node) {
+COMPILE_FUNC(if, AstIf* if_node) {
bh_arr(WasmInstruction) code = *pcode;
compile_expression(mod, &code, if_node->cond);
*pcode = code;
}
-static void compile_while(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstWhile* while_node) {
+COMPILE_FUNC(while, AstWhile* while_node) {
bh_arr(WasmInstruction) code = *pcode;
WID(WI_BLOCK_START, 0x40);
*pcode = code;
}
-static void compile_assignment(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstAssign* assign) {
+COMPILE_FUNC(assignment, AstAssign* assign) {
bh_arr(WasmInstruction) code = *pcode;
compile_expression(mod, &code, assign->expr);
/* GTE */ { WI_I32_GE_S, WI_I64_GE_S, WI_F32_GE, WI_F64_GE },
};
-static void compile_binop(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstBinaryOp* binop) {
+COMPILE_FUNC(binop, AstBinaryOp* binop) {
bh_arr(WasmInstruction) code = *pcode;
b32 is_sign_significant = 0;
*pcode = code;
}
-static void compile_unaryop(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstUnaryOp* unop) {
+COMPILE_FUNC(unaryop, AstUnaryOp* unop) {
bh_arr(WasmInstruction) code = *pcode;
switch (unop->operation) {
*pcode = code;
}
-static void compile_call(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstCall* call) {
+COMPILE_FUNC(call, AstCall* call) {
bh_arr(WasmInstruction) code = *pcode;
for (AstArgument *arg = call->arguments;
*pcode = code;
}
-static void compile_intrinsic_call(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstIntrinsicCall* call) {
+COMPILE_FUNC(intrinsic_call, AstIntrinsicCall* call) {
bh_arr(WasmInstruction) code = *pcode;
i32 place_arguments_normally = 1;
*pcode = code;
}
-static void compile_expression(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstTyped* expr) {
+COMPILE_FUNC(expression, AstTyped* expr) {
bh_arr(WasmInstruction) code = *pcode;
switch (expr->kind) {
/* F64 */ { WI_I32_FROM_F64_S, WI_I32_FROM_F64_U, WI_I64_FROM_F64_S, WI_I64_FROM_F64_U, WI_F32_FROM_F64, WI_NOP, },
};
-static void compile_cast(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstUnaryOp* cast) {
+COMPILE_FUNC(cast, AstUnaryOp* cast) {
bh_arr(WasmInstruction) code = *pcode;
compile_expression(mod, &code, cast->expr);
*pcode = code;
}
-static void compile_return(OnyxWasmModule* mod, bh_arr(WasmInstruction)* pcode, AstReturn* ret) {
+COMPILE_FUNC(return, AstReturn* ret) {
bh_arr(WasmInstruction) code = *pcode;
if (ret->expr) {
i32 type_idx = generate_type_idx(mod, fd);
+ if (fd->base.flags & Ast_Flag_Foreign) {
+ WasmImport import = {
+ .kind = WASM_FOREIGN_FUNCTION,
+ .idx = type_idx,
+ .mod = fd->foreign_module,
+ .name = fd->foreign_name,
+ };
+
+ bh_arr_push(mod->imports, import);
+ return;
+ }
+
WasmFunc wasm_func = {
.type_idx = type_idx,
.locals = {
void onyx_wasm_module_compile(OnyxWasmModule* module, OnyxProgram* program) {
// NOTE: First, introduce all indicies for globals and functions
- bh_arr_each(AstForeign *, foreign, program->foreigns) {
- AstKind import_kind = (*foreign)->import->kind;
+ // bh_arr_each(AstForeign *, foreign, program->foreigns) {
+ // AstKind import_kind = (*foreign)->import->kind;
- if (import_kind == Ast_Kind_Function) {
- module->next_func_idx++;
- bh_imap_put(&module->func_map, (u64) (*foreign)->import, module->next_import_func_idx++);
- }
- else if (import_kind == Ast_Kind_Global) {
- module->next_global_idx++;
- bh_imap_put(&module->global_map, (u64) (*foreign)->import, module->next_import_global_idx++);
- }
+ // if (import_kind == Ast_Kind_Function) {
+ // module->next_func_idx++;
+ // bh_imap_put(&module->func_map, (u64) (*foreign)->import, module->next_import_func_idx++);
+ // }
+ // else if (import_kind == Ast_Kind_Global) {
+ // module->next_global_idx++;
+ // bh_imap_put(&module->global_map, (u64) (*foreign)->import, module->next_import_global_idx++);
+ // }
- compile_foreign(module, *foreign);
+ // compile_foreign(module, *foreign);
+ // }
+
+ bh_arr_each(AstFunction *, function, program->functions) {
+ if ((*function)->base.flags & Ast_Flag_Foreign) {
+ bh_imap_put(&module->func_map, (u64) *function, module->next_func_idx++);
+ }
}
bh_arr_each(AstFunction *, function, program->functions) {
+ if ((*function)->base.flags & Ast_Flag_Foreign) continue;
+
if (((*function)->base.flags & Ast_Flag_Intrinsic) == 0)
bh_imap_put(&module->func_map, (u64) *function, module->next_func_idx++);
}
- bh_arr_each(AstGlobal *, global, program->globals)
- bh_imap_put(&module->global_map, (u64) *global, module->next_global_idx++);
-
-
// NOTE: Then, compile everything
bh_arr_each(AstFunction *, function, program->functions)
compile_function(module, *function);
- bh_arr_each(AstGlobal *, global, program->globals)
- compile_global_declaration(module, *global);
+ // bh_arr_each(AstGlobal *, global, program->globals)
+ // compile_global_declaration(module, *global);
}
void onyx_wasm_module_free(OnyxWasmModule* module) {