commit 5c9795844aebf8f4141ad09db177de3d49e79008
parent bc83bc7bc01c35826d4631c27d04f04c0677e0ae
Author: citbl <citbl@citbl.org>
Date: Sun, 7 Dec 2025 11:39:47 +1000
handle fx/fn function definitions
Diffstat:
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)
-}