ox

The Ox programming language, compiler and tools (WIP)
Log | Files | Refs | README | LICENSE

commit 5c9795844aebf8f4141ad09db177de3d49e79008
parent bc83bc7bc01c35826d4631c27d04f04c0677e0ae
Author: citbl <citbl@citbl.org>
Date:   Sun,  7 Dec 2025 11:39:47 +1000

handle fx/fn function definitions

Diffstat:
M.zed/debug.json | 2+-
MTODO | 5+++++
Moxdesign.ox | 52+++++++++++++++++++++++++++-------------------------
Msrc/gen/gen.c | 10++++++----
Msrc/parser.h | 2++
Msrc/parser/decl.c | 11+++++++++++
Msrc/parser/parser.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/types.h | 2+-
Mtests/ex-return-func-call.ox | 6+++---
Mtests/ex1.ox | 20--------------------
10 files changed, 107 insertions(+), 56 deletions(-)

diff --git a/.zed/debug.json b/.zed/debug.json @@ -11,7 +11,7 @@ "cwd": "$ZED_WORKTREE_ROOT" }, "program": "$ZED_WORKTREE_ROOT/oxc", - "args": ["$ZED_WORKTREE_ROOT/tests/ex-binop-simple1.ox"], + "args": ["$ZED_WORKTREE_ROOT/tests/ex-return-func-call.ox"], "request": "launch", "adapter": "CodeLLDB" } diff --git a/TODO b/TODO @@ -13,7 +13,11 @@ [x] call another function that prints the passed argument [x] if without { } should be an error, e.g. `if(true) break;` doesn't work [x] inner blocks are ignored completely { { } } +[ ] type checking +[ ] casting [ ] while loops +[ ] typer +[ ] matcher @later @@ -21,6 +25,7 @@ [-] ARC memory management, new keyword. [ ] Instantiating anything should initialise it [ ] instantiating anything in a loop should be a warning (store curr_loop in ctx) +[ ] when as a post-fixed of a statement @cruft [x] redo arguments as list and not linked list, handle in parse and in gen (2 places in gen?) diff --git a/oxdesign.ox b/oxdesign.ox @@ -1,23 +1,14 @@ -var a i64 = 42; // make a integer 32bit that is a variable (can be updated) `~` initialised with 2. -let b i64 = 42; // like a "let" statement or final variable, runtime constant. -const C i32 = 42; // compile time constant +i64 a = 42; // like a "let" statement or final variable, runtime constant. +#i32 X = 42; // compile time constant +~i64 z = 42; // make a integer 32bit that is a variable (can be updated) `~` initialised with 2. -let x i32 = 35; -var y i32 = 12; -y = 15; +z = 67; -let x = i32(35); -let y = 55; // auto i64 -let z = 40.3; // auto f64 (double) -let a = "some"; -let b = str("some"); -let c = 'c'; - -let x<i32> = <i32>12; - -let ages<arr<i32>>[14] = []; -let names<set<str>> = {}; +str b = str("some string somehow"); +chr c = 'c'; +arr<i32>[14] = arr[1, 2, 3]; +set<str> names = {}; fx add (x i32, y i32) i32 { return x + y; @@ -25,11 +16,23 @@ fx add (x i32, y i32) i32 { fx flip(a some, b some) (some, some) => b, a - fx add (i i32, j i32) i32 { return i + j; } +struct Person { + +} + +fx main() { + print("Hello, world!"); +} + +fx fetch_url(str url) str?! async { + str res = await fetch(url); + return res; +} + // default types // i8, i16, i32, i64, // u8, u16, u32, u64, @@ -37,14 +40,13 @@ fx add (i i32, j i32) i32 { // str, chr, bool, // arrays (fixed) and lists (dynamic) -let arr<i32>[16] ages; // fixed slice of 16 -let vec<i32> bobs; // dynamic list -let set<str> names; // sets +arr<i32>[16] ages; // fixed slice of 16 +vec<i32> bobs; // dynamic list +set<str> names; // sets - -struct Person { - i32 age, - str name, +struct<Person> { + i32 age; + str name; } fn say_hello () void { diff --git a/src/gen/gen.c b/src/gen/gen.c @@ -314,8 +314,7 @@ handle_bin_expr(Gen* gen, Node* node, int gcc_jit_op, bool cmp) || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG)) || (Rt == gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_UNSIGNED_LONG_LONG)); - gcc_jit_type* T - = gcc_jit_context_get_type(ctx, any_unsigned ? GCC_JIT_TYPE_UINT64_T : GCC_JIT_TYPE_INT64_T); + gcc_jit_type* T = gcc_jit_context_get_type(ctx, any_unsigned ? GCC_JIT_TYPE_UINT64_T : GCC_JIT_TYPE_INT64_T); L = gcc_jit_context_new_cast(ctx, loc, L, T); R = gcc_jit_context_new_cast(ctx, loc, R, T); @@ -735,6 +734,8 @@ handle_expr(Gen* gen, Node* node) static gcc_jit_type* ox_type_to_c_type(Gen* gen, Node* node) { + if (node == NULL) return type_void; + const char* type_name = span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }); if (strcmp(type_name, "int") == 0) { @@ -1062,8 +1063,9 @@ build_statement(Gen* gen, Node* node) case NODE_RETURN: { Node* return_expr = node->data.ret.expr; if (return_expr) { - gcc_jit_rvalue* rv = handle_expr(gen, return_expr); - gcc_jit_block_end_with_return(gen->curr_block, loc, rv); + gcc_jit_rvalue* ret_val = handle_expr(gen, return_expr); + gcc_jit_rvalue_get_type(ret_val); + gcc_jit_block_end_with_return(gen->curr_block, loc, ret_val); } else { gcc_jit_block_end_with_void_return(gen->curr_block, loc); } diff --git a/src/parser.h b/src/parser.h @@ -33,9 +33,11 @@ Node* parse_assignment_expr(Parser*); Node* parse_statement(Parser*); Node* parse_block(Parser*); Node* parse_declaration_statement(Parser*); +Node* parse_func_decl(Parser*); Node* parse_decl_or_func_decl(Parser*); NodeVec parse_param_list(Parser*); Node* parse_type(Parser*); +Node* parse_type_or_void(Parser*); Node* parse_func_call(Parser*); NodeVec parse_func_arguments(Parser*); Node* parse_if(Parser*); diff --git a/src/parser/decl.c b/src/parser/decl.c @@ -7,6 +7,17 @@ #include <assert.h> Node* +parse_type_or_void(Parser* par) +{ + Token tok = peek(par); + if (tok.type == TOKEN_COMP_TIME || tok.type == TOKEN_VARIADIC || tok.type == TOKEN_IDENT) { + return parse_type(par); + } else { + return NULL; + } +} + +Node* parse_type(Parser* par) { bool comp_time = (peek(par).type == TOKEN_COMP_TIME); diff --git a/src/parser/parser.c b/src/parser/parser.c @@ -232,7 +232,7 @@ parse_statement(Parser* par) bool tok_is_a_type = (tok.type == TOKEN_IDENT || tok.type == TOKEN_VARIADIC || tok.type == TOKEN_COMP_TIME); - if (tok_is_a_type && tok2.type == TOKEN_IDENT) { return parse_decl_or_func_decl(par); } + if (tok_is_a_type && tok2.type == TOKEN_IDENT) { return parse_decl_or_func_decl(par); } // TODO @next FN/FX handling here too if (tok_is_a_type && tok2.type == TOKEN_EQUAL) { return parse_assignment(par); } switch (tok.type) { @@ -280,7 +280,7 @@ parse_block(Parser* par) block->data.block.stmts[block->data.block.len++] = stmt; } - expect(par, TOKEN_RBRACE); + expect(par, TOKEN_RBRACE); // TODO move out to parent caller // TODO next the parsing of this was relying on next and cannot // anymmore, e.g. print return block; @@ -312,6 +312,51 @@ parse_declaration_statement(Parser* par) } Node* +parse_func_decl(Parser* par) +{ + bool exported = false; // TODO use exported in the sem/typer/matcher/gen + if (match(par, TOKEN_FX)) { + // parse exported function + exported = true; + } else if (match(par, TOKEN_FN)) { + // parse internal function + } else { + panic("parse_func_decl: func: expected fn/fx"); + } + + Token ident = expect(par, TOKEN_IDENT); // variable or function name + + expect(par, TOKEN_LPAREN); + NodeVec params = parse_param_list(par); + expect(par, TOKEN_RPAREN); + + Node* return_type = parse_type_or_void(par); + + expect(par, TOKEN_LBRACE); + Node* body = parse_block(par); + + Node* fn = calloc(1, sizeof(Node)); + if (fn == NULL) panic("parse_func_decl: func: could not alloc"); + + fn->type = NODE_FUNCTION_DECL; + fn->scope = NULL; + + fn->data.function_decl.params = params.items; + fn->data.function_decl.p_cap = params.cap; + fn->data.function_decl.p_len = params.len; + + fn->data.function_decl.body = body; + + fn->data.function_decl.name = (Span) { ident.start, ident.end }; + fn->data.function_decl.return_type = return_type; + fn->data.function_decl.exported = exported; + fn->filename = par->filename; + fn->line = ident.line; + fn->col = ident.col; + return fn; +} + +Node* parse_decl_or_func_decl(Parser* par) { Node* type_node = parse_type(par); // consumes the type (e.g., "float") @@ -374,6 +419,10 @@ parse_declarations(Parser* par) case TOKEN_IDENT: return parse_decl_or_func_decl(par); break; + case TOKEN_FN: + case TOKEN_FX: + return parse_func_decl(par); + break; default: printf("unknown token to parse!: %s\n", token_type_str(tok.type)); return NULL; diff --git a/src/types.h b/src/types.h @@ -166,7 +166,7 @@ typedef struct Node { union { /* clang-format off */ struct { struct Node** decl; size_t len, cap; } program; - struct { Span name; struct Node* return_type; struct Node** params; size_t p_cap, p_len; struct Node* body; } function_decl; + struct { Span name; struct Node* return_type; struct Node** params; size_t p_cap, p_len; struct Node* body; bool exported; } function_decl; struct { Span name; struct Node* type; } param; struct { struct Node* cond; struct Node* then_body; struct Node* else_body; } if_statement; struct { struct Node* cond; struct Node* body; } while_statement; diff --git a/tests/ex-return-func-call.ox b/tests/ex-return-func-call.ox @@ -1,11 +1,11 @@ -int yes_we_can() { +fn yes_we_can() i64 { return 5; } -int can_we_do_this() { +fn can_we_do_this() i64 { return yes_we_can(); } -void main() { +fx main() { print(can_we_do_this()); } diff --git a/tests/ex1.ox b/tests/ex1.ox @@ -3,23 +3,3 @@ fx main() { print("hello world"); } - -fx main => print "hello world" - -fx main() => print "hello world" - -fx main() => print("hello world") - -fx main() => print("hello world"); - -fx main() { - print("hello world"); -} - -fx main(void) void { - print("hello world"); -} - -fx print(contents: str) void { - print_("%s\n", contents) -}