diff --git a/src/common.cpp b/src/common.cpp index 676a0d9..7ce007a 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -229,6 +229,7 @@ Result Function::constants() const { } Result reverse(Value& val) { + if (val.is()) return Value(TRY(Nil::create())); if (!val.is()) return ERROR(TypeMismatch); auto res = Value(TRY(Nil::create())); diff --git a/src/compiler.cpp b/src/compiler.cpp index 5ba4cf8..a963276 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -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 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 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 get_closure(const Value& sym) { + auto idx = closures_dict.get(sym); + if (idx.has_value()) { + if (!idx.value().is()) return ERROR(TypeMismatch); + + return idx.value().to()->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 Expression::add_opcode(Oc opcode, OpArg arg1, OpArg arg2, @@ -269,7 +307,7 @@ Result Compiler::compile_if(Context& context, Symbol& op, Result 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 Compiler::compile_lambda(Context& context, Symbol& op, Pair& first_pair = *first.to(); auto param = TRY(first_pair.first()); - if (param.is()) { - return ERROR(CompilationError); - } uint64_t arity = 0; while (!param.is()) { @@ -320,16 +355,39 @@ Result 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()); - 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(); 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; } @@ -435,14 +493,26 @@ Result Compiler::compile_symbol(Context& context, Symbol& 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); } - auto var_reg = maybe_reg.value(); + auto var_closure = maybe_closure.value(); 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; return std::move(ex); diff --git a/src/opcode.cpp b/src/opcode.cpp index ad51006..62289f3 100644 --- a/src/opcode.cpp +++ b/src/opcode.cpp @@ -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"); } diff --git a/src/opcode.hpp b/src/opcode.hpp index 033c02a..e5c6b9f 100644 --- a/src/opcode.hpp +++ b/src/opcode.hpp @@ -62,6 +62,9 @@ enum class Oc : uint8_t { // Globals SetGlobal, GetGlobal, + // Closures + MakeClosure, + ClosureLoad, }; struct OpArg {