293 lines
8.5 KiB
Text
293 lines
8.5 KiB
Text
Interp :: struct {
|
|
allocator: Allocator;
|
|
|
|
toplevel: []*Node;
|
|
global: *Interp_Scope;
|
|
}
|
|
|
|
Interp_Scope :: struct {
|
|
parent: *Interp_Scope;
|
|
bindings: kv.Kv(string, *Interp_Value);
|
|
}
|
|
|
|
Interp_Value :: struct {
|
|
kind: Kind;
|
|
union {
|
|
b: bool;
|
|
i: s64;
|
|
u: u64;
|
|
f: float64;
|
|
s: string;
|
|
p: *void;
|
|
proc: *Node_Procedure;
|
|
val: *Interp_Value;
|
|
}
|
|
|
|
Kind :: enum {
|
|
none;
|
|
nil;
|
|
bool;
|
|
int;
|
|
float;
|
|
string;
|
|
pointer;
|
|
procedure;
|
|
value;
|
|
}
|
|
}
|
|
|
|
init :: (i: *Interp, allocator: Allocator) {
|
|
value_nil = make_interp_value(i, .nil);
|
|
|
|
value_true = make_interp_value(i, .bool);
|
|
value_true.b = true;
|
|
|
|
value_false = make_interp_value(i, .bool);
|
|
value_false.b = false;
|
|
|
|
i.global = make_scope(null,, allocator = allocator);
|
|
}
|
|
|
|
interp_program :: (i: *Interp) {
|
|
for i.toplevel {
|
|
interp_statement(i, it, i.global);
|
|
}
|
|
}
|
|
|
|
interp_statement :: (i: *Interp, stmt: *Node, scope: *Interp_Scope) {
|
|
if stmt.kind == {
|
|
case .variable;
|
|
var := stmt.(*Node_Var);
|
|
sym := var.symbol;
|
|
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, scope);
|
|
basic.assert(value != null); // @errors
|
|
}
|
|
|
|
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;
|
|
proc := stmt.(*Node_Procedure);
|
|
sym := proc.symbol;
|
|
basic.assert(!kv.exists(*scope.bindings, sym.str), "redeclaring procedure '%'", sym.str);
|
|
|
|
value := make_interp_value(i, .procedure);
|
|
value.proc = proc;
|
|
kv.set(*scope.bindings, sym.str, value);
|
|
|
|
case .print;
|
|
print := stmt.(*Node_Print);
|
|
expr := interp_expr(i, print.expr, scope);
|
|
if expr == null return;
|
|
|
|
if expr.kind == {
|
|
case .none; // do nothing
|
|
case .nil; basic.print("nil");
|
|
case .bool; basic.print("%", expr.b);
|
|
case .int; basic.print("%", expr.i);
|
|
case .float; basic.print("%", expr.f);
|
|
case .string; basic.print("%", expr.s);
|
|
case; basic.assert(false, "unhandled interp value kind: %", expr.kind);
|
|
}
|
|
|
|
basic.print("\n");
|
|
|
|
case;
|
|
interp_expr(i, stmt, scope);
|
|
// basic.assert(false, "unhandled node kind: %", stmt.kind); // @errors
|
|
}
|
|
}
|
|
|
|
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 == {
|
|
case .procedure_call;
|
|
call := expr.(*Node_Procedure_Call);
|
|
args := call.all_arguments;
|
|
|
|
// @temp
|
|
sym := call.call_expr.(*Node_Symbol);
|
|
basic.assert(sym.kind == .symbol, "%", sym.kind);
|
|
|
|
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));
|
|
}
|
|
|
|
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], proc_scope);
|
|
}
|
|
|
|
break;
|
|
}
|
|
else {
|
|
interp_statement(i, expr, proc_scope);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
|
|
case .unary;
|
|
do_unop :: (code: Code) #expand {
|
|
if rhs.kind == {
|
|
case .int;
|
|
right := rhs.i;
|
|
res.i = #insert,scope() code;
|
|
case .float;
|
|
right := rhs.f;
|
|
res.f = #insert,scope() code;
|
|
case;
|
|
basic.assert(false, "cannot use unary operator '%' on values of type '%'", un.op, rhs.kind);
|
|
}
|
|
}
|
|
|
|
un := expr.(*Node_Unary);
|
|
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);
|
|
case .minus; do_unop(#code -right);
|
|
case; basic.assert(false, "unhandled unary operator '%'", un.op.str); // @errors
|
|
}
|
|
|
|
return res;
|
|
|
|
case .binary;
|
|
bin := expr.(*Node_Binary);
|
|
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 {
|
|
if lhs.kind == {
|
|
case .int;
|
|
left := lhs.i;
|
|
right := rhs.i;
|
|
res.i = #insert,scope() code;
|
|
case .float;
|
|
left := lhs.f;
|
|
right := rhs.f;
|
|
res.f = #insert,scope() code;
|
|
case;
|
|
basic.assert(false, "cannot use binary operator '%' on values of type '%'", bin.op, lhs.kind);
|
|
}
|
|
}
|
|
|
|
res := make_interp_value(i, lhs.kind);
|
|
if bin.op.kind == {
|
|
case .plus; do_binop(#code left + right);
|
|
case .minus; do_binop(#code left - right);
|
|
case .star; do_binop(#code left * right);
|
|
case .f_slash;
|
|
basic.assert(rhs.i != 0, "divide by zero"); // @errors
|
|
do_binop(#code left / right);
|
|
case .percent;
|
|
basic.assert(lhs.kind == .int, "cannot use binary operator '%%' on values of type '%'", lhs.kind);
|
|
res.i = lhs.i % rhs.i;
|
|
|
|
case; basic.assert(false, "unhandled binary operator '%'", bin.op.str);
|
|
}
|
|
|
|
return res;
|
|
|
|
case .symbol;
|
|
sym := expr.(*Node_Symbol);
|
|
|
|
value := find_symbol(scope, sym.str);
|
|
basic.assert(value != null, "use of undeclared symbol '%'", sym.str); // @errors
|
|
return value;
|
|
|
|
case .literal;
|
|
lit := expr.(*Node_Literal);
|
|
if lit.value_kind == {
|
|
case .int;
|
|
value := make_interp_value(i, .int);
|
|
value.i = lit.i;
|
|
return value;
|
|
|
|
case .float;
|
|
value := make_interp_value(i, .float);
|
|
value.f = lit.f;
|
|
return value;
|
|
|
|
case; basic.assert(false, "unhandled literal kind: %", lit.value_kind); // @errors
|
|
}
|
|
|
|
case; basic.assert(false, "unhandled node kind: %", expr.kind); // @errors
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
#scope_file;
|
|
|
|
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;
|
|
return value;
|
|
}
|