commit 22dfcdf909035989a981063ad5fc84204fa7201f
parent 2db3f9c4a41b9f00e1f02ab71206edd808630fdf
Author: citbl <citbl@citbl.org>
Date: Sun, 30 Nov 2025 13:30:15 +1000
while loops and truthy bools
Diffstat:
15 files changed, 211 insertions(+), 21 deletions(-)
diff --git a/TODO b/TODO
@@ -19,6 +19,8 @@
[-] implement all or most of C's into libgccjit
[-] ARC memory management, new keyword.
+[ ] Instantiating anything should initialise it
+[ ] instantiating anything in a loop should be a warning (store curr_loop in ctx)
@cruft
[x] redo arguments as list and not linked list, handle in parse and in gen (2 places in gen?)
diff --git a/makefile b/makefile
@@ -59,3 +59,4 @@ test-hmap: clean default
MallocNanoZone=0 ./oxc --test-hmap
again: clean default
+
diff --git a/oxdesign.ox b/oxdesign.ox
@@ -1,10 +1,34 @@
-~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
+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
-// give me the raw pointer of x and assign it to y
-i32* y = &x;
+let x i32 = 35;
+var y i32 = 12;
+y = 15;
+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>> = {};
+
+
+fx add (x i32, y i32) i32 {
+ return x + y;
+}
+
+fx flip(a some, b some) (some, some) => b, a
+
+
+fx add (i i32, j i32) i32 {
+ return i + j;
+}
// default types
// i8, i16, i32, i64,
@@ -13,9 +37,10 @@ i32* y = &x;
// str, chr, bool,
// arrays (fixed) and lists (dynamic)
-arr<i32>[16] ages; // fixed slice of 16
-vec<i32> bobs; // dynamic list
-set<str> names; // sets
+let arr<i32>[16] ages; // fixed slice of 16
+let vec<i32> bobs; // dynamic list
+let set<str> names; // sets
+
struct Person {
i32 age,
diff --git a/src/gen/gen.c b/src/gen/gen.c
@@ -9,6 +9,7 @@
#include <assert.h>
#include <sys/param.h>
+static gcc_jit_type* type_boolean;
static gcc_jit_type* type_int;
static gcc_jit_type* type_i8;
static gcc_jit_type* type_i16;
@@ -108,6 +109,8 @@ gen_init(Scope* scope, const char* src, Node* node, bool quiet)
GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
/*0-3 for O3*/ 0);
+ type_boolean = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_BOOL);
+
type_int = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT64_T);
type_i8 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT8_T);
type_i16 = gcc_jit_context_get_type(ctx, GCC_JIT_TYPE_INT16_T);
@@ -444,6 +447,13 @@ handle_unary_expr(Gen* gen, Node* node)
}
static gcc_jit_rvalue*
+emit_literal_bool(Gen* gen, Node* node)
+{
+ int v = node->data.boolean.value ? 1 : 0;
+ return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, v);
+}
+
+static gcc_jit_rvalue*
emit_literal_string(Gen* gen, Node* node)
{
size_t len = node->data.string.value.end - node->data.string.value.start;
@@ -478,6 +488,8 @@ build_program(Gen* gen, Node* node)
static gcc_jit_rvalue*
lower_builtin_print(Gen* gen, Node* node)
{
+ printf("lower_builtin_print\n");
+
size_t argc = node->data.call_expr.len;
// for (size_t i = 0; i < argc; i++) {
@@ -494,16 +506,13 @@ lower_builtin_print(Gen* gen, Node* node)
gcc_jit_rvalue* args[] = { arg };
return gcc_jit_context_new_call(gen->ctx, loc, gen->puts_fn, 1, args);
}
- // print a integer
- else if (is_intlike(gen->ctx, t)) {
+ // print an integer or bool
+ else if (is_intlike(gen->ctx, t) || t == type_boolean) {
// cast to int for a clean %d
// gcc_jit_type* t_int
// = gcc_jit_context_get_type(gen->ctx, GCC_JIT_TYPE_INT64_T);
gcc_jit_rvalue* fmt = gcc_jit_context_new_string_literal(gen->ctx, "%d\n");
- gcc_jit_rvalue* ival = arg; //(t == t_int)
- //? arg
- //: gcc_jit_context_new_cast(gen->ctx,
- //: NULL, arg, t_int);
+ gcc_jit_rvalue* ival = arg;
gcc_jit_rvalue* args[] = { fmt, ival };
return gcc_jit_context_new_call(gen->ctx, loc, gen->printf_fn, 2, args);
}
@@ -575,6 +584,8 @@ lower_builtin_print(Gen* gen, Node* node)
// double is
} else if (ty == type_cstr) {
// leave as const char*
+ } else if (ty == type_boolean) {
+ arg = gcc_jit_context_new_cast(gen->ctx, loc_from_node(gen, node), arg, type_cstr);
} else {
// fallback: pass pointer as void*
arg = gcc_jit_context_new_cast(gen->ctx,
@@ -696,6 +707,8 @@ handle_expr(Gen* gen, Node* node)
case NODE_STRING_LITERAL:
return emit_literal_string(gen, node);
break;
+ case NODE_BOOL_LITERAL:
+ return emit_literal_bool(gen, node);
case NODE_CALL_EXPR:
return handle_func_call(gen, node);
break;
@@ -789,7 +802,53 @@ static bool build_statement(Gen*, Node*);
static gcc_jit_rvalue* build_bool_value(Gen*, Node*);
-static int block_counter = 0, loop_counter = 0;
+static int block_counter = 0, loop_counter = 0, while_counter = 0;
+
+static bool
+build_while_statement(Gen* gen, Node* node)
+{
+ gcc_jit_location* loc = loc_from_node(gen, node);
+
+ Node* cond = node->data.while_statement.cond;
+ Node* body = node->data.while_statement.body;
+
+ while_counter++;
+ char label_cond[64], label_body[64], label_end[64];
+ snprintf(label_cond, sizeof label_cond, "while.cond%d", while_counter);
+ snprintf(label_body, sizeof label_body, "while.body%d", while_counter);
+ snprintf(label_end, sizeof label_end, "while.end%d", while_counter);
+
+ gcc_jit_block* cond_block = gcc_jit_function_new_block(gen->curr_func, label_cond);
+ gcc_jit_block* body_block = gcc_jit_function_new_block(gen->curr_func, label_body);
+ gcc_jit_block* end_block = gcc_jit_function_new_block(gen->curr_func, label_end);
+
+ /* jump from current block into the loop */
+ gcc_jit_block_end_with_jump(gen->curr_block, loc, cond_block);
+
+ gen->curr_block = cond_block;
+ gcc_jit_rvalue* cnd = NULL;
+
+ if (cond != NULL) {
+ cnd = build_bool_value(gen, cond);
+ gcc_jit_block_end_with_conditional(cond_block, loc, cnd, body_block, end_block);
+ } else {
+ /* while (1) */
+ gcc_jit_block_end_with_jump(cond_block, loc, body_block);
+ }
+
+ gcc_jit_block* continue_target = cond != NULL ? cond_block : body_block;
+ push_loop(gen, /*break to*/ end_block, /*continue to*/ continue_target);
+
+ gen->curr_block = body_block;
+ bool body_ended = build_block(gen, body);
+ if (!body_ended) { gcc_jit_block_end_with_jump(gen->curr_block, loc, continue_target); }
+
+ pop_loop(gen);
+
+ /* resume after loop */
+ gen->curr_block = end_block;
+ return false;
+}
static bool
build_for_statement(Gen* gen, Node* node)
@@ -1094,6 +1153,8 @@ build_statement(Gen* gen, Node* node)
return build_if_statement(gen, node);
case NODE_FOR:
return build_for_statement(gen, node);
+ case NODE_WHILE:
+ return build_while_statement(gen, node);
case NODE_FUNCTION_DECL:
// "closure"
build_func_decl(gen, node);
@@ -1113,6 +1174,22 @@ build_bool_value(Gen* gen, Node* node)
gcc_jit_location* loc = loc_from_node(gen, node);
switch (node->type) {
+ case NODE_BOOL_LITERAL:
+ /* true/false literal */
+ return gcc_jit_context_new_rvalue_from_int(gen->ctx, type_boolean, node->data.boolean.value ? 1 : 0);
+ break;
+ case NODE_INT_LITERAL: {
+ /* while (1) style – C truthiness */
+ gcc_jit_rvalue* iv = emit_literal_int(gen, node);
+ return gcc_jit_context_new_cast(gen->ctx, loc, iv, type_boolean);
+ }
+ case NODE_STRING_LITERAL: {
+ /* while("word") should also be truthy if the pointer is non null */
+ gcc_jit_rvalue* v = emit_literal_string(gen, node);
+ gcc_jit_type* ty = gcc_jit_rvalue_get_type(v);
+ gcc_jit_rvalue* nullv = gcc_jit_context_null(gen->ctx, ty);
+ return gcc_jit_context_new_comparison(gen->ctx, loc, GCC_JIT_COMPARISON_NE, v, nullv);
+ }
case NODE_BINARY_EXPR: {
gcc_jit_rvalue* lvalue = handle_expr(gen, node->data.binary_expr.lhs);
gcc_jit_rvalue* rvalue = handle_expr(gen, node->data.binary_expr.rhs);
@@ -1140,7 +1217,7 @@ build_bool_value(Gen* gen, Node* node)
break;
}
default:
- printf("build_bool_rvalue unhandled, %s\n", node_type_str(node->type));
+ printf("/!\\ build_bool_rvalue unhandled, %s\n", node_type_str(node->type));
break;
}
@@ -1153,6 +1230,7 @@ build_block(Gen* gen, Node* body)
{
// TODO if there is no block ({ ... }), we presume there is only 1 statement.
// This might not always be valid, func declaration do require a block {}, but there might be cases...
+ // FIXME: a symbol declared in a child block is accessible in the parent or in a sibling block...
if (body->data.block.len == 0) { return build_statement(gen, body); }
for (size_t i = 0; i < body->data.block.len; i++) {
diff --git a/src/lexer.c b/src/lexer.c
@@ -94,6 +94,11 @@ make_ident(Lexer* lex, size_t pos, size_t line, size_t col)
else if ((lex->pos - pos) == 6 && strncmp(lex->src + pos, "extend", 6) == 0)
type = TOKEN_EXTEND;
+ else if ((lex->pos - pos) == 4 && strncmp(lex->src + pos, "true", 4) == 0)
+ type = TOKEN_BOOL_TRUE_LITERAL;
+ else if ((lex->pos - pos) == 5 && strncmp(lex->src + pos, "false", 5) == 0)
+ type = TOKEN_BOOL_FALSE_LITERAL;
+
else if ((lex->pos - pos) == 2 && strncmp(lex->src + pos, "fx", 2) == 0)
type = TOKEN_FX;
else if ((lex->pos - pos) == 2 && strncmp(lex->src + pos, "fn", 2) == 0)
@@ -302,6 +307,8 @@ print_token(const Token* t, const char* contents)
[TOKEN_INT_LITERAL] = "integer literal",
[TOKEN_FLOAT_LITERAL] = "float literal",
[TOKEN_STRING_LITERAL] = "string literal",
+ [TOKEN_BOOL_TRUE_LITERAL] = "bool TRUE literal",
+ [TOKEN_BOOL_FALSE_LITERAL] = "bool FALSE literal",
[TOKEN_SLASH] = "slash",
[TOKEN_STAR] = "star",
[TOKEN_PLUS] = "plus",
@@ -382,6 +389,8 @@ token_type_str(TokenType t)
[TOKEN_INT_LITERAL] = "TOKEN_INT_LITERAL",
[TOKEN_FLOAT_LITERAL] = "TOKEN_FLOAT_LITERAL",
[TOKEN_STRING_LITERAL] = "TOKEN_STRING_LITERAL",
+ [TOKEN_BOOL_TRUE_LITERAL] = "TOKEN_BOOL_TRUE_LITERAL",
+ [TOKEN_BOOL_FALSE_LITERAL] = "TOKEN_BOOL_FALSE_LITERAL",
[TOKEN_SLASH] = "TOKEN_SLASH",
[TOKEN_STAR] = "TOKEN_STAR",
[TOKEN_PLUS] = "TOKEN_PLUS",
diff --git a/src/parser.h b/src/parser.h
@@ -54,6 +54,7 @@ Node* make_ident_node(Span);
Node* make_postfix_node(UnaryOp, Node*);
Node* make_number_node(Parser*);
Node* make_unary_node(UnaryOp, Node*);
+Node* make_boolean_node(Parser*, bool);
Node* make_string_node(Parser*);
Node* make_binary_node(OpType, Node*, Node*);
Node* parse_return_statement(Parser*);
diff --git a/src/parser/ast.c b/src/parser/ast.c
@@ -67,6 +67,18 @@ make_unary_node(UnaryOp op, Node* operand)
}
Node*
+make_boolean_node(Parser* par, bool truthy)
+{
+ consume(par);
+ Node* node = (Node*)calloc(1, sizeof(Node));
+ if (node == NULL) panic("make_boolean_node: could not alloc");
+ node->type = NODE_BOOL_LITERAL;
+ node->scope = NULL;
+ node->data.boolean.value = truthy;
+ return node;
+}
+
+Node*
make_string_node(Parser* par)
{
Token tok = consume(par);
diff --git a/src/parser/expr.c b/src/parser/expr.c
@@ -187,6 +187,9 @@ Node*
parse_primary(Parser* par)
{
Token tok = peek(par);
+ if (tok.type == TOKEN_BOOL_TRUE_LITERAL || tok.type == TOKEN_BOOL_FALSE_LITERAL) {
+ return make_boolean_node(par, tok.type == TOKEN_BOOL_TRUE_LITERAL);
+ }
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 || tok.type == TOKEN_VARIADIC || tok.type == TOKEN_COMP_TIME) { return parse_ident(par); }
diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c
@@ -76,7 +76,8 @@ print_node(const char* source, Node* node, int level)
const char* name;
switch (node->type) {
case NODE_FUNCTION_DECL:
- name = range_str(source, node->data.function_decl.name.start, node->data.function_decl.name.end, (char[IDENTSZ]) { 0 });
+ name = range_str(
+ source, node->data.function_decl.name.start, node->data.function_decl.name.end, (char[IDENTSZ]) { 0 });
printf("%*s FUNC DECL: name='%s'\n", level, "", name);
if (node->data.function_decl.return_type) {
printf("%*s ↳ return type:\n", level * 2, "");
@@ -157,6 +158,12 @@ print_node(const char* source, Node* node, int level)
case NODE_FLOAT_LITERAL:
printf("%*s ↳ LITERAL FLOAT NUMBER value=%f\n", level * 2, "", node->data.number.value);
break;
+ case NODE_BOOL_LITERAL:
+ if (node->data.boolean.value)
+ printf("%*s LITERAL BOOL TRUE\n", level * 2, "");
+ else
+ printf("%*s LITERAL BOOL FALSE\n", level * 2, "");
+ break;
case NODE_STRING_LITERAL: {
const char* lit = span_str(source, node->data.string.value, (char[IDENTSZ]) { 0 });
printf("%*s ↳ LITERAL STRING value=\"%s\"\n", level * 2, "", lit);
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_BOOL_TRUE_LITERAL,
+ TOKEN_BOOL_FALSE_LITERAL,
TOKEN_COMP_TIME,
TOKEN_VARIADIC,
TOKEN_SLASH,
@@ -85,6 +87,7 @@ typedef enum {
NODE_INT_LITERAL,
NODE_FLOAT_LITERAL,
NODE_STRING_LITERAL,
+ NODE_BOOL_LITERAL,
NODE_IDENT,
NODE_TYPE,
NODE_BINARY_EXPR,
@@ -179,6 +182,7 @@ typedef struct Node {
struct { UnaryOp op; struct Node* operand; bool is_postfix; } unary_expr;
struct { struct Node* array; struct Node* index; } subscript_expr;
struct { double value; } number;
+ struct { bool value; } boolean;
struct { Span value; } string;
struct { Span name; } ident;
struct { Span name; bool is_comp_time; bool is_variadic; } ident_type;
diff --git a/tests/ex-bool.ox b/tests/ex-bool.ox
@@ -0,0 +1,7 @@
+void main() {
+
+ print("true is true");
+ print(true);
+ print("false is false");
+ print(false);
+}
diff --git a/tests/ex-for-simple2.ox b/tests/ex-for-simple2.ox
@@ -1,6 +1,7 @@
void main() {
~int x;
- print("initial value:");
+ print("initial value:");
+ // do we initialise at 0 or not?
print(x);
for(int x = 3; x < 5; x++) {
//TODO the x defined in the for init is never actually used
diff --git a/tests/ex-variadic.ox b/tests/ex-variadic.ox
@@ -4,5 +4,4 @@ void main() {
print(variadic_num);
i64 let_num = 11;
- let_num = 12;
}
diff --git a/tests/ex-while.ox b/tests/ex-while.ox
@@ -0,0 +1,22 @@
+void main() {
+
+ print("before");
+ int i = 1;
+ while(true) {
+ print("while true prints once 1/3");
+ break;
+ }
+ while(1) {
+ print("while 1 prints once 2/3");
+ break;
+ }
+ while(0) {
+ print("should never happen!");
+ break;
+ }
+ while("in vino veritas") {
+ print("while string prints once 3/3");
+ break;
+ }
+ print("after");
+}
diff --git a/tests/ex1.ox b/tests/ex1.ox
@@ -1,6 +1,25 @@
// Hello world and comment
-void main(int var) {
+fx main() {
print("hello world");
- //print(param1);
+}
+
+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)
}