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-17 11:33:45 +00:00
|
|
|
Context(Value&& env, Array&& constants, Dict&& constants_dict,
|
2024-08-21 22:45:19 +00:00
|
|
|
Dict&& variables_dict, Array&& closures, Dict&& closures_dict,
|
2024-08-23 23:28:29 +00:00
|
|
|
Dict&& globals_dict, Context* parent, bool toplevel)
|
2024-08-04 19:38:56 +00:00
|
|
|
: env(std::move(env)),
|
|
|
|
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-23 23:28:29 +00:00
|
|
|
globals_dict(std::move(globals_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() {
|
|
|
|
auto env = TRY(Nil::create());
|
|
|
|
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-23 23:28:29 +00:00
|
|
|
auto globals_dict = TRY(Dict::create());
|
2024-08-04 19:38:56 +00:00
|
|
|
|
2024-08-10 17:46:20 +00:00
|
|
|
return Context(std::move(env), std::move(constants),
|
2024-08-21 22:45:19 +00:00
|
|
|
std::move(constants_dict), std::move(variables_dict),
|
2024-08-23 23:28:29 +00:00
|
|
|
std::move(closures), std::move(closures_dict),
|
|
|
|
std::move(globals_dict), 0, true);
|
2024-08-20 22:18:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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());
|
2024-08-23 23:28:29 +00:00
|
|
|
auto globals_dict = TRY(Dict::create());
|
2024-08-20 22:18:48 +00:00
|
|
|
|
|
|
|
return Context(std::move(env), std::move(constants),
|
2024-08-21 22:45:19 +00:00
|
|
|
std::move(constants_dict), std::move(variables_dict),
|
2024-08-23 23:28:29 +00:00
|
|
|
std::move(closures), std::move(closures_dict),
|
|
|
|
std::move(globals_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-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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-08-23 23:28:29 +00:00
|
|
|
Result<void> add_global(const Value& name, const Value& value) {
|
|
|
|
globals_dict = TRY(globals_dict.insert(name, value));
|
|
|
|
return Result<void>();
|
|
|
|
}
|
|
|
|
|
2024-08-04 19:38:56 +00:00
|
|
|
Value env;
|
|
|
|
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-23 23:28:29 +00:00
|
|
|
Dict globals_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())));
|
|
|
|
|
|
|
|
auto mod = TRY(Module::create(name, fun, context.globals_dict));
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pair& expr_pair = *expr.to<Pair>();
|
|
|
|
|
|
|
|
auto ex = TRY(compile_body(context, expr_pair));
|
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
|
|
|
|
|
|
|
auto mod = TRY(Module::create(name, fun, context.globals_dict));
|
|
|
|
|
|
|
|
return Value(std::move(mod));
|
2024-08-04 19:38:56 +00:00
|
|
|
}
|
2024-08-10 17:56:42 +00:00
|
|
|
|
2024-08-04 19:38:56 +00:00
|
|
|
Result<Expression> Compiler::compile_expr(Context& context, Value& expr) {
|
2024-07-30 22:52:23 +00:00
|
|
|
switch (expr.tag()) {
|
|
|
|
case Tag::Pair:
|
2024-08-04 19:38:56 +00:00
|
|
|
return compile_list(context, *expr.to<Pair>());
|
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-15 00:18:05 +00:00
|
|
|
return compile_bool(context, *expr.to<Bool>());
|
2024-08-17 11:33:45 +00:00
|
|
|
case Tag::Symbol:
|
|
|
|
return compile_symbol(context, *expr.to<Symbol>());
|
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-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,
|
|
|
|
Pair& expr) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
Pair& pair = *cur.to<Pair>();
|
|
|
|
auto subexpr = TRY(pair.first());
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
cur = TRY(pair.rest());
|
2024-08-10 17:24:16 +00:00
|
|
|
|
2024-08-10 10:17:20 +00:00
|
|
|
while (!cur.is<Nil>()) {
|
|
|
|
Pair& pair = *cur.to<Pair>();
|
|
|
|
auto subexpr = TRY(pair.first());
|
|
|
|
|
|
|
|
auto comp = TRY(compile_expr(context, subexpr));
|
|
|
|
|
2024-08-10 20:47:07 +00:00
|
|
|
ex.add_code(comp.code);
|
|
|
|
|
2024-08-11 18:25:37 +00:00
|
|
|
auto rest = TRY(pair.rest());
|
|
|
|
|
|
|
|
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,
|
|
|
|
Pair& expr) {
|
|
|
|
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}));
|
|
|
|
|
|
|
|
Pair& pair = *cur.to<Pair>();
|
|
|
|
auto subexpr = TRY(pair.first());
|
|
|
|
|
|
|
|
auto comp = TRY(compile_expr(context, subexpr));
|
|
|
|
|
|
|
|
ex.add_code(comp.code);
|
|
|
|
uint64_t firstreg = comp.reg;
|
|
|
|
uint64_t reg = firstreg;
|
|
|
|
|
|
|
|
cur = TRY(pair.rest());
|
|
|
|
|
|
|
|
while (!cur.is<Nil>()) {
|
|
|
|
Pair& pair = *cur.to<Pair>();
|
|
|
|
auto subexpr = TRY(pair.first());
|
|
|
|
|
|
|
|
auto comp = TRY(compile_expr(context, subexpr));
|
|
|
|
|
|
|
|
ex.add_code(comp.code);
|
|
|
|
|
|
|
|
auto rest = TRY(pair.rest());
|
|
|
|
|
|
|
|
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,
|
|
|
|
Pair& expr) {
|
|
|
|
Value first = TRY(expr.rest());
|
|
|
|
Expression ex = TRY(Expression::create());
|
|
|
|
if (first.is<Nil>() || !first.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pair& first_pair = *first.to<Pair>();
|
|
|
|
|
|
|
|
Value second = TRY(first_pair.rest());
|
|
|
|
|
|
|
|
if (second.is<Nil>() || !second.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pair& second_pair = *second.to<Pair>();
|
|
|
|
|
|
|
|
Value third = TRY(second_pair.rest());
|
|
|
|
|
|
|
|
if (third.is<Nil>() || !third.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pair& third_pair = *third.to<Pair>();
|
|
|
|
|
|
|
|
auto condition = TRY(first_pair.first());
|
|
|
|
|
|
|
|
auto condition_comp = TRY(compile_expr(context, condition));
|
|
|
|
|
|
|
|
ex.add_code(condition_comp.code);
|
|
|
|
uint64_t firstreg = condition_comp.reg;
|
|
|
|
uint64_t reg = firstreg;
|
|
|
|
|
|
|
|
auto option1 = TRY(second_pair.first());
|
|
|
|
auto option1_comp = TRY(compile_expr(context, option1));
|
|
|
|
|
|
|
|
uint64_t option1_reg = option1_comp.reg;
|
|
|
|
|
|
|
|
context.maxreg = firstreg + 1;
|
|
|
|
|
|
|
|
auto option2 = TRY(third_pair.first());
|
|
|
|
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-23 21:08:02 +00:00
|
|
|
Result<Expression> Compiler::compile_fn(Context& context, Symbol& op,
|
|
|
|
Pair& 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());
|
|
|
|
|
|
|
|
if (first.is<Nil>() || !first.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pair& first_pair = *first.to<Pair>();
|
|
|
|
|
2024-08-23 21:08:02 +00:00
|
|
|
Value name = TRY(Nil::create());
|
|
|
|
|
|
|
|
auto maybe_name = TRY(first_pair.first());
|
|
|
|
|
|
|
|
if (maybe_name.is<Symbol>()) {
|
|
|
|
name = TRY(maybe_name.to<Symbol>()->copy_value());
|
|
|
|
|
|
|
|
first = TRY(first_pair.rest());
|
|
|
|
|
|
|
|
if (first.is<Nil>() || !first.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pair& first_pair = *first.to<Pair>();
|
|
|
|
}
|
|
|
|
|
2024-08-17 11:33:45 +00:00
|
|
|
auto param = TRY(first_pair.first());
|
|
|
|
|
|
|
|
uint64_t arity = 0;
|
|
|
|
while (!param.is<Nil>()) {
|
|
|
|
if (!param.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
Pair& param_pair = *param.to<Pair>();
|
|
|
|
|
|
|
|
auto param_first = TRY(param_pair.first());
|
|
|
|
|
|
|
|
if (!param_first.is<Symbol>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t reg = TRY(ctx.add_var(param_first));
|
|
|
|
|
|
|
|
param = TRY(param_pair.rest());
|
|
|
|
arity++;
|
|
|
|
}
|
|
|
|
|
|
|
|
Value second = TRY(first_pair.rest());
|
|
|
|
|
|
|
|
if (second.is<Nil>() || !second.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
|
|
|
|
Pair& second_pair = *second.to<Pair>();
|
|
|
|
|
|
|
|
auto ex = TRY(compile_body(ctx, second_pair));
|
|
|
|
|
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";
|
|
|
|
// TRY(debug_print(TRY(expr.copy_value())));
|
2024-08-23 23:28:29 +00:00
|
|
|
// TRY(debug_print(TRY(ctx.constants.copy())));
|
2024-08-17 22:22:21 +00:00
|
|
|
// TRY(debug_print(TRY(ex.code.copy())));
|
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-08-23 23:28:29 +00:00
|
|
|
if (context.toplevel && !name.is<Nil>()) {
|
|
|
|
context.add_global(name, TRY(fun.copy_value()));
|
|
|
|
}
|
|
|
|
|
2024-08-20 22:18:48 +00:00
|
|
|
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}));
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t c = TRY(context.add_const(TRY(fun.copy_value())));
|
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));
|
|
|
|
|
|
|
|
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}));
|
|
|
|
}
|
|
|
|
|
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-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;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result<Expression> Compiler::compile_body(Context& context, Pair& expr) {
|
|
|
|
auto cur = TRY(expr.copy_value());
|
|
|
|
|
|
|
|
Expression ex_res = TRY(Expression::create());
|
|
|
|
|
|
|
|
int64_t maxreg = context.maxreg;
|
|
|
|
|
|
|
|
while (!cur.is<Nil>()) {
|
|
|
|
if (!cur.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
Pair& cur_pair = *cur.to<Pair>();
|
|
|
|
auto expr_val = TRY(cur_pair.first());
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
cur = TRY(cur_pair.rest());
|
|
|
|
|
|
|
|
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,
|
|
|
|
Pair& expr) {
|
|
|
|
auto ex = TRY(Expression::create());
|
|
|
|
|
|
|
|
auto first = TRY(expr.first());
|
|
|
|
auto param = TRY(expr.rest());
|
|
|
|
|
|
|
|
auto fun_ex = TRY(compile_expr(context, first));
|
|
|
|
TRY(ex.add_code(fun_ex.code));
|
|
|
|
|
|
|
|
int64_t maxreg = context.maxreg;
|
|
|
|
while (!param.is<Nil>()) {
|
|
|
|
if (!param.is<Pair>()) {
|
|
|
|
return ERROR(CompilationError);
|
|
|
|
}
|
|
|
|
Pair& param_pair = *param.to<Pair>();
|
|
|
|
Value param_val = TRY(param_pair.first());
|
|
|
|
|
|
|
|
auto param_ex = TRY(compile_expr(context, param_val));
|
|
|
|
TRY(ex.add_code(param_ex.code));
|
|
|
|
|
|
|
|
param = TRY(param_pair.rest());
|
|
|
|
}
|
|
|
|
|
|
|
|
TRY(ex.add_opcode(Oc::Call, {0, (int64_t)fun_ex.reg},
|
|
|
|
{0, (int64_t)context.maxreg}));
|
|
|
|
|
|
|
|
ex.reg = fun_ex.reg;
|
2024-08-22 16:21:05 +00:00
|
|
|
context.maxreg = maxreg;
|
2024-08-17 22:22:21 +00:00
|
|
|
return ex;
|
|
|
|
}
|
|
|
|
|
2024-08-04 19:38:56 +00:00
|
|
|
Result<Expression> Compiler::compile_list(Context& context, Pair& 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-23 21:08:02 +00:00
|
|
|
} else if (TRY(sym.cmp("fn")) == 0) {
|
|
|
|
return compile_fn(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-24 23:55:11 +00:00
|
|
|
Result<Expression> Compiler::compile_constant(Context& context, 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-17 11:33:45 +00:00
|
|
|
Result<Expression> Compiler::compile_symbol(Context& context, Symbol& value) {
|
|
|
|
Expression ex = TRY(Expression::create());
|
|
|
|
|
|
|
|
auto maybe_reg = context.get_var(TRY(value.copy_value()));
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto maybe_closure = context.get_closure(TRY(value.copy_value()));
|
|
|
|
|
2024-08-23 23:28:29 +00:00
|
|
|
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-17 11:33:45 +00:00
|
|
|
}
|
|
|
|
|
2024-08-24 23:55:11 +00:00
|
|
|
auto maybe_stdlib_fun = get_stdlib_function(value);
|
|
|
|
|
|
|
|
if (!maybe_stdlib_fun.has_error()) {
|
|
|
|
auto stdlib_fun = TRY(StdlibFunction::create(maybe_stdlib_fun.value()));
|
|
|
|
int64_t c = TRY(context.add_const(TRY(stdlib_fun.copy_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-23 23:28:29 +00:00
|
|
|
// Otherwise treat unknown symbol as a global and try to load it from the
|
|
|
|
// global scope
|
|
|
|
|
|
|
|
int64_t c = TRY(context.add_const(TRY(value.copy_value())));
|
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-15 00:18:05 +00:00
|
|
|
Result<Expression> Compiler::compile_bool(Context& context, Bool& value) {
|
|
|
|
Expression ex = TRY(Expression::create());
|
|
|
|
uint64_t reg = context.alloc_reg();
|
|
|
|
|
|
|
|
int64_t c = TRY(context.add_const(TRY(value.copy_value())));
|
|
|
|
|
|
|
|
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)reg}, {1, (int64_t)c}));
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|