commit 0669e8dc447ea5541917817d17b02464d505279c
parent 94c9ed18098ee36420987ef6fddf04b0d15ca9aa
Author: citbl <citbl@citbl.org>
Date: Tue, 25 Nov 2025 19:38:21 +1000
wip variadic and comp time types
Diffstat:
12 files changed, 138 insertions(+), 64 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-call-many-args.ox"],
+ "args": ["$ZED_WORKTREE_ROOT/tests/ex-for-simple2.ox"],
"request": "launch",
"adapter": "CodeLLDB"
}
diff --git a/oxdesign.ox b/oxdesign.ox
@@ -1,6 +1,6 @@
-i32 ~x = 42; // make a integer 32bit that is a variable (can be updated) `~` initialised with 2.
+~i32 x = 42; // make a integer 32bit that is a variable (can be updated) `~` initialised with 2.
i32 x = 42; // like a "let" statement or final variable, runtime constant.
-i32 #x = 42; // compile time constant
+#i32 x = 42; // compile time constant
// give me the raw pointer of x and assign it to y
i32* y = &x;
@@ -192,20 +192,20 @@ str website_data = await try fetch_data(url: "fleacebook.com") or Error e => pri
fx flip(i32 a, b) (i32, i32)! {
-
+
}
-// APPENDIX
+// APPENDIX
// keywords
-// return, break, continue, if, else, for, while, loop, goto, defer, heap, free,
-// type, ext, union, arr, vec, set, typedef, fx, fn, maybe, mut, ref, ptr, voidptr,
+// return, break, continue, if, else, for, while, loop, goto, defer, heap, free,
+// type, ext, union, arr, vec, set, typedef, fx, fn, maybe, mut, ref, ptr, voidptr,
// unsafe, inline
// types
-// all of the
+// all of the
// stdlib.local auto imported
// equivalent of `use always { print, print_, warn, warn_, fatal }`
// print, print_, warn, warn_, fatal
@@ -232,7 +232,7 @@ extern c {
// don't use `const char*` etc. and warn against it at compile time
// use cstr or ccstr instead to avoid the baggage of weird
// pointer precedence and const position
- //
+ //
}
fn main() {
diff --git a/src/gen/gen.c b/src/gen/gen.c
@@ -236,7 +236,8 @@ handle_ident_call(Gen* gen, Node* node)
Symbol* sym = find_symbol(gen, gen->scope, node->data.ident.name);
if (sym == NULL) {
- softpanic("handle_ident_call: undefined variable: %s\n",
+ panic_at(node,
+ "handle_ident_call: undefined variable: %s\n",
span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }));
}
return gcc_jit_lvalue_as_rvalue(sym->d.lvalue);
@@ -402,26 +403,36 @@ handle_unary_expr(Gen* gen, Node* node)
Node* opnd = node->data.unary_expr.operand;
Symbol* sym = find_symbol(gen, gen->scope, opnd->data.ident.name);
if (sym == NULL) {
- softpanic("handle_unary_expr: undefined variable: %s\n",
+ panic_at(node,
+ "handle_unary_expr: undefined variable: %s\n",
span_str(gen->src, opnd->data.ident.name, (char[IDENTSZ]) { 0 }));
}
gcc_jit_lvalue* lv = sym->d.lvalue;
- gcc_jit_rvalue* orig = gcc_jit_lvalue_as_rvalue(sym->d.lvalue);
+ if (lv == NULL) {
+ printf("handle_unary_expr: no lvalue\n");
+ return NULL;
+ }
+ gcc_jit_rvalue* orig = gcc_jit_lvalue_as_rvalue(lv);
gcc_jit_type* ty = gcc_jit_rvalue_get_type(orig);
gcc_jit_rvalue* one = gcc_jit_context_one(ctx, ty);
gcc_jit_rvalue* inc = gcc_jit_context_new_binary_op(ctx, NULL, GCC_JIT_BINARY_OP_PLUS, ty, orig, one);
switch (node->data.unary_expr.op) {
case OPER_POSTINC: { // i++
+
+ if (!sym->is_variadic) {
+ panic_at(node,
+ "OPER_POSTINC: cannot change non variadic variable %s\n",
+ span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 }));
+ } else if (sym->is_comp_time) {
+ panic_at(node,
+ "OPER_POSTINC: cannot change comp time constant %s\n",
+ span_str(gen->src, opnd->data.ident_type.name, (char[IDENTSZ]) { 0 }));
+ }
gcc_jit_block_add_assignment(bb, NULL, lv, inc);
return orig; // return value before incr as ++ is postfix
- if (lv == NULL) {
- printf("handle_unary_expr: no lvalue\n");
- return NULL;
- }
- printf("wef\n");
break;
}
default:
@@ -437,7 +448,7 @@ emit_literal_string(Gen* gen, Node* node)
{
size_t len = node->data.string.value.end - node->data.string.value.start;
char* str = calloc(len + 1, sizeof(char));
- if (str == NULL) panic("emit_literal_string: could not alloc");
+ if (str == NULL) panic_at(node, "emit_literal_string: could not alloc");
memcpy(str, gen->src + node->data.string.value.start, len);
str[len] = '\0';
return gcc_jit_context_new_string_literal(gen->ctx, str);
@@ -538,8 +549,9 @@ lower_builtin_print(Gen* gen, Node* node)
gcc_jit_rvalue** args = (gcc_jit_rvalue**)calloc(MAXARGS, sizeof(gcc_jit_rvalue*));
if (argc > MAXARGS) {
- softpanic("we do not currently support more than 16 args to a "
- "print call");
+ panic_at(node,
+ "we do not currently support more than 16 args to a "
+ "print call");
}
for (size_t i = 0; i < argc; i++) {
@@ -559,8 +571,8 @@ lower_builtin_print(Gen* gen, Node* node)
if (ty == type_int) {
arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
} else if (ty == type_f32) { // TODO print() for other types
- // variadics already promote float→double;
- // double is
+ // variadics already promote float→double;
+ // double is
} else if (ty == type_cstr) {
// leave as const char*
} else {
@@ -757,7 +769,7 @@ ox_type_to_c_type(Gen* gen, Node* node)
} else if (strcmp(type_name, "void") == 0) {
return type_void;
} else {
- softpanic("unhandled type in gen %s", type_name);
+ panic_at(node, "unhandled type in gen %s", type_name);
}
return NULL;
}
@@ -975,7 +987,9 @@ build_var_decl_statement(Gen* gen, Node* node)
sym->ctype = declared_type;
sym->d.lvalue = var_decl;
sym->english_type = type_var;
- add_symbol(gen, sym);
+ sym->is_variadic = node->data.var_decl.type->data.ident_type.is_variadic;
+ sym->is_comp_time = node->data.var_decl.type->data.ident_type.is_comp_time;
+ add_symbol(gen, sym); // add var_decl symbol
return false;
}
@@ -1021,19 +1035,29 @@ build_statement(Gen* gen, Node* node)
Symbol* sym = gen->scope->symbols[i];
if (span_ident_same(sym->name, node->data.var_assign.lhs->data.ident.name, gen->src)) {
// we found the symbol we are assigning to
+ // TODO check for variadic and comp_time
+ if (!sym->is_variadic) {
+ const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
+ panic_at(node, "symbol %s is not variadic, cannot mutate.", name);
+ }
+ if (sym->is_comp_time) {
+ const char* name = span_str(gen->src, sym->name, (char[IDENTSZ]) { 0 });
+ panic_at(node, "symbol %s is comp time, cannot mutate.", name);
+ }
+
gcc_jit_lvalue* lvalue = sym->d.lvalue;
// check the type of lvalue matches the rvalue
- // gcc_jit_type* ltype = sym->ctype;
- // gcc_jit_type* rtype = gcc_jit_rvalue_get_type(rvalue);
- // if (rtype != ltype) {
- // panic_at(node,
- // "right hand side of assigment "
- // "doesn't match the "
- // "type of the left hand side: %s, %s",
- // get_english_type(ltype),
- // get_english_type(rtype));
- // }
+ gcc_jit_type* ltype = sym->ctype;
+ gcc_jit_type* rtype = gcc_jit_rvalue_get_type(rvalue);
+ if (rtype != ltype) {
+ panic_at(node,
+ "right hand side of assigment "
+ "doesn't match the "
+ "type of the left hand side: %s, %s",
+ get_english_type(ltype),
+ get_english_type(rtype));
+ }
if (lvalue) gcc_jit_block_add_assignment(gen->curr_block, loc, lvalue, rvalue);
}
}
@@ -1041,7 +1065,10 @@ build_statement(Gen* gen, Node* node)
}
case NODE_BINARY_EXPR: {
Symbol* sym = find_symbol(gen, gen->scope, node->data.binary_expr.lhs->data.ident.name);
- if (!sym) softpanic("undefined variable: %s\n", span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }));
+ if (!sym)
+ panic_at(node,
+ "NODE_BINARY_EXPR: Undefined symbol: %s\n",
+ span_str(gen->src, node->data.ident.name, (char[IDENTSZ]) { 0 }));
gcc_jit_lvalue* lv = sym->d.lvalue;
gcc_jit_rvalue* rhs = handle_expr(gen, node->data.binary_expr.rhs);
@@ -1074,7 +1101,7 @@ build_statement(Gen* gen, Node* node)
case NODE_BLOCK:
build_block(gen, node);
default:
- printf("build_statement unhandled, %s\n", node_type_str(node->type));
+ printf("--- WARNING: build_statement unhandled, %s\n", node_type_str(node->type));
break;
}
return false;
@@ -1185,7 +1212,7 @@ build_func_decl(Gen* gen, Node* node)
sym->ctype = type_voidp; // whatever you use for function type metadata
sym->d.funcvalue = func;
sym->english_type = type_func;
- add_symbol(gen, sym); // to the 'parent scope', before we push_scope
+ add_symbol(gen, sym); // add function symbol -- to the 'parent scope', before we push_scope
// ENTER FUNCTION SCOPE
push_scope(gen);
@@ -1204,7 +1231,7 @@ build_func_decl(Gen* gen, Node* node)
ps->ctype = declared_type;
ps->d.param = p; // <-- this is associated with 'func'
ps->english_type = type_var; // or type_param if you distinguish
- add_symbol(gen, ps);
+ add_symbol(gen, ps); // add param symbol
}
print_symbols_here(gen);
diff --git a/src/lexer.c b/src/lexer.c
@@ -203,6 +203,14 @@ next_token(Lexer* lex)
nudge(lex);
type = TOKEN_PERCENT;
break;
+ case '#':
+ nudge(lex);
+ type = TOKEN_COMP_TIME;
+ break;
+ case '~':
+ nudge(lex);
+ type = TOKEN_VARIADIC;
+ break;
case '/':
nudge(lex);
type = TOKEN_SLASH;
diff --git a/src/parser/decl.c b/src/parser/decl.c
@@ -9,6 +9,10 @@
Node*
parse_type(Parser* par)
{
+ bool comp_time = (peek(par).type == TOKEN_COMP_TIME);
+ bool variadic = (peek(par).type == TOKEN_VARIADIC);
+ if (comp_time || variadic) { consume(par); }
+
Token tok = expect(par, TOKEN_IDENT);
// @later we will resolve types later, incl. custom vs. system, allow for now
@@ -22,7 +26,9 @@ parse_type(Parser* par)
if (node == NULL) panic("parse_type: alloc failed");
node->type = NODE_TYPE;
node->scope = NULL;
- node->data.ident.name = (Span) { .start = tok.start, .end = tok.end };
+ node->data.ident_type.name = (Span) { .start = tok.start, .end = tok.end };
+ node->data.ident_type.is_comp_time = comp_time;
+ node->data.ident_type.is_variadic = variadic;
return node;
}
diff --git a/src/parser/expr.c b/src/parser/expr.c
@@ -76,12 +76,19 @@ Node*
parse_ident(Parser* par)
{
Token tok = consume(par);
+
+ bool comp_time = (tok.type == TOKEN_COMP_TIME);
+ bool variadic = (tok.type == TOKEN_VARIADIC);
+ if (comp_time || variadic) { tok = consume(par); }
+
assert(tok.type == TOKEN_IDENT);
Node* ident_node = (Node*)calloc(1, sizeof(Node));
if (ident_node == NULL) panic("parse_ident: alloc failed");
ident_node->type = NODE_IDENT;
ident_node->scope = NULL;
- ident_node->data.ident.name = (Span) { .start = tok.start, .end = tok.end };
+ ident_node->data.ident_type.name = (Span) { .start = tok.start, .end = tok.end };
+ ident_node->data.ident_type.is_comp_time = comp_time;
+ ident_node->data.ident_type.is_variadic = variadic;
// const char* name = span_str(
// par->src, (Span) { .start = tok.start, .end = tok.end }, (char[IDENTSZ]) { 0 });
@@ -182,7 +189,7 @@ parse_primary(Parser* par)
Token tok = peek(par);
if (tok.type == TOKEN_STRING_LITERAL) { return make_string_node(par); }
if (tok.type == TOKEN_INT_LITERAL || tok.type == TOKEN_FLOAT_LITERAL) { return make_number_node(par); }
- if (tok.type == TOKEN_IDENT) { return parse_ident(par); }
+ if (tok.type == TOKEN_IDENT || tok.type == TOKEN_VARIADIC || tok.type == TOKEN_COMP_TIME) { return parse_ident(par); }
if (tok.type == TOKEN_LPAREN) {
consume(par); // consume '('
Node* node = parse_expression(par);
diff --git a/src/parser/parser.c b/src/parser/parser.c
@@ -12,9 +12,12 @@
Parser
parser_init(Lexer* lex)
{
- return (Parser) {
- .pos = 0, .tokens = lex->tokens, .token_count = lex->token_count, .src = lex->src, .src_len = lex->src_len, .filename = lex->filename
- };
+ return (Parser) { .pos = 0,
+ .tokens = lex->tokens,
+ .token_count = lex->token_count,
+ .src = lex->src,
+ .src_len = lex->src_len,
+ .filename = lex->filename };
}
Token
@@ -54,7 +57,13 @@ expect(Parser* par, TokenType type)
if (tok.type != type) {
const char* name = range_str(par->src, tok.start, tok.end, (char[IDENTSZ]) { 0 });
printf("name: %s\n", name);
- panic("Expected %s, but got '%s' (%d) at %s:%zu:%zu", token_type_str(type), name, tok.type, par->filename, tok.line, tok.col);
+ panic("Expected %s, but got '%s' (%d) at %s:%zu:%zu",
+ token_type_str(type),
+ name,
+ tok.type,
+ par->filename,
+ tok.line,
+ tok.col);
assert(tok.type == type);
}
return consume(par);
@@ -221,9 +230,10 @@ parse_statement(Parser* par)
return parse_block(par);
}
- if (tok.type == TOKEN_IDENT && tok2.type == TOKEN_IDENT) { return parse_decl_or_func_decl(par); }
+ bool tok_is_a_type = (tok.type == TOKEN_IDENT || tok.type == TOKEN_VARIADIC || tok.type == TOKEN_COMP_TIME);
- if (tok.type == TOKEN_IDENT && tok2.type == TOKEN_EQUAL) { return parse_assignment(par); }
+ if (tok_is_a_type && tok2.type == TOKEN_IDENT) { return parse_decl_or_func_decl(par); }
+ if (tok_is_a_type && tok2.type == TOKEN_EQUAL) { return parse_assignment(par); }
switch (tok.type) {
case TOKEN_RETURN:
@@ -279,7 +289,7 @@ parse_block(Parser* par)
Node*
parse_declaration_statement(Parser* par)
{
- Node* type = parse_type(par); // consumes the type (e.g., "float")
+ Node* type_node = parse_type(par); // consumes the type (e.g., "float")
Token ident = expect(par, TOKEN_IDENT); // variable or function name
if (match(par, TOKEN_LPAREN)) { perror("called a var decl but this looks to be a func decl"); }
@@ -288,7 +298,7 @@ parse_declaration_statement(Parser* par)
var->type = NODE_VAR_DECL;
var->scope = NULL;
var->data.var_decl.name = (Span) { ident.start, ident.end };
- var->data.var_decl.type = type;
+ var->data.var_decl.type = type_node;
Token next_tok = peek(par);
if (next_tok.type == TOKEN_EQUAL) {
consume(par);
@@ -304,7 +314,7 @@ parse_declaration_statement(Parser* par)
Node*
parse_decl_or_func_decl(Parser* par)
{
- Node* type = parse_type(par); // consumes the type (e.g., "float")
+ Node* type_node = parse_type(par); // consumes the type (e.g., "float")
Token ident = expect(par, TOKEN_IDENT); // variable or function name
if (match(par, TOKEN_LPAREN)) { // function
@@ -326,7 +336,7 @@ parse_decl_or_func_decl(Parser* par)
fn->data.function_decl.body = body;
fn->data.function_decl.name = (Span) { ident.start, ident.end };
- fn->data.function_decl.return_type = type;
+ fn->data.function_decl.return_type = type_node;
fn->filename = par->filename;
fn->line = ident.line;
fn->col = ident.col;
@@ -338,7 +348,7 @@ parse_decl_or_func_decl(Parser* par)
var->type = NODE_VAR_DECL;
var->scope = NULL;
var->data.var_decl.name = (Span) { ident.start, ident.end };
- var->data.var_decl.type = type;
+ var->data.var_decl.type = type_node;
var->filename = par->filename;
var->line = ident.line;
var->col = ident.col;
@@ -382,7 +392,8 @@ parser_parse(Ast* ast, Parser* par)
if (node == NULL) break;
if (program->data.program.len == program->data.program.cap) {
program->data.program.cap *= 2;
- program->data.program.decl = (Node**)realloc(program->data.program.decl, program->data.program.cap * sizeof(Node*));
+ program->data.program.decl
+ = (Node**)realloc(program->data.program.decl, program->data.program.cap * sizeof(Node*));
assert(program->data.program.decl != NULL && "realloc failed");
}
program->data.program.decl[program->data.program.len++] = node;
diff --git a/src/sem.c b/src/sem.c
@@ -25,8 +25,8 @@ scope_init(Node* node)
.owner = node,
.id = next_id++ };
- if (s.symbols == NULL) panic("scope_init: could not alloc");
- if (s.children == NULL) panic("scope_init: could not alloc");
+ if (s.symbols == NULL) panic_at(node, "scope_init: could not alloc");
+ if (s.children == NULL) panic_at(node, "scope_init: could not alloc");
return s;
}
@@ -35,7 +35,7 @@ new_scope_from_scope(Scope* parent_scope, Node* node)
{
// new scope
Scope* scope = (Scope*)calloc(1, sizeof(Scope));
- if (scope == NULL) panic("new_scope_from_scope: could not alloc");
+ if (scope == NULL) panic_at(node, "new_scope_from_scope: could not alloc");
scope->id = next_id++;
scope->owner = node;
@@ -43,12 +43,12 @@ new_scope_from_scope(Scope* parent_scope, Node* node)
// init symbols list
scope->symbols = (Symbol**)calloc(CALLOC_SZ, sizeof(Symbol*));
- if (scope->symbols == NULL) panic("new_scope_from_scope: symbols: could not alloc");
+ if (scope->symbols == NULL) panic_at(node, "new_scope_from_scope: symbols: could not alloc");
scope->cap = CALLOC_SZ;
scope->len = 0;
scope->children = (Scope**)calloc(CALLOC_SZ, sizeof(Scope*));
- if (scope->children == NULL) panic("new_scope_from_scope: children: could not alloc");
+ if (scope->children == NULL) panic_at(node, "new_scope_from_scope: children: could not alloc");
scope->ch_cap = CALLOC_SZ;
scope->ch_len = 0;
@@ -88,10 +88,10 @@ scope_var(Scope* scope, Ast* ast, Node* node)
const char* type_name = span_str(ast->src, node->data.var_decl.type->data.ident.name, (char[IDENTSZ]) { 0 });
Symbol* sym = (Symbol*)calloc(1, sizeof(Symbol));
- if (sym == NULL) panic("scope_var: symbol: could not alloc");
+ if (sym == NULL) panic_at(node, "scope_var: symbol: could not alloc");
TypeInfo* type = (TypeInfo*)calloc(1, sizeof(TypeInfo));
- if (type == NULL) panic("scope_var: type: could not alloc");
+ if (type == NULL) panic_at(node, "scope_var: type: could not alloc");
if (strcmp(type_name, "float") == 0) {
type->type = SYMTYPE_FLOAT;
@@ -143,7 +143,7 @@ scope_var(Scope* scope, Ast* ast, Node* node)
if (type_name[0] >= 'A' && type_name[0] <= 'Z') {
type->type = SYMTYPE_USER;
} else {
- panic("sem: not yet defined type '%s' for variable '%s'", type_name, var_name);
+ panic_at(node, "sem: not yet defined type '%s' for variable '%s'", type_name, var_name);
}
}
diff --git a/src/types.h b/src/types.h
@@ -23,6 +23,8 @@ typedef enum {
TOKEN_INT_LITERAL,
TOKEN_FLOAT_LITERAL,
TOKEN_STRING_LITERAL,
+ TOKEN_COMP_TIME,
+ TOKEN_VARIADIC,
TOKEN_SLASH,
TOKEN_STAR,
TOKEN_PLUS,
@@ -179,6 +181,7 @@ typedef struct Node {
struct { double value; } number;
struct { Span value; } string;
struct { Span name; } ident;
+ struct { Span name; bool is_comp_time; bool is_variadic; } ident_type;
/* clang-format on */
} data;
} Node;
@@ -296,6 +299,8 @@ typedef struct Symbol {
gcc_jit_type* ctype;
const char* english_type;
+ bool is_variadic;
+ bool is_comp_time;
union {
gcc_jit_function* funcvalue;
gcc_jit_lvalue* lvalue;
diff --git a/tests/ex-for-simple2.ox b/tests/ex-for-simple2.ox
@@ -1,6 +1,8 @@
void main() {
- for(int i = 0; i < 5; i++) {
- print(i);
+ ~int x;
+ print(x);
+ for(; x < 5; x++) {
+ print(x);
print("bozo!");
}
}
diff --git a/tests/ex-variadic.ox b/tests/ex-variadic.ox
@@ -0,0 +1,8 @@
+void main() {
+ ~i64 variadic_num = 5;
+ variadic_num = 6;
+ print(variadic_num);
+
+ i64 let_num = 11;
+ let_num = 12;
+}
diff --git a/tests/ex6.ox b/tests/ex6.ox
@@ -1,7 +1,7 @@
// simple loop
int main() {
- for (int a = 0; a < 10; a++) {
+ for (~int a = 0; a < 10; a++) {
print(a);
print("hi");
}