Implement closure compilation (no running yet)

This commit is contained in:
Konstantin Nazarov 2024-08-20 23:18:48 +01:00
parent fdf38c71bf
commit edc0a89ed9
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
4 changed files with 90 additions and 13 deletions

View file

@ -229,6 +229,7 @@ Result<Array> Function::constants() const {
}
Result<Value> reverse(Value& val) {
if (val.is<Nil>()) return Value(TRY(Nil::create()));
if (!val.is<Pair>()) return ERROR(TypeMismatch);
auto res = Value(TRY(Nil::create()));

View file

@ -6,21 +6,37 @@ struct Context {
Context() {}
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)),
constants(std::move(constants)),
constants_dict(std::move(constants_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() {
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(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() {
@ -72,11 +88,33 @@ struct Context {
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;
Array constants;
Dict constants_dict;
Dict variables_dict;
Array closures;
Dict closures_dict;
uint64_t maxreg;
Context* parent;
};
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,
Pair& expr) {
Context ctx = TRY(Context::create());
Context ctx = TRY(Context::create(context));
ctx.maxreg = 1; // Reserve the slot for function itself
auto first = TRY(expr.rest());
@ -281,9 +319,6 @@ Result<Expression> Compiler::compile_lambda(Context& context, Symbol& op,
Pair& first_pair = *first.to<Pair>();
auto param = TRY(first_pair.first());
if (param.is<Nil>()) {
return ERROR(CompilationError);
}
uint64_t arity = 0;
while (!param.is<Nil>()) {
@ -320,16 +355,39 @@ Result<Expression> Compiler::compile_lambda(Context& context, Symbol& op,
Value name = TRY(Nil::create());
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())));
Expression ex_res = TRY(Expression::create());
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.reg = reg;
ex_res.reg = reg;
return ex_res;
}
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}));
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;
}
@ -435,10 +493,7 @@ Result<Expression> Compiler::compile_symbol(Context& context, Symbol& value) {
auto maybe_reg = context.get_var(TRY(value.copy_value()));
if (maybe_reg.has_error()) {
return ERROR(CompilationError);
}
if (!maybe_reg.has_error()) {
auto var_reg = maybe_reg.value();
uint64_t reg = context.alloc_reg();
@ -448,6 +503,21 @@ Result<Expression> Compiler::compile_symbol(Context& context, Symbol& value) {
return std::move(ex);
}
auto maybe_closure = context.get_closure(TRY(value.copy_value()));
if (maybe_closure.has_error()) {
return ERROR(CompilationError);
}
auto var_closure = maybe_closure.value();
uint64_t reg = context.alloc_reg();
TRY(ex.add_opcode(Oc::ClosureLoad, {0, (int64_t)reg}, {0, (int64_t)var_closure}));
ex.reg = reg;
return std::move(ex);
}
Result<Expression> Compiler::compile_bool(Context& context, Bool& value) {
Expression ex = TRY(Expression::create());
uint64_t reg = context.alloc_reg();

View file

@ -81,7 +81,10 @@ op_t get_op(Oc op) {
return op_t{"setglobal", OpcodeType::Reg2};
case Oc::GetGlobal:
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:
die("Unknown opcode\n");
}

View file

@ -62,6 +62,9 @@ enum class Oc : uint8_t {
// Globals
SetGlobal,
GetGlobal,
// Closures
MakeClosure,
ClosureLoad,
};
struct OpArg {