2024-07-30 22:52:23 +00:00
|
|
|
#include "compiler.hpp"
|
|
|
|
|
2024-08-10 20:47:07 +00:00
|
|
|
#include "common.hpp"
|
2024-08-24 23:55:11 +00:00
|
|
|
#include "stdlib.hpp"
|
2024-08-10 20:47:07 +00:00
|
|
|
|
2024-08-04 19:38:56 +00:00
|
|
|
struct Context {
|
|
|
|
Context() {}
|
2024-08-15 00:18:05 +00:00
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
Context(Value&& fname, Array&& constants, Dict&& constants_dict,
|
2024-08-21 22:45:19 +00:00
|
|
|
Dict&& variables_dict, Array&& closures, Dict&& closures_dict,
|
2024-09-01 13:31:25 +00:00
|
|
|
Context* parent, bool toplevel)
|
2024-08-31 18:42:27 +00:00
|
|
|
: fname(std::move(fname)),
|
2024-08-04 19:38:56 +00:00
|
|
|
constants(std::move(constants)),
|
2024-08-10 17:46:20 +00:00
|
|
|
constants_dict(std::move(constants_dict)),
|
2024-08-17 11:33:45 +00:00
|
|
|
variables_dict(std::move(variables_dict)),
|
2024-08-20 22:18:48 +00:00
|
|
|
closures(std::move(closures)),
|
|
|
|
closures_dict(std::move(closures_dict)),
|
2024-08-21 22:45:19 +00:00
|
|
|
maxreg(0),
|
2024-08-23 23:28:29 +00:00
|
|
|
parent(parent),
|
|
|
|
toplevel(toplevel) {}
|
2024-08-04 19:38:56 +00:00
|
|
|
|
2024-08-09 22:45:06 +00:00
|
|
|
static Result<Context> create() {
|
2024-08-31 18:42:27 +00:00
|
|
|
auto fname = TRY(Nil::create());
|
2024-08-09 22:45:06 +00:00
|
|
|
auto constants = TRY(Array::create());
|
2024-08-10 17:46:20 +00:00
|
|
|
auto constants_dict = TRY(Dict::create());
|
2024-08-17 11:33:45 +00:00
|
|
|
auto variables_dict = TRY(Dict::create());
|
2024-08-20 22:18:48 +00:00
|
|
|
auto closures = TRY(Array::create());
|
|
|
|
auto closures_dict = TRY(Dict::create());
|
2024-08-04 19:38:56 +00:00
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
return Context(std::move(fname), std::move(constants),
|
2024-08-21 22:45:19 +00:00
|
|
|
std::move(constants_dict), std::move(variables_dict),
|
2024-09-01 13:31:25 +00:00
|
|
|
std::move(closures), std::move(closures_dict), 0, true);
|
2024-08-20 22:18:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static Result<Context> create(Context& parent) {
|
2024-08-31 18:42:27 +00:00
|
|
|
auto fname = TRY(Nil::create());
|
2024-08-20 22:18:48 +00:00
|
|
|
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());
|
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
return Context(std::move(fname), std::move(constants),
|
2024-08-21 22:45:19 +00:00
|
|
|
std::move(constants_dict), std::move(variables_dict),
|
2024-09-01 13:31:25 +00:00
|
|
|
std::move(closures), std::move(closures_dict), &parent,
|
|
|
|
false);
|
2024-08-04 19:38:56 +00:00
|
|
|
}
|
|
|
|
|
2024-08-10 17:24:16 +00:00
|
|
|
uint64_t alloc_reg() {
|
|
|
|
uint64_t reg = maxreg;
|
|
|
|
++maxreg;
|
|
|
|
return reg;
|
|
|
|
}
|
|
|
|
|
2024-08-10 17:56:42 +00:00
|
|
|
Result<int64_t> add_const(const Value& val) {
|
2024-08-10 17:46:20 +00:00
|
|
|
auto idx = constants_dict.get(val);
|
|
|
|
|
|
|
|
if (idx.has_value()) {
|
|
|
|
if (!idx.value().is<Int64>()) return ERROR(TypeMismatch);
|
|
|
|
|
|
|
|
return idx.value().to<Int64>()->value();
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t i = constants.size();
|
|
|
|
|
|
|
|
constants = TRY(constants.append(val));
|
|
|
|
constants_dict = TRY(constants_dict.insert(val, TRY(Value::create(i))));
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2024-08-31 17:10:17 +00:00
|
|
|
template <class T>
|
|
|
|
Result<int64_t> add_const(const T& val)
|
|
|
|
requires std::derived_from<T, Object>
|
|
|
|
{
|
|
|
|
return add_const(TRY(val.copy_value()));
|
|
|
|
}
|
|
|
|
|
2024-08-17 11:33:45 +00:00
|
|
|
Result<int64_t> add_var(const Value& sym) {
|
|
|
|
auto idx = variables_dict.get(sym);
|
|
|
|
if (idx.has_value()) {
|
|
|
|
if (!idx.value().is<Int64>()) return ERROR(TypeMismatch);
|
|
|
|
|
|
|
|
return idx.value().to<Int64>()->value();
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t i = maxreg;
|
|
|
|
|
|
|
|
variables_dict = TRY(variables_dict.insert(sym, TRY(Value::create(i))));
|
|
|
|
|
|
|
|
maxreg++;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2024-08-28 22:57:01 +00:00
|
|
|
Result<int64_t> update_var(const Value& sym) {
|
|
|
|
int64_t i = maxreg;
|
|
|
|
|
|
|
|
variables_dict = TRY(variables_dict.insert(sym, TRY(Value::create(i))));
|
|
|
|
|
|
|
|
maxreg++;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2024-08-17 11:33:45 +00:00
|
|
|
Result<int64_t> get_var(const Value& sym) {
|
|
|
|
auto idx = variables_dict.get(sym);
|
|
|
|
if (idx.has_value()) {
|
|
|
|
if (!idx.value().is<Int64>()) return ERROR(TypeMismatch);
|
|
|
|
|
|
|
|
return idx.value().to<Int64>()->value();
|
|
|
|
}
|
|
|
|
|
|
|
|
return ERROR(KeyError);
|
|
|
|
}
|
|
|
|
|
2024-08-20 22:18:48 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
// We need to try and look up a symbol in the parent scope. If there's no
|
|
|
|
// parent scope, we will still create a closure, but that closure will be
|
|
|
|
// resolved dynamically at runtime.
|
|
|
|
if (parent) {
|
|
|
|
auto maybe_var = parent->get_var(sym);
|
|
|
|
// If parent has no variable with name <sym>, then we need to propagate
|
|
|
|
// the closure to it. The variable may be defined somewhere in outer
|
|
|
|
// scope.
|
|
|
|
if (!maybe_var.has_value()) {
|
|
|
|
TRY(parent->get_closure(sym));
|
|
|
|
}
|
2024-08-31 17:10:17 +00:00
|
|
|
}
|
2024-08-20 22:18:48 +00:00
|
|
|
|
|
|
|
int64_t i = closures.size();
|
|
|
|
closures = TRY(closures.append(sym));
|
|
|
|
closures_dict = TRY(closures_dict.insert(sym, TRY(Value::create(i))));
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
Value fname;
|
2024-08-04 19:38:56 +00:00
|
|
|
Array constants;
|
2024-08-10 17:46:20 +00:00
|
|
|
Dict constants_dict;
|
2024-08-17 11:33:45 +00:00
|
|
|
Dict variables_dict;
|
2024-08-20 22:18:48 +00:00
|
|
|
Array closures;
|
|
|
|
Dict closures_dict;
|
2024-08-04 19:38:56 +00:00
|
|
|
uint64_t maxreg;
|
2024-08-20 22:18:48 +00:00
|
|
|
Context* parent;
|
2024-08-23 21:08:02 +00:00
|
|
|
bool toplevel;
|
2024-08-04 19:38:56 +00:00
|
|
|
};
|
2024-07-30 22:52:23 +00:00
|
|
|
|
2024-08-10 20:47:07 +00:00
|
|
|
Result<void> Expression::add_opcode(Oc opcode, OpArg arg1, OpArg arg2,
|
|
|
|
OpArg arg3, OpArg arg4) {
|
2024-08-10 17:24:16 +00:00
|
|
|
Value oc = Value(TRY(Opcode::create(opcode, arg1, arg2, arg3, arg4)));
|
2024-08-10 20:47:07 +00:00
|
|
|
code = TRY(code.append(oc));
|
|
|
|
return Result<void>();
|
2024-08-10 17:24:16 +00:00
|
|
|
}
|
|
|
|
|
2024-08-10 10:17:20 +00:00
|
|
|
Result<bool> is_primitive_op(Symbol& sym) {
|
|
|
|
return TRY(sym.cmp("+")) == 0 || TRY(sym.cmp("-")) == 0 ||
|
|
|
|
TRY(sym.cmp("*")) == 0 || TRY(sym.cmp("/")) == 0;
|
2024-08-04 19:38:56 +00:00
|
|
|
return false;
|
2024-07-30 22:52:23 +00:00
|
|
|
}
|
|
|
|
|
2024-08-23 20:30:05 +00:00
|
|
|
Result<bool> is_comparison_op(Symbol& sym) {
|
|
|
|
return TRY(sym.cmp("<")) == 0 || TRY(sym.cmp("<=")) == 0 ||
|
|
|
|
TRY(sym.cmp(">")) == 0 || TRY(sym.cmp(">=")) == 0 ||
|
|
|
|
TRY(sym.cmp("=")) == 0 || TRY(sym.cmp("!=")) == 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-08-04 19:38:56 +00:00
|
|
|
Result<Value> Compiler::compile(Value& expr) {
|
2024-08-09 22:45:06 +00:00
|
|
|
auto context = TRY(Context::create());
|
2024-08-04 19:38:56 +00:00
|
|
|
|
2024-08-26 12:36:01 +00:00
|
|
|
// If expression is empty - just return nil
|
|
|
|
if (expr.is<Nil>()) {
|
|
|
|
auto ex = TRY(Expression::create());
|
|
|
|
uint64_t reg = context.alloc_reg();
|
|
|
|
int64_t c = TRY(context.add_const(TRY(Nil::create())));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, c}));
|
|
|
|
TRY(ex.add_opcode(Oc::Ret, {0, (int64_t)reg}));
|
|
|
|
ex.reg = reg;
|
|
|
|
|
|
|
|
Value name = TRY(Nil::create());
|
|
|
|
auto fun = TRY(Function::create(name, 0, context.constants, ex.code,
|
|
|
|
TRY(Array::create())));
|
|
|
|
|
2024-09-01 13:31:25 +00:00
|
|
|
auto mod = TRY(Module::create(name, fun));
|
2024-08-26 12:36:01 +00:00
|
|
|
|
|
|
|
return Value(std::move(mod));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise perform actual compilation of the expression
|
2024-08-18 21:08:47 +00:00
|
|
|
if (!expr.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto ex = TRY(compile_body(context, expr));
|
2024-08-11 20:37:37 +00:00
|
|
|
TRY(ex.add_opcode(Oc::Ret, {0, (int64_t)ex.reg}));
|
2024-08-10 20:47:07 +00:00
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Value name = TRY(Nil::create());
|
2024-08-13 00:11:23 +00:00
|
|
|
// TRY(debug_print(context.constants));
|
|
|
|
// TRY(debug_print(ex.code));
|
2024-08-11 20:37:37 +00:00
|
|
|
|
2024-08-21 22:45:19 +00:00
|
|
|
auto fun = TRY(Function::create(name, 0, context.constants, ex.code,
|
|
|
|
TRY(Array::create())));
|
2024-08-23 23:28:29 +00:00
|
|
|
|
2024-09-01 13:31:25 +00:00
|
|
|
auto mod = TRY(Module::create(name, fun));
|
2024-08-23 23:28:29 +00:00
|
|
|
|
|
|
|
return Value(std::move(mod));
|
2024-08-04 19:38:56 +00:00
|
|
|
}
|
2024-08-10 17:56:42 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
Result<Expression> Compiler::compile_expr(Context& context, const Value& expr) {
|
2024-07-30 22:52:23 +00:00
|
|
|
switch (expr.tag()) {
|
|
|
|
case Tag::Pair:
|
2024-08-29 22:14:33 +00:00
|
|
|
return compile_list(context, expr);
|
2024-08-10 17:24:16 +00:00
|
|
|
case Tag::Int64:
|
2024-08-24 23:55:11 +00:00
|
|
|
return compile_constant(context, expr);
|
2024-07-30 22:52:23 +00:00
|
|
|
case Tag::Bool:
|
2024-08-29 22:14:33 +00:00
|
|
|
return compile_constant(context, expr);
|
2024-08-17 11:33:45 +00:00
|
|
|
case Tag::Symbol:
|
2024-08-29 22:14:33 +00:00
|
|
|
return compile_symbol(context, expr);
|
2024-08-24 23:55:11 +00:00
|
|
|
case Tag::String:
|
|
|
|
return compile_constant(context, expr);
|
2024-08-15 00:18:05 +00:00
|
|
|
case Tag::Nil:
|
2024-08-26 12:45:13 +00:00
|
|
|
return compile_constant(context, expr);
|
2024-07-30 22:52:23 +00:00
|
|
|
case Tag::Float:
|
2024-08-27 19:15:22 +00:00
|
|
|
return compile_constant(context, expr);
|
2024-09-05 22:22:45 +00:00
|
|
|
case Tag::SrcLoc:
|
2024-07-30 22:52:23 +00:00
|
|
|
case Tag::Syntax:
|
|
|
|
case Tag::Array:
|
|
|
|
case Tag::ByteArray:
|
2024-08-01 17:56:38 +00:00
|
|
|
case Tag::Dict:
|
2024-08-10 17:24:16 +00:00
|
|
|
case Tag::Opcode:
|
2024-08-11 20:37:37 +00:00
|
|
|
case Tag::Function:
|
2024-08-24 23:55:11 +00:00
|
|
|
case Tag::StdlibFunction:
|
2024-08-23 23:28:29 +00:00
|
|
|
case Tag::Module:
|
2024-08-12 22:05:40 +00:00
|
|
|
case Tag::Stack:
|
2024-08-10 10:17:20 +00:00
|
|
|
return ERROR(TypeMismatch);
|
2024-07-30 22:52:23 +00:00
|
|
|
}
|
2024-08-10 10:17:20 +00:00
|
|
|
return ERROR(TypeMismatch);
|
2024-07-30 22:52:23 +00:00
|
|
|
}
|
|
|
|
|
2024-08-10 10:17:20 +00:00
|
|
|
Result<Expression> Compiler::compile_primop(Context& context, Symbol& op,
|
2024-08-29 22:14:33 +00:00
|
|
|
const Value& expr) {
|
2024-08-10 10:17:20 +00:00
|
|
|
Value cur = TRY(expr.rest());
|
2024-08-10 20:47:07 +00:00
|
|
|
Expression ex = TRY(Expression::create());
|
2024-08-10 10:17:20 +00:00
|
|
|
|
2024-08-11 18:25:37 +00:00
|
|
|
Oc opcode = Oc::Unknown;
|
|
|
|
|
|
|
|
if (TRY(op.cmp("+")) == 0) {
|
|
|
|
opcode = Oc::Add;
|
|
|
|
} else if (TRY(op.cmp("*")) == 0) {
|
|
|
|
opcode = Oc::Mul;
|
2024-08-13 00:11:23 +00:00
|
|
|
} else if (TRY(op.cmp("-")) == 0) {
|
|
|
|
opcode = Oc::Sub;
|
|
|
|
} else if (TRY(op.cmp("/")) == 0) {
|
|
|
|
opcode = Oc::Div;
|
2024-08-11 18:25:37 +00:00
|
|
|
} else {
|
|
|
|
return ERROR(NotImplemented);
|
|
|
|
}
|
|
|
|
|
2024-08-11 11:37:00 +00:00
|
|
|
if (cur.is<Nil>()) {
|
|
|
|
uint64_t reg = context.alloc_reg();
|
|
|
|
int64_t zero = TRY(context.add_const(TRY(Value::create((int64_t)0))));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, zero}));
|
|
|
|
|
|
|
|
return ex;
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto subexpr = TRY(cur.first());
|
2024-08-11 11:37:00 +00:00
|
|
|
|
|
|
|
auto comp = TRY(compile_expr(context, subexpr));
|
|
|
|
|
|
|
|
ex.add_code(comp.code);
|
2024-08-11 18:25:37 +00:00
|
|
|
uint64_t firstreg = comp.reg;
|
|
|
|
uint64_t reg = firstreg;
|
2024-08-11 11:37:00 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
cur = TRY(cur.rest());
|
2024-08-10 17:24:16 +00:00
|
|
|
|
2024-08-10 10:17:20 +00:00
|
|
|
while (!cur.is<Nil>()) {
|
2024-08-29 22:14:33 +00:00
|
|
|
auto subexpr = TRY(cur.first());
|
2024-08-10 10:17:20 +00:00
|
|
|
|
|
|
|
auto comp = TRY(compile_expr(context, subexpr));
|
|
|
|
|
2024-08-10 20:47:07 +00:00
|
|
|
ex.add_code(comp.code);
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto rest = TRY(cur.rest());
|
2024-08-11 18:25:37 +00:00
|
|
|
|
|
|
|
uint64_t res = 0;
|
|
|
|
if (rest.is<Nil>())
|
|
|
|
res = firstreg;
|
|
|
|
else
|
|
|
|
res = context.alloc_reg();
|
|
|
|
|
|
|
|
TRY(ex.add_opcode(opcode, {0, (int64_t)res}, {0, (int64_t)reg},
|
2024-08-10 20:47:07 +00:00
|
|
|
{0, (int64_t)comp.reg}));
|
2024-08-10 17:24:16 +00:00
|
|
|
|
|
|
|
reg = res;
|
2024-08-11 18:25:37 +00:00
|
|
|
cur = std::move(rest);
|
2024-08-10 10:17:20 +00:00
|
|
|
}
|
2024-08-10 17:24:16 +00:00
|
|
|
|
2024-08-11 18:25:37 +00:00
|
|
|
context.maxreg = firstreg + 1;
|
2024-08-10 20:47:07 +00:00
|
|
|
ex.reg = reg;
|
|
|
|
return std::move(ex);
|
2024-08-10 10:17:20 +00:00
|
|
|
}
|
2024-08-10 17:24:16 +00:00
|
|
|
|
2024-08-23 20:30:05 +00:00
|
|
|
Result<Expression> Compiler::compile_comparison(Context& context, Symbol& op,
|
2024-08-29 22:14:33 +00:00
|
|
|
const Value& expr) {
|
2024-08-23 20:30:05 +00:00
|
|
|
Value cur = TRY(expr.rest());
|
|
|
|
Expression ex = TRY(Expression::create());
|
|
|
|
|
|
|
|
Oc opcode = Oc::Unknown;
|
|
|
|
int64_t cmp_expected = 0;
|
|
|
|
|
|
|
|
if (TRY(op.cmp("<")) == 0) {
|
|
|
|
opcode = Oc::Less;
|
|
|
|
cmp_expected = 0;
|
|
|
|
} else if (TRY(op.cmp("<=")) == 0) {
|
|
|
|
opcode = Oc::LessEqual;
|
|
|
|
cmp_expected = 0;
|
|
|
|
} else if (TRY(op.cmp(">")) == 0) {
|
|
|
|
opcode = Oc::LessEqual;
|
|
|
|
cmp_expected = 1;
|
|
|
|
} else if (TRY(op.cmp(">=")) == 0) {
|
|
|
|
opcode = Oc::Less;
|
|
|
|
cmp_expected = 1;
|
|
|
|
} else if (TRY(op.cmp("=")) == 0) {
|
|
|
|
opcode = Oc::Equal;
|
|
|
|
cmp_expected = 0;
|
|
|
|
} else if (TRY(op.cmp("!=")) == 0) {
|
|
|
|
opcode = Oc::Equal;
|
|
|
|
cmp_expected = 1;
|
|
|
|
} else {
|
|
|
|
return ERROR(NotImplemented);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cur.is<Nil>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t result = context.alloc_reg();
|
|
|
|
int64_t true_c = TRY(context.add_const(TRY(Bool::create(true))));
|
|
|
|
int64_t false_c = TRY(context.add_const(TRY(Bool::create(false))));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, true_c}));
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto subexpr = TRY(cur.first());
|
2024-08-23 20:30:05 +00:00
|
|
|
|
|
|
|
auto comp = TRY(compile_expr(context, subexpr));
|
|
|
|
|
|
|
|
ex.add_code(comp.code);
|
|
|
|
uint64_t firstreg = comp.reg;
|
|
|
|
uint64_t reg = firstreg;
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
cur = TRY(cur.rest());
|
2024-08-23 20:30:05 +00:00
|
|
|
|
|
|
|
while (!cur.is<Nil>()) {
|
2024-08-29 22:14:33 +00:00
|
|
|
auto subexpr = TRY(cur.first());
|
2024-08-23 20:30:05 +00:00
|
|
|
|
|
|
|
auto comp = TRY(compile_expr(context, subexpr));
|
|
|
|
|
|
|
|
ex.add_code(comp.code);
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto rest = TRY(cur.rest());
|
2024-08-23 20:30:05 +00:00
|
|
|
|
|
|
|
TRY(ex.add_opcode(opcode, {0, (int64_t)reg}, {0, (int64_t)comp.reg},
|
|
|
|
{0, (int64_t)cmp_expected}));
|
|
|
|
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, false_c}));
|
|
|
|
|
|
|
|
reg = comp.reg;
|
|
|
|
cur = std::move(rest);
|
|
|
|
}
|
|
|
|
|
|
|
|
context.maxreg = result + 1;
|
|
|
|
ex.reg = result;
|
|
|
|
return std::move(ex);
|
|
|
|
}
|
|
|
|
|
2024-08-15 00:18:05 +00:00
|
|
|
Result<Expression> Compiler::compile_if(Context& context, Symbol& op,
|
2024-08-29 22:14:33 +00:00
|
|
|
const Value& expr) {
|
2024-08-15 00:18:05 +00:00
|
|
|
Value first = TRY(expr.rest());
|
|
|
|
Expression ex = TRY(Expression::create());
|
2024-08-29 22:14:33 +00:00
|
|
|
if (!first.is<Pair>()) {
|
2024-08-15 00:18:05 +00:00
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
Value second = TRY(first.rest());
|
2024-08-15 00:18:05 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
if (!second.is<Pair>()) {
|
2024-08-15 00:18:05 +00:00
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
Value third = TRY(second.rest());
|
2024-08-15 00:18:05 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
if (!third.is<Pair>()) {
|
2024-08-15 00:18:05 +00:00
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto condition = TRY(first.first());
|
2024-08-15 00:18:05 +00:00
|
|
|
|
|
|
|
auto condition_comp = TRY(compile_expr(context, condition));
|
|
|
|
|
|
|
|
ex.add_code(condition_comp.code);
|
|
|
|
uint64_t firstreg = condition_comp.reg;
|
|
|
|
uint64_t reg = firstreg;
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto option1 = TRY(second.first());
|
2024-08-15 00:18:05 +00:00
|
|
|
auto option1_comp = TRY(compile_expr(context, option1));
|
|
|
|
|
|
|
|
uint64_t option1_reg = option1_comp.reg;
|
|
|
|
|
|
|
|
context.maxreg = firstreg + 1;
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto option2 = TRY(third.first());
|
2024-08-15 00:18:05 +00:00
|
|
|
auto option2_comp = TRY(compile_expr(context, option2));
|
|
|
|
|
|
|
|
int64_t true_const = TRY(context.add_const(TRY(Bool::create(true))));
|
|
|
|
|
2024-08-23 20:30:05 +00:00
|
|
|
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)firstreg}, {1, (int64_t)true_const},
|
|
|
|
{0, (int64_t)0}));
|
|
|
|
|
|
|
|
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)option1_comp.code.size() + 2}));
|
2024-08-15 00:18:05 +00:00
|
|
|
|
|
|
|
ex.add_code(option1_comp.code);
|
|
|
|
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)option2_comp.code.size() + 1}));
|
|
|
|
ex.add_code(option2_comp.code);
|
|
|
|
|
|
|
|
uint64_t option2_reg = option2_comp.reg;
|
|
|
|
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)firstreg},
|
|
|
|
{0, (int64_t)option1_reg}));
|
|
|
|
|
|
|
|
context.maxreg = firstreg + 1;
|
|
|
|
ex.reg = firstreg;
|
|
|
|
return std::move(ex);
|
|
|
|
}
|
|
|
|
|
2024-08-27 21:31:13 +00:00
|
|
|
Result<Expression> Compiler::compile_and(Context& context, Symbol& op,
|
2024-08-29 22:14:33 +00:00
|
|
|
const Value& expr) {
|
2024-08-27 21:31:13 +00:00
|
|
|
Value param = TRY(expr.rest());
|
|
|
|
Expression ex = TRY(Expression::create());
|
|
|
|
if (param.is<Nil>() || !param.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t prev_reg = 0;
|
|
|
|
uint64_t result = context.alloc_reg();
|
|
|
|
int64_t true_c = TRY(context.add_const(TRY(Bool::create(true))));
|
|
|
|
int64_t false_c = TRY(context.add_const(TRY(Bool::create(false))));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, false_c}));
|
|
|
|
|
|
|
|
bool is_first = true;
|
|
|
|
|
|
|
|
while (!param.is<Nil>()) {
|
|
|
|
if (!param.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto rest = TRY(param.rest());
|
|
|
|
Value param_val = TRY(param.first());
|
2024-08-27 21:31:13 +00:00
|
|
|
|
|
|
|
auto param_ex = TRY(compile_expr(context, param_val));
|
|
|
|
|
|
|
|
if (!is_first) {
|
|
|
|
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)prev_reg}, {1, (int64_t)true_c},
|
|
|
|
{0, (int64_t)0}));
|
|
|
|
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)param_ex.code.size() + 2}));
|
|
|
|
}
|
|
|
|
prev_reg = param_ex.reg;
|
|
|
|
TRY(ex.add_code(param_ex.code));
|
|
|
|
|
|
|
|
param = std::move(rest);
|
|
|
|
is_first = false;
|
|
|
|
}
|
|
|
|
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)prev_reg}, {1, (int64_t)true_c},
|
|
|
|
{0, (int64_t)0}));
|
|
|
|
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)2}));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, true_c}));
|
|
|
|
|
|
|
|
context.maxreg = result + 1;
|
|
|
|
ex.reg = result;
|
|
|
|
return std::move(ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
Result<Expression> Compiler::compile_or(Context& context, Symbol& op,
|
2024-08-29 22:14:33 +00:00
|
|
|
const Value& expr) {
|
2024-08-27 21:31:13 +00:00
|
|
|
Value param = TRY(expr.rest());
|
|
|
|
Expression ex = TRY(Expression::create());
|
|
|
|
if (param.is<Nil>() || !param.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t prev_reg = 0;
|
|
|
|
uint64_t result = context.alloc_reg();
|
|
|
|
int64_t true_c = TRY(context.add_const(TRY(Bool::create(true))));
|
|
|
|
int64_t false_c = TRY(context.add_const(TRY(Bool::create(false))));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, true_c}));
|
|
|
|
|
|
|
|
bool is_first = true;
|
|
|
|
|
|
|
|
while (!param.is<Nil>()) {
|
|
|
|
if (!param.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto rest = TRY(param.rest());
|
|
|
|
Value param_val = TRY(param.first());
|
2024-08-27 21:31:13 +00:00
|
|
|
|
|
|
|
auto param_ex = TRY(compile_expr(context, param_val));
|
|
|
|
|
|
|
|
if (!is_first) {
|
|
|
|
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)prev_reg}, {1, (int64_t)true_c},
|
|
|
|
{0, (int64_t)1}));
|
|
|
|
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)param_ex.code.size() + 2}));
|
|
|
|
}
|
|
|
|
prev_reg = param_ex.reg;
|
|
|
|
TRY(ex.add_code(param_ex.code));
|
|
|
|
|
|
|
|
param = std::move(rest);
|
|
|
|
is_first = false;
|
|
|
|
}
|
|
|
|
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)prev_reg}, {1, (int64_t)true_c},
|
|
|
|
{0, (int64_t)1}));
|
|
|
|
TRY(ex.add_opcode(Oc::Jump, {0, (int64_t)2}));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, false_c}));
|
|
|
|
|
|
|
|
context.maxreg = result + 1;
|
|
|
|
ex.reg = result;
|
|
|
|
return std::move(ex);
|
|
|
|
}
|
|
|
|
|
|
|
|
Result<Expression> Compiler::compile_not(Context& context, Symbol& op,
|
2024-08-29 22:14:33 +00:00
|
|
|
const Value& expr) {
|
2024-08-27 21:31:13 +00:00
|
|
|
Value first = TRY(expr.rest());
|
|
|
|
Expression ex = TRY(Expression::create());
|
|
|
|
if (first.is<Nil>() || !first.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto first_expr = TRY(first.first());
|
2024-08-27 21:31:13 +00:00
|
|
|
|
|
|
|
uint64_t result = context.alloc_reg();
|
|
|
|
int64_t true_c = TRY(context.add_const(TRY(Bool::create(true))));
|
|
|
|
int64_t false_c = TRY(context.add_const(TRY(Bool::create(false))));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, true_c}));
|
|
|
|
|
|
|
|
auto comp = TRY(compile_expr(context, first_expr));
|
|
|
|
TRY(ex.add_code(comp.code));
|
|
|
|
|
|
|
|
TRY(ex.add_opcode(Oc::Equal, {0, (int64_t)comp.reg}, {1, (int64_t)true_c},
|
|
|
|
{0, (int64_t)1}));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)result}, {1, false_c}));
|
|
|
|
|
|
|
|
context.maxreg = result + 1;
|
|
|
|
ex.reg = result;
|
|
|
|
return std::move(ex);
|
|
|
|
}
|
|
|
|
|
2024-09-01 22:45:20 +00:00
|
|
|
Result<Expression> Compiler::compile_def(Context& context, Symbol& op,
|
|
|
|
const Value& expr) {
|
|
|
|
auto varname = TRY(expr.second());
|
|
|
|
if (!varname.is<Symbol>()) return ERROR(CompilationError);
|
|
|
|
|
|
|
|
auto var_expr = TRY(expr.third());
|
|
|
|
|
|
|
|
uint64_t maxreg = context.maxreg;
|
|
|
|
Expression ex = TRY(Expression::create());
|
|
|
|
|
|
|
|
auto comp = TRY(compile_expr(context, var_expr));
|
|
|
|
ex.add_code(comp.code);
|
|
|
|
|
|
|
|
int64_t gname = TRY(context.add_const(varname));
|
|
|
|
TRY(ex.add_opcode(Oc::GlobalStore, {1, (int64_t)gname},
|
|
|
|
{0, (int64_t)comp.reg}));
|
|
|
|
|
|
|
|
ex.reg = comp.reg;
|
|
|
|
context.maxreg = maxreg + 1;
|
|
|
|
|
|
|
|
return std::move(ex);
|
|
|
|
}
|
|
|
|
|
2024-08-23 21:08:02 +00:00
|
|
|
Result<Expression> Compiler::compile_fn(Context& context, Symbol& op,
|
2024-08-29 22:14:33 +00:00
|
|
|
const Value& expr) {
|
2024-08-20 22:18:48 +00:00
|
|
|
Context ctx = TRY(Context::create(context));
|
2024-08-17 22:22:21 +00:00
|
|
|
ctx.maxreg = 1; // Reserve the slot for function itself
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
auto first = TRY(expr.rest());
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
if (!first.is<Pair>()) {
|
2024-08-17 11:33:45 +00:00
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-23 21:08:02 +00:00
|
|
|
Value name = TRY(Nil::create());
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto maybe_name = TRY(first.first());
|
2024-08-23 21:08:02 +00:00
|
|
|
|
|
|
|
if (maybe_name.is<Symbol>()) {
|
2024-08-29 22:14:33 +00:00
|
|
|
name = TRY(maybe_name.copy());
|
2024-08-31 18:42:27 +00:00
|
|
|
ctx.fname = TRY(name.copy());
|
2024-08-23 21:08:02 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
first = TRY(first.rest());
|
2024-08-23 21:08:02 +00:00
|
|
|
|
|
|
|
if (first.is<Nil>() || !first.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto param = TRY(first.first());
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
uint64_t arity = 0;
|
|
|
|
while (!param.is<Nil>()) {
|
|
|
|
if (!param.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
2024-08-29 22:14:33 +00:00
|
|
|
auto param_name = TRY(param.first());
|
2024-08-17 11:33:45 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
if (!param_name.is<Symbol>()) {
|
2024-08-17 11:33:45 +00:00
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
int64_t reg = TRY(ctx.add_var(param_name));
|
2024-08-17 11:33:45 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
param = TRY(param.rest());
|
2024-08-17 11:33:45 +00:00
|
|
|
arity++;
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
Value second = TRY(first.rest());
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
if (second.is<Nil>() || !second.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto ex = TRY(compile_body(ctx, second));
|
2024-08-17 11:33:45 +00:00
|
|
|
|
2024-08-17 22:22:21 +00:00
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)0}, {0, (int64_t)ex.reg}));
|
|
|
|
TRY(ex.add_opcode(Oc::Ret, {0, (int64_t)0}));
|
|
|
|
|
2024-08-21 22:45:19 +00:00
|
|
|
auto fun = TRY(Function::create(name, arity, ctx.constants, ex.code,
|
|
|
|
TRY(Array::create())));
|
2024-08-17 22:22:21 +00:00
|
|
|
|
2024-08-20 22:18:48 +00:00
|
|
|
// std::cout << "--------------- LAMBDA " << arity << "\n";
|
2024-08-31 16:30:10 +00:00
|
|
|
// TRY(debug_print(expr));
|
|
|
|
// TRY(debug_print(ctx.constants));
|
|
|
|
// TRY(debug_print(ex.code));
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
Expression ex_res = TRY(Expression::create());
|
|
|
|
|
2024-08-20 22:18:48 +00:00
|
|
|
if (ctx.closures.size() == 0) {
|
2024-09-01 13:31:25 +00:00
|
|
|
int64_t c = TRY(context.add_const(TRY(fun.copy())));
|
|
|
|
|
2024-08-23 23:28:29 +00:00
|
|
|
if (context.toplevel && !name.is<Nil>()) {
|
2024-09-01 13:31:25 +00:00
|
|
|
int64_t gname = TRY(context.add_const(name));
|
|
|
|
TRY(ex_res.add_opcode(Oc::GlobalStore, {1, (int64_t)gname},
|
|
|
|
{1, (int64_t)c}));
|
2024-08-23 23:28:29 +00:00
|
|
|
}
|
|
|
|
|
2024-08-20 22:18:48 +00:00
|
|
|
uint64_t reg = context.alloc_reg();
|
|
|
|
TRY(ex_res.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, (int64_t)c}));
|
2024-08-17 11:33:45 +00:00
|
|
|
|
2024-08-22 16:21:05 +00:00
|
|
|
context.maxreg = reg + 1;
|
2024-08-20 22:18:48 +00:00
|
|
|
ex_res.reg = reg;
|
|
|
|
|
|
|
|
return ex_res;
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
int64_t c = TRY(context.add_const(TRY(fun.copy())));
|
2024-08-17 11:33:45 +00:00
|
|
|
uint64_t reg = context.alloc_reg();
|
|
|
|
TRY(ex_res.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, (int64_t)c}));
|
|
|
|
|
2024-08-20 22:18:48 +00:00
|
|
|
for (uint64_t i = 0; i < ctx.closures.size(); i++) {
|
|
|
|
Value sym = TRY(ctx.closures.get(i));
|
|
|
|
|
2024-08-31 17:10:17 +00:00
|
|
|
// If this is a regular variable from outer scope - it is present in a
|
|
|
|
// register, and as such we can just move it to the correct place
|
|
|
|
auto maybe_var_reg = context.get_var(sym);
|
|
|
|
if (maybe_var_reg.has_value()) {
|
|
|
|
int64_t var_reg = maybe_var_reg.value();
|
|
|
|
uint64_t vr = context.alloc_reg();
|
|
|
|
TRY(ex_res.add_opcode(Oc::Mov, {0, (int64_t)vr}, {0, (int64_t)var_reg}));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise this can be a variable we get from the intermediate closure
|
|
|
|
// (such as in case of 3 nested functions where a var we close over is
|
|
|
|
// defined in the outermost function.
|
|
|
|
auto maybe_closure = context.get_closure(sym);
|
2024-08-31 18:42:27 +00:00
|
|
|
if (maybe_closure.has_value() && !context.toplevel) {
|
2024-08-31 17:10:17 +00:00
|
|
|
int64_t closure_id = maybe_closure.value();
|
|
|
|
uint64_t vr = context.alloc_reg();
|
|
|
|
TRY(ex_res.add_opcode(Oc::ClosureLoad, {0, (int64_t)vr},
|
|
|
|
{0, (int64_t)closure_id}));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
int64_t c = TRY(context.add_const(sym));
|
|
|
|
|
|
|
|
uint64_t vr = context.alloc_reg();
|
|
|
|
TRY(ex_res.add_opcode(Oc::GlobalLoad, {0, (int64_t)vr}, {1, (int64_t)c}));
|
2024-08-20 22:18:48 +00:00
|
|
|
}
|
|
|
|
|
2024-08-21 22:45:19 +00:00
|
|
|
TRY(ex_res.add_opcode(Oc::MakeClosure, {0, (int64_t)reg},
|
|
|
|
{0, (int64_t)reg + (int64_t)ctx.closures.size() + 1}));
|
2024-08-20 22:18:48 +00:00
|
|
|
|
2024-09-01 17:04:12 +00:00
|
|
|
if (context.toplevel && !name.is<Nil>()) {
|
|
|
|
int64_t gname = TRY(context.add_const(name));
|
|
|
|
TRY(ex_res.add_opcode(Oc::GlobalStore, {1, (int64_t)gname},
|
|
|
|
{0, (int64_t)reg}));
|
|
|
|
}
|
|
|
|
|
2024-08-22 16:21:05 +00:00
|
|
|
context.maxreg = reg + 1;
|
2024-08-20 22:18:48 +00:00
|
|
|
ex_res.reg = reg;
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
return ex_res;
|
|
|
|
}
|
|
|
|
|
2024-08-28 22:57:01 +00:00
|
|
|
Result<Expression> Compiler::compile_let(Context& context, Symbol& op,
|
2024-08-29 22:14:33 +00:00
|
|
|
const Value& expr) {
|
2024-08-28 22:57:01 +00:00
|
|
|
// Save the variable bindings to restore it later
|
|
|
|
Dict saved_vars = TRY(context.variables_dict.copy());
|
|
|
|
|
|
|
|
auto first = TRY(expr.rest());
|
|
|
|
|
|
|
|
if (first.is<Nil>() || !first.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t maxreg = context.maxreg;
|
|
|
|
|
|
|
|
auto bindings = TRY(first.first());
|
|
|
|
|
|
|
|
Expression ex_res = TRY(Expression::create());
|
|
|
|
|
|
|
|
while (!bindings.is<Nil>()) {
|
|
|
|
auto binding = TRY(bindings.first());
|
|
|
|
|
|
|
|
if (!binding.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto binding_name = TRY(binding.first());
|
|
|
|
auto binding_expr = TRY(binding.second());
|
|
|
|
|
|
|
|
if (!binding_name.is<Symbol>()) return ERROR(CompilationError);
|
|
|
|
|
|
|
|
int64_t reg = TRY(context.update_var(binding_name));
|
|
|
|
auto ex = TRY(compile_expr(context, binding_expr));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)reg}, {0, (int64_t)ex.reg}));
|
|
|
|
context.maxreg = reg + 1;
|
|
|
|
|
|
|
|
ex_res.add_code(ex.code);
|
|
|
|
|
|
|
|
bindings = TRY(bindings.rest());
|
|
|
|
}
|
|
|
|
|
|
|
|
Value second = TRY(first.rest());
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
if (!second.is<Pair>()) {
|
2024-08-28 22:57:01 +00:00
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto ex = TRY(compile_body(context, second));
|
2024-08-28 22:57:01 +00:00
|
|
|
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)maxreg}, {0, (int64_t)ex.reg}));
|
|
|
|
ex_res.add_code(ex.code);
|
|
|
|
|
|
|
|
// Restore the variables back
|
|
|
|
context.variables_dict = std::move(saved_vars);
|
|
|
|
ex_res.reg = maxreg;
|
|
|
|
|
|
|
|
return std::move(ex_res);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
Result<Expression> Compiler::compile_body(Context& context, const Value& expr) {
|
|
|
|
auto cur = TRY(expr.copy());
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
Expression ex_res = TRY(Expression::create());
|
|
|
|
|
|
|
|
int64_t maxreg = context.maxreg;
|
|
|
|
|
|
|
|
while (!cur.is<Nil>()) {
|
|
|
|
if (!cur.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
2024-08-29 22:14:33 +00:00
|
|
|
auto expr_val = TRY(cur.first());
|
2024-08-17 11:33:45 +00:00
|
|
|
|
2024-08-17 22:22:21 +00:00
|
|
|
// debug_print(expr_val);
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
auto expr = TRY(compile_expr(context, expr_val));
|
|
|
|
|
|
|
|
TRY(ex_res.add_code(expr.code));
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
cur = TRY(cur.rest());
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
if (cur.is<Nil>()) {
|
|
|
|
ex_res.reg = expr.reg;
|
|
|
|
} else {
|
|
|
|
context.maxreg = maxreg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ex_res;
|
|
|
|
}
|
|
|
|
|
2024-08-17 22:22:21 +00:00
|
|
|
Result<Expression> Compiler::compile_function_call(Context& context,
|
2024-08-29 22:14:33 +00:00
|
|
|
const Value& expr) {
|
2024-08-17 22:22:21 +00:00
|
|
|
auto ex = TRY(Expression::create());
|
|
|
|
|
|
|
|
auto first = TRY(expr.first());
|
|
|
|
auto param = TRY(expr.rest());
|
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
bool is_self_call = false;
|
|
|
|
uint64_t firstreg = context.maxreg;
|
|
|
|
if (first.is<Symbol>() && TRY(first.cmp(context.fname)) == 0) {
|
|
|
|
int64_t r = context.alloc_reg();
|
|
|
|
int64_t c = TRY(context.add_const(TRY(Nil::create())));
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)r}, {1, (int64_t)c}));
|
|
|
|
is_self_call = true;
|
|
|
|
} else {
|
|
|
|
auto fun_ex = TRY(compile_expr(context, first));
|
|
|
|
TRY(ex.add_code(fun_ex.code));
|
|
|
|
}
|
2024-08-17 22:22:21 +00:00
|
|
|
|
|
|
|
int64_t maxreg = context.maxreg;
|
|
|
|
while (!param.is<Nil>()) {
|
|
|
|
if (!param.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
2024-08-29 22:14:33 +00:00
|
|
|
Value param_val = TRY(param.first());
|
2024-08-17 22:22:21 +00:00
|
|
|
|
|
|
|
auto param_ex = TRY(compile_expr(context, param_val));
|
|
|
|
TRY(ex.add_code(param_ex.code));
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
param = TRY(param.rest());
|
2024-08-17 22:22:21 +00:00
|
|
|
}
|
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
if (is_self_call) {
|
|
|
|
TRY(ex.add_opcode(Oc::SelfCall, {0, (int64_t)firstreg},
|
|
|
|
{0, (int64_t)context.maxreg}));
|
|
|
|
} else {
|
|
|
|
TRY(ex.add_opcode(Oc::Call, {0, (int64_t)firstreg},
|
|
|
|
{0, (int64_t)context.maxreg}));
|
|
|
|
}
|
2024-08-17 22:22:21 +00:00
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
ex.reg = firstreg;
|
2024-08-22 16:21:05 +00:00
|
|
|
context.maxreg = maxreg;
|
2024-08-17 22:22:21 +00:00
|
|
|
return ex;
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
Result<Expression> Compiler::compile_list(Context& context, const Value& expr) {
|
2024-08-09 22:45:06 +00:00
|
|
|
auto first = TRY(expr.first());
|
2024-08-04 19:38:56 +00:00
|
|
|
|
|
|
|
if (first.is<Symbol>()) {
|
|
|
|
Symbol& sym = *first.to<Symbol>();
|
2024-08-10 10:17:20 +00:00
|
|
|
if (TRY(is_primitive_op(sym))) {
|
|
|
|
return compile_primop(context, sym, expr);
|
2024-08-23 20:30:05 +00:00
|
|
|
} else if (TRY(is_comparison_op(sym))) {
|
|
|
|
return compile_comparison(context, sym, expr);
|
2024-08-15 00:18:05 +00:00
|
|
|
} else if (TRY(sym.cmp("if")) == 0) {
|
|
|
|
return compile_if(context, sym, expr);
|
2024-08-27 21:31:13 +00:00
|
|
|
} else if (TRY(sym.cmp("and")) == 0) {
|
|
|
|
return compile_and(context, sym, expr);
|
|
|
|
} else if (TRY(sym.cmp("or")) == 0) {
|
|
|
|
return compile_or(context, sym, expr);
|
|
|
|
} else if (TRY(sym.cmp("not")) == 0) {
|
|
|
|
return compile_not(context, sym, expr);
|
2024-09-01 22:45:20 +00:00
|
|
|
} else if (TRY(sym.cmp("def")) == 0) {
|
|
|
|
return compile_def(context, sym, expr);
|
2024-08-23 21:08:02 +00:00
|
|
|
} else if (TRY(sym.cmp("fn")) == 0) {
|
|
|
|
return compile_fn(context, sym, expr);
|
2024-08-28 22:57:01 +00:00
|
|
|
} else if (TRY(sym.cmp("let")) == 0) {
|
|
|
|
return compile_let(context, sym, expr);
|
2024-08-18 11:56:54 +00:00
|
|
|
} else {
|
|
|
|
return compile_function_call(context, expr);
|
2024-08-10 10:17:20 +00:00
|
|
|
}
|
2024-08-17 22:22:21 +00:00
|
|
|
} else if (first.is<Pair>()) {
|
|
|
|
return compile_function_call(context, expr);
|
2024-08-04 19:38:56 +00:00
|
|
|
}
|
2024-08-18 11:56:54 +00:00
|
|
|
|
2024-08-10 10:17:20 +00:00
|
|
|
return ERROR(TypeMismatch);
|
2024-07-30 22:52:23 +00:00
|
|
|
}
|
2024-08-10 17:24:16 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
Result<Expression> Compiler::compile_constant(Context& context,
|
|
|
|
const Value& value) {
|
2024-08-10 20:47:07 +00:00
|
|
|
Expression ex = TRY(Expression::create());
|
2024-08-10 17:24:16 +00:00
|
|
|
uint64_t reg = context.alloc_reg();
|
2024-08-10 20:47:07 +00:00
|
|
|
|
2024-08-24 23:55:11 +00:00
|
|
|
int64_t c = TRY(context.add_const(value));
|
2024-08-10 20:47:07 +00:00
|
|
|
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, (int64_t)c}));
|
2024-08-10 17:24:16 +00:00
|
|
|
|
2024-08-10 20:47:07 +00:00
|
|
|
ex.reg = reg;
|
|
|
|
return std::move(ex);
|
2024-08-10 17:24:16 +00:00
|
|
|
}
|
2024-08-11 20:37:37 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
Result<Expression> Compiler::compile_symbol(Context& context,
|
|
|
|
const Value& value) {
|
2024-08-17 11:33:45 +00:00
|
|
|
Expression ex = TRY(Expression::create());
|
|
|
|
|
2024-09-04 22:24:12 +00:00
|
|
|
// Symbol may be self-evaluating (e.g. :foo), in which case we just use it as
|
|
|
|
// its own value
|
|
|
|
|
|
|
|
if (value.is<Symbol>()) {
|
|
|
|
Symbol& sym = *value.to<Symbol>();
|
|
|
|
|
|
|
|
if (sym.size() > 0 && TRY(sym[0]) == ':') {
|
|
|
|
int64_t c = TRY(context.add_const(value));
|
|
|
|
|
|
|
|
uint64_t reg = context.alloc_reg();
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, (int64_t)c}));
|
|
|
|
|
|
|
|
ex.reg = reg;
|
|
|
|
return std::move(ex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto maybe_reg = context.get_var(TRY(value.copy()));
|
2024-08-17 11:33:45 +00:00
|
|
|
|
2024-08-20 22:18:48 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
auto maybe_stdlib_fun = get_stdlib_function(*value.to<Symbol>());
|
2024-08-24 23:55:11 +00:00
|
|
|
|
|
|
|
if (!maybe_stdlib_fun.has_error()) {
|
|
|
|
auto stdlib_fun = TRY(StdlibFunction::create(maybe_stdlib_fun.value()));
|
2024-08-31 17:10:17 +00:00
|
|
|
int64_t c = TRY(context.add_const(stdlib_fun));
|
2024-08-24 23:55:11 +00:00
|
|
|
|
|
|
|
uint64_t reg = context.alloc_reg();
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, (int64_t)c}));
|
|
|
|
|
|
|
|
ex.reg = reg;
|
|
|
|
return std::move(ex);
|
|
|
|
}
|
|
|
|
|
2024-08-31 18:42:27 +00:00
|
|
|
// Most variable references in function bodies will fall back to loading the
|
|
|
|
// accessed variables from parent closures.
|
|
|
|
if (!context.toplevel) {
|
|
|
|
auto maybe_closure = context.get_closure(TRY(value.copy()));
|
|
|
|
|
|
|
|
if (!maybe_closure.has_error()) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-23 23:28:29 +00:00
|
|
|
// Otherwise treat unknown symbol as a global and try to load it from the
|
2024-08-31 18:42:27 +00:00
|
|
|
// global scope. This usually happens in toplevel expressions.
|
2024-08-23 23:28:29 +00:00
|
|
|
|
2024-08-29 22:14:33 +00:00
|
|
|
int64_t c = TRY(context.add_const(TRY(value.copy())));
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
uint64_t reg = context.alloc_reg();
|
2024-08-23 23:28:29 +00:00
|
|
|
TRY(ex.add_opcode(Oc::GlobalLoad, {0, (int64_t)reg}, {1, (int64_t)c}));
|
2024-08-17 11:33:45 +00:00
|
|
|
|
|
|
|
ex.reg = reg;
|
|
|
|
return std::move(ex);
|
|
|
|
}
|
|
|
|
|
2024-08-11 20:37:37 +00:00
|
|
|
Result<Value> compile(Value& expr) {
|
|
|
|
Compiler c = Compiler();
|
|
|
|
return c.compile(expr);
|
|
|
|
}
|