Implement closure compilation (no running yet)
This commit is contained in:
parent
fdf38c71bf
commit
edc0a89ed9
4 changed files with 90 additions and 13 deletions
|
@ -229,6 +229,7 @@ Result<Array> Function::constants() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Value> reverse(Value& val) {
|
Result<Value> reverse(Value& val) {
|
||||||
|
if (val.is<Nil>()) return Value(TRY(Nil::create()));
|
||||||
if (!val.is<Pair>()) return ERROR(TypeMismatch);
|
if (!val.is<Pair>()) return ERROR(TypeMismatch);
|
||||||
|
|
||||||
auto res = Value(TRY(Nil::create()));
|
auto res = Value(TRY(Nil::create()));
|
||||||
|
|
|
@ -6,21 +6,37 @@ struct Context {
|
||||||
Context() {}
|
Context() {}
|
||||||
|
|
||||||
Context(Value&& env, Array&& constants, Dict&& constants_dict,
|
Context(Value&& env, Array&& constants, Dict&& constants_dict,
|
||||||
Dict&& variables_dict)
|
Dict&& variables_dict, Array&& closures, Dict&& closures_dict, Context* parent)
|
||||||
: env(std::move(env)),
|
: env(std::move(env)),
|
||||||
constants(std::move(constants)),
|
constants(std::move(constants)),
|
||||||
constants_dict(std::move(constants_dict)),
|
constants_dict(std::move(constants_dict)),
|
||||||
variables_dict(std::move(variables_dict)),
|
variables_dict(std::move(variables_dict)),
|
||||||
maxreg(0) {}
|
closures(std::move(closures)),
|
||||||
|
closures_dict(std::move(closures_dict)),
|
||||||
|
maxreg(0), parent(parent) {}
|
||||||
|
|
||||||
static Result<Context> create() {
|
static Result<Context> create() {
|
||||||
auto env = TRY(Nil::create());
|
auto env = TRY(Nil::create());
|
||||||
auto constants = TRY(Array::create());
|
auto constants = TRY(Array::create());
|
||||||
auto constants_dict = TRY(Dict::create());
|
auto constants_dict = TRY(Dict::create());
|
||||||
auto variables_dict = TRY(Dict::create());
|
auto variables_dict = TRY(Dict::create());
|
||||||
|
auto closures = TRY(Array::create());
|
||||||
|
auto closures_dict = TRY(Dict::create());
|
||||||
|
|
||||||
return Context(std::move(env), std::move(constants),
|
return Context(std::move(env), std::move(constants),
|
||||||
std::move(constants_dict), std::move(variables_dict));
|
std::move(constants_dict), std::move(variables_dict), std::move(closures), std::move(closures_dict), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result<Context> create(Context& parent) {
|
||||||
|
auto env = TRY(Nil::create());
|
||||||
|
auto constants = TRY(Array::create());
|
||||||
|
auto constants_dict = TRY(Dict::create());
|
||||||
|
auto variables_dict = TRY(Dict::create());
|
||||||
|
auto closures = TRY(Array::create());
|
||||||
|
auto closures_dict = TRY(Dict::create());
|
||||||
|
|
||||||
|
return Context(std::move(env), std::move(constants),
|
||||||
|
std::move(constants_dict), std::move(variables_dict), std::move(closures), std::move(closures_dict), &parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t alloc_reg() {
|
uint64_t alloc_reg() {
|
||||||
|
@ -72,11 +88,33 @@ struct Context {
|
||||||
return ERROR(KeyError);
|
return ERROR(KeyError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<int64_t> get_closure(const Value& sym) {
|
||||||
|
auto idx = closures_dict.get(sym);
|
||||||
|
if (idx.has_value()) {
|
||||||
|
if (!idx.value().is<Int64>()) return ERROR(TypeMismatch);
|
||||||
|
|
||||||
|
return idx.value().to<Int64>()->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parent) return ERROR(KeyError);
|
||||||
|
|
||||||
|
TRY(parent->get_var(sym));
|
||||||
|
|
||||||
|
int64_t i = closures.size();
|
||||||
|
closures = TRY(closures.append(sym));
|
||||||
|
closures_dict = TRY(closures_dict.insert(sym, TRY(Value::create(i))));
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
Value env;
|
Value env;
|
||||||
Array constants;
|
Array constants;
|
||||||
Dict constants_dict;
|
Dict constants_dict;
|
||||||
Dict variables_dict;
|
Dict variables_dict;
|
||||||
|
Array closures;
|
||||||
|
Dict closures_dict;
|
||||||
uint64_t maxreg;
|
uint64_t maxreg;
|
||||||
|
Context* parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<void> Expression::add_opcode(Oc opcode, OpArg arg1, OpArg arg2,
|
Result<void> Expression::add_opcode(Oc opcode, OpArg arg1, OpArg arg2,
|
||||||
|
@ -269,7 +307,7 @@ Result<Expression> Compiler::compile_if(Context& context, Symbol& op,
|
||||||
|
|
||||||
Result<Expression> Compiler::compile_lambda(Context& context, Symbol& op,
|
Result<Expression> Compiler::compile_lambda(Context& context, Symbol& op,
|
||||||
Pair& expr) {
|
Pair& expr) {
|
||||||
Context ctx = TRY(Context::create());
|
Context ctx = TRY(Context::create(context));
|
||||||
ctx.maxreg = 1; // Reserve the slot for function itself
|
ctx.maxreg = 1; // Reserve the slot for function itself
|
||||||
|
|
||||||
auto first = TRY(expr.rest());
|
auto first = TRY(expr.rest());
|
||||||
|
@ -281,9 +319,6 @@ Result<Expression> Compiler::compile_lambda(Context& context, Symbol& op,
|
||||||
Pair& first_pair = *first.to<Pair>();
|
Pair& first_pair = *first.to<Pair>();
|
||||||
|
|
||||||
auto param = TRY(first_pair.first());
|
auto param = TRY(first_pair.first());
|
||||||
if (param.is<Nil>()) {
|
|
||||||
return ERROR(CompilationError);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t arity = 0;
|
uint64_t arity = 0;
|
||||||
while (!param.is<Nil>()) {
|
while (!param.is<Nil>()) {
|
||||||
|
@ -320,16 +355,39 @@ Result<Expression> Compiler::compile_lambda(Context& context, Symbol& op,
|
||||||
Value name = TRY(Nil::create());
|
Value name = TRY(Nil::create());
|
||||||
auto fun = TRY(Function::create(name, arity, ctx.constants, ex.code));
|
auto fun = TRY(Function::create(name, arity, ctx.constants, ex.code));
|
||||||
|
|
||||||
|
// std::cout << "--------------- LAMBDA " << arity << "\n";
|
||||||
|
// TRY(debug_print(TRY(expr.copy_value())));
|
||||||
// TRY(debug_print(TRY(ex.code.copy())));
|
// TRY(debug_print(TRY(ex.code.copy())));
|
||||||
|
|
||||||
Expression ex_res = TRY(Expression::create());
|
Expression ex_res = TRY(Expression::create());
|
||||||
|
|
||||||
int64_t c = TRY(context.add_const(TRY(fun.copy_value())));
|
if (ctx.closures.size() == 0) {
|
||||||
|
int64_t c = TRY(context.add_const(TRY(fun.copy_value())));
|
||||||
|
|
||||||
|
uint64_t reg = context.alloc_reg();
|
||||||
|
TRY(ex_res.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, (int64_t)c}));
|
||||||
|
|
||||||
|
ex_res.reg = reg;
|
||||||
|
|
||||||
|
return ex_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t c = TRY(context.add_const(TRY(fun.copy_value())));
|
||||||
uint64_t reg = context.alloc_reg();
|
uint64_t reg = context.alloc_reg();
|
||||||
TRY(ex_res.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, (int64_t)c}));
|
TRY(ex_res.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, (int64_t)c}));
|
||||||
|
|
||||||
ex.reg = reg;
|
for (uint64_t i = 0; i < ctx.closures.size(); i++) {
|
||||||
|
Value sym = TRY(ctx.closures.get(i));
|
||||||
|
|
||||||
|
int64_t var_reg = TRY(context.get_var(sym));
|
||||||
|
uint64_t vr = context.alloc_reg();
|
||||||
|
TRY(ex_res.add_opcode(Oc::Mov, {0, (int64_t)vr}, {0, (int64_t)var_reg}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(ex_res.add_opcode(Oc::MakeClosure, {0, (int64_t)reg}, {0, (int64_t)reg + (int64_t)ctx.closures.size() + 1}));
|
||||||
|
|
||||||
|
context.maxreg = reg;
|
||||||
|
ex_res.reg = reg;
|
||||||
|
|
||||||
return ex_res;
|
return ex_res;
|
||||||
}
|
}
|
||||||
|
@ -435,14 +493,26 @@ Result<Expression> Compiler::compile_symbol(Context& context, Symbol& value) {
|
||||||
|
|
||||||
auto maybe_reg = context.get_var(TRY(value.copy_value()));
|
auto maybe_reg = context.get_var(TRY(value.copy_value()));
|
||||||
|
|
||||||
if (maybe_reg.has_error()) {
|
if (!maybe_reg.has_error()) {
|
||||||
|
auto var_reg = maybe_reg.value();
|
||||||
|
|
||||||
|
uint64_t reg = context.alloc_reg();
|
||||||
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)reg}, {0, (int64_t)var_reg}));
|
||||||
|
|
||||||
|
ex.reg = reg;
|
||||||
|
return std::move(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto maybe_closure = context.get_closure(TRY(value.copy_value()));
|
||||||
|
|
||||||
|
if (maybe_closure.has_error()) {
|
||||||
return ERROR(CompilationError);
|
return ERROR(CompilationError);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto var_reg = maybe_reg.value();
|
auto var_closure = maybe_closure.value();
|
||||||
|
|
||||||
uint64_t reg = context.alloc_reg();
|
uint64_t reg = context.alloc_reg();
|
||||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)reg}, {0, (int64_t)var_reg}));
|
TRY(ex.add_opcode(Oc::ClosureLoad, {0, (int64_t)reg}, {0, (int64_t)var_closure}));
|
||||||
|
|
||||||
ex.reg = reg;
|
ex.reg = reg;
|
||||||
return std::move(ex);
|
return std::move(ex);
|
||||||
|
|
|
@ -81,7 +81,10 @@ op_t get_op(Oc op) {
|
||||||
return op_t{"setglobal", OpcodeType::Reg2};
|
return op_t{"setglobal", OpcodeType::Reg2};
|
||||||
case Oc::GetGlobal:
|
case Oc::GetGlobal:
|
||||||
return op_t{"getglobal", OpcodeType::Reg2};
|
return op_t{"getglobal", OpcodeType::Reg2};
|
||||||
|
case Oc::MakeClosure:
|
||||||
|
return op_t{"make-closure", OpcodeType::Reg2};
|
||||||
|
case Oc::ClosureLoad:
|
||||||
|
return op_t{"closure-load", OpcodeType::Reg1I};
|
||||||
default:
|
default:
|
||||||
die("Unknown opcode\n");
|
die("Unknown opcode\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,9 @@ enum class Oc : uint8_t {
|
||||||
// Globals
|
// Globals
|
||||||
SetGlobal,
|
SetGlobal,
|
||||||
GetGlobal,
|
GetGlobal,
|
||||||
|
// Closures
|
||||||
|
MakeClosure,
|
||||||
|
ClosureLoad,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OpArg {
|
struct OpArg {
|
||||||
|
|
Loading…
Reference in a new issue