Compare commits

...

3 commits

Author SHA1 Message Date
1d82f6f549 . 2025-06-27 15:39:24 -06:00
3f81359ab1 improvements 2025-06-26 21:09:03 -06:00
3bfa0679ae basic procedures 2025-06-26 13:43:00 -06:00
7 changed files with 403 additions and 42 deletions

45
bytes/buffer.jai Normal file
View file

@ -0,0 +1,45 @@
Buffer :: struct {
allocator: Allocator;
data: [..]u8;
count: int;
}
append :: inline (buf: *Buffer, ptr: *void, count: int) {
free_space := ensure_buffer_has_room(buf, count);
memcpy(free_space, ptr, count);
buf.count += count;
}
append :: inline (buf: *Buffer, data: []u8) {
append(buf, data.data, data.count);
}
append :: inline (buf: *Buffer, ptr: *$T) {
append(buf, ptr, size_of(T));
}
reset :: inline (buf: *Buffer) {
buf.count = 0;
}
#scope_file;
array :: #import "jc/array";
ensure_buffer_has_room :: (buf: *Buffer, count: int) -> *u8 {
ptr := buf.data.data + buf.count;
if buf.count + count >= buf.data.allocated {
array.resize(*buf.data, buf.data.allocated * 2);
ptr = buf.data.data + buf.count;
buf.data.count = buf.data.allocated;
}
return ptr;
}
#if RUN_TESTS #run {
test.run("basic operations", t => {
buf: Buffer;
});
}

9
bytes/module.jai Normal file
View file

@ -0,0 +1,9 @@
#module_parameters(RUN_TESTS := false);
#load "buffer.jai";
#scope_module;
#if RUN_TESTS {
test :: #import "jc/test";
}

1
jc.jai Symbolic link
View file

@ -0,0 +1 @@
jc.jai/

190
thirdparty/luajit/x/module.jai vendored Normal file
View file

@ -0,0 +1,190 @@
#module_parameters(STATIC := true);
c_context: #Context;
#scope_export;
using #import,file "../module.jai"(STATIC = STATIC);
expose :: (L: *State, proc: $T, $caller_code := #caller_code, $loc := #caller_location)
#modify {
return T.(*Type_Info).type == .PROCEDURE, "expose must take a procedure";
} #expand {
#insert -> string {
info := T.(*Type_Info_Procedure);
code := compiler_get_nodes(caller_code);
call := code.(*Code_Procedure_Call);
proc := call.arguments_sorted[1].(*Code_Ident);
basic.assert(proc.kind == .IDENT, "must be an identifier", loc = loc);
body_builder: basic.String_Builder;
args_builder: basic.String_Builder;
call_builder: basic.String_Builder;
rets_builder: basic.String_Builder;
if info.return_types.count != 0 {
basic.append(*call_builder, "\t\t\t\t");
for info.return_types {
basic.print_to_builder(*call_builder, "r%", it_index);
if it_index < info.return_types.count -1 {
basic.append(*call_builder, ", ");
}
}
basic.append(*call_builder, " := ");
}
basic.append(*call_builder, "proc(");
for < info.argument_types {
index := -(it_index + 1);
basic.print_to_builder(*args_builder, "\t\t\ta% := %;\n", it_index, gen_stack_pull(it, index));
basic.print_to_builder(*call_builder, "a%", it_index);
if it_index > 0 {
basic.append(*call_builder, ", ");
}
}
basic.append(*call_builder, ");");
for info.return_types {
basic.print_to_builder(*rets_builder, "\t\t\t\t%;", gen_stack_push(it, it_index));
if it_index < info.return_types.count - 1 {
basic.append(*rets_builder, "\n");
}
}
return basic.sprint(#string END
lua_name :: "%1";
lua_wrapper :: (L: *State) -> s32 #c_call {
%2
push_context c_context {
%3
%4
}
return %5;
}
END,
proc.name,
basic.builder_to_string(*args_builder),
basic.builder_to_string(*call_builder),
basic.builder_to_string(*rets_builder),
info.return_types.count,
);
}
info := T.(*Type_Info_Procedure);
pushcclosure(L, lua_wrapper, info.argument_types.count.(s32));
setfield(L, LUA_GLOBALSINDEX, lua_name);
}
#scope_file;
gen_stack_pull :: (info: *Type_Info, index: int) -> string {
if info.type == {
case .BOOL;
return basic.tprint("get_lua_bool(L, %)", index);
case .INTEGER;
return basic.tprint("tointeger(L, %)", index);
case .FLOAT;
return basic.tprint("tonumber(L, %)", index);
case .STRING;
return basic.tprint("get_lua_string(L, %)", index);
case .STRUCT;
return basic.tprint("get_lua_table(L, %, %)", info.(*Type_Info_Struct).name, index);
case;
basic.assert(false, "% (%)", info.type, index);
}
return "";
}
gen_stack_push :: (info: *Type_Info, index: int) -> string {
if info.type == {
case .BOOL;
return basic.tprint("pushboolean(L, r%.(s32))", index);
case .INTEGER;
return basic.tprint("pushinteger(L, r%)", index);
case;
basic.assert(false, "% (%)", info.type, index);
}
return "";
}
get_lua_string :: inline (L: *State, index: s32) -> string #c_call {
len: u64;
ptr := tolstring(L, index, *len);
return string.{ data = ptr, count = xx len };
}
get_lua_bool :: inline (L: *State, index: s32) -> bool #c_call {
return toboolean(L, index) == 1;
}
get_lua_table :: (L: *State, $T: Type, index: s32) -> T #c_call {
push_context c_context {
return get_lua_table(L, T.(*Type_Info_Struct), index).(*T).*;
}
}
temp_storage: [4096]u8;
temp_offset: int;
get_lua_table :: (L: *State, info: *Type_Info_Struct, index: s32) -> *void {
res := temp_storage.data + temp_offset;
memset(res, 0, info.runtime_size);
temp_offset = (temp_offset + info.runtime_size) % temp_storage.count;
for info.members {
defer settop(L, -2);
pushlstring(L, it.name.data, xx it.name.count);
rawget(L, -2);
vp: *void;
if it.type.type == {
case .BOOL;
v := get_lua_bool(L, -1);
vp = *v;
case .INTEGER;
v := tointeger(L, -1);
if it.type.runtime_size == {
case 1; vp = *(v.(u8, no_check));
case 2; vp = *(v.(u16, no_check));
case 4; vp = *(v.(u32, no_check));
case 8; vp = *(v.(u64, no_check));
}
case .FLOAT;
v := tonumber(L, -1);
if it.type.runtime_size == {
case 4; vp = *(v.(float32, no_check));
case 8; vp = *(v.(float64, no_check));
}
case .STRING;
v := get_lua_string(L, -1);
vp = *v;
case .STRUCT;
v := get_lua_table(L, it.type.(*Type_Info_Struct), -1);
vp = *v;
case;
basic.assert(false, "% (%)", info.type, index);
}
memcpy(res + it.offset_in_bytes, vp, it.type.runtime_size);
}
return res;
}
basic :: #import "Basic"; // @future
#import "Compiler";

View file

@ -1,8 +1,13 @@
Interp :: struct { Interp :: struct {
allocator: Allocator; allocator: Allocator;
symbols: kv.Kv(string, *Interp_Value);
toplevel: []*Node; toplevel: []*Node;
global: *Interp_Scope;
}
Interp_Scope :: struct {
parent: *Interp_Scope;
bindings: kv.Kv(string, *Interp_Value);
} }
Interp_Value :: struct { Interp_Value :: struct {
@ -15,6 +20,7 @@ Interp_Value :: struct {
s: string; s: string;
p: *void; p: *void;
proc: *Node_Procedure; proc: *Node_Procedure;
val: *Interp_Value;
} }
Kind :: enum { Kind :: enum {
@ -26,6 +32,7 @@ Interp_Value :: struct {
string; string;
pointer; pointer;
procedure; procedure;
value;
} }
} }
@ -37,36 +44,60 @@ init :: (i: *Interp, allocator: Allocator) {
value_false = make_interp_value(i, .bool); value_false = make_interp_value(i, .bool);
value_false.b = false; value_false.b = false;
i.global = make_scope(null,, allocator = allocator);
} }
interp_program :: (i: *Interp) { interp_program :: (i: *Interp) {
for i.toplevel if it.kind == { for i.toplevel {
interp_statement(i, it, i.global);
}
}
interp_statement :: (i: *Interp, stmt: *Node, scope: *Interp_Scope) {
if stmt.kind == {
case .variable; case .variable;
var := it.(*Node_Var); var := stmt.(*Node_Var);
sym := var.symbol; sym := var.symbol;
basic.assert(!kv.exists(*i.symbols, sym.str), "redeclaring symbol '%'", sym.str); // @errors basic.assert(!kv.exists(*scope.bindings, sym.str), "redeclaring symbol '%'", sym.str); // @errors
value := value_nil; value := value_nil;
if var.value_expr != null { if var.value_expr != null {
value = interp_expr(i, var.value_expr); value = interp_expr(i, var.value_expr, scope);
basic.assert(value != null); // @errors basic.assert(value != null); // @errors
} }
kv.set(*i.symbols, sym.str, value); kv.set(*scope.bindings, sym.str, value);
case .assign;
assign := stmt.(*Node_Assign);
src := interp_expr(i, assign.src, scope);
basic.assert(src != null); // @errors
dst := interp_lvalue(i, assign.dst, scope);
basic.assert(dst != null); // @errors
basic.assert(dst.kind == .value); // @errors
// @todo: typechecking
if assign.op.kind == {
case .equal;
dst.val.* = src.*;
}
case .procedure; case .procedure;
proc := it.(*Node_Procedure); proc := stmt.(*Node_Procedure);
sym := proc.symbol; sym := proc.symbol;
basic.assert(!kv.exists(*i.symbols, sym.str), "redeclaring procedure '%'", sym.str); basic.assert(!kv.exists(*scope.bindings, sym.str), "redeclaring procedure '%'", sym.str);
value := make_interp_value(i, .procedure); value := make_interp_value(i, .procedure);
value.proc = proc; value.proc = proc;
kv.set(*i.symbols, sym.str, value); kv.set(*scope.bindings, sym.str, value);
case .print; case .print;
print := it.(*Node_Print); print := stmt.(*Node_Print);
expr := interp_expr(i, print.expr); expr := interp_expr(i, print.expr, scope);
if expr == null continue; if expr == null return;
if expr.kind == { if expr.kind == {
case .none; // do nothing case .none; // do nothing
@ -81,11 +112,28 @@ interp_program :: (i: *Interp) {
basic.print("\n"); basic.print("\n");
case; case;
basic.assert(false, "unhandled node kind: %", it.kind); // @errors interp_expr(i, stmt, scope);
// basic.assert(false, "unhandled node kind: %", stmt.kind); // @errors
} }
} }
interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value { interp_lvalue :: (i: *Interp, expr: *Node, scope: *Interp_Scope) -> *Interp_Value {
if expr.kind == {
case .symbol;
sym := expr.(*Node_Symbol);
lval := find_symbol(scope, sym.str);
oval := make_interp_value(i, .value);
oval.val = lval;
return oval;
case;
basic.assert(false, "unable to get lvalue from %", expr.kind);
}
return null;
}
interp_expr :: (i: *Interp, expr: *Node, scope: *Interp_Scope) -> *Interp_Value {
if expr.kind == { if expr.kind == {
case .procedure_call; case .procedure_call;
call := expr.(*Node_Procedure_Call); call := expr.(*Node_Procedure_Call);
@ -93,25 +141,33 @@ interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
// @temp // @temp
sym := call.call_expr.(*Node_Symbol); sym := call.call_expr.(*Node_Symbol);
basic.assert(sym.kind == .symbol); basic.assert(sym.kind == .symbol, "%", sym.kind);
value, ok := kv.get(*i.symbols, sym.str); value := find_symbol(scope, sym.str);
basic.assert(ok, "procedure didn't exists '%'", sym.str); basic.assert(value != null, "procedure didn't exists '%'", sym.str);
basic.assert(value.kind == .procedure, "attempt to call non procedure '%'", sym.str); basic.assert(value.kind == .procedure, "attempt to call non procedure '%'", sym.str);
result := value_nil; result := value_nil;
proc := value.proc; proc := value.proc;
basic.assert(proc.arguments.count == args.count, "argument mismatch. expected %, given %", proc.arguments.count, args.count); // @errors
proc_scope := make_scope(scope);
for proc.arguments {
kv.set(*proc_scope.bindings, it.symbol.str, interp_expr(i, args[it_index], scope));
}
// @todo(judah): check arity, create scope, map args to locals, exec
for expr: proc.block.body { for expr: proc.block.body {
if expr.kind == .return_ { if expr.kind == .return_ {
ret := expr.(*Node_Return); ret := expr.(*Node_Return);
if ret.values.count != 0 { if ret.values.count != 0 {
result = interp_expr(i, ret.values[0]); result = interp_expr(i, ret.values[0], proc_scope);
} }
break; break;
} }
else {
interp_statement(i, expr, proc_scope);
}
} }
return result; return result;
@ -131,7 +187,7 @@ interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
} }
un := expr.(*Node_Unary); un := expr.(*Node_Unary);
rhs := interp_expr(i, un.right); rhs := interp_expr(i, un.right, scope);
res := make_interp_value(i, rhs.kind); res := make_interp_value(i, rhs.kind);
if un.op.kind == { if un.op.kind == {
case .plus; do_unop(#code ifx right < 0 then -right else right); case .plus; do_unop(#code ifx right < 0 then -right else right);
@ -143,8 +199,8 @@ interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
case .binary; case .binary;
bin := expr.(*Node_Binary); bin := expr.(*Node_Binary);
lhs := interp_expr(i, bin.left); lhs := interp_expr(i, bin.left, scope);
rhs := interp_expr(i, bin.right); rhs := interp_expr(i, bin.right, scope);
basic.assert(lhs.kind == rhs.kind, "type mismatch % vs. %", lhs.kind, rhs.kind); // @errors basic.assert(lhs.kind == rhs.kind, "type mismatch % vs. %", lhs.kind, rhs.kind); // @errors
do_binop :: (code: Code) #expand { do_binop :: (code: Code) #expand {
@ -182,8 +238,8 @@ interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
case .symbol; case .symbol;
sym := expr.(*Node_Symbol); sym := expr.(*Node_Symbol);
value, ok := kv.get(*i.symbols, sym.str); value := find_symbol(scope, sym.str);
basic.assert(ok, "use of undeclared symbol '%'", sym.str); // @errors basic.assert(value != null, "use of undeclared symbol '%'", sym.str); // @errors
return value; return value;
case .literal; case .literal;
@ -214,6 +270,22 @@ value_nil: *Interp_Value;
value_true: *Interp_Value; value_true: *Interp_Value;
value_false: *Interp_Value; value_false: *Interp_Value;
find_symbol :: (scope: *Interp_Scope, symbol: string, all_the_way_up := true) -> *Interp_Value {
value, ok := kv.get(*scope.bindings, symbol);
if !ok && (all_the_way_up && scope.parent != null) {
return find_symbol(scope.parent, symbol, true);
}
return value;
}
make_scope :: (parent: *Interp_Scope) -> *Interp_Scope {
scope := mem.request_memory(Interp_Scope);
scope.parent = parent;
kv.init(*scope.bindings, context.allocator);
return scope;
}
make_interp_value :: (i: *Interp, kind: Interp_Value.Kind) -> *Interp_Value { make_interp_value :: (i: *Interp, kind: Interp_Value.Kind) -> *Interp_Value {
value := mem.request_memory(Interp_Value,, allocator = i.allocator); value := mem.request_memory(Interp_Value,, allocator = i.allocator);
value.kind = kind; value.kind = kind;

View file

@ -21,19 +21,21 @@ strings :: #import "String"; // @future
init(*parser, context.allocator); init(*parser, context.allocator);
ok := parse_string(*parser, #string END ok := parse_string(*parser, #string END
fn add(x, y) do return x + y end fn add(l, r) do return l + r end
fn sub(x, y) do return x - y end fn sub(l, r) do return l - r end
fn mul(l, r) do return l * r end
fn div(l, r) do return l / r end
var x = 11.0 var x = 21.0
var y = 22.0 var y = 22.0
var z = x + y * 2.0 / 3.0
var w = add(x, y)
print x // 10 x = x + 1.0 / 2.0
print y // 20 print x
print z // 23.3
print w // 30 x = add(x, div(1.0, 2.0))
// print z print x
print add(x, y)
END); END);
interp: Interp; interp: Interp;

View file

@ -67,6 +67,7 @@ Node :: struct {
stmt_start; stmt_start;
print; print;
return_; return_;
assign;
stmt_end; stmt_end;
decl_start; decl_start;
@ -99,6 +100,15 @@ Node_Var :: struct {
} }
} }
Node_Assign :: struct {
#as using n: Node;
n.kind = .assign;
op: Token;
dst: *Node;
src: *Node;
}
Node_Unary :: struct { Node_Unary :: struct {
#as using n: Node; #as using n: Node;
n.kind = .unary; n.kind = .unary;
@ -256,7 +266,7 @@ parse_string :: (p: *Parser, source: string) -> bool {
break; break;
} }
node := parse_toplevel(p); node := parse_statement(p);
if node != null array.append(*p.toplevel, node); if node != null array.append(*p.toplevel, node);
} }
@ -266,17 +276,17 @@ parse_string :: (p: *Parser, source: string) -> bool {
#scope_file; #scope_file;
parse_toplevel :: (p: *Parser) -> *Node { parse_statement :: (p: *Parser) -> *Node {
t, ok := expect_token(p, .kw_var, .kw_def, .kw_fn, .kw_print, .kw_do, .kw_return); t := peek_token(p);
basic.assert(ok, "var, def, print, found '%'", t.str); // @errors
if t.kind == { if t.kind == {
// var sym type_expr // var sym type_expr
// var sym type_expr = expr // var sym type_expr = expr
// var sym = expr // var sym = expr
case .kw_var; #through; case .kw_var; #through;
case .kw_def; case .kw_def;
s:, ok = expect_token(p, .symbol); consume_token(p);
s, ok := expect_token(p, .symbol);
basic.assert(ok, "symbol"); // @errors basic.assert(ok, "symbol"); // @errors
type_expr: *Node_Type; type_expr: *Node_Type;
@ -317,6 +327,8 @@ parse_toplevel :: (p: *Parser) -> *Node {
// return // return
// return expr0, ..exprN // return expr0, ..exprN
case .kw_return; case .kw_return;
consume_token(p);
node := make_node(p, Node_Return); node := make_node(p, Node_Return);
prev_offset := p.offset; prev_offset := p.offset;
@ -332,6 +344,8 @@ parse_toplevel :: (p: *Parser) -> *Node {
// print(expr) // print(expr)
// print expr // print expr
case .kw_print; case .kw_print;
consume_token(p);
expr := parse_expression(p); expr := parse_expression(p);
basic.assert(expr != null, "expected expression"); // @errors basic.assert(expr != null, "expected expression"); // @errors
@ -341,6 +355,8 @@ parse_toplevel :: (p: *Parser) -> *Node {
// fn symbol(arg0, ..argN) do ... end // fn symbol(arg0, ..argN) do ... end
case .kw_fn; case .kw_fn;
consume_token(p);
symbol, ok := expect_token(p, .symbol); symbol, ok := expect_token(p, .symbol);
basic.assert(ok, "expected name for procedure"); // @errors @todo(judah): lambdas basic.assert(ok, "expected name for procedure"); // @errors @todo(judah): lambdas
@ -381,9 +397,35 @@ parse_toplevel :: (p: *Parser) -> *Node {
basic.assert(node.block != null, "expected block"); // @errors basic.assert(node.block != null, "expected block"); // @errors
return node; return node;
// do ... end
case .kw_do;
return parse_block(p);
} }
return null; return parse_simple_statement(p);
}
parse_simple_statement :: (p: *Parser) -> *Node {
dst := parse_expression(p);
basic.assert(dst != null, "expected expression for simple statement");
t := peek_token(p);
if t.kind == {
case .equal;
consume_token(p);
src := parse_expression(p);
basic.assert(src != null, "expected right-hand side of assignment");
node := make_node(p, Node_Assign);
node.op = t;
node.dst = dst;
node.src = src;
return node;
}
return dst;
} }
parse_block :: (p: *Parser) -> *Node_Block { parse_block :: (p: *Parser) -> *Node_Block {
@ -396,7 +438,7 @@ parse_block :: (p: *Parser) -> *Node_Block {
t = peek_token(p); t = peek_token(p);
if t.kind == .kw_end break; if t.kind == .kw_end break;
node := parse_toplevel(p); node := parse_statement(p);
basic.assert(node != null); // @errors basic.assert(node != null); // @errors
array.append(*block.body, node); array.append(*block.body, node);