Compare commits
3 commits
b1a9e84d8b
...
1d82f6f549
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d82f6f549 | |||
| 3f81359ab1 | |||
| 3bfa0679ae |
7 changed files with 403 additions and 42 deletions
45
bytes/buffer.jai
Normal file
45
bytes/buffer.jai
Normal 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
9
bytes/module.jai
Normal 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
1
jc.jai
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
jc.jai/
|
||||||
190
thirdparty/luajit/x/module.jai
vendored
Normal file
190
thirdparty/luajit/x/module.jai
vendored
Normal 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";
|
||||||
120
vm/interp.jai
120
vm/interp.jai
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue