basic procedures

This commit is contained in:
Judah Caruso 2025-06-26 13:43:00 -06:00
parent b1a9e84d8b
commit 3bfa0679ae
6 changed files with 294 additions and 24 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 {
allocator: Allocator;
symbols: kv.Kv(string, *Interp_Value);
toplevel: []*Node;
global: *Interp_Scope;
}
Interp_Scope :: struct {
parent: *Interp_Scope;
bindings: kv.Kv(string, *Interp_Value);
}
Interp_Value :: struct {
@ -37,35 +42,39 @@ init :: (i: *Interp, allocator: Allocator) {
value_false = make_interp_value(i, .bool);
value_false.b = false;
i.global = make_scope(null,, allocator = allocator);
}
interp_program :: (i: *Interp) {
scope := i.global;
for i.toplevel if it.kind == {
case .variable;
var := it.(*Node_Var);
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;
if var.value_expr != null {
value = interp_expr(i, var.value_expr);
value = interp_expr(i, var.value_expr, i.global);
basic.assert(value != null); // @errors
}
kv.set(*i.symbols, sym.str, value);
kv.set(*scope.bindings, sym.str, value);
case .procedure;
proc := it.(*Node_Procedure);
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.proc = proc;
kv.set(*i.symbols, sym.str, value);
kv.set(*scope.bindings, sym.str, value);
case .print;
print := it.(*Node_Print);
expr := interp_expr(i, print.expr);
expr := interp_expr(i, print.expr, i.global);
if expr == null continue;
if expr.kind == {
@ -85,7 +94,7 @@ interp_program :: (i: *Interp) {
}
}
interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
interp_expr :: (i: *Interp, expr: *Node, scope: *Interp_Scope) -> *Interp_Value {
if expr.kind == {
case .procedure_call;
call := expr.(*Node_Procedure_Call);
@ -95,19 +104,24 @@ interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
sym := call.call_expr.(*Node_Symbol);
basic.assert(sym.kind == .symbol);
value, ok := kv.get(*i.symbols, sym.str);
basic.assert(ok, "procedure didn't exists '%'", sym.str);
value := find_symbol(scope, sym.str);
basic.assert(value != null, "procedure didn't exists '%'", sym.str);
basic.assert(value.kind == .procedure, "attempt to call non procedure '%'", sym.str);
result := value_nil;
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 {
if expr.kind == .return_ {
ret := expr.(*Node_Return);
if ret.values.count != 0 {
result = interp_expr(i, ret.values[0]);
result = interp_expr(i, ret.values[0], proc_scope);
}
break;
@ -131,7 +145,7 @@ interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
}
un := expr.(*Node_Unary);
rhs := interp_expr(i, un.right);
rhs := interp_expr(i, un.right, scope);
res := make_interp_value(i, rhs.kind);
if un.op.kind == {
case .plus; do_unop(#code ifx right < 0 then -right else right);
@ -143,8 +157,8 @@ interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
case .binary;
bin := expr.(*Node_Binary);
lhs := interp_expr(i, bin.left);
rhs := interp_expr(i, bin.right);
lhs := interp_expr(i, bin.left, scope);
rhs := interp_expr(i, bin.right, scope);
basic.assert(lhs.kind == rhs.kind, "type mismatch % vs. %", lhs.kind, rhs.kind); // @errors
do_binop :: (code: Code) #expand {
@ -182,8 +196,8 @@ interp_expr :: (i: *Interp, expr: *Node) -> *Interp_Value {
case .symbol;
sym := expr.(*Node_Symbol);
value, ok := kv.get(*i.symbols, sym.str);
basic.assert(ok, "use of undeclared symbol '%'", sym.str); // @errors
value := find_symbol(scope, sym.str);
basic.assert(value != null, "use of undeclared symbol '%'", sym.str); // @errors
return value;
case .literal;
@ -214,6 +228,22 @@ value_nil: *Interp_Value;
value_true: *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 {
value := mem.request_memory(Interp_Value,, allocator = i.allocator);
value.kind = kind;

View file

@ -26,14 +26,9 @@ strings :: #import "String"; // @future
var x = 11.0
var y = 22.0
var z = x + y * 2.0 / 3.0
var w = add(x, y)
print x // 10
print y // 20
print z // 23.3
print w // 30
// print z
print add(x, y)
print add(x+1.0, y)
END);
interp: Interp;