Compare commits

..

No commits in common. "c710b622da7c57c3da6cd2f0ffed64454ae790d3" and "5b80e3aa63c68867d85647d8cf4428395ea5b045" have entirely different histories.

15 changed files with 2252 additions and 2341 deletions

View file

@ -1,9 +0,0 @@
---
Checks: >
bugprone-branch-clone,
bugprone-assert-side-effect,
bugprone-string-constructor,
performance-inefficient-vector-operation,
performance-unnecessary-value-param,
WarningsAsErrors: ''
HeaderFilterRegex: '^(src/|tests/)'

View file

@ -9,7 +9,6 @@
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
in { in {
default = pkgs.mkShell { default = pkgs.mkShell {
name = "nixir";
buildInputs = with pkgs; [ buildInputs = with pkgs; [
boost.dev boost.dev
libblake3.dev libblake3.dev
@ -28,10 +27,11 @@
pkg-config pkg-config
ninja ninja
bear bear
clang-tools
]; ];
env.NIX_PLUGINABI = "0.2"; env = {
NIX_PLUGINABI = "0.2";
};
}; };
}); });
}; };

View file

@ -7,457 +7,481 @@
#include "nix/expr/value.hh" #include "nix/expr/value.hh"
#include "nix/util/error.hh" #include "nix/util/error.hh"
#include <stdexcept>
#include <unordered_map> #include <unordered_map>
#include <stdexcept>
namespace nix_irc { namespace nix_irc {
using namespace nix; using namespace nix;
struct IREnvironment { struct IREnvironment {
IREnvironment* parent; IREnvironment* parent;
std::vector<Value*> bindings; std::vector<Value*> bindings;
Value* with_attrs; Value* with_attrs;
explicit IREnvironment(IREnvironment* p = nullptr) : parent(p), with_attrs(nullptr) {} explicit IREnvironment(IREnvironment* p = nullptr) : parent(p), with_attrs(nullptr) {}
void bind(Value* val) { bindings.push_back(val); } void bind(Value* val) {
bindings.push_back(val);
Value* lookup(uint32_t index) {
IREnvironment* env = this;
while (env) {
if (index < env->bindings.size()) {
return env->bindings[index];
}
index -= env->bindings.size();
env = env->parent;
} }
return nullptr;
}
Value* lookup_with(EvalState& state, const std::string& name) { Value* lookup(uint32_t index) {
IREnvironment* env = this; IREnvironment* env = this;
while (env) { while (env) {
if (env->with_attrs && env->with_attrs->type() == nAttrs) { if (index < env->bindings.size()) {
auto sym = state.symbols.create(name); return env->bindings[index];
auto attr = env->with_attrs->attrs()->get(sym); }
if (attr) { index -= env->bindings.size();
return attr->value; env = env->parent;
} }
} return nullptr;
env = env->parent; }
Value* lookup_with(EvalState& state, const std::string& name) {
IREnvironment* env = this;
while (env) {
if (env->with_attrs && env->with_attrs->type() == nAttrs) {
auto sym = state.symbols.create(name);
auto attr = env->with_attrs->attrs()->get(sym);
if (attr) {
return attr->value;
}
}
env = env->parent;
}
return nullptr;
} }
return nullptr;
}
}; };
struct Thunk { struct Thunk {
std::shared_ptr<Node> expr; std::shared_ptr<Node> expr;
IREnvironment* env; IREnvironment* env;
bool blackholed; bool blackholed;
Thunk(const std::shared_ptr<Node>& e, IREnvironment* en) : expr(e), env(en), blackholed(false) {} Thunk(std::shared_ptr<Node> e, IREnvironment* en)
: expr(e), env(en), blackholed(false) {}
}; };
struct Evaluator::Impl { struct Evaluator::Impl {
EvalState& state; EvalState& state;
std::unordered_map<Value*, std::unique_ptr<Thunk>> thunks; std::unordered_map<Value*, std::unique_ptr<Thunk>> thunks;
std::vector<std::unique_ptr<IREnvironment>> environments; std::vector<std::unique_ptr<IREnvironment>> environments;
explicit Impl(EvalState& s) : state(s) {} explicit Impl(EvalState& s) : state(s) {}
~Impl() { ~Impl() {
for (auto& env : environments) { for (auto& env : environments) {
delete env.release(); delete env.release();
} }
}
IREnvironment* make_env(IREnvironment* parent = nullptr) {
auto env = new IREnvironment(parent);
environments.push_back(std::unique_ptr<IREnvironment>(env));
return env;
}
Value* make_thunk(const std::shared_ptr<Node>& expr, IREnvironment* env) {
Value* v = state.allocValue();
thunks[v] = std::make_unique<Thunk>(expr, env);
return v;
}
void force(Value* v) {
auto it = thunks.find(v);
if (it == thunks.end()) {
return;
} }
Thunk* thunk = it->second.get(); IREnvironment* make_env(IREnvironment* parent = nullptr) {
if (thunk->blackholed) { auto env = new IREnvironment(parent);
state.error<EvalError>("infinite recursion encountered").debugThrow(); environments.push_back(std::unique_ptr<IREnvironment>(env));
return env;
} }
thunk->blackholed = true; Value* make_thunk(const std::shared_ptr<Node>& expr, IREnvironment* env) {
eval_node(thunk->expr, *v, thunk->env); Value* v = state.allocValue();
thunks.erase(v); thunks[v] = std::make_unique<Thunk>(expr, env);
} return v;
void eval_node(const std::shared_ptr<Node>& node, Value& v, IREnvironment* env) {
if (!node) {
v.mkNull();
return;
} }
if (auto* n = node->get_if<ConstIntNode>()) { void force(Value* v) {
v.mkInt(n->value); auto it = thunks.find(v);
} else if (auto* n = node->get_if<ConstStringNode>()) { if (it == thunks.end()) {
v.mkString(n->value); return;
} else if (auto* n = node->get_if<ConstPathNode>()) {
v.mkPath(state.rootPath(CanonPath(n->value)));
} else if (auto* n = node->get_if<ConstBoolNode>()) {
v.mkBool(n->value);
} else if (auto* n = node->get_if<ConstNullNode>()) { // NOLINT(bugprone-branch-clone)
v.mkNull();
} else if (auto* n = node->get_if<VarNode>()) {
Value* bound = env ? env->lookup(n->index) : nullptr;
if (!bound && env && n->name.has_value()) {
bound = env->lookup_with(state, n->name.value());
}
if (!bound) {
state.error<EvalError>("variable not found").debugThrow();
}
force(bound);
v = *bound;
} else if (auto* n = node->get_if<LambdaNode>()) {
auto lambda_env = env;
auto body = n->body;
auto primOp = [this, lambda_env, body](EvalState& state, PosIdx pos, Value** args,
Value& result) {
auto call_env = make_env(lambda_env);
call_env->bind(args[0]);
eval_node(body, result, call_env);
};
v.mkPrimOp(new PrimOp{.name = n->param_name.value_or("lambda"),
.arity = static_cast<size_t>(n->arity),
.fun = primOp});
} else if (auto* n = node->get_if<AppNode>()) {
Value* func_val = state.allocValue();
eval_node(n->func, *func_val, env);
force(func_val);
Value* arg_val = make_thunk(n->arg, env);
state.callFunction(*func_val, *arg_val, v, noPos);
} else if (auto* n = node->get_if<BinaryOpNode>()) {
Value* left = state.allocValue();
Value* right = state.allocValue();
switch (n->op) {
case BinaryOp::AND:
eval_node(n->left, *left, env);
force(left);
if (left->type() != nBool) {
state.error<EvalError>("type error in logical AND").debugThrow();
} }
if (!left->boolean()) {
v.mkBool(false);
} else {
eval_node(n->right, *right, env);
force(right);
if (right->type() != nBool) {
state.error<EvalError>("type error in logical AND").debugThrow();
}
v.mkBool(right->boolean());
}
break;
case BinaryOp::OR:
eval_node(n->left, *left, env);
force(left);
if (left->type() != nBool) {
state.error<EvalError>("type error in logical OR").debugThrow();
}
if (left->boolean()) {
v.mkBool(true);
} else {
eval_node(n->right, *right, env);
force(right);
if (right->type() != nBool) {
state.error<EvalError>("type error in logical OR").debugThrow();
}
v.mkBool(right->boolean());
}
break;
case BinaryOp::IMPL:
eval_node(n->left, *left, env);
force(left);
if (left->type() != nBool) {
state.error<EvalError>("type error in implication").debugThrow();
}
if (!left->boolean()) {
v.mkBool(true);
} else {
eval_node(n->right, *right, env);
force(right);
if (right->type() != nBool) {
state.error<EvalError>("type error in implication").debugThrow();
}
v.mkBool(right->boolean());
}
break;
default:
eval_node(n->left, *left, env);
eval_node(n->right, *right, env);
force(left);
force(right);
switch (n->op) { Thunk* thunk = it->second.get();
case BinaryOp::ADD: if (thunk->blackholed) {
if (left->type() == nInt && right->type() == nInt) { state.error<EvalError>("infinite recursion encountered").debugThrow();
v.mkInt((left->integer() + right->integer()).valueWrapping()); }
} else if (left->type() == nString && right->type() == nString) {
v.mkString(std::string(left->c_str()) + std::string(right->c_str())); thunk->blackholed = true;
} else { eval_node(thunk->expr, *v, thunk->env);
state.error<EvalError>("type error in addition").debugThrow(); thunks.erase(v);
} }
break;
case BinaryOp::SUB: void eval_node(const std::shared_ptr<Node>& node, Value& v, IREnvironment* env) {
if (left->type() == nInt && right->type() == nInt) { if (!node) {
v.mkInt((left->integer() - right->integer()).valueWrapping()); v.mkNull();
} else { return;
state.error<EvalError>("type error in subtraction").debugThrow(); }
}
break; if (auto* n = node->get_if<ConstIntNode>()) {
case BinaryOp::MUL: v.mkInt(n->value);
if (left->type() == nInt && right->type() == nInt) { }
v.mkInt((left->integer() * right->integer()).valueWrapping()); else if (auto* n = node->get_if<ConstStringNode>()) {
} else { v.mkString(n->value);
state.error<EvalError>("type error in multiplication").debugThrow(); }
} else if (auto* n = node->get_if<ConstPathNode>()) {
break; v.mkPath(state.rootPath(CanonPath(n->value)));
case BinaryOp::DIV: }
if (left->type() == nInt && right->type() == nInt) { else if (auto* n = node->get_if<ConstBoolNode>()) {
if (right->integer() == NixInt(0)) { v.mkBool(n->value);
state.error<EvalError>("division by zero").debugThrow(); }
else if (auto* n = node->get_if<ConstNullNode>()) {
v.mkNull();
}
else if (auto* n = node->get_if<VarNode>()) {
Value* bound = env ? env->lookup(n->index) : nullptr;
if (!bound && env && n->name.has_value()) {
bound = env->lookup_with(state, n->name.value());
} }
v.mkInt((left->integer() / right->integer()).valueWrapping()); if (!bound) {
} else { state.error<EvalError>("variable not found").debugThrow();
state.error<EvalError>("type error in division").debugThrow(); }
} force(bound);
break; v = *bound;
case BinaryOp::EQ:
v.mkBool(state.eqValues(*left, *right, noPos, ""));
break;
case BinaryOp::NE:
v.mkBool(!state.eqValues(*left, *right, noPos, ""));
break;
case BinaryOp::LT:
if (left->type() == nInt && right->type() == nInt) {
v.mkBool(left->integer() < right->integer());
} else if (left->type() == nString && right->type() == nString) {
v.mkBool(std::string(left->c_str()) < std::string(right->c_str()));
} else {
state.error<EvalError>("type error in comparison").debugThrow();
}
break;
case BinaryOp::GT:
if (left->type() == nInt && right->type() == nInt) {
v.mkBool(left->integer() > right->integer());
} else if (left->type() == nString && right->type() == nString) {
v.mkBool(std::string(left->c_str()) > std::string(right->c_str()));
} else {
state.error<EvalError>("type error in comparison").debugThrow();
}
break;
case BinaryOp::LE:
if (left->type() == nInt && right->type() == nInt) {
v.mkBool(left->integer() <= right->integer());
} else if (left->type() == nString && right->type() == nString) {
v.mkBool(std::string(left->c_str()) <= std::string(right->c_str()));
} else {
state.error<EvalError>("type error in comparison").debugThrow();
}
break;
case BinaryOp::GE:
if (left->type() == nInt && right->type() == nInt) {
v.mkBool(left->integer() >= right->integer());
} else if (left->type() == nString && right->type() == nString) {
v.mkBool(std::string(left->c_str()) >= std::string(right->c_str()));
} else {
state.error<EvalError>("type error in comparison").debugThrow();
}
break;
case BinaryOp::CONCAT:
// ++ is list concatenation in Nix; string concat uses ADD (+)
state.error<EvalError>("list concatenation not yet implemented").debugThrow();
break;
default:
state.error<EvalError>("unknown binary operator").debugThrow();
} }
break; else if (auto* n = node->get_if<LambdaNode>()) {
} auto lambda_env = env;
} else if (auto* n = node->get_if<UnaryOpNode>()) { auto body = n->body;
Value* operand = state.allocValue();
eval_node(n->operand, *operand, env);
force(operand);
switch (n->op) { auto primOp = [this, lambda_env, body](EvalState& state, PosIdx pos,
case UnaryOp::NEG: Value** args, Value& result) {
if (operand->type() == nInt) { auto call_env = make_env(lambda_env);
v.mkInt((NixInt(0) - operand->integer()).valueWrapping()); call_env->bind(args[0]);
} else { eval_node(body, result, call_env);
state.error<EvalError>("type error in negation").debugThrow(); };
v.mkPrimOp(new PrimOp {
.name = n->param_name.value_or("lambda"),
.arity = static_cast<size_t>(n->arity),
.fun = primOp
});
} }
break; else if (auto* n = node->get_if<AppNode>()) {
case UnaryOp::NOT: Value* func_val = state.allocValue();
if (operand->type() == nBool) { eval_node(n->func, *func_val, env);
v.mkBool(!operand->boolean()); force(func_val);
} else {
state.error<EvalError>("type error in logical NOT").debugThrow(); Value* arg_val = make_thunk(n->arg, env);
state.callFunction(*func_val, *arg_val, v, noPos);
} }
break; else if (auto* n = node->get_if<BinaryOpNode>()) {
default: Value* left = state.allocValue();
state.error<EvalError>("unknown unary operator").debugThrow(); Value* right = state.allocValue();
}
} else if (auto* n = node->get_if<IfNode>()) {
Value* cond = state.allocValue();
eval_node(n->cond, *cond, env);
force(cond);
if (cond->type() != nBool) { switch (n->op) {
state.error<EvalError>("condition must be a boolean").debugThrow(); case BinaryOp::AND:
} eval_node(n->left, *left, env);
force(left);
if (left->type() != nBool) {
state.error<EvalError>("type error in logical AND").debugThrow();
}
if (!left->boolean()) {
v.mkBool(false);
} else {
eval_node(n->right, *right, env);
force(right);
if (right->type() != nBool) {
state.error<EvalError>("type error in logical AND").debugThrow();
}
v.mkBool(right->boolean());
}
break;
case BinaryOp::OR:
eval_node(n->left, *left, env);
force(left);
if (left->type() != nBool) {
state.error<EvalError>("type error in logical OR").debugThrow();
}
if (left->boolean()) {
v.mkBool(true);
} else {
eval_node(n->right, *right, env);
force(right);
if (right->type() != nBool) {
state.error<EvalError>("type error in logical OR").debugThrow();
}
v.mkBool(right->boolean());
}
break;
case BinaryOp::IMPL:
eval_node(n->left, *left, env);
force(left);
if (left->type() != nBool) {
state.error<EvalError>("type error in implication").debugThrow();
}
if (!left->boolean()) {
v.mkBool(true);
} else {
eval_node(n->right, *right, env);
force(right);
if (right->type() != nBool) {
state.error<EvalError>("type error in implication").debugThrow();
}
v.mkBool(right->boolean());
}
break;
default:
eval_node(n->left, *left, env);
eval_node(n->right, *right, env);
force(left);
force(right);
if (cond->boolean()) { switch (n->op) {
eval_node(n->then_branch, v, env); case BinaryOp::ADD:
} else { if (left->type() == nInt && right->type() == nInt) {
eval_node(n->else_branch, v, env); v.mkInt((left->integer() + right->integer()).valueWrapping());
} } else if (left->type() == nString && right->type() == nString) {
} else if (auto* n = node->get_if<LetNode>()) { v.mkString(std::string(left->c_str()) + std::string(right->c_str()));
auto let_env = make_env(env); } else {
for (const auto& [name, expr] : n->bindings) { state.error<EvalError>("type error in addition").debugThrow();
Value* val = make_thunk(expr, env); }
let_env->bind(val); break;
} case BinaryOp::SUB:
eval_node(n->body, v, let_env); if (left->type() == nInt && right->type() == nInt) {
} else if (auto* n = node->get_if<LetRecNode>()) { v.mkInt((left->integer() - right->integer()).valueWrapping());
auto letrec_env = make_env(env); } else {
std::vector<Value*> thunk_vals; state.error<EvalError>("type error in subtraction").debugThrow();
}
for (const auto& [name, expr] : n->bindings) { break;
Value* val = make_thunk(expr, letrec_env); case BinaryOp::MUL:
thunk_vals.push_back(val); if (left->type() == nInt && right->type() == nInt) {
letrec_env->bind(val); v.mkInt((left->integer() * right->integer()).valueWrapping());
} } else {
state.error<EvalError>("type error in multiplication").debugThrow();
eval_node(n->body, v, letrec_env); }
} else if (auto* n = node->get_if<AttrsetNode>()) { break;
auto bindings = state.buildBindings(n->attrs.size()); case BinaryOp::DIV:
if (left->type() == nInt && right->type() == nInt) {
IREnvironment* attr_env = env; if (right->integer() == NixInt(0)) {
if (n->recursive) { state.error<EvalError>("division by zero").debugThrow();
attr_env = make_env(env); }
for (const auto& [key, val] : n->attrs) { v.mkInt((left->integer() / right->integer()).valueWrapping());
Value* thunk = make_thunk(val, attr_env); } else {
attr_env->bind(thunk); state.error<EvalError>("type error in division").debugThrow();
}
break;
case BinaryOp::EQ:
v.mkBool(state.eqValues(*left, *right, noPos, ""));
break;
case BinaryOp::NE:
v.mkBool(!state.eqValues(*left, *right, noPos, ""));
break;
case BinaryOp::LT:
if (left->type() == nInt && right->type() == nInt) {
v.mkBool(left->integer() < right->integer());
} else if (left->type() == nString && right->type() == nString) {
v.mkBool(std::string(left->c_str()) < std::string(right->c_str()));
} else {
state.error<EvalError>("type error in comparison").debugThrow();
}
break;
case BinaryOp::GT:
if (left->type() == nInt && right->type() == nInt) {
v.mkBool(left->integer() > right->integer());
} else if (left->type() == nString && right->type() == nString) {
v.mkBool(std::string(left->c_str()) > std::string(right->c_str()));
} else {
state.error<EvalError>("type error in comparison").debugThrow();
}
break;
case BinaryOp::LE:
if (left->type() == nInt && right->type() == nInt) {
v.mkBool(left->integer() <= right->integer());
} else if (left->type() == nString && right->type() == nString) {
v.mkBool(std::string(left->c_str()) <= std::string(right->c_str()));
} else {
state.error<EvalError>("type error in comparison").debugThrow();
}
break;
case BinaryOp::GE:
if (left->type() == nInt && right->type() == nInt) {
v.mkBool(left->integer() >= right->integer());
} else if (left->type() == nString && right->type() == nString) {
v.mkBool(std::string(left->c_str()) >= std::string(right->c_str()));
} else {
state.error<EvalError>("type error in comparison").debugThrow();
}
break;
case BinaryOp::CONCAT:
// ++ is list concatenation in Nix; string concat uses ADD (+)
state.error<EvalError>("list concatenation not yet implemented").debugThrow();
break;
default:
state.error<EvalError>("unknown binary operator").debugThrow();
}
break;
}
} }
} else if (auto* n = node->get_if<UnaryOpNode>()) {
Value* operand = state.allocValue();
eval_node(n->operand, *operand, env);
force(operand);
for (const auto& [key, val] : n->attrs) { switch (n->op) {
Value* attr_val = state.allocValue(); case UnaryOp::NEG:
if (n->recursive) { if (operand->type() == nInt) {
eval_node(val, *attr_val, attr_env); v.mkInt((NixInt(0) - operand->integer()).valueWrapping());
} else { } else {
eval_node(val, *attr_val, env); state.error<EvalError>("type error in negation").debugThrow();
}
break;
case UnaryOp::NOT:
if (operand->type() == nBool) {
v.mkBool(!operand->boolean());
} else {
state.error<EvalError>("type error in logical NOT").debugThrow();
}
break;
default:
state.error<EvalError>("unknown unary operator").debugThrow();
}
} }
bindings.insert(state.symbols.create(key), attr_val); else if (auto* n = node->get_if<IfNode>()) {
} Value* cond = state.allocValue();
eval_node(n->cond, *cond, env);
force(cond);
v.mkAttrs(bindings.finish()); if (cond->type() != nBool) {
} else if (auto* n = node->get_if<SelectNode>()) { state.error<EvalError>("condition must be a boolean").debugThrow();
Value* obj = state.allocValue(); }
eval_node(n->expr, *obj, env);
force(obj);
Value* attr_val = state.allocValue(); if (cond->boolean()) {
eval_node(n->attr, *attr_val, env); eval_node(n->then_branch, v, env);
force(attr_val); } else {
eval_node(n->else_branch, v, env);
}
}
else if (auto* n = node->get_if<LetNode>()) {
auto let_env = make_env(env);
for (const auto& [name, expr] : n->bindings) {
Value* val = make_thunk(expr, env);
let_env->bind(val);
}
eval_node(n->body, v, let_env);
}
else if (auto* n = node->get_if<LetRecNode>()) {
auto letrec_env = make_env(env);
std::vector<Value*> thunk_vals;
if (obj->type() != nAttrs) { for (const auto& [name, expr] : n->bindings) {
state.error<EvalError>("selection on non-attrset").debugThrow(); Value* val = make_thunk(expr, letrec_env);
} thunk_vals.push_back(val);
letrec_env->bind(val);
}
if (attr_val->type() != nString) { eval_node(n->body, v, letrec_env);
state.error<EvalError>("attribute name must be string").debugThrow(); }
} else if (auto* n = node->get_if<AttrsetNode>()) {
auto bindings = state.buildBindings(n->attrs.size());
auto sym = state.symbols.create(attr_val->c_str()); IREnvironment* attr_env = env;
auto attr = obj->attrs()->get(sym); if (n->recursive) {
attr_env = make_env(env);
for (const auto& [key, val] : n->attrs) {
Value* thunk = make_thunk(val, attr_env);
attr_env->bind(thunk);
}
}
if (attr) { for (const auto& [key, val] : n->attrs) {
Value* val = attr->value; Value* attr_val = state.allocValue();
force(val); if (n->recursive) {
v = *val; eval_node(val, *attr_val, attr_env);
} else if (n->default_expr) { } else {
eval_node(*n->default_expr, v, env); eval_node(val, *attr_val, env);
} else { }
state.error<EvalError>("attribute not found").debugThrow(); bindings.insert(state.symbols.create(key), attr_val);
} }
} else if (auto* n = node->get_if<HasAttrNode>()) {
Value* obj = state.allocValue();
eval_node(n->expr, *obj, env);
force(obj);
Value* attr_val = state.allocValue(); v.mkAttrs(bindings.finish());
eval_node(n->attr, *attr_val, env); }
force(attr_val); else if (auto* n = node->get_if<SelectNode>()) {
Value* obj = state.allocValue();
eval_node(n->expr, *obj, env);
force(obj);
if (obj->type() != nAttrs) { Value* attr_val = state.allocValue();
v.mkBool(false); eval_node(n->attr, *attr_val, env);
} else if (attr_val->type() != nString) { force(attr_val);
state.error<EvalError>("attribute name must be string").debugThrow();
} else {
auto sym = state.symbols.create(attr_val->c_str());
auto attr = obj->attrs()->get(sym);
v.mkBool(attr != nullptr);
}
} else if (auto* n = node->get_if<WithNode>()) {
Value* attrs = state.allocValue();
eval_node(n->attrs, *attrs, env);
force(attrs);
if (attrs->type() != nAttrs) { if (obj->type() != nAttrs) {
state.error<EvalError>("with expression requires attrset").debugThrow(); state.error<EvalError>("selection on non-attrset").debugThrow();
} }
auto with_env = make_env(env); if (attr_val->type() != nString) {
with_env->with_attrs = attrs; state.error<EvalError>("attribute name must be string").debugThrow();
eval_node(n->body, v, with_env); }
} else if (auto* n = node->get_if<AssertNode>()) {
Value* cond = state.allocValue();
eval_node(n->cond, *cond, env);
force(cond);
if (cond->type() != nBool) { auto sym = state.symbols.create(attr_val->c_str());
state.error<EvalError>("assertion must be boolean").debugThrow(); auto attr = obj->attrs()->get(sym);
}
if (!cond->boolean()) { if (attr) {
state.error<EvalError>("assertion failed").debugThrow(); Value* val = attr->value;
} force(val);
v = *val;
} else if (n->default_expr) {
eval_node(*n->default_expr, v, env);
} else {
state.error<EvalError>("attribute not found").debugThrow();
}
}
else if (auto* n = node->get_if<HasAttrNode>()) {
Value* obj = state.allocValue();
eval_node(n->expr, *obj, env);
force(obj);
eval_node(n->body, v, env); Value* attr_val = state.allocValue();
} else { eval_node(n->attr, *attr_val, env);
v.mkNull(); force(attr_val);
if (obj->type() != nAttrs) {
v.mkBool(false);
} else if (attr_val->type() != nString) {
state.error<EvalError>("attribute name must be string").debugThrow();
} else {
auto sym = state.symbols.create(attr_val->c_str());
auto attr = obj->attrs()->get(sym);
v.mkBool(attr != nullptr);
}
}
else if (auto* n = node->get_if<WithNode>()) {
Value* attrs = state.allocValue();
eval_node(n->attrs, *attrs, env);
force(attrs);
if (attrs->type() != nAttrs) {
state.error<EvalError>("with expression requires attrset").debugThrow();
}
auto with_env = make_env(env);
with_env->with_attrs = attrs;
eval_node(n->body, v, with_env);
}
else if (auto* n = node->get_if<AssertNode>()) {
Value* cond = state.allocValue();
eval_node(n->cond, *cond, env);
force(cond);
if (cond->type() != nBool) {
state.error<EvalError>("assertion must be boolean").debugThrow();
}
if (!cond->boolean()) {
state.error<EvalError>("assertion failed").debugThrow();
}
eval_node(n->body, v, env);
}
else {
v.mkNull();
}
} }
}
}; };
Evaluator::Evaluator(EvalState& state) : pImpl(std::make_unique<Impl>(state)) {} Evaluator::Evaluator(EvalState& state) : pImpl(std::make_unique<Impl>(state)) {}
Evaluator::~Evaluator() = default; Evaluator::~Evaluator() = default;
void Evaluator::eval_to_nix(const std::shared_ptr<Node>& ir_node, Value& result, void Evaluator::eval_to_nix(const std::shared_ptr<Node>& ir_node,
Value& result,
IREnvironment* env) { IREnvironment* env) {
pImpl->eval_node(ir_node, result, env); pImpl->eval_node(ir_node, result, env);
} }
} // namespace nix_irc }

View file

@ -9,7 +9,7 @@ namespace nix {
class EvalState; class EvalState;
class Value; class Value;
class PosIdx; class PosIdx;
} // namespace nix }
namespace nix_irc { namespace nix_irc {
@ -18,17 +18,18 @@ class IREnvironment;
class Evaluator { class Evaluator {
public: public:
explicit Evaluator(nix::EvalState& state); explicit Evaluator(nix::EvalState& state);
~Evaluator(); ~Evaluator();
void eval_to_nix(const std::shared_ptr<Node>& ir_node, nix::Value& result, void eval_to_nix(const std::shared_ptr<Node>& ir_node,
IREnvironment* env = nullptr); nix::Value& result,
IREnvironment* env = nullptr);
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> pImpl; std::unique_ptr<Impl> pImpl;
}; };
} // namespace nix_irc }
#endif #endif

View file

@ -1,222 +1,219 @@
#include "ir_gen.h" #include "ir_gen.h"
#include <algorithm>
#include <stack> #include <stack>
#include <unordered_map> #include <unordered_map>
#include <algorithm>
namespace nix_irc { namespace nix_irc {
struct NameResolver::Impl { struct NameResolver::Impl {
std::vector<std::unordered_map<std::string, uint32_t>> scopes; std::vector<std::unordered_map<std::string, uint32_t>> scopes;
std::vector<std::vector<std::string>> scope_names; std::vector<std::vector<std::string>> scope_names;
Impl() { Impl() {
scopes.push_back({}); scopes.push_back({});
scope_names.push_back({}); scope_names.push_back({});
} }
}; };
NameResolver::NameResolver() : pImpl(std::make_unique<Impl>()) {} NameResolver::NameResolver() : pImpl(std::make_unique<Impl>()) {}
NameResolver::~NameResolver() = default; NameResolver::~NameResolver() = default;
void NameResolver::enter_scope() { void NameResolver::enter_scope() {
pImpl->scopes.push_back({}); pImpl->scopes.push_back({});
pImpl->scope_names.push_back({}); pImpl->scope_names.push_back({});
} }
void NameResolver::exit_scope() { void NameResolver::exit_scope() {
if (!pImpl->scopes.empty()) { if (!pImpl->scopes.empty()) {
pImpl->scopes.pop_back(); pImpl->scopes.pop_back();
pImpl->scope_names.pop_back(); pImpl->scope_names.pop_back();
} }
} }
void NameResolver::bind(const std::string& name) { void NameResolver::bind(const std::string& name) {
if (pImpl->scopes.empty()) if (pImpl->scopes.empty()) return;
return; uint32_t idx = pImpl->scope_names.back().size();
uint32_t idx = pImpl->scope_names.back().size(); pImpl->scopes.back()[name] = idx;
pImpl->scopes.back()[name] = idx; pImpl->scope_names.back().push_back(name);
pImpl->scope_names.back().push_back(name);
} }
uint32_t NameResolver::resolve(const std::string& name) { uint32_t NameResolver::resolve(const std::string& name) {
for (int i = (int) pImpl->scopes.size() - 1; i >= 0; --i) { for (int i = (int)pImpl->scopes.size() - 1; i >= 0; --i) {
auto it = pImpl->scopes[i].find(name); auto it = pImpl->scopes[i].find(name);
if (it != pImpl->scopes[i].end()) { if (it != pImpl->scopes[i].end()) {
uint32_t depth = pImpl->scopes.size() - 1 - i; uint32_t depth = pImpl->scopes.size() - 1 - i;
uint32_t offset = it->second; uint32_t offset = it->second;
return depth << 16 | offset; return depth << 16 | offset;
}
} }
} return 0xFFFFFFFF;
return 0xFFFFFFFF;
} }
bool NameResolver::is_bound(const std::string& name) const { bool NameResolver::is_bound(const std::string& name) const {
for (auto it = pImpl->scopes.rbegin(); it != pImpl->scopes.rend(); ++it) { for (auto it = pImpl->scopes.rbegin(); it != pImpl->scopes.rend(); ++it) {
if (it->count(name)) if (it->count(name)) return true;
return true; }
} return false;
return false;
} }
struct IRGenerator::Impl { struct IRGenerator::Impl {
std::unordered_map<std::string, uint32_t> string_table; std::unordered_map<std::string, uint32_t> string_table;
uint32_t next_string_id = 0; uint32_t next_string_id = 0;
NameResolver name_resolver; NameResolver name_resolver;
Impl() {} Impl() {}
uint32_t add_string(const std::string& str) { uint32_t add_string(const std::string& str) {
auto it = string_table.find(str); auto it = string_table.find(str);
if (it != string_table.end()) { if (it != string_table.end()) {
return it->second; return it->second;
}
uint32_t id = next_string_id++;
string_table[str] = id;
return id;
} }
uint32_t id = next_string_id++;
string_table[str] = id;
return id;
}
std::shared_ptr<Node> convert(const std::shared_ptr<Node>& node_ptr) { std::shared_ptr<Node> convert(const std::shared_ptr<Node>& node_ptr) {
if (!node_ptr) if (!node_ptr) return std::make_shared<Node>(ConstNullNode{});
return std::make_shared<Node>(ConstNullNode{});
const Node& node = *node_ptr; const Node& node = *node_ptr;
if (auto* n = node.get_if<ConstIntNode>()) { if (auto* n = node.get_if<ConstIntNode>()) {
return std::make_shared<Node>(*n); return std::make_shared<Node>(*n);
}
if (auto* n = node.get_if<ConstStringNode>()) {
return std::make_shared<Node>(*n);
}
if (auto* n = node.get_if<ConstPathNode>()) {
return std::make_shared<Node>(*n);
}
if (auto* n = node.get_if<ConstBoolNode>()) {
return std::make_shared<Node>(*n);
}
if (auto* n = node.get_if<ConstNullNode>()) {
return std::make_shared<Node>(*n);
}
if (auto* n = node.get_if<VarNode>()) {
uint32_t idx = name_resolver.resolve(n->name.value_or(""));
VarNode converted(idx);
converted.name = n->name;
converted.line = n->line;
return std::make_shared<Node>(converted);
}
if (auto* n = node.get_if<LambdaNode>()) {
name_resolver.enter_scope();
if (n->param_name) {
name_resolver.bind(*n->param_name);
}
auto body = convert(n->body);
name_resolver.exit_scope();
LambdaNode lambda(n->arity, body, n->line);
lambda.param_name = n->param_name;
return std::make_shared<Node>(lambda);
}
if (auto* n = node.get_if<AppNode>()) {
auto func = convert(n->func);
auto arg = convert(n->arg);
return std::make_shared<Node>(AppNode(func, arg, n->line));
}
if (auto* n = node.get_if<AttrsetNode>()) {
AttrsetNode attrs(n->recursive, n->line);
name_resolver.enter_scope();
for (const auto& [key, val] : n->attrs) {
name_resolver.bind(key);
}
for (const auto& [key, val] : n->attrs) {
attrs.attrs.push_back({key, convert(val)});
}
name_resolver.exit_scope();
return std::make_shared<Node>(attrs);
}
if (auto* n = node.get_if<SelectNode>()) {
auto expr = convert(n->expr);
auto attr = convert(n->attr);
SelectNode select(expr, attr, n->line);
if (n->default_expr) {
select.default_expr = convert(*n->default_expr);
}
return std::make_shared<Node>(select);
}
if (auto* n = node.get_if<HasAttrNode>()) {
auto expr = convert(n->expr);
auto attr = convert(n->attr);
return std::make_shared<Node>(HasAttrNode(expr, attr, n->line));
}
if (auto* n = node.get_if<WithNode>()) {
auto attrs = convert(n->attrs);
auto body = convert(n->body);
return std::make_shared<Node>(WithNode(attrs, body, n->line));
}
if (auto* n = node.get_if<IfNode>()) {
auto cond = convert(n->cond);
auto then_b = convert(n->then_branch);
auto else_b = convert(n->else_branch);
return std::make_shared<Node>(IfNode(cond, then_b, else_b, n->line));
}
if (auto* n = node.get_if<LetNode>()) {
name_resolver.enter_scope();
for (const auto& [key, val] : n->bindings) {
name_resolver.bind(key);
}
std::vector<std::pair<std::string, std::shared_ptr<Node>>> new_bindings;
for (const auto& [key, val] : n->bindings) {
new_bindings.push_back({key, convert(val)});
}
auto body = convert(n->body);
name_resolver.exit_scope();
LetNode let(body, n->line);
let.bindings = std::move(new_bindings);
return std::make_shared<Node>(let);
}
if (auto* n = node.get_if<LetRecNode>()) {
name_resolver.enter_scope();
for (const auto& [key, val] : n->bindings) {
name_resolver.bind(key);
}
std::vector<std::pair<std::string, std::shared_ptr<Node>>> new_bindings;
for (const auto& [key, val] : n->bindings) {
new_bindings.push_back({key, convert(val)});
}
auto body = convert(n->body);
name_resolver.exit_scope();
LetRecNode letrec(body, n->line);
letrec.bindings = std::move(new_bindings);
return std::make_shared<Node>(letrec);
}
if (auto* n = node.get_if<AssertNode>()) {
auto cond = convert(n->cond);
auto body = convert(n->body);
return std::make_shared<Node>(AssertNode(cond, body, n->line));
}
if (auto* n = node.get_if<BinaryOpNode>()) {
auto left = convert(n->left);
auto right = convert(n->right);
return std::make_shared<Node>(BinaryOpNode(n->op, left, right, n->line));
}
if (auto* n = node.get_if<UnaryOpNode>()) {
auto operand = convert(n->operand);
return std::make_shared<Node>(UnaryOpNode(n->op, operand, n->line));
}
return std::make_shared<Node>(ConstNullNode{});
} }
if (auto* n = node.get_if<ConstStringNode>()) {
return std::make_shared<Node>(*n);
}
if (auto* n = node.get_if<ConstPathNode>()) {
return std::make_shared<Node>(*n);
}
if (auto* n = node.get_if<ConstBoolNode>()) {
return std::make_shared<Node>(*n);
}
if (auto* n = node.get_if<ConstNullNode>()) {
return std::make_shared<Node>(*n);
}
if (auto* n = node.get_if<VarNode>()) {
uint32_t idx = name_resolver.resolve(n->name.value_or(""));
VarNode converted(idx);
converted.name = n->name;
converted.line = n->line;
return std::make_shared<Node>(converted);
}
if (auto* n = node.get_if<LambdaNode>()) {
name_resolver.enter_scope();
if (n->param_name) {
name_resolver.bind(*n->param_name);
}
auto body = convert(n->body);
name_resolver.exit_scope();
LambdaNode lambda(n->arity, body, n->line);
lambda.param_name = n->param_name;
return std::make_shared<Node>(lambda);
}
if (auto* n = node.get_if<AppNode>()) {
auto func = convert(n->func);
auto arg = convert(n->arg);
return std::make_shared<Node>(AppNode(func, arg, n->line));
}
if (auto* n = node.get_if<AttrsetNode>()) {
AttrsetNode attrs(n->recursive, n->line);
name_resolver.enter_scope();
for (const auto& [key, val] : n->attrs) {
name_resolver.bind(key);
}
for (const auto& [key, val] : n->attrs) {
attrs.attrs.push_back({key, convert(val)});
}
name_resolver.exit_scope();
return std::make_shared<Node>(attrs);
}
if (auto* n = node.get_if<SelectNode>()) {
auto expr = convert(n->expr);
auto attr = convert(n->attr);
SelectNode select(expr, attr, n->line);
if (n->default_expr) {
select.default_expr = convert(*n->default_expr);
}
return std::make_shared<Node>(select);
}
if (auto* n = node.get_if<HasAttrNode>()) {
auto expr = convert(n->expr);
auto attr = convert(n->attr);
return std::make_shared<Node>(HasAttrNode(expr, attr, n->line));
}
if (auto* n = node.get_if<WithNode>()) {
auto attrs = convert(n->attrs);
auto body = convert(n->body);
return std::make_shared<Node>(WithNode(attrs, body, n->line));
}
if (auto* n = node.get_if<IfNode>()) {
auto cond = convert(n->cond);
auto then_b = convert(n->then_branch);
auto else_b = convert(n->else_branch);
return std::make_shared<Node>(IfNode(cond, then_b, else_b, n->line));
}
if (auto* n = node.get_if<LetNode>()) {
name_resolver.enter_scope();
for (const auto& [key, val] : n->bindings) {
name_resolver.bind(key);
}
std::vector<std::pair<std::string, std::shared_ptr<Node>>> new_bindings;
for (const auto& [key, val] : n->bindings) {
new_bindings.push_back({key, convert(val)});
}
auto body = convert(n->body);
name_resolver.exit_scope();
LetNode let(body, n->line);
let.bindings = std::move(new_bindings);
return std::make_shared<Node>(let);
}
if (auto* n = node.get_if<LetRecNode>()) {
name_resolver.enter_scope();
for (const auto& [key, val] : n->bindings) {
name_resolver.bind(key);
}
std::vector<std::pair<std::string, std::shared_ptr<Node>>> new_bindings;
for (const auto& [key, val] : n->bindings) {
new_bindings.push_back({key, convert(val)});
}
auto body = convert(n->body);
name_resolver.exit_scope();
LetRecNode letrec(body, n->line);
letrec.bindings = std::move(new_bindings);
return std::make_shared<Node>(letrec);
}
if (auto* n = node.get_if<AssertNode>()) {
auto cond = convert(n->cond);
auto body = convert(n->body);
return std::make_shared<Node>(AssertNode(cond, body, n->line));
}
if (auto* n = node.get_if<BinaryOpNode>()) {
auto left = convert(n->left);
auto right = convert(n->right);
return std::make_shared<Node>(BinaryOpNode(n->op, left, right, n->line));
}
if (auto* n = node.get_if<UnaryOpNode>()) {
auto operand = convert(n->operand);
return std::make_shared<Node>(UnaryOpNode(n->op, operand, n->line));
}
return std::make_shared<Node>(ConstNullNode{});
}
}; };
IRGenerator::IRGenerator() : pImpl(std::make_unique<Impl>()) {} IRGenerator::IRGenerator() : pImpl(std::make_unique<Impl>()) {}
IRGenerator::~IRGenerator() = default; IRGenerator::~IRGenerator() = default;
void IRGenerator::set_string_table(const std::unordered_map<std::string, uint32_t>& table) { void IRGenerator::set_string_table(const std::unordered_map<std::string, uint32_t>& table) {
pImpl->string_table = table; pImpl->string_table = table;
} }
uint32_t IRGenerator::add_string(const std::string& str) { uint32_t IRGenerator::add_string(const std::string& str) {
return pImpl->add_string(str); return pImpl->add_string(str);
} }
std::shared_ptr<Node> IRGenerator::generate(const std::shared_ptr<Node>& ast) { std::shared_ptr<Node> IRGenerator::generate(const std::shared_ptr<Node>& ast) {
return pImpl->convert(ast); return pImpl->convert(ast);
} }
} // namespace nix_irc }

View file

@ -2,44 +2,44 @@
#define NIX_IRC_IR_GEN_H #define NIX_IRC_IR_GEN_H
#include "types.h" #include "types.h"
#include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <memory>
namespace nix_irc { namespace nix_irc {
class IRGenerator { class IRGenerator {
public: public:
IRGenerator(); IRGenerator();
~IRGenerator(); ~IRGenerator();
void set_string_table(const std::unordered_map<std::string, uint32_t>& table); void set_string_table(const std::unordered_map<std::string, uint32_t>& table);
uint32_t add_string(const std::string& str); uint32_t add_string(const std::string& str);
std::shared_ptr<Node> generate(const std::shared_ptr<Node>& ast); std::shared_ptr<Node> generate(const std::shared_ptr<Node>& ast);
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> pImpl; std::unique_ptr<Impl> pImpl;
}; };
class NameResolver { class NameResolver {
public: public:
NameResolver(); NameResolver();
~NameResolver(); ~NameResolver();
void enter_scope(); void enter_scope();
void exit_scope(); void exit_scope();
void bind(const std::string& name); void bind(const std::string& name);
uint32_t resolve(const std::string& name); uint32_t resolve(const std::string& name);
bool is_bound(const std::string& name) const; bool is_bound(const std::string& name) const;
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> pImpl; std::unique_ptr<Impl> pImpl;
}; };
} // namespace nix_irc }
#endif #endif

View file

@ -1,150 +1,150 @@
#include "ir_gen.h" #include <iostream>
#include "parser.h" #include "parser.h"
#include "resolver.h" #include "resolver.h"
#include "ir_gen.h"
#include "serializer.h" #include "serializer.h"
#include <cstring>
#include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstring>
namespace nix_irc { namespace nix_irc {
void print_usage(const char* prog) { void print_usage(const char* prog) {
std::cout << "Usage: " << prog << " [options] <input.nix> [output.nixir]\n" std::cout << "Usage: " << prog << " [options] <input.nix> [output.nixir]\n"
<< "\nOptions:\n" << "\nOptions:\n"
<< " -I <path> Add search path for imports\n" << " -I <path> Add search path for imports\n"
<< " --no-imports Disable import resolution\n" << " --no-imports Disable import resolution\n"
<< " --help Show this help\n"; << " --help Show this help\n";
} }
int run_compile(int argc, char** argv) { int run_compile(int argc, char** argv) {
std::string input_file; std::string input_file;
std::string output_file; std::string output_file;
std::vector<std::string> search_paths; std::vector<std::string> search_paths;
bool resolve_imports = true; bool resolve_imports = true;
int i = 1; int i = 1;
while (i < argc) { while (i < argc) {
std::string arg = argv[i]; std::string arg = argv[i];
if (arg == "-I") { if (arg == "-I") {
if (i + 1 >= argc) { if (i + 1 >= argc) {
std::cerr << "Error: -I requires a path argument\n"; std::cerr << "Error: -I requires a path argument\n";
return 1;
}
search_paths.push_back(argv[++i]);
} else if (arg == "--no-imports") {
resolve_imports = false;
} else if (arg == "--help" || arg == "-h") {
print_usage(argv[0]);
return 0;
} else if (arg[0] != '-') {
input_file = arg;
if (i + 1 < argc && argv[i + 1][0] != '-') {
output_file = argv[++i];
}
} else {
std::cerr << "Unknown option: " << arg << "\n";
print_usage(argv[0]);
return 1;
}
i++;
}
if (input_file.empty()) {
std::cerr << "Error: No input file specified\n";
print_usage(argv[0]);
return 1; return 1;
}
search_paths.push_back(argv[++i]);
} else if (arg == "--no-imports") {
resolve_imports = false;
} else if (arg == "--help" || arg == "-h") {
print_usage(argv[0]);
return 0;
} else if (arg[0] != '-') {
input_file = arg;
if (i + 1 < argc && argv[i + 1][0] != '-') {
output_file = argv[++i];
}
} else {
std::cerr << "Unknown option: " << arg << "\n";
print_usage(argv[0]);
return 1;
}
i++;
}
if (input_file.empty()) {
std::cerr << "Error: No input file specified\n";
print_usage(argv[0]);
return 1;
}
if (output_file.empty()) {
output_file = input_file + "r";
}
try {
Parser parser;
Resolver resolver;
for (const auto& path : search_paths) {
resolver.add_search_path(path);
} }
std::cout << "Parsing: " << input_file << "\n"; if (output_file.empty()) {
auto ast = parser.parse_file(input_file); output_file = input_file + "r";
if (!ast) {
std::cerr << "Error: Failed to parse input\n";
return 1;
} }
std::cout << "Resolving imports...\n"; try {
Parser parser;
Resolver resolver;
IRGenerator ir_gen; for (const auto& path : search_paths) {
resolver.add_search_path(path);
}
std::cout << "Generating IR...\n"; std::cout << "Parsing: " << input_file << "\n";
auto ir = ir_gen.generate(ast); auto ast = parser.parse_file(input_file);
IRModule module; if (!ast) {
module.version = IR_VERSION; std::cerr << "Error: Failed to parse input\n";
module.entry = ir; return 1;
}
std::cout << "Serializing to: " << output_file << "\n"; std::cout << "Resolving imports...\n";
Serializer serializer;
serializer.serialize(module, output_file);
std::cout << "Done!\n"; IRGenerator ir_gen;
return 0;
} catch (const std::exception& e) { std::cout << "Generating IR...\n";
std::cerr << "Error: " << e.what() << "\n"; auto ir = ir_gen.generate(ast);
return 1;
} IRModule module;
module.version = IR_VERSION;
module.entry = ir;
std::cout << "Serializing to: " << output_file << "\n";
Serializer serializer;
serializer.serialize(module, output_file);
std::cout << "Done!\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return 1;
}
} }
void print_decompile_usage(const char* prog) { void print_decompile_usage(const char* prog) {
std::cout << "Usage: " << prog << " decompile <input.nixir>\n"; std::cout << "Usage: " << prog << " decompile <input.nixir>\n";
} }
int run_decompile(int argc, char** argv) { int run_decompile(int argc, char** argv) {
if (argc < 3) { if (argc < 3) {
print_decompile_usage(argv[0]); print_decompile_usage(argv[0]);
return 1; return 1;
} }
std::string input_file = argv[2]; std::string input_file = argv[2];
try { try {
Deserializer deserializer; Deserializer deserializer;
auto module = deserializer.deserialize(input_file); auto module = deserializer.deserialize(input_file);
std::cout << "IR Version: " << module.version << "\n"; std::cout << "IR Version: " << module.version << "\n";
std::cout << "Sources: " << module.sources.size() << "\n"; std::cout << "Sources: " << module.sources.size() << "\n";
std::cout << "Imports: " << module.imports.size() << "\n"; std::cout << "Imports: " << module.imports.size() << "\n";
return 0; return 0;
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n"; std::cerr << "Error: " << e.what() << "\n";
return 1; return 1;
} }
} }
} // namespace nix_irc }
int main(int argc, char** argv) { int main(int argc, char** argv) {
if (argc < 2) { if (argc < 2) {
nix_irc::print_usage(argv[0]); nix_irc::print_usage(argv[0]);
return 1; return 1;
} }
std::string cmd = argv[1]; std::string cmd = argv[1];
if (cmd == "compile" || cmd == "c") { if (cmd == "compile" || cmd == "c") {
return nix_irc::run_compile(argc - 1, argv + 1); return nix_irc::run_compile(argc - 1, argv + 1);
} else if (cmd == "decompile" || cmd == "d") { } else if (cmd == "decompile" || cmd == "d") {
return nix_irc::run_decompile(argc, argv); return nix_irc::run_decompile(argc, argv);
} else if (cmd == "help" || cmd == "--help" || cmd == "-h") { } else if (cmd == "help" || cmd == "--help" || cmd == "-h") {
nix_irc::print_usage(argv[0]); nix_irc::print_usage(argv[0]);
return 0; return 0;
} else { } else {
return nix_irc::run_compile(argc, argv); return nix_irc::run_compile(argc, argv);
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -2,24 +2,24 @@
#define NIX_IRC_PARSER_H #define NIX_IRC_PARSER_H
#include "types.h" #include "types.h"
#include <memory>
#include <string> #include <string>
#include <memory>
namespace nix_irc { namespace nix_irc {
class Parser { class Parser {
public: public:
Parser(); Parser();
~Parser(); ~Parser();
std::shared_ptr<Node> parse(const std::string& source, const std::string& path = "<stdin>"); std::shared_ptr<Node> parse(const std::string& source, const std::string& path = "<stdin>");
std::shared_ptr<Node> parse_file(const std::string& path); std::shared_ptr<Node> parse_file(const std::string& path);
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> pImpl; std::unique_ptr<Impl> pImpl;
}; };
} // namespace nix_irc }
#endif #endif

View file

@ -1,114 +1,111 @@
#include "resolver.h" #include "resolver.h"
#include "parser.h" #include "parser.h"
#include <filesystem>
#include <fstream>
#include <iostream> #include <iostream>
#include <regex> #include <fstream>
#include <sstream> #include <sstream>
#include <filesystem>
#include <regex>
namespace nix_irc { namespace nix_irc {
namespace fs = std::filesystem; namespace fs = std::filesystem;
struct Resolver::Impl { struct Resolver::Impl {
ResolverConfig config; ResolverConfig config;
std::vector<std::pair<std::string, std::string>> resolved_imports; std::vector<std::pair<std::string, std::string>> resolved_imports;
std::unordered_set<std::string> visited; std::unordered_set<std::string> visited;
Parser parser; Parser parser;
Impl(const ResolverConfig& cfg) : config(cfg) {} Impl(const ResolverConfig& cfg) : config(cfg) {}
std::string resolve_path(const std::string& path, const std::string& from_file) { std::string resolve_path(const std::string& path, const std::string& from_file) {
fs::path p(path); fs::path p(path);
if (p.is_absolute()) { if (p.is_absolute()) {
if (fs::exists(p)) if (fs::exists(p)) return path;
return path; return "";
return ""; }
fs::path from_dir = fs::path(from_file).parent_path();
fs::path candidate = from_dir / p;
if (fs::exists(candidate)) return candidate.string();
for (const auto& search : config.search_paths) {
candidate = fs::path(search) / p;
if (fs::exists(candidate)) return candidate.string();
}
return "";
} }
fs::path from_dir = fs::path(from_file).parent_path(); ImportResult do_resolve(const std::string& path, const std::string& from_file) {
fs::path candidate = from_dir / p; std::string resolved = resolve_path(path, from_file);
if (fs::exists(candidate))
return candidate.string();
for (const auto& search : config.search_paths) { if (resolved.empty()) {
candidate = fs::path(search) / p; return {false, "", "Cannot find file: " + path, nullptr};
if (fs::exists(candidate)) }
return candidate.string();
if (visited.count(resolved)) {
return {true, resolved, "", nullptr};
}
visited.insert(resolved);
try {
auto ast = parser.parse_file(resolved);
return {true, resolved, "", ast};
} catch (const std::exception& e) {
return {false, "", e.what(), nullptr};
}
} }
return "";
}
ImportResult do_resolve(const std::string& path, const std::string& from_file) {
std::string resolved = resolve_path(path, from_file);
if (resolved.empty()) {
return {false, "", "Cannot find file: " + path, nullptr};
}
if (visited.count(resolved)) {
return {true, resolved, "", nullptr};
}
visited.insert(resolved);
try {
auto ast = parser.parse_file(resolved);
return {true, resolved, "", ast};
} catch (const std::exception& e) {
return {false, "", e.what(), nullptr};
}
}
}; };
Resolver::Resolver(const ResolverConfig& config) : pImpl(std::make_unique<Impl>(config)) {} Resolver::Resolver(const ResolverConfig& config) : pImpl(std::make_unique<Impl>(config)) {}
Resolver::~Resolver() = default; Resolver::~Resolver() = default;
void Resolver::add_search_path(const std::string& path) { void Resolver::add_search_path(const std::string& path) {
pImpl->config.search_paths.push_back(path); pImpl->config.search_paths.push_back(path);
} }
void Resolver::set_search_paths(const std::vector<std::string>& paths) { void Resolver::set_search_paths(const std::vector<std::string>& paths) {
pImpl->config.search_paths = paths; pImpl->config.search_paths = paths;
} }
ImportResult Resolver::resolve_import(const std::string& path, const std::string& from_file) { ImportResult Resolver::resolve_import(const std::string& path, const std::string& from_file) {
auto result = pImpl->do_resolve(path, from_file); auto result = pImpl->do_resolve(path, from_file);
if (result.success && result.ast) { if (result.success && result.ast) {
pImpl->resolved_imports.push_back({path, result.path}); pImpl->resolved_imports.push_back({path, result.path});
} }
return result; return result;
} }
ImportResult Resolver::resolve_import(const Node& import_node, const std::string& from_file) { ImportResult Resolver::resolve_import(const Node& import_node, const std::string& from_file) {
const ConstPathNode* path_node = import_node.get_if<ConstPathNode>(); const ConstPathNode* path_node = import_node.get_if<ConstPathNode>();
if (!path_node) { if (!path_node) {
return {false, "", "Dynamic import not supported", nullptr}; return {false, "", "Dynamic import not supported", nullptr};
} }
return resolve_import(path_node->value, from_file); return resolve_import(path_node->value, from_file);
} }
std::vector<std::string> Resolver::get_resolved_files() const { std::vector<std::string> Resolver::get_resolved_files() const {
std::vector<std::string> files; std::vector<std::string> files;
for (const auto& [orig, resolved] : pImpl->resolved_imports) { for (const auto& [orig, resolved] : pImpl->resolved_imports) {
(void) orig; (void)orig;
files.push_back(resolved); files.push_back(resolved);
} }
return files; return files;
} }
std::vector<std::pair<std::string, std::string>> Resolver::get_imports() const { std::vector<std::pair<std::string, std::string>> Resolver::get_imports() const {
return pImpl->resolved_imports; return pImpl->resolved_imports;
} }
bool is_static_import(const Node& node) { bool is_static_import(const Node& node) {
return node.holds<ConstPathNode>(); return node.holds<ConstPathNode>();
} }
std::string normalize_path(const std::string& path) { std::string normalize_path(const std::string& path) {
fs::path p(path); fs::path p(path);
return fs::absolute(p).string(); return fs::absolute(p).string();
} }
} // namespace nix_irc }

View file

@ -2,47 +2,47 @@
#define NIX_IRC_RESOLVER_H #define NIX_IRC_RESOLVER_H
#include "types.h" #include "types.h"
#include <filesystem>
#include <string> #include <string>
#include <unordered_set>
#include <vector> #include <vector>
#include <unordered_set>
#include <filesystem>
namespace nix_irc { namespace nix_irc {
struct ImportResult { struct ImportResult {
bool success; bool success;
std::string path; std::string path;
std::string error; std::string error;
std::shared_ptr<Node> ast; std::shared_ptr<Node> ast;
}; };
struct ResolverConfig { struct ResolverConfig {
std::vector<std::string> search_paths; std::vector<std::string> search_paths;
bool resolve_imports = true; bool resolve_imports = true;
}; };
class Resolver { class Resolver {
public: public:
Resolver(const ResolverConfig& config = {}); Resolver(const ResolverConfig& config = {});
~Resolver(); ~Resolver();
void add_search_path(const std::string& path); void add_search_path(const std::string& path);
void set_search_paths(const std::vector<std::string>& paths); void set_search_paths(const std::vector<std::string>& paths);
ImportResult resolve_import(const std::string& path, const std::string& from_file); ImportResult resolve_import(const std::string& path, const std::string& from_file);
ImportResult resolve_import(const Node& import_node, const std::string& from_file); ImportResult resolve_import(const Node& import_node, const std::string& from_file);
std::vector<std::string> get_resolved_files() const; std::vector<std::string> get_resolved_files() const;
std::vector<std::pair<std::string, std::string>> get_imports() const; std::vector<std::pair<std::string, std::string>> get_imports() const;
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> pImpl; std::unique_ptr<Impl> pImpl;
}; };
bool is_static_import(const Node& node); bool is_static_import(const Node& node);
std::string normalize_path(const std::string& path); std::string normalize_path(const std::string& path);
} // namespace nix_irc }
#endif #endif

View file

@ -1,428 +1,392 @@
#include "serializer.h" #include "serializer.h"
#include <cstring> #include <cstring>
#include <iostream>
#include <sstream> #include <sstream>
#include <iostream>
namespace nix_irc { namespace nix_irc {
struct Serializer::Impl { struct Serializer::Impl {
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
void write_u32(uint32_t val) { void write_u32(uint32_t val) {
buffer.push_back((val >> 0) & 0xFF); buffer.push_back((val >> 0) & 0xFF);
buffer.push_back((val >> 8) & 0xFF); buffer.push_back((val >> 8) & 0xFF);
buffer.push_back((val >> 16) & 0xFF); buffer.push_back((val >> 16) & 0xFF);
buffer.push_back((val >> 24) & 0xFF); buffer.push_back((val >> 24) & 0xFF);
}
void write_u64(uint64_t val) {
for (int i = 0; i < 8; i++) {
buffer.push_back((val >> (i * 8)) & 0xFF);
} }
}
void write_u8(uint8_t val) { buffer.push_back(val); } void write_u64(uint64_t val) {
for (int i = 0; i < 8; i++) {
void write_string(const std::string& str) { buffer.push_back((val >> (i * 8)) & 0xFF);
write_u32(str.size()); }
buffer.insert(buffer.end(), str.begin(), str.end()); }
}
void write_u8(uint8_t val) {
NodeType get_node_type(const Node& node) { buffer.push_back(val);
if (node.holds<ConstIntNode>()) }
return NodeType::CONST_INT;
if (node.holds<ConstStringNode>()) void write_string(const std::string& str) {
return NodeType::CONST_STRING; write_u32(str.size());
if (node.holds<ConstPathNode>()) buffer.insert(buffer.end(), str.begin(), str.end());
return NodeType::CONST_PATH; }
if (node.holds<ConstBoolNode>())
return NodeType::CONST_BOOL; NodeType get_node_type(const Node& node) {
if (node.holds<ConstNullNode>()) if (node.holds<ConstIntNode>()) return NodeType::CONST_INT;
return NodeType::CONST_NULL; if (node.holds<ConstStringNode>()) return NodeType::CONST_STRING;
if (node.holds<VarNode>()) if (node.holds<ConstPathNode>()) return NodeType::CONST_PATH;
return NodeType::VAR; if (node.holds<ConstBoolNode>()) return NodeType::CONST_BOOL;
if (node.holds<LambdaNode>()) if (node.holds<ConstNullNode>()) return NodeType::CONST_NULL;
return NodeType::LAMBDA; if (node.holds<VarNode>()) return NodeType::VAR;
if (node.holds<AppNode>()) if (node.holds<LambdaNode>()) return NodeType::LAMBDA;
return NodeType::APP; if (node.holds<AppNode>()) return NodeType::APP;
if (node.holds<BinaryOpNode>()) if (node.holds<BinaryOpNode>()) return NodeType::BINARY_OP;
return NodeType::BINARY_OP; if (node.holds<UnaryOpNode>()) return NodeType::UNARY_OP;
if (node.holds<UnaryOpNode>()) if (node.holds<AttrsetNode>()) return NodeType::ATTRSET;
return NodeType::UNARY_OP; if (node.holds<SelectNode>()) return NodeType::SELECT;
if (node.holds<AttrsetNode>()) if (node.holds<HasAttrNode>()) return NodeType::HAS_ATTR;
return NodeType::ATTRSET; if (node.holds<WithNode>()) return NodeType::WITH;
if (node.holds<SelectNode>()) if (node.holds<IfNode>()) return NodeType::IF;
return NodeType::SELECT; if (node.holds<LetNode>()) return NodeType::LET;
if (node.holds<HasAttrNode>()) if (node.holds<LetRecNode>()) return NodeType::LETREC;
return NodeType::HAS_ATTR; if (node.holds<AssertNode>()) return NodeType::ASSERT;
if (node.holds<WithNode>()) return NodeType::ERROR;
return NodeType::WITH; }
if (node.holds<IfNode>())
return NodeType::IF; uint32_t get_node_line(const Node& node) {
if (node.holds<LetNode>()) return std::visit([](const auto& n) { return n.line; }, node.data);
return NodeType::LET; }
if (node.holds<LetRecNode>())
return NodeType::LETREC; void write_node(const Node& node) {
if (node.holds<AssertNode>()) write_u8(static_cast<uint8_t>(get_node_type(node)));
return NodeType::ASSERT; write_u32(get_node_line(node));
return NodeType::ERROR;
} if (auto* n = node.get_if<ConstIntNode>()) {
write_u64(static_cast<uint64_t>(n->value));
uint32_t get_node_line(const Node& node) { } else if (auto* n = node.get_if<ConstStringNode>()) {
return std::visit([](const auto& n) { return n.line; }, node.data); write_string(n->value);
} } else if (auto* n = node.get_if<ConstPathNode>()) {
write_string(n->value);
void write_node(const Node& node) { } else if (auto* n = node.get_if<ConstBoolNode>()) {
write_u8(static_cast<uint8_t>(get_node_type(node))); write_u8(n->value ? 1 : 0);
write_u32(get_node_line(node)); } else if (auto* n = node.get_if<ConstNullNode>()) {
// No data for null
if (auto* n = node.get_if<ConstIntNode>()) { } else if (auto* n = node.get_if<VarNode>()) {
write_u64(static_cast<uint64_t>(n->value)); write_u32(n->index);
} else if (auto* n = node.get_if<ConstStringNode>()) { } else if (auto* n = node.get_if<LambdaNode>()) {
write_string(n->value); write_u32(n->arity);
} else if (auto* n = node.get_if<ConstPathNode>()) { if (n->body) write_node(*n->body);
write_string(n->value); } else if (auto* n = node.get_if<AppNode>()) {
} else if (auto* n = node.get_if<ConstBoolNode>()) { if (n->func) write_node(*n->func);
write_u8(n->value ? 1 : 0); if (n->arg) write_node(*n->arg);
} else if (auto* n = node.get_if<ConstNullNode>()) { } else if (auto* n = node.get_if<BinaryOpNode>()) {
// No data for null write_u8(static_cast<uint8_t>(n->op));
} else if (auto* n = node.get_if<VarNode>()) { if (n->left) write_node(*n->left);
write_u32(n->index); if (n->right) write_node(*n->right);
} else if (auto* n = node.get_if<LambdaNode>()) { } else if (auto* n = node.get_if<UnaryOpNode>()) {
write_u32(n->arity); write_u8(static_cast<uint8_t>(n->op));
if (n->body) if (n->operand) write_node(*n->operand);
write_node(*n->body); } else if (auto* n = node.get_if<AttrsetNode>()) {
} else if (auto* n = node.get_if<AppNode>()) { write_u8(n->recursive ? 1 : 0);
if (n->func) write_u32(n->attrs.size());
write_node(*n->func); for (const auto& [key, val] : n->attrs) {
if (n->arg) write_string(key);
write_node(*n->arg); if (val) write_node(*val);
} else if (auto* n = node.get_if<BinaryOpNode>()) { }
write_u8(static_cast<uint8_t>(n->op)); } else if (auto* n = node.get_if<SelectNode>()) {
if (n->left) if (n->expr) write_node(*n->expr);
write_node(*n->left); if (n->attr) write_node(*n->attr);
if (n->right) if (n->default_expr && *n->default_expr) {
write_node(*n->right); write_u8(1);
} else if (auto* n = node.get_if<UnaryOpNode>()) { write_node(**n->default_expr);
write_u8(static_cast<uint8_t>(n->op)); } else {
if (n->operand) write_u8(0);
write_node(*n->operand); }
} else if (auto* n = node.get_if<AttrsetNode>()) { } else if (auto* n = node.get_if<HasAttrNode>()) {
write_u8(n->recursive ? 1 : 0); if (n->expr) write_node(*n->expr);
write_u32(n->attrs.size()); if (n->attr) write_node(*n->attr);
for (const auto& [key, val] : n->attrs) { } else if (auto* n = node.get_if<WithNode>()) {
write_string(key); if (n->attrs) write_node(*n->attrs);
if (val) if (n->body) write_node(*n->body);
write_node(*val); } else if (auto* n = node.get_if<IfNode>()) {
} if (n->cond) write_node(*n->cond);
} else if (auto* n = node.get_if<SelectNode>()) { if (n->then_branch) write_node(*n->then_branch);
if (n->expr) if (n->else_branch) write_node(*n->else_branch);
write_node(*n->expr); } else if (auto* n = node.get_if<LetNode>()) {
if (n->attr) write_u32(n->bindings.size());
write_node(*n->attr); for (const auto& [key, val] : n->bindings) {
if (n->default_expr && *n->default_expr) { write_string(key);
write_u8(1); if (val) write_node(*val);
write_node(**n->default_expr); }
} else { if (n->body) write_node(*n->body);
write_u8(0); } else if (auto* n = node.get_if<LetRecNode>()) {
} write_u32(n->bindings.size());
} else if (auto* n = node.get_if<HasAttrNode>()) { for (const auto& [key, val] : n->bindings) {
if (n->expr) write_string(key);
write_node(*n->expr); if (val) write_node(*val);
if (n->attr) }
write_node(*n->attr); if (n->body) write_node(*n->body);
} else if (auto* n = node.get_if<WithNode>()) { } else if (auto* n = node.get_if<AssertNode>()) {
if (n->attrs) if (n->cond) write_node(*n->cond);
write_node(*n->attrs); if (n->body) write_node(*n->body);
if (n->body) }
write_node(*n->body);
} else if (auto* n = node.get_if<IfNode>()) {
if (n->cond)
write_node(*n->cond);
if (n->then_branch)
write_node(*n->then_branch);
if (n->else_branch)
write_node(*n->else_branch);
} else if (auto* n = node.get_if<LetNode>()) {
write_u32(n->bindings.size());
for (const auto& [key, val] : n->bindings) {
write_string(key);
if (val)
write_node(*val);
}
if (n->body)
write_node(*n->body);
} else if (auto* n = node.get_if<LetRecNode>()) {
write_u32(n->bindings.size());
for (const auto& [key, val] : n->bindings) {
write_string(key);
if (val)
write_node(*val);
}
if (n->body)
write_node(*n->body);
} else if (auto* n = node.get_if<AssertNode>()) {
if (n->cond)
write_node(*n->cond);
if (n->body)
write_node(*n->body);
} }
}
}; };
Serializer::Serializer() : pImpl(std::make_unique<Impl>()) {} Serializer::Serializer() : pImpl(std::make_unique<Impl>()) {}
Serializer::~Serializer() = default; Serializer::~Serializer() = default;
void Serializer::serialize(const IRModule& module, const std::string& path) { void Serializer::serialize(const IRModule& module, const std::string& path) {
auto bytes = serialize_to_bytes(module); auto bytes = serialize_to_bytes(module);
std::ofstream out(path, std::ios::binary); std::ofstream out(path, std::ios::binary);
out.write(reinterpret_cast<const char*>(bytes.data()), bytes.size()); out.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
} }
std::vector<uint8_t> Serializer::serialize_to_bytes(const IRModule& module) { std::vector<uint8_t> Serializer::serialize_to_bytes(const IRModule& module) {
pImpl->buffer.clear(); pImpl->buffer.clear();
pImpl->write_u32(IR_MAGIC); pImpl->write_u32(IR_MAGIC);
pImpl->write_u32(IR_VERSION); pImpl->write_u32(IR_VERSION);
pImpl->write_u32(module.sources.size()); pImpl->write_u32(module.sources.size());
for (const auto& src : module.sources) { for (const auto& src : module.sources) {
pImpl->write_string(src.path); pImpl->write_string(src.path);
pImpl->write_string(src.content); pImpl->write_string(src.content);
} }
pImpl->write_u32(module.imports.size()); pImpl->write_u32(module.imports.size());
for (const auto& [from, to] : module.imports) { for (const auto& [from, to] : module.imports) {
pImpl->write_string(from); pImpl->write_string(from);
pImpl->write_string(to); pImpl->write_string(to);
} }
pImpl->write_u32(module.string_table.size()); pImpl->write_u32(module.string_table.size());
for (const auto& [str, id] : module.string_table) { for (const auto& [str, id] : module.string_table) {
pImpl->write_string(str); pImpl->write_string(str);
pImpl->write_u32(id); pImpl->write_u32(id);
} }
if (module.entry && module.entry != nullptr) { if (module.entry && module.entry != nullptr) {
pImpl->write_u8(1); pImpl->write_u8(1);
pImpl->write_node(*module.entry); pImpl->write_node(*module.entry);
} else { } else {
pImpl->write_u8(0); pImpl->write_u8(0);
} }
return pImpl->buffer; return pImpl->buffer;
} }
struct Deserializer::Impl { struct Deserializer::Impl {
std::vector<uint8_t> buffer; std::vector<uint8_t> buffer;
size_t pos = 0; size_t pos = 0;
uint32_t read_u32() { uint32_t read_u32() {
uint32_t val = 0; uint32_t val = 0;
val |= buffer[pos + 0]; val |= buffer[pos + 0];
val |= (uint32_t) buffer[pos + 1] << 8; val |= (uint32_t)buffer[pos + 1] << 8;
val |= (uint32_t) buffer[pos + 2] << 16; val |= (uint32_t)buffer[pos + 2] << 16;
val |= (uint32_t) buffer[pos + 3] << 24; val |= (uint32_t)buffer[pos + 3] << 24;
pos += 4; pos += 4;
return val; return val;
} }
uint64_t read_u64() { uint64_t read_u64() {
uint64_t val = 0; uint64_t val = 0;
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
val |= (uint64_t) buffer[pos + i] << (i * 8); val |= (uint64_t)buffer[pos + i] << (i * 8);
}
pos += 8;
return val;
} }
pos += 8;
return val;
}
uint8_t read_u8() { return buffer[pos++]; } uint8_t read_u8() {
return buffer[pos++];
}
std::string read_string() { std::string read_string() {
uint32_t len = read_u32(); uint32_t len = read_u32();
std::string str(reinterpret_cast<const char*>(&buffer[pos]), len); std::string str(reinterpret_cast<const char*>(&buffer[pos]), len);
pos += len; pos += len;
return str; return str;
} }
std::shared_ptr<Node> read_node() { std::shared_ptr<Node> read_node() {
NodeType type = static_cast<NodeType>(read_u8()); NodeType type = static_cast<NodeType>(read_u8());
uint32_t line = read_u32(); uint32_t line = read_u32();
switch (type) { switch (type) {
case NodeType::CONST_INT: { case NodeType::CONST_INT: {
int64_t val = static_cast<int64_t>(read_u64()); int64_t val = static_cast<int64_t>(read_u64());
return std::make_shared<Node>(ConstIntNode(val, line)); return std::make_shared<Node>(ConstIntNode(val, line));
}
case NodeType::CONST_STRING: {
std::string val = read_string();
return std::make_shared<Node>(ConstStringNode(val, line));
}
case NodeType::CONST_PATH: {
std::string val = read_string();
return std::make_shared<Node>(ConstPathNode(val, line));
}
case NodeType::CONST_BOOL: {
bool val = read_u8() != 0;
return std::make_shared<Node>(ConstBoolNode(val, line));
}
case NodeType::CONST_NULL:
return std::make_shared<Node>(ConstNullNode(line));
case NodeType::VAR: {
uint32_t index = read_u32();
return std::make_shared<Node>(VarNode(index, "", line));
}
case NodeType::LAMBDA: {
uint32_t arity = read_u32();
auto body = read_node();
return std::make_shared<Node>(LambdaNode(arity, body, line));
}
case NodeType::APP: {
auto func = read_node();
auto arg = read_node();
return std::make_shared<Node>(AppNode(func, arg, line));
}
case NodeType::BINARY_OP: {
BinaryOp op = static_cast<BinaryOp>(read_u8());
auto left = read_node();
auto right = read_node();
return std::make_shared<Node>(BinaryOpNode(op, left, right, line));
}
case NodeType::UNARY_OP: {
UnaryOp op = static_cast<UnaryOp>(read_u8());
auto operand = read_node();
return std::make_shared<Node>(UnaryOpNode(op, operand, line));
}
case NodeType::ATTRSET: {
bool recursive = read_u8() != 0;
uint32_t num_attrs = read_u32();
AttrsetNode attrs(recursive, line);
for (uint32_t i = 0; i < num_attrs; i++) {
std::string key = read_string();
auto val = read_node();
attrs.attrs.push_back({key, val});
}
return std::make_shared<Node>(std::move(attrs));
}
case NodeType::SELECT: {
auto expr = read_node();
auto attr = read_node();
uint8_t has_default = read_u8();
std::optional<std::shared_ptr<Node>> default_expr;
if (has_default) {
default_expr = read_node();
}
SelectNode select_node(expr, attr, line);
select_node.default_expr = default_expr;
return std::make_shared<Node>(std::move(select_node));
}
case NodeType::HAS_ATTR: {
auto expr = read_node();
auto attr = read_node();
return std::make_shared<Node>(HasAttrNode(expr, attr, line));
}
case NodeType::WITH: {
auto attrs = read_node();
auto body = read_node();
return std::make_shared<Node>(WithNode(attrs, body, line));
}
case NodeType::IF: {
auto cond = read_node();
auto then_branch = read_node();
auto else_branch = read_node();
return std::make_shared<Node>(IfNode(cond, then_branch, else_branch, line));
}
case NodeType::LET: {
uint32_t num_bindings = read_u32();
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
for (uint32_t i = 0; i < num_bindings; i++) {
std::string key = read_string();
auto val = read_node();
bindings.push_back({key, val});
}
auto body = read_node();
LetNode let(body, line);
let.bindings = std::move(bindings);
return std::make_shared<Node>(std::move(let));
}
case NodeType::LETREC: {
uint32_t num_bindings = read_u32();
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
for (uint32_t i = 0; i < num_bindings; i++) {
std::string key = read_string();
auto val = read_node();
bindings.push_back({key, val});
}
auto body = read_node();
LetRecNode letrec(body, line);
letrec.bindings = std::move(bindings);
return std::make_shared<Node>(std::move(letrec));
}
case NodeType::ASSERT: {
auto cond = read_node();
auto body = read_node();
return std::make_shared<Node>(AssertNode(cond, body, line));
}
default:
throw std::runtime_error("Unknown node type in IR");
}
} }
case NodeType::CONST_STRING: {
std::string val = read_string();
return std::make_shared<Node>(ConstStringNode(val, line));
}
case NodeType::CONST_PATH: {
std::string val = read_string();
return std::make_shared<Node>(ConstPathNode(val, line));
}
case NodeType::CONST_BOOL: {
bool val = read_u8() != 0;
return std::make_shared<Node>(ConstBoolNode(val, line));
}
case NodeType::CONST_NULL:
return std::make_shared<Node>(ConstNullNode(line));
case NodeType::VAR: {
uint32_t index = read_u32();
return std::make_shared<Node>(VarNode(index, "", line));
}
case NodeType::LAMBDA: {
uint32_t arity = read_u32();
auto body = read_node();
return std::make_shared<Node>(LambdaNode(arity, body, line));
}
case NodeType::APP: {
auto func = read_node();
auto arg = read_node();
return std::make_shared<Node>(AppNode(func, arg, line));
}
case NodeType::BINARY_OP: {
BinaryOp op = static_cast<BinaryOp>(read_u8());
auto left = read_node();
auto right = read_node();
return std::make_shared<Node>(BinaryOpNode(op, left, right, line));
}
case NodeType::UNARY_OP: {
UnaryOp op = static_cast<UnaryOp>(read_u8());
auto operand = read_node();
return std::make_shared<Node>(UnaryOpNode(op, operand, line));
}
case NodeType::ATTRSET: {
bool recursive = read_u8() != 0;
uint32_t num_attrs = read_u32();
AttrsetNode attrs(recursive, line);
for (uint32_t i = 0; i < num_attrs; i++) {
std::string key = read_string();
auto val = read_node();
attrs.attrs.push_back({key, val});
}
return std::make_shared<Node>(std::move(attrs));
}
case NodeType::SELECT: {
auto expr = read_node();
auto attr = read_node();
uint8_t has_default = read_u8();
std::optional<std::shared_ptr<Node>> default_expr;
if (has_default) {
default_expr = read_node();
}
SelectNode select_node(expr, attr, line);
select_node.default_expr = default_expr;
return std::make_shared<Node>(std::move(select_node));
}
case NodeType::HAS_ATTR: {
auto expr = read_node();
auto attr = read_node();
return std::make_shared<Node>(HasAttrNode(expr, attr, line));
}
case NodeType::WITH: {
auto attrs = read_node();
auto body = read_node();
return std::make_shared<Node>(WithNode(attrs, body, line));
}
case NodeType::IF: {
auto cond = read_node();
auto then_branch = read_node();
auto else_branch = read_node();
return std::make_shared<Node>(IfNode(cond, then_branch, else_branch, line));
}
case NodeType::LET: {
uint32_t num_bindings = read_u32();
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
for (uint32_t i = 0; i < num_bindings; i++) {
std::string key = read_string();
auto val = read_node();
bindings.push_back({key, val});
}
auto body = read_node();
LetNode let(body, line);
let.bindings = std::move(bindings);
return std::make_shared<Node>(std::move(let));
}
case NodeType::LETREC: {
uint32_t num_bindings = read_u32();
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
for (uint32_t i = 0; i < num_bindings; i++) {
std::string key = read_string();
auto val = read_node();
bindings.push_back({key, val});
}
auto body = read_node();
LetRecNode letrec(body, line);
letrec.bindings = std::move(bindings);
return std::make_shared<Node>(std::move(letrec));
}
case NodeType::ASSERT: {
auto cond = read_node();
auto body = read_node();
return std::make_shared<Node>(AssertNode(cond, body, line));
}
default:
throw std::runtime_error("Unknown node type in IR");
}
}
}; };
Deserializer::Deserializer() : pImpl(std::make_unique<Impl>()) {} Deserializer::Deserializer() : pImpl(std::make_unique<Impl>()) {}
Deserializer::~Deserializer() = default; Deserializer::~Deserializer() = default;
IRModule Deserializer::deserialize(const std::string& path) { IRModule Deserializer::deserialize(const std::string& path) {
std::ifstream in(path, std::ios::binary | std::ios::ate); std::ifstream in(path, std::ios::binary | std::ios::ate);
size_t size = in.tellg(); size_t size = in.tellg();
in.seekg(0); in.seekg(0);
pImpl->buffer.resize(size); pImpl->buffer.resize(size);
in.read(reinterpret_cast<char*>(pImpl->buffer.data()), size); in.read(reinterpret_cast<char*>(pImpl->buffer.data()), size);
pImpl->pos = 0; pImpl->pos = 0;
return deserialize(pImpl->buffer); return deserialize(pImpl->buffer);
} }
IRModule Deserializer::deserialize(const std::vector<uint8_t>& data) { IRModule Deserializer::deserialize(const std::vector<uint8_t>& data) {
pImpl->buffer = data; pImpl->buffer = data;
pImpl->pos = 0; pImpl->pos = 0;
IRModule module; IRModule module;
uint32_t magic = pImpl->read_u32(); uint32_t magic = pImpl->read_u32();
if (magic != IR_MAGIC) { if (magic != IR_MAGIC) {
throw std::runtime_error("Invalid IR file"); throw std::runtime_error("Invalid IR file");
} }
uint32_t version = pImpl->read_u32(); uint32_t version = pImpl->read_u32();
if (version != IR_VERSION) { if (version != IR_VERSION) {
throw std::runtime_error("Unsupported IR version"); throw std::runtime_error("Unsupported IR version");
} }
uint32_t num_sources = pImpl->read_u32(); uint32_t num_sources = pImpl->read_u32();
for (uint32_t i = 0; i < num_sources; i++) { for (uint32_t i = 0; i < num_sources; i++) {
SourceFile src; SourceFile src;
src.path = pImpl->read_string(); src.path = pImpl->read_string();
src.content = pImpl->read_string(); src.content = pImpl->read_string();
module.sources.push_back(src); module.sources.push_back(src);
} }
uint32_t num_imports = pImpl->read_u32(); uint32_t num_imports = pImpl->read_u32();
for (uint32_t i = 0; i < num_imports; i++) { for (uint32_t i = 0; i < num_imports; i++) {
module.imports.push_back({pImpl->read_string(), pImpl->read_string()}); module.imports.push_back({pImpl->read_string(), pImpl->read_string()});
} }
uint32_t num_strings = pImpl->read_u32(); uint32_t num_strings = pImpl->read_u32();
for (uint32_t i = 0; i < num_strings; i++) { for (uint32_t i = 0; i < num_strings; i++) {
std::string str = pImpl->read_string(); std::string str = pImpl->read_string();
uint32_t id = pImpl->read_u32(); uint32_t id = pImpl->read_u32();
module.string_table[str] = id; module.string_table[str] = id;
} }
if (pImpl->read_u8()) { if (pImpl->read_u8()) {
module.entry = pImpl->read_node(); module.entry = pImpl->read_node();
} }
return module; return module;
} }
} // namespace nix_irc }

View file

@ -2,38 +2,38 @@
#define NIX_IRC_SERIALIZER_H #define NIX_IRC_SERIALIZER_H
#include "types.h" #include "types.h"
#include <fstream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <fstream>
namespace nix_irc { namespace nix_irc {
class Serializer { class Serializer {
public: public:
Serializer(); Serializer();
~Serializer(); ~Serializer();
void serialize(const IRModule& module, const std::string& path); void serialize(const IRModule& module, const std::string& path);
std::vector<uint8_t> serialize_to_bytes(const IRModule& module); std::vector<uint8_t> serialize_to_bytes(const IRModule& module);
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> pImpl; std::unique_ptr<Impl> pImpl;
}; };
class Deserializer { class Deserializer {
public: public:
Deserializer(); Deserializer();
~Deserializer(); ~Deserializer();
IRModule deserialize(const std::string& path); IRModule deserialize(const std::string& path);
IRModule deserialize(const std::vector<uint8_t>& data); IRModule deserialize(const std::vector<uint8_t>& data);
private: private:
struct Impl; struct Impl;
std::unique_ptr<Impl> pImpl; std::unique_ptr<Impl> pImpl;
}; };
} // namespace nix_irc }
#endif #endif

View file

@ -2,14 +2,14 @@
#define NIX_IRC_TYPES_H #define NIX_IRC_TYPES_H
#include <cstdint> #include <cstdint>
#include <fstream>
#include <memory>
#include <optional>
#include <sstream>
#include <string> #include <string>
#include <unordered_map>
#include <variant>
#include <vector> #include <vector>
#include <unordered_map>
#include <optional>
#include <memory>
#include <variant>
#include <fstream>
#include <sstream>
namespace nix_irc { namespace nix_irc {
@ -17,191 +17,219 @@ constexpr uint32_t IR_MAGIC = 0x4E495258;
constexpr uint32_t IR_VERSION = 2; constexpr uint32_t IR_VERSION = 2;
enum class NodeType : uint8_t { enum class NodeType : uint8_t {
CONST_INT = 0x01, CONST_INT = 0x01,
CONST_STRING = 0x02, CONST_STRING = 0x02,
CONST_PATH = 0x03, CONST_PATH = 0x03,
CONST_BOOL = 0x04, CONST_BOOL = 0x04,
CONST_NULL = 0x05, CONST_NULL = 0x05,
VAR = 0x10, VAR = 0x10,
LAMBDA = 0x20, LAMBDA = 0x20,
APP = 0x21, APP = 0x21,
BINARY_OP = 0x22, BINARY_OP = 0x22,
UNARY_OP = 0x23, UNARY_OP = 0x23,
ATTRSET = 0x30, ATTRSET = 0x30,
SELECT = 0x31, SELECT = 0x31,
HAS_ATTR = 0x34, HAS_ATTR = 0x34,
WITH = 0x32, WITH = 0x32,
IF = 0x40, IF = 0x40,
LET = 0x50, LET = 0x50,
LETREC = 0x51, LETREC = 0x51,
ASSERT = 0x52, ASSERT = 0x52,
THUNK = 0x60, THUNK = 0x60,
FORCE = 0x61, FORCE = 0x61,
ERROR = 0xFF ERROR = 0xFF
}; };
enum class BinaryOp : uint8_t { ADD, SUB, MUL, DIV, CONCAT, EQ, NE, LT, GT, LE, GE, AND, OR, IMPL }; enum class BinaryOp : uint8_t {
ADD, SUB, MUL, DIV, CONCAT,
EQ, NE, LT, GT, LE, GE,
AND, OR, IMPL
};
enum class UnaryOp : uint8_t { NEG, NOT }; enum class UnaryOp : uint8_t {
NEG, NOT
};
// Forward declare Node for use in shared_ptr // Forward declare Node for use in shared_ptr
class Node; class Node;
struct ConstIntNode { struct ConstIntNode {
int64_t value; int64_t value;
uint32_t line = 0; uint32_t line = 0;
ConstIntNode(int64_t v = 0, uint32_t l = 0) : value(v), line(l) {} ConstIntNode(int64_t v = 0, uint32_t l = 0) : value(v), line(l) {}
}; };
struct ConstStringNode { struct ConstStringNode {
std::string value; std::string value;
uint32_t line = 0; uint32_t line = 0;
ConstStringNode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {} ConstStringNode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}
}; };
struct ConstPathNode { struct ConstPathNode {
std::string value; std::string value;
uint32_t line = 0; uint32_t line = 0;
ConstPathNode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {} ConstPathNode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}
}; };
struct ConstBoolNode { struct ConstBoolNode {
bool value; bool value;
uint32_t line = 0; uint32_t line = 0;
ConstBoolNode(bool v = false, uint32_t l = 0) : value(v), line(l) {} ConstBoolNode(bool v = false, uint32_t l = 0) : value(v), line(l) {}
}; };
struct ConstNullNode { struct ConstNullNode {
uint32_t line = 0; uint32_t line = 0;
ConstNullNode(uint32_t l = 0) : line(l) {} ConstNullNode(uint32_t l = 0) : line(l) {}
}; };
struct VarNode { struct VarNode {
uint32_t index = 0; uint32_t index = 0;
std::optional<std::string> name; std::optional<std::string> name;
uint32_t line = 0; uint32_t line = 0;
VarNode(uint32_t idx = 0, std::string n = "", uint32_t l = 0) VarNode(uint32_t idx = 0, std::string n = "", uint32_t l = 0)
: index(idx), name(n.empty() ? std::nullopt : std::optional<std::string>(n)), line(l) {} : index(idx), name(n.empty() ? std::nullopt : std::optional<std::string>(n)), line(l) {}
}; };
struct LambdaNode { struct LambdaNode {
uint32_t arity = 1; uint32_t arity = 1;
std::shared_ptr<Node> body; std::shared_ptr<Node> body;
std::optional<std::string> param_name; std::optional<std::string> param_name;
bool strict_pattern = true; bool strict_pattern = true;
uint32_t line = 0; uint32_t line = 0;
LambdaNode(uint32_t a, std::shared_ptr<Node> b, uint32_t l = 0); LambdaNode(uint32_t a, std::shared_ptr<Node> b, uint32_t l = 0);
}; };
struct AppNode { struct AppNode {
std::shared_ptr<Node> func; std::shared_ptr<Node> func;
std::shared_ptr<Node> arg; std::shared_ptr<Node> arg;
uint32_t line = 0; uint32_t line = 0;
AppNode(std::shared_ptr<Node> f, std::shared_ptr<Node> a, uint32_t l = 0); AppNode(std::shared_ptr<Node> f, std::shared_ptr<Node> a, uint32_t l = 0);
}; };
struct BinaryOpNode { struct BinaryOpNode {
BinaryOp op; BinaryOp op;
std::shared_ptr<Node> left; std::shared_ptr<Node> left;
std::shared_ptr<Node> right; std::shared_ptr<Node> right;
uint32_t line = 0; uint32_t line = 0;
BinaryOpNode(BinaryOp o, std::shared_ptr<Node> l, std::shared_ptr<Node> r, uint32_t ln = 0); BinaryOpNode(BinaryOp o, std::shared_ptr<Node> l, std::shared_ptr<Node> r, uint32_t ln = 0);
}; };
struct UnaryOpNode { struct UnaryOpNode {
UnaryOp op; UnaryOp op;
std::shared_ptr<Node> operand; std::shared_ptr<Node> operand;
uint32_t line = 0; uint32_t line = 0;
UnaryOpNode(UnaryOp o, std::shared_ptr<Node> operand, uint32_t l = 0); UnaryOpNode(UnaryOp o, std::shared_ptr<Node> operand, uint32_t l = 0);
}; };
struct AttrsetNode { struct AttrsetNode {
std::vector<std::pair<std::string, std::shared_ptr<Node>>> attrs; std::vector<std::pair<std::string, std::shared_ptr<Node>>> attrs;
bool recursive = false; bool recursive = false;
uint32_t line = 0; uint32_t line = 0;
AttrsetNode(bool rec = false, uint32_t l = 0) : recursive(rec), line(l) {} AttrsetNode(bool rec = false, uint32_t l = 0) : recursive(rec), line(l) {}
}; };
struct SelectNode { struct SelectNode {
std::shared_ptr<Node> expr; std::shared_ptr<Node> expr;
std::shared_ptr<Node> attr; std::shared_ptr<Node> attr;
std::optional<std::shared_ptr<Node>> default_expr; std::optional<std::shared_ptr<Node>> default_expr;
uint32_t line = 0; uint32_t line = 0;
SelectNode(std::shared_ptr<Node> e, std::shared_ptr<Node> a, uint32_t l = 0); SelectNode(std::shared_ptr<Node> e, std::shared_ptr<Node> a, uint32_t l = 0);
}; };
struct HasAttrNode { struct HasAttrNode {
std::shared_ptr<Node> expr; std::shared_ptr<Node> expr;
std::shared_ptr<Node> attr; std::shared_ptr<Node> attr;
uint32_t line = 0; uint32_t line = 0;
HasAttrNode(std::shared_ptr<Node> e, std::shared_ptr<Node> a, uint32_t l = 0); HasAttrNode(std::shared_ptr<Node> e, std::shared_ptr<Node> a, uint32_t l = 0);
}; };
struct WithNode { struct WithNode {
std::shared_ptr<Node> attrs; std::shared_ptr<Node> attrs;
std::shared_ptr<Node> body; std::shared_ptr<Node> body;
uint32_t line = 0; uint32_t line = 0;
WithNode(std::shared_ptr<Node> a, std::shared_ptr<Node> b, uint32_t l = 0); WithNode(std::shared_ptr<Node> a, std::shared_ptr<Node> b, uint32_t l = 0);
}; };
struct IfNode { struct IfNode {
std::shared_ptr<Node> cond; std::shared_ptr<Node> cond;
std::shared_ptr<Node> then_branch; std::shared_ptr<Node> then_branch;
std::shared_ptr<Node> else_branch; std::shared_ptr<Node> else_branch;
uint32_t line = 0; uint32_t line = 0;
IfNode(std::shared_ptr<Node> c, std::shared_ptr<Node> t, std::shared_ptr<Node> e, uint32_t l = 0); IfNode(std::shared_ptr<Node> c, std::shared_ptr<Node> t, std::shared_ptr<Node> e, uint32_t l = 0);
}; };
struct LetNode { struct LetNode {
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings; std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
std::shared_ptr<Node> body; std::shared_ptr<Node> body;
uint32_t line = 0; uint32_t line = 0;
LetNode(std::shared_ptr<Node> b, uint32_t l = 0); LetNode(std::shared_ptr<Node> b, uint32_t l = 0);
}; };
struct LetRecNode { struct LetRecNode {
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings; std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
std::shared_ptr<Node> body; std::shared_ptr<Node> body;
uint32_t line = 0; uint32_t line = 0;
LetRecNode(std::shared_ptr<Node> b, uint32_t l = 0); LetRecNode(std::shared_ptr<Node> b, uint32_t l = 0);
}; };
struct AssertNode { struct AssertNode {
std::shared_ptr<Node> cond; std::shared_ptr<Node> cond;
std::shared_ptr<Node> body; std::shared_ptr<Node> body;
uint32_t line = 0; uint32_t line = 0;
AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l = 0); AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l = 0);
}; };
struct ThunkNode { struct ThunkNode {
std::shared_ptr<Node> expr; std::shared_ptr<Node> expr;
uint32_t line = 0; uint32_t line = 0;
ThunkNode(std::shared_ptr<Node> e, uint32_t l = 0); ThunkNode(std::shared_ptr<Node> e, uint32_t l = 0);
}; };
struct ForceNode { struct ForceNode {
std::shared_ptr<Node> expr; std::shared_ptr<Node> expr;
uint32_t line = 0; uint32_t line = 0;
ForceNode(std::shared_ptr<Node> e, uint32_t l = 0); ForceNode(std::shared_ptr<Node> e, uint32_t l = 0);
}; };
// Node wraps a variant for type-safe AST // Node wraps a variant for type-safe AST
class Node { class Node {
public: public:
using Variant = std::variant<ConstIntNode, ConstStringNode, ConstPathNode, ConstBoolNode, using Variant = std::variant<
ConstNullNode, VarNode, LambdaNode, AppNode, BinaryOpNode, ConstIntNode,
UnaryOpNode, AttrsetNode, SelectNode, HasAttrNode, WithNode, IfNode, ConstStringNode,
LetNode, LetRecNode, AssertNode, ThunkNode, ForceNode>; ConstPathNode,
ConstBoolNode,
ConstNullNode,
VarNode,
LambdaNode,
AppNode,
BinaryOpNode,
UnaryOpNode,
AttrsetNode,
SelectNode,
HasAttrNode,
WithNode,
IfNode,
LetNode,
LetRecNode,
AssertNode,
ThunkNode,
ForceNode
>;
Variant data; Variant data;
template <typename T> Node(T&& value) : data(std::forward<T>(value)) {} template<typename T>
Node(T&& value) : data(std::forward<T>(value)) {}
template <typename T> T* get_if() { return std::get_if<T>(&data); } template<typename T>
T* get_if() { return std::get_if<T>(&data); }
template <typename T> const T* get_if() const { return std::get_if<T>(&data); } template<typename T>
const T* get_if() const { return std::get_if<T>(&data); }
template <typename T> bool holds() const { return std::holds_alternative<T>(data); } template<typename T>
bool holds() const { return std::holds_alternative<T>(data); }
}; };
// Constructor implementations // Constructor implementations
@ -211,8 +239,7 @@ inline LambdaNode::LambdaNode(uint32_t a, std::shared_ptr<Node> b, uint32_t l)
inline AppNode::AppNode(std::shared_ptr<Node> f, std::shared_ptr<Node> a, uint32_t l) inline AppNode::AppNode(std::shared_ptr<Node> f, std::shared_ptr<Node> a, uint32_t l)
: func(f), arg(a), line(l) {} : func(f), arg(a), line(l) {}
inline BinaryOpNode::BinaryOpNode(BinaryOp o, std::shared_ptr<Node> l, std::shared_ptr<Node> r, inline BinaryOpNode::BinaryOpNode(BinaryOp o, std::shared_ptr<Node> l, std::shared_ptr<Node> r, uint32_t ln)
uint32_t ln)
: op(o), left(l), right(r), line(ln) {} : op(o), left(l), right(r), line(ln) {}
inline UnaryOpNode::UnaryOpNode(UnaryOp o, std::shared_ptr<Node> operand, uint32_t l) inline UnaryOpNode::UnaryOpNode(UnaryOp o, std::shared_ptr<Node> operand, uint32_t l)
@ -227,34 +254,37 @@ inline HasAttrNode::HasAttrNode(std::shared_ptr<Node> e, std::shared_ptr<Node> a
inline WithNode::WithNode(std::shared_ptr<Node> a, std::shared_ptr<Node> b, uint32_t l) inline WithNode::WithNode(std::shared_ptr<Node> a, std::shared_ptr<Node> b, uint32_t l)
: attrs(a), body(b), line(l) {} : attrs(a), body(b), line(l) {}
inline IfNode::IfNode(std::shared_ptr<Node> c, std::shared_ptr<Node> t, std::shared_ptr<Node> e, inline IfNode::IfNode(std::shared_ptr<Node> c, std::shared_ptr<Node> t, std::shared_ptr<Node> e, uint32_t l)
uint32_t l)
: cond(c), then_branch(t), else_branch(e), line(l) {} : cond(c), then_branch(t), else_branch(e), line(l) {}
inline LetNode::LetNode(std::shared_ptr<Node> b, uint32_t l) : body(b), line(l) {} inline LetNode::LetNode(std::shared_ptr<Node> b, uint32_t l)
: body(b), line(l) {}
inline LetRecNode::LetRecNode(std::shared_ptr<Node> b, uint32_t l) : body(b), line(l) {} inline LetRecNode::LetRecNode(std::shared_ptr<Node> b, uint32_t l)
: body(b), line(l) {}
inline AssertNode::AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l) inline AssertNode::AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l)
: cond(c), body(b), line(l) {} : cond(c), body(b), line(l) {}
inline ThunkNode::ThunkNode(std::shared_ptr<Node> e, uint32_t l) : expr(e), line(l) {} inline ThunkNode::ThunkNode(std::shared_ptr<Node> e, uint32_t l)
: expr(e), line(l) {}
inline ForceNode::ForceNode(std::shared_ptr<Node> e, uint32_t l) : expr(e), line(l) {} inline ForceNode::ForceNode(std::shared_ptr<Node> e, uint32_t l)
: expr(e), line(l) {}
struct SourceFile { struct SourceFile {
std::string path; std::string path;
std::string content; std::string content;
std::shared_ptr<Node> ast; std::shared_ptr<Node> ast;
}; };
struct IRModule { struct IRModule {
uint32_t version = IR_VERSION; uint32_t version = IR_VERSION;
std::vector<SourceFile> sources; std::vector<SourceFile> sources;
std::vector<std::pair<std::string, std::string>> imports; std::vector<std::pair<std::string, std::string>> imports;
std::shared_ptr<Node> entry; std::shared_ptr<Node> entry;
std::unordered_map<std::string, uint32_t> string_table; std::unordered_map<std::string, uint32_t> string_table;
}; };
} // namespace nix_irc }
#endif #endif

View file

@ -5,14 +5,20 @@
#include "nix/expr/eval.hh" #include "nix/expr/eval.hh"
#include "nix/expr/primops.hh" #include "nix/expr/primops.hh"
#include "nix/expr/value.hh" #include "nix/expr/value.hh"
#include "nix/store/store-api.hh"
#include "nix/util/source-path.hh"
#include "irc/evaluator.h"
#include "irc/ir_gen.h" #include "irc/ir_gen.h"
#include "irc/parser.h" #include "irc/parser.h"
#include "irc/resolver.h"
#include "irc/serializer.h" #include "irc/serializer.h"
#include "irc/types.h" #include "irc/types.h"
#include "irc/evaluator.h"
#include <fstream>
#include <iostream> #include <iostream>
#include <memory>
#include <optional>
namespace nix_ir_plugin { namespace nix_ir_plugin {
@ -23,9 +29,11 @@ using namespace nix_irc;
* Load and evaluate a pre-compiled IR bundle * Load and evaluate a pre-compiled IR bundle
* Usage: builtins.nixIR.loadIR "/path/to/file.nixir" * Usage: builtins.nixIR.loadIR "/path/to/file.nixir"
*/ */
static void prim_loadIR(EvalState& state, const PosIdx pos, Value** args, Value& v) { static void prim_loadIR(EvalState &state, const PosIdx pos, Value **args,
Value &v) {
auto path = state.forceStringNoCtx( auto path = state.forceStringNoCtx(
*args[0], pos, "while evaluating the first argument to builtins.nixIR.loadIR"); *args[0], pos,
"while evaluating the first argument to builtins.nixIR.loadIR");
std::string pathStr(path); std::string pathStr(path);
@ -34,19 +42,25 @@ static void prim_loadIR(EvalState& state, const PosIdx pos, Value** args, Value&
try { try {
module = deserializer.deserialize(pathStr); module = deserializer.deserialize(pathStr);
} catch (const std::exception& e) { } catch (const std::exception &e) {
state.error<EvalError>("failed to deserialize IR bundle: %s", e.what()).atPos(pos).debugThrow(); state.error<EvalError>("failed to deserialize IR bundle: %s", e.what())
.atPos(pos)
.debugThrow();
} }
if (!module.entry) { if (!module.entry) {
state.error<EvalError>("IR bundle has no entry point").atPos(pos).debugThrow(); state.error<EvalError>("IR bundle has no entry point")
.atPos(pos)
.debugThrow();
} }
try { try {
Evaluator evaluator(state); Evaluator evaluator(state);
evaluator.eval_to_nix(module.entry, v); evaluator.eval_to_nix(module.entry, v);
} catch (const std::exception& e) { } catch (const std::exception &e) {
state.error<EvalError>("failed to evaluate IR: %s", e.what()).atPos(pos).debugThrow(); state.error<EvalError>("failed to evaluate IR: %s", e.what())
.atPos(pos)
.debugThrow();
} }
} }
@ -54,9 +68,11 @@ static void prim_loadIR(EvalState& state, const PosIdx pos, Value** args, Value&
* Compile Nix source to IR on-the-fly * Compile Nix source to IR on-the-fly
* Usage: builtins.nixIR.compile "{ x = 1; }" * Usage: builtins.nixIR.compile "{ x = 1; }"
*/ */
static void prim_compileNix(EvalState& state, const PosIdx pos, Value** args, Value& v) { static void prim_compileNix(EvalState &state, const PosIdx pos, Value **args,
Value &v) {
auto source = state.forceStringNoCtx( auto source = state.forceStringNoCtx(
*args[0], pos, "while evaluating the first argument to builtins.nixIR.compile"); *args[0], pos,
"while evaluating the first argument to builtins.nixIR.compile");
std::string sourceStr(source); std::string sourceStr(source);
@ -65,7 +81,9 @@ static void prim_compileNix(EvalState& state, const PosIdx pos, Value** args, Va
auto ast = parser.parse(sourceStr, "<inline>"); auto ast = parser.parse(sourceStr, "<inline>");
if (!ast) { if (!ast) {
state.error<EvalError>("failed to parse Nix expression").atPos(pos).debugThrow(); state.error<EvalError>("failed to parse Nix expression")
.atPos(pos)
.debugThrow();
} }
IRGenerator ir_gen; IRGenerator ir_gen;
@ -74,8 +92,10 @@ static void prim_compileNix(EvalState& state, const PosIdx pos, Value** args, Va
Evaluator evaluator(state); Evaluator evaluator(state);
evaluator.eval_to_nix(ir, v); evaluator.eval_to_nix(ir, v);
} catch (const std::exception& e) { } catch (const std::exception &e) {
state.error<EvalError>("IR compilation failed: %s", e.what()).atPos(pos).debugThrow(); state.error<EvalError>("IR compilation failed: %s", e.what())
.atPos(pos)
.debugThrow();
} }
} }
@ -83,18 +103,19 @@ static void prim_compileNix(EvalState& state, const PosIdx pos, Value** args, Va
* Get information about the IR plugin * Get information about the IR plugin
* Usage: builtins.nixIR.info * Usage: builtins.nixIR.info
*/ */
static void prim_info(EvalState& state, const PosIdx pos, Value** args, Value& v) { static void prim_info(EvalState &state, const PosIdx pos, Value **args,
Value &v) {
auto bindings = state.buildBindings(3); auto bindings = state.buildBindings(3);
Value* vName = state.allocValue(); Value *vName = state.allocValue();
vName->mkString("nix-ir-plugin"); vName->mkString("nix-ir-plugin");
bindings.insert(state.symbols.create("name"), vName); bindings.insert(state.symbols.create("name"), vName);
Value* vVersion = state.allocValue(); Value *vVersion = state.allocValue();
vVersion->mkString("0.1.0"); vVersion->mkString("0.1.0");
bindings.insert(state.symbols.create("version"), vVersion); bindings.insert(state.symbols.create("version"), vVersion);
Value* vStatus = state.allocValue(); Value *vStatus = state.allocValue();
vStatus->mkString("runtime-active"); vStatus->mkString("runtime-active");
bindings.insert(state.symbols.create("status"), vStatus); bindings.insert(state.symbols.create("status"), vStatus);