From: Brendan Hansen Date: Thu, 2 Jul 2020 02:34:10 +0000 (-0500) Subject: 'use' statements work; only initial version X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=19af8b7a3724f959149428a2ed6adc5e332af550;p=onyx.git 'use' statements work; only initial version --- diff --git a/.vimspector.json b/.vimspector.json index b101bde4..e309f7f9 100644 --- a/.vimspector.json +++ b/.vimspector.json @@ -6,7 +6,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/onyx", - "args": ["-ast", "progs/other.onyx", "progs/test.onyx"], + "args": ["progs/test.onyx"], "stopAtEntry": true, "cwd": "${workspaceFolder}", "environment": [], diff --git a/docs/thoughts b/docs/thoughts index de164d11..639488db 100644 --- a/docs/thoughts +++ b/docs/thoughts @@ -1,26 +1,18 @@ -Type checking at parse time: - Why couldn't this work? - * Every variable is of known type or the type must be known by immediate assignment - * This requires that functions are declared in a particular order like C - * This also requires immediate evaluation of external symbols (C #include style) - - Don't like this at all - - Want a proper module system <<<< +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 - /* foo.onyx */ - foo :: proc (a i32) -> f32 { - return a as f32; - } + - 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 - /* main.onyx */ - use "foo"; - - export main :: proc () -> void { - a := 2.0f + foo(5); - } - - foo(5) would have a left node of SYMBOL:foo - This will be resolved in a later stage between the parsing and semantic pass - Type checking and resolving would have to occur afterwards - -Creating an IR: + Example use: + {{{ + ptr: ^i32 = 0; // Address starting at 0 + ptr_ptr := ^ptr; + }}} diff --git a/include/onyxparser.h b/include/onyxparser.h index 6f256564..137a1987 100644 --- a/include/onyxparser.h +++ b/include/onyxparser.h @@ -20,6 +20,7 @@ typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef; typedef struct OnyxAstNodeForeign OnyxAstNodeForeign; typedef struct OnyxAstNodeGlobal OnyxAstNodeGlobal; typedef struct OnyxAstNodeCall OnyxAstNodeCall; +typedef struct OnyxAstNodeUse OnyxAstNodeUse; typedef struct OnyxAstNodeFile OnyxAstNodeFile; typedef struct OnyxParser { @@ -38,6 +39,7 @@ typedef struct OnyxParser { typedef enum OnyxAstNodeKind { ONYX_AST_NODE_KIND_ERROR, ONYX_AST_NODE_KIND_PROGRAM, + ONYX_AST_NODE_KIND_USE, ONYX_AST_NODE_KIND_FUNCDEF, ONYX_AST_NODE_KIND_FOREIGN, @@ -265,6 +267,16 @@ struct OnyxAstNodeCall { // unless this becomes used by something else }; +struct OnyxAstNodeUse { + OnyxAstNodeKind kind; + u32 flags; + OnyxToken *token; + OnyxTypeInfo *type; + u64 data; + OnyxAstNode *next; + OnyxToken *filename; +}; + struct OnyxAstNodeFile { OnyxAstNodeKind kind; u32 flags; @@ -302,6 +314,7 @@ union OnyxAstNode { OnyxAstNodeGlobal as_global; OnyxAstNodeIf as_if; OnyxAstNodeWhile as_while; + OnyxAstNodeUse as_use; OnyxAstNodeFile as_file; }; diff --git a/misc/onyx.vim b/misc/onyx.vim index 6a375966..8edffc14 100644 --- a/misc/onyx.vim +++ b/misc/onyx.vim @@ -16,8 +16,9 @@ syn keyword onyxKeyword for while loop return do syn keyword onyxKeyword break continue return syn keyword onyxKeyword as -syn keyword onyxType i32 -syn keyword onyxType i64 +syn keyword onyxType unknown bool +syn keyword onyxType i32 u32 +syn keyword onyxType i64 u64 syn keyword onyxType f32 syn keyword onyxType f64 diff --git a/onyx b/onyx index eb5f5418..c8eaac11 100755 Binary files a/onyx and b/onyx differ diff --git a/progs/break_test.onyx b/progs/break_test.onyx index c5cb7b20..f2847cca 100644 --- a/progs/break_test.onyx +++ b/progs/break_test.onyx @@ -1,5 +1,5 @@ -print :: foreign "host" "print" proc (val i32) --- +print :: foreign "host" "print" proc (val: i32) --- export main :: proc { diff --git a/progs/game.onyx b/progs/game.onyx deleted file mode 100644 index a37d4c59..00000000 --- a/progs/game.onyx +++ /dev/null @@ -1,14 +0,0 @@ - -gfx_draw_rect :: foreign "gfx" "draw_rect" proc (x i32, y i32, w i32, h i32) --- - -export main :: proc { - update(); - draw(); -} - -update :: proc {} - -draw :: proc { - gfx_draw_rect(0, 0, 100, 100); -} - diff --git a/progs/minimal.onyx b/progs/minimal.onyx index 7fd39bb6..3d4fac4f 100644 --- a/progs/minimal.onyx +++ b/progs/minimal.onyx @@ -1,7 +1,7 @@ -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) --- +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() - 1; @@ -25,7 +25,7 @@ export main :: proc { print_if(output, float_test()); } -factorial :: proc (n i32) -> i32 { +factorial :: proc (n: i32) -> i32 { if n <= 1 { return 1; } return n * factorial(n - 1); @@ -35,7 +35,7 @@ foo :: proc -> i32 { return 10; } -add :: proc (a i32, b i32) -> i32 { +add :: proc (a: i32, b: i32) -> i32 { return a + b; } @@ -43,18 +43,18 @@ add :: proc (a i32, b i32) -> i32 { // This is because the WASM embedder does not think that it // is possible that all code paths are covered with returning // an i32. This will need to be fixed. -abs :: proc (val i32) -> i32 { +abs :: proc (val: i32) -> i32 { if val <= 0 { return -val; } // else { return val; }; return val; } -fib :: proc (n i32) -> i32 { +fib :: proc (n: i32) -> i32 { if n <= 1 { return 1; } return fib(n - 1) + fib(n - 2); } -diff_square :: proc (a i32, b i32) -> i32 { +diff_square :: proc (a: i32, b: i32) -> i32 { // Typechecked c := a - b; // Mutable d :: a + b; // Constant diff --git a/progs/mvp.onyx b/progs/mvp.onyx deleted file mode 100644 index f75b65dd..00000000 --- a/progs/mvp.onyx +++ /dev/null @@ -1,21 +0,0 @@ -foreign console { - log :: proc (data ptr, length i32) -> void ---; -} - -export add :: proc (a i32, b i32) -> i32 { - return a + b; -} - -export max :: proc (a i32, b i32) -> i32 { - // Curly braces are always required - if a > b { - return a; - } else { - return b; - } -} - -export main :: proc () { - console.log(add(2, 3)); - console.log(max(5, 10)); -} diff --git a/progs/new_minimal.onyx b/progs/new_minimal.onyx index d0e74323..d561a466 100644 --- a/progs/new_minimal.onyx +++ b/progs/new_minimal.onyx @@ -1,4 +1,4 @@ -print :: foreign "host" "print" proc (value i32) --- +print :: foreign "host" "print" proc (value: i32) --- simple_test :: proc { a: i32 = 5; @@ -13,7 +13,7 @@ simple_test :: proc { print(c); } -fib :: proc (n i32) -> i32 { +fib :: proc (n: i32) -> i32 { if n <= 1 { return 1; } return fib(n - 1) + fib(n - 2); @@ -23,7 +23,7 @@ foo :: proc -> i32 { return 10; } -print_nums :: proc (a i32, b i32, c i32) { +print_nums :: proc (a: i32, b: i32, c: i32) { print(a); print(b); print(c); diff --git a/progs/other.onyx b/progs/other.onyx index 253c1a48..a9202ace 100644 --- a/progs/other.onyx +++ b/progs/other.onyx @@ -1,9 +1,11 @@ -other_value :: proc (n i32) -> i32 { +use "progs/test" + +other_value :: proc (n: i32) -> i32 { return 8675309 + something_else(n) + global_value; } -export fib :: proc (n i32) -> i32 { +export fib :: proc (n: i32) -> i32 { if n == 0 { return 1; } if n == 1 { return 1; } @@ -23,7 +25,7 @@ export fib :: proc (n i32) -> i32 { return a; } -export factorial :: proc (n i32) -> i32 { +export factorial :: proc (n: i32) -> i32 { if n <= 1 { return 1; } f := 1; diff --git a/progs/print_funcs.onyx b/progs/print_funcs.onyx new file mode 100644 index 00000000..bb2a3d60 --- /dev/null +++ b/progs/print_funcs.onyx @@ -0,0 +1,5 @@ +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) --- diff --git a/progs/test.onyx b/progs/test.onyx index 54069430..74600384 100644 --- a/progs/test.onyx +++ b/progs/test.onyx @@ -1,27 +1,15 @@ +use "progs/print_funcs" +use "progs/other" -// Foreign functions are included this way: -// sym_name :: foreign "module" "name" proc ... - -// TODO: Make this work -// use "progs/other"; - -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) --- - -something_else :: proc (n i32) -> i32 { +something_else :: proc (n: i32) -> i32 { return 100 * n + global_value; } -// symbol :: proc {} Global function -// symbol :: struct { x i32, y i32 } -// symbol :: foreign "" "" proc Global foreign function -// symbol :: foreign "" "" i32 Global foreign mutable i32 -// symbol :: 5 Global constant value -// symbol := 5 Global mutable variable +global_value := 100 -export global_value := 100 +export in_unit_circle :: proc (x: f32, y: f32) -> bool { + return (x * x) + (y * y) < 1.0f; +} // This is the entry point export main2 :: proc { diff --git a/src/onyx.c b/src/onyx.c index 4d3fafca..c7decfff 100644 --- a/src/onyx.c +++ b/src/onyx.c @@ -13,12 +13,15 @@ static const char* docstring = "Onyx compiler version " VERSION "\n" "\n" - "The standard compiler for the Onyx programming language.\n" + "The compiler for the Onyx programming language.\n" "\n" - " $ onyx [-o ] [--help] \n" - "\n" - " -o Specify the target file\n" - " --help Print this help message\n"; + "Usage:\n" + "\tonyx [-o ] [-ast] \n" + "\tonyx -help\n" + "\nFlags:\n" + "\t-o Specify the target file (default: out.wasm)\n" + "\t-ast Print the abstract syntax tree after parsing\n" + "\t-help Print this help message\n"; typedef enum CompileAction { ONYX_COMPILE_ACTION_COMPILE, @@ -48,7 +51,12 @@ typedef enum CompilerProgress { typedef struct CompilerState { bh_arena ast_arena, msg_arena, sp_arena; bh_allocator token_alloc, ast_alloc, msg_alloc, sp_alloc; + bh_table(bh_file_contents) loaded_files; + bh_arr(const char *) queued_files; + + OnyxAstNodeFile* first_file; + OnyxAstNodeFile* last_processed_file; OnyxMessages msgs; OnyxWasmModule wasm_mod; @@ -68,7 +76,7 @@ static OnyxCompileOptions compile_opts_parse(bh_allocator alloc, int argc, char bh_arr_new(alloc, options.files, 1); fori(i, 1, argc - 1) { - if (!strcmp(argv[i], "--help")) { + if (!strcmp(argv[i], "-help")) { options.action = ONYX_COMPILE_ACTION_PRINT_HELP; break; } @@ -92,17 +100,78 @@ void compile_opts_free(OnyxCompileOptions* opts) { bh_arr_free(opts->files); } -OnyxAstNodeFile* parse_source_file(bh_file_contents* file_contents, CompilerState* compiler_state) { +OnyxAstNodeFile* 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 OnyxTokenizer tokenizer = onyx_tokenizer_create(compiler_state->token_alloc, file_contents); - bh_printf("Lexing %s\n", file_contents->filename); + bh_printf("[Lexing] %s\n", file_contents->filename); onyx_lex_tokens(&tokenizer); - bh_printf("Parsing %s\n", file_contents->filename); + bh_printf("[Parsing] %s\n", file_contents->filename); OnyxParser parser = onyx_parser_create(compiler_state->ast_alloc, &tokenizer, &compiler_state->msgs); return onyx_parse(&parser); } +CompilerProgress process_source_file(CompilerState* compiler_state, OnyxCompileOptions* opts, char* filename) { + if (bh_table_has(bh_file_contents, compiler_state->loaded_files, filename)) return ONYX_COMPILER_PROGRESS_SUCCESS; + + bh_file file; + + bh_file_error err = bh_file_open(&file, filename); + if (err != BH_FILE_ERROR_NONE) { + bh_printf_err("Failed to open file %s\n", filename); + return ONYX_COMPILER_PROGRESS_FAILED_READ; + } + + bh_printf("[Reading] %s\n", file.filename); + bh_file_contents fc = bh_file_read_contents(compiler_state->token_alloc, &file); + bh_file_close(&file); + + bh_table_put(bh_file_contents, compiler_state->loaded_files, (char *) filename, fc); + // NOTE: Need to reget the value out of the table so token references work + fc = bh_table_get(bh_file_contents, compiler_state->loaded_files, (char *) filename); + + OnyxAstNodeFile* file_node = parse_source_file(compiler_state, &fc); + + if (opts->print_ast) { + onyx_ast_print((OnyxAstNode *) file_node, 0); + bh_printf("\n"); + } + + if (!compiler_state->first_file) + compiler_state->first_file = file_node; + + if (compiler_state->last_processed_file) + compiler_state->last_processed_file->next = file_node; + + compiler_state->last_processed_file = file_node; + + + // HACK: This is very cobbled together right now but it will + // do for now + OnyxAstNode* walker = file_node->contents; + while (walker) { + if (walker->kind == ONYX_AST_NODE_KIND_USE) { + OnyxAstNodeUse* use_node = &walker->as_use; + + char* formatted_name = bh_aprintf( + global_heap_allocator, + "%b.onyx", + use_node->filename->token, use_node->filename->length); + + bh_arr_push(compiler_state->queued_files, formatted_name); + } + + walker = walker->next; + } + + + if (onyx_message_has_errors(&compiler_state->msgs)) { + return ONYX_COMPILER_PROGRESS_FAILED_PARSE; + } else { + return ONYX_COMPILER_PROGRESS_SUCCESS; + } +} + i32 onyx_compile(OnyxCompileOptions* opts, CompilerState* compiler_state) { bh_arena_init(&compiler_state->msg_arena, opts->allocator, 4096); @@ -120,71 +189,53 @@ i32 onyx_compile(OnyxCompileOptions* opts, CompilerState* compiler_state) { bh_arena_init(&compiler_state->sp_arena, opts->allocator, 16 * 1024); compiler_state->sp_alloc = bh_arena_allocator(&compiler_state->sp_arena); - bh_table_init(opts->allocator, compiler_state->loaded_files, 7); + bh_table_init(opts->allocator, compiler_state->loaded_files, 15); - bh_arr_each(const char *, filename, opts->files) { - bh_file file; + bh_arr_new(opts->allocator, compiler_state->queued_files, 4); - bh_file_error err = bh_file_open(&file, *filename); - if (err != BH_FILE_ERROR_NONE) { - bh_printf_err("Failed to open file %s\n", *filename); - return ONYX_COMPILER_PROGRESS_FAILED_READ; - } + // NOTE: Add all files passed by command line to the queue + bh_arr_each(const char *, filename, opts->files) + bh_arr_push(compiler_state->queued_files, (char *) *filename); - bh_printf("Reading %s\n", file.filename); - bh_file_contents fc = bh_file_read_contents(compiler_state->token_alloc, &file); - bh_file_close(&file); - bh_table_put(bh_file_contents, compiler_state->loaded_files, (char *) filename, fc); - } + // NOTE: While the queue is not empty, process the next file + while (!bh_arr_is_empty(compiler_state->queued_files)) { + CompilerProgress result = process_source_file(compiler_state, opts, (char *) compiler_state->queued_files[0]); - OnyxAstNodeFile* root_file = NULL; - OnyxAstNodeFile* prev_file = NULL; - bh_table_each_start(bh_file_contents, compiler_state->loaded_files); - OnyxAstNodeFile* file_node = parse_source_file(&value, compiler_state); - - if (opts->print_ast) { - onyx_ast_print((OnyxAstNode *) file_node, 0); - bh_printf("\n"); - } + if (result != ONYX_COMPILER_PROGRESS_SUCCESS) + return result; - if (!root_file) { - root_file = file_node; - } - - if (prev_file) { - prev_file->next = file_node; - } - - prev_file = file_node; - bh_table_each_end; - - if (onyx_message_has_errors(&compiler_state->msgs)) { - return ONYX_COMPILER_PROGRESS_FAILED_PARSE; + bh_arr_fastdelete(compiler_state->queued_files, 0); } - bh_printf("Checking semantics and types\n"); + + // NOTE: Check types and semantic rules + bh_printf("[Checking semantics]\n"); OnyxSemPassState sp_state = onyx_sempass_create(compiler_state->sp_alloc, compiler_state->ast_alloc, &compiler_state->msgs); - onyx_sempass(&sp_state, root_file); + onyx_sempass(&sp_state, compiler_state->first_file); if (onyx_message_has_errors(&compiler_state->msgs)) { return ONYX_COMPILER_PROGRESS_FAILED_SEMPASS; } - bh_printf("Creating WASM code\n"); + + // NOTE: Generate WASM instructions + bh_printf("[Generating WASM]\n"); compiler_state->wasm_mod = onyx_wasm_module_create(opts->allocator, &compiler_state->msgs); - onyx_wasm_module_compile(&compiler_state->wasm_mod, root_file); + onyx_wasm_module_compile(&compiler_state->wasm_mod, compiler_state->first_file); if (onyx_message_has_errors(&compiler_state->msgs)) { return ONYX_COMPILER_PROGRESS_FAILED_BINARY_GEN; } + + // NOTE: Output to file bh_file output_file; if (bh_file_create(&output_file, opts->target_file) != BH_FILE_ERROR_NONE) { return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT; } - bh_printf("Writing WASM to %s\n", output_file.filename); + bh_printf("[Writing WASM] %s\n", output_file.filename); onyx_wasm_module_write_to_file(&compiler_state->wasm_mod, output_file); return ONYX_COMPILER_PROGRESS_SUCCESS; diff --git a/src/onyxparser.c b/src/onyxparser.c index 3a698b6a..5cf6f232 100644 --- a/src/onyxparser.c +++ b/src/onyxparser.c @@ -6,6 +6,7 @@ static const char* ast_node_names[] = { "ERROR", "PROGRAM", + "USE", "FUNCDEF", "FOREIGN", @@ -669,6 +670,7 @@ static OnyxAstNodeParam* parse_function_params(OnyxParser* parser) { if (parser->curr_token->type == TOKEN_TYPE_SYM_COMMA) parser_next_token(parser); symbol = expect(parser, TOKEN_TYPE_SYMBOL); + expect(parser, TOKEN_TYPE_SYM_COLON); curr_param = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PARAM); curr_param->token = symbol; @@ -759,8 +761,13 @@ static OnyxAstNode* parse_top_level_constant_symbol(OnyxParser* parser) { static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) { switch (parser->curr_token->type) { case TOKEN_TYPE_KEYWORD_USE: - assert(0); - break; + { + OnyxAstNodeUse* use_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_USE); + use_node->token = expect(parser, TOKEN_TYPE_KEYWORD_USE); + use_node->filename = expect(parser, TOKEN_TYPE_LITERAL_STRING); + + return (OnyxAstNode *) use_node; + } case TOKEN_TYPE_KEYWORD_EXPORT: { diff --git a/src/onyxutils.c b/src/onyxutils.c index cae6546a..b40a44c0 100644 --- a/src/onyxutils.c +++ b/src/onyxutils.c @@ -28,6 +28,16 @@ void onyx_ast_print(OnyxAstNode* node, i32 indent) { break; } + case ONYX_AST_NODE_KIND_USE: { + OnyxAstNodeUse* use_node = &node->as_use; + bh_printf("%b", use_node->filename->token, use_node->filename->length); + + if (use_node->next) + onyx_ast_print(use_node->next, indent); + + break; + } + case ONYX_AST_NODE_KIND_FUNCDEF: { if (node->token) bh_printf("(%b) ", node->token->token, node->token->length);