[vm] more procedure things

This commit is contained in:
Judah Caruso 2025-06-04 22:59:22 -06:00
parent 7e685785be
commit b1a9e84d8b
3 changed files with 101 additions and 51 deletions

View file

@ -13,6 +13,8 @@ Interp_Value :: struct {
u: u64; u: u64;
f: float64; f: float64;
s: string; s: string;
p: *void;
proc: *Node_Procedure;
} }
Kind :: enum { Kind :: enum {
@ -22,6 +24,8 @@ Interp_Value :: struct {
int; int;
float; float;
string; string;
pointer;
procedure;
} }
} }
@ -39,7 +43,7 @@ interp_program :: (i: *Interp) {
for i.toplevel if it.kind == { for i.toplevel if it.kind == {
case .variable; case .variable;
var := it.(*Node_Var); var := it.(*Node_Var);
sym := var.symbol.(*Node_Symbol); sym := var.symbol;
basic.assert(!kv.exists(*i.symbols, sym.str), "redeclaring symbol '%'", sym.str); // @errors basic.assert(!kv.exists(*i.symbols, sym.str), "redeclaring symbol '%'", sym.str); // @errors
value := value_nil; value := value_nil;
@ -50,6 +54,15 @@ interp_program :: (i: *Interp) {
kv.set(*i.symbols, sym.str, value); kv.set(*i.symbols, sym.str, value);
case .procedure;
proc := it.(*Node_Procedure);
sym := proc.symbol;
basic.assert(!kv.exists(*i.symbols, sym.str), "redeclaring procedure '%'", sym.str);
value := make_interp_value(i, .procedure);
value.proc = proc;
kv.set(*i.symbols, sym.str, value);
case .print; case .print;
print := it.(*Node_Print); print := it.(*Node_Print);
expr := interp_expr(i, print.expr); expr := interp_expr(i, print.expr);
@ -81,18 +94,27 @@ 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);
if sym.str == {
case "add";
basic.assert(args.count == 2, "mismatched number of arguments; require 2, given %", args.count); // @errors
node := mem.request_memory(Node_Binary,, allocator = basic.temp); value, ok := kv.get(*i.symbols, sym.str);
node.op = .{ kind = .plus, str = "+" }; basic.assert(ok, "procedure didn't exists '%'", sym.str);
node.left = args[0]; basic.assert(value.kind == .procedure, "attempt to call non procedure '%'", sym.str);
node.right = args[1];
return interp_expr(i, node); result := value_nil;
proc := value.proc;
// @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]);
}
break;
}
} }
return value_nil; return result;
case .unary; case .unary;
do_unop :: (code: Code) #expand { do_unop :: (code: Code) #expand {

View file

@ -24,8 +24,8 @@ strings :: #import "String"; // @future
fn add(x, y) do return x + y end fn add(x, y) do return x + y end
fn sub(x, y) do return x - y end fn sub(x, y) do return x - y end
var x = 10.0 var x = 11.0
var y = 20.0 var y = 22.0
var z = x + y * 2.0 / 3.0 var z = x + y * 2.0 / 3.0
var w = add(x, y) var w = add(x, y)

View file

@ -89,8 +89,8 @@ Node_Var :: struct {
#as using n: Node; #as using n: Node;
n.kind = .variable; n.kind = .variable;
symbol: *Node; // always *Node_Symbol symbol: *Node_Symbol;
type_expr: *Node; // always *Node_Type type_expr: *Node_Type;
value_expr: *Node; value_expr: *Node;
var_flags: Var_Flag; var_flags: Var_Flag;
@ -158,18 +158,23 @@ Node_Type :: struct {
type_kind: Type_Kind; type_kind: Type_Kind;
union { union {
alias_target: *Node; name_target: *Node_Symbol;
pointer_target: *Node_Type; pointer_target: *Node_Type;
struct { struct {
array_element: *Node_Type; array_element: *Node_Type;
array_count: *Node; // can be null array_count: *Node; // can be null
}; };
struct {
procedure_arguments: [..]*Node_Type;
procedure_returns: [..]*Node_Type;
};
} }
Type_Kind :: enum { Type_Kind :: enum {
alias; named;
pointer; pointer;
array; array;
procedure;
} }
} }
@ -177,29 +182,25 @@ Node_Procedure :: struct {
#as using n: Node; #as using n: Node;
n.kind = .procedure; n.kind = .procedure;
header: *Node_Procedure_Header; arguments: [..]Parameter;
returns: [..]Parameter;
args: [..]Node_Parameter; symbol: *Node_Symbol; // can be null
rets: [..]Node_Parameter; head: *Node_Type; // will always be of type .procedure
body: *Node_Block; block: *Node_Block;
flags: Flag; flags: Flag;
Flag :: enum_flags { Flag :: enum_flags {
must_inline; must_inline;
} }
} }
Node_Parameter :: struct { Parameter :: struct {
symbol: *Node_Symbol; symbol: *Node_Symbol;
type: *Node_Type; type: *Node_Type;
value: *Node; // always an expression, can be null value: *Node; // always an expression, can be null
} }
Node_Procedure_Header :: struct {
args: [..]*Node_Type;
rets: [..]*Node_Type;
}
Node_Print :: struct { Node_Print :: struct {
#as using n: Node; #as using n: Node;
n.kind = .print; n.kind = .print;
@ -211,14 +212,15 @@ Node_Procedure_Call :: struct {
#as using n: Node; #as using n: Node;
n.kind = .procedure_call; n.kind = .procedure_call;
call_expr: *Node; call_expr: *Node;
named_arguments: kv.Kv(*Node, *Node); named_arguments: kv.Kv(*Node, *Node);
all_arguments: [..]*Node; all_arguments: [..]*Node;
} }
Node_Block :: struct { Node_Block :: struct {
#as using n: Node; #as using n: Node;
n.kind = .block; n.kind = .block;
body: [..]*Node; body: [..]*Node;
} }
@ -277,7 +279,7 @@ parse_toplevel :: (p: *Parser) -> *Node {
s:, ok = expect_token(p, .symbol); s:, ok = expect_token(p, .symbol);
basic.assert(ok, "symbol"); // @errors basic.assert(ok, "symbol"); // @errors
type_expr: *Node; type_expr: *Node_Type;
value_expr: *Node; value_expr: *Node;
is_const := t.kind == .kw_def; is_const := t.kind == .kw_def;
@ -316,7 +318,6 @@ parse_toplevel :: (p: *Parser) -> *Node {
// return expr0, ..exprN // return expr0, ..exprN
case .kw_return; case .kw_return;
node := make_node(p, Node_Return); node := make_node(p, Node_Return);
array.init(*node.values, p.allocator);
prev_offset := p.offset; prev_offset := p.offset;
expr := parse_expression(p); expr := parse_expression(p);
@ -338,7 +339,7 @@ parse_toplevel :: (p: *Parser) -> *Node {
node.expr = expr; node.expr = expr;
return node; return node;
// fn symbol(arg0, ..argN) do end // fn symbol(arg0, ..argN) do ... end
case .kw_fn; case .kw_fn;
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
@ -346,12 +347,20 @@ parse_toplevel :: (p: *Parser) -> *Node {
t, ok = expect_token(p, .l_paren); t, ok = expect_token(p, .l_paren);
basic.assert(ok, "expected '(' but found '%'", t.str); // @errors basic.assert(ok, "expected '(' but found '%'", t.str); // @errors
node := make_node(p, Node_Procedure);
node.symbol = make_node(p, Node_Symbol);
node.symbol.str = symbol.str;
while !at_end(p) { while !at_end(p) {
t = peek_token(p); t = peek_token(p);
if t.kind == .r_paren break; if t.kind == .r_paren break;
expr := parse_expression(p); sym, ok := expect_token(p, .symbol);
basic.assert(expr != null); // @errors basic.assert(ok, "expected symbol"); // @errors
arg := array.append(*node.arguments);
arg.symbol = make_node(p, Node_Symbol);
arg.symbol.str = sym.str;
t = peek_token(p); t = peek_token(p);
if t.kind == { if t.kind == {
@ -368,12 +377,10 @@ parse_toplevel :: (p: *Parser) -> *Node {
_, ok = expect_token(p, .r_paren); _, ok = expect_token(p, .r_paren);
basic.assert(ok, "expected ')'"); // @errors basic.assert(ok, "expected ')'"); // @errors
block := parse_block(p); node.block = parse_block(p);
basic.assert(block != null, "expected block"); // @errors basic.assert(node.block != null, "expected block"); // @errors
node := make_node(p, Node_Procedure); return node;
return null;
} }
return null; return null;
@ -384,7 +391,6 @@ parse_block :: (p: *Parser) -> *Node_Block {
basic.assert(ok, "expected 'do' found '%'", t.str); // @errors basic.assert(ok, "expected 'do' found '%'", t.str); // @errors
block := make_node(p, Node_Block); block := make_node(p, Node_Block);
array.init(*block.body, p.allocator);
while !at_end(p) { while !at_end(p) {
t = peek_token(p); t = peek_token(p);
@ -411,14 +417,12 @@ parse_type_expression :: (p: *Parser) -> *Node_Type {
target := parse_type_expression(p); target := parse_type_expression(p);
basic.assert(target != null, "pointer target"); // @errors basic.assert(target != null, "pointer target"); // @errors
node := make_node(p, Node_Type); node := make_node(p, .pointer);
node.type_kind = .pointer;
node.pointer_target = target; node.pointer_target = target;
return node; return node;
case .l_square; case .l_square;
node := make_node(p, Node_Type); node := make_node(p, .array);
node.type_kind = .array;
// slice // slice
if peek_token(p).kind == .r_square { if peek_token(p).kind == .r_square {
@ -449,9 +453,8 @@ parse_type_expression :: (p: *Parser) -> *Node_Type {
symbol := make_node(p, Node_Symbol); symbol := make_node(p, Node_Symbol);
symbol.str = t.str; symbol.str = t.str;
node := make_node(p, Node_Type); node := make_node(p, .named);
node.type_kind = .alias; node.name_target = symbol;
node.alias_target = symbol;
return node; return node;
} }
@ -540,9 +543,6 @@ parse_expression_postfix :: (p: *Parser) -> *Node {
node := make_node(p, Node_Procedure_Call); node := make_node(p, Node_Procedure_Call);
node.call_expr = base; node.call_expr = base;
array.init(*node.all_arguments, p.allocator);
kv.init(*node.named_arguments, p.allocator);
while !at_end(p) { while !at_end(p) {
t = peek_token(p); t = peek_token(p);
if t.kind == .r_paren break; if t.kind == .r_paren break;
@ -641,7 +641,35 @@ parse_expression_base :: (p: *Parser) -> *Node {
} }
make_node :: (p: *Parser, $T: Type) -> *T { make_node :: (p: *Parser, $T: Type) -> *T {
return mem.request_memory(T,, allocator = p.allocator); node := mem.request_memory(T,, allocator = p.allocator);
#if T == { // nodes that require initialization
case Node_Block;
array.init(*node.body, p.allocator);
case Node_Return;
array.init(*node.values, p.allocator);
case Node_Procedure_Call;
array.init(*node.all_arguments, p.allocator);
kv.init(*node.named_arguments, p.allocator);
case Node_Procedure;
array.init(*node.arguments, p.allocator);
array.init(*node.returns, p.allocator);
}
return node;
}
make_node :: (p: *Parser, $type_kind: Node_Type.Type_Kind) -> *Node_Type {
type := mem.request_memory(Node_Type,, allocator = p.allocator);
type.type_kind = type_kind;
#if type_kind == {
case .procedure;
array.init(*type.procedure_arguments, p.allocator);
array.init(*type.procedure_returns, p.allocator);
}
return type;
} }
peek_token :: (p: *Parser) -> Token { peek_token :: (p: *Parser) -> Token {