Compare commits
No commits in common. "c710b622da7c57c3da6cd2f0ffed64454ae790d3" and "5b80e3aa63c68867d85647d8cf4428395ea5b045" have entirely different histories.
c710b622da
...
5b80e3aa63
15 changed files with 2252 additions and 2341 deletions
|
|
@ -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/)'
|
|
||||||
|
|
@ -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";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
#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 {
|
||||||
|
|
||||||
|
|
@ -21,7 +21,9 @@ struct IREnvironment {
|
||||||
|
|
||||||
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) {
|
Value* lookup(uint32_t index) {
|
||||||
IREnvironment* env = this;
|
IREnvironment* env = this;
|
||||||
|
|
@ -56,7 +58,8 @@ struct Thunk {
|
||||||
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 {
|
||||||
|
|
@ -108,15 +111,20 @@ struct Evaluator::Impl {
|
||||||
|
|
||||||
if (auto* n = node->get_if<ConstIntNode>()) {
|
if (auto* n = node->get_if<ConstIntNode>()) {
|
||||||
v.mkInt(n->value);
|
v.mkInt(n->value);
|
||||||
} else if (auto* n = node->get_if<ConstStringNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<ConstStringNode>()) {
|
||||||
v.mkString(n->value);
|
v.mkString(n->value);
|
||||||
} else if (auto* n = node->get_if<ConstPathNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<ConstPathNode>()) {
|
||||||
v.mkPath(state.rootPath(CanonPath(n->value)));
|
v.mkPath(state.rootPath(CanonPath(n->value)));
|
||||||
} else if (auto* n = node->get_if<ConstBoolNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<ConstBoolNode>()) {
|
||||||
v.mkBool(n->value);
|
v.mkBool(n->value);
|
||||||
} else if (auto* n = node->get_if<ConstNullNode>()) { // NOLINT(bugprone-branch-clone)
|
}
|
||||||
|
else if (auto* n = node->get_if<ConstNullNode>()) {
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
} else if (auto* n = node->get_if<VarNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<VarNode>()) {
|
||||||
Value* bound = env ? env->lookup(n->index) : nullptr;
|
Value* bound = env ? env->lookup(n->index) : nullptr;
|
||||||
if (!bound && env && n->name.has_value()) {
|
if (!bound && env && n->name.has_value()) {
|
||||||
bound = env->lookup_with(state, n->name.value());
|
bound = env->lookup_with(state, n->name.value());
|
||||||
|
|
@ -126,21 +134,25 @@ struct Evaluator::Impl {
|
||||||
}
|
}
|
||||||
force(bound);
|
force(bound);
|
||||||
v = *bound;
|
v = *bound;
|
||||||
} else if (auto* n = node->get_if<LambdaNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<LambdaNode>()) {
|
||||||
auto lambda_env = env;
|
auto lambda_env = env;
|
||||||
auto body = n->body;
|
auto body = n->body;
|
||||||
|
|
||||||
auto primOp = [this, lambda_env, body](EvalState& state, PosIdx pos, Value** args,
|
auto primOp = [this, lambda_env, body](EvalState& state, PosIdx pos,
|
||||||
Value& result) {
|
Value** args, Value& result) {
|
||||||
auto call_env = make_env(lambda_env);
|
auto call_env = make_env(lambda_env);
|
||||||
call_env->bind(args[0]);
|
call_env->bind(args[0]);
|
||||||
eval_node(body, result, call_env);
|
eval_node(body, result, call_env);
|
||||||
};
|
};
|
||||||
|
|
||||||
v.mkPrimOp(new PrimOp{.name = n->param_name.value_or("lambda"),
|
v.mkPrimOp(new PrimOp {
|
||||||
|
.name = n->param_name.value_or("lambda"),
|
||||||
.arity = static_cast<size_t>(n->arity),
|
.arity = static_cast<size_t>(n->arity),
|
||||||
.fun = primOp});
|
.fun = primOp
|
||||||
} else if (auto* n = node->get_if<AppNode>()) {
|
});
|
||||||
|
}
|
||||||
|
else if (auto* n = node->get_if<AppNode>()) {
|
||||||
Value* func_val = state.allocValue();
|
Value* func_val = state.allocValue();
|
||||||
eval_node(n->func, *func_val, env);
|
eval_node(n->func, *func_val, env);
|
||||||
force(func_val);
|
force(func_val);
|
||||||
|
|
@ -148,7 +160,8 @@ struct Evaluator::Impl {
|
||||||
Value* arg_val = make_thunk(n->arg, env);
|
Value* arg_val = make_thunk(n->arg, env);
|
||||||
|
|
||||||
state.callFunction(*func_val, *arg_val, v, noPos);
|
state.callFunction(*func_val, *arg_val, v, noPos);
|
||||||
} else if (auto* n = node->get_if<BinaryOpNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<BinaryOpNode>()) {
|
||||||
Value* left = state.allocValue();
|
Value* left = state.allocValue();
|
||||||
Value* right = state.allocValue();
|
Value* right = state.allocValue();
|
||||||
|
|
||||||
|
|
@ -295,7 +308,8 @@ struct Evaluator::Impl {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (auto* n = node->get_if<UnaryOpNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<UnaryOpNode>()) {
|
||||||
Value* operand = state.allocValue();
|
Value* operand = state.allocValue();
|
||||||
eval_node(n->operand, *operand, env);
|
eval_node(n->operand, *operand, env);
|
||||||
force(operand);
|
force(operand);
|
||||||
|
|
@ -318,7 +332,8 @@ struct Evaluator::Impl {
|
||||||
default:
|
default:
|
||||||
state.error<EvalError>("unknown unary operator").debugThrow();
|
state.error<EvalError>("unknown unary operator").debugThrow();
|
||||||
}
|
}
|
||||||
} else if (auto* n = node->get_if<IfNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<IfNode>()) {
|
||||||
Value* cond = state.allocValue();
|
Value* cond = state.allocValue();
|
||||||
eval_node(n->cond, *cond, env);
|
eval_node(n->cond, *cond, env);
|
||||||
force(cond);
|
force(cond);
|
||||||
|
|
@ -332,14 +347,16 @@ struct Evaluator::Impl {
|
||||||
} else {
|
} else {
|
||||||
eval_node(n->else_branch, v, env);
|
eval_node(n->else_branch, v, env);
|
||||||
}
|
}
|
||||||
} else if (auto* n = node->get_if<LetNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<LetNode>()) {
|
||||||
auto let_env = make_env(env);
|
auto let_env = make_env(env);
|
||||||
for (const auto& [name, expr] : n->bindings) {
|
for (const auto& [name, expr] : n->bindings) {
|
||||||
Value* val = make_thunk(expr, env);
|
Value* val = make_thunk(expr, env);
|
||||||
let_env->bind(val);
|
let_env->bind(val);
|
||||||
}
|
}
|
||||||
eval_node(n->body, v, let_env);
|
eval_node(n->body, v, let_env);
|
||||||
} else if (auto* n = node->get_if<LetRecNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<LetRecNode>()) {
|
||||||
auto letrec_env = make_env(env);
|
auto letrec_env = make_env(env);
|
||||||
std::vector<Value*> thunk_vals;
|
std::vector<Value*> thunk_vals;
|
||||||
|
|
||||||
|
|
@ -350,7 +367,8 @@ struct Evaluator::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_node(n->body, v, letrec_env);
|
eval_node(n->body, v, letrec_env);
|
||||||
} else if (auto* n = node->get_if<AttrsetNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<AttrsetNode>()) {
|
||||||
auto bindings = state.buildBindings(n->attrs.size());
|
auto bindings = state.buildBindings(n->attrs.size());
|
||||||
|
|
||||||
IREnvironment* attr_env = env;
|
IREnvironment* attr_env = env;
|
||||||
|
|
@ -373,7 +391,8 @@ struct Evaluator::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
v.mkAttrs(bindings.finish());
|
v.mkAttrs(bindings.finish());
|
||||||
} else if (auto* n = node->get_if<SelectNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<SelectNode>()) {
|
||||||
Value* obj = state.allocValue();
|
Value* obj = state.allocValue();
|
||||||
eval_node(n->expr, *obj, env);
|
eval_node(n->expr, *obj, env);
|
||||||
force(obj);
|
force(obj);
|
||||||
|
|
@ -402,7 +421,8 @@ struct Evaluator::Impl {
|
||||||
} else {
|
} else {
|
||||||
state.error<EvalError>("attribute not found").debugThrow();
|
state.error<EvalError>("attribute not found").debugThrow();
|
||||||
}
|
}
|
||||||
} else if (auto* n = node->get_if<HasAttrNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<HasAttrNode>()) {
|
||||||
Value* obj = state.allocValue();
|
Value* obj = state.allocValue();
|
||||||
eval_node(n->expr, *obj, env);
|
eval_node(n->expr, *obj, env);
|
||||||
force(obj);
|
force(obj);
|
||||||
|
|
@ -420,7 +440,8 @@ struct Evaluator::Impl {
|
||||||
auto attr = obj->attrs()->get(sym);
|
auto attr = obj->attrs()->get(sym);
|
||||||
v.mkBool(attr != nullptr);
|
v.mkBool(attr != nullptr);
|
||||||
}
|
}
|
||||||
} else if (auto* n = node->get_if<WithNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<WithNode>()) {
|
||||||
Value* attrs = state.allocValue();
|
Value* attrs = state.allocValue();
|
||||||
eval_node(n->attrs, *attrs, env);
|
eval_node(n->attrs, *attrs, env);
|
||||||
force(attrs);
|
force(attrs);
|
||||||
|
|
@ -432,7 +453,8 @@ struct Evaluator::Impl {
|
||||||
auto with_env = make_env(env);
|
auto with_env = make_env(env);
|
||||||
with_env->with_attrs = attrs;
|
with_env->with_attrs = attrs;
|
||||||
eval_node(n->body, v, with_env);
|
eval_node(n->body, v, with_env);
|
||||||
} else if (auto* n = node->get_if<AssertNode>()) {
|
}
|
||||||
|
else if (auto* n = node->get_if<AssertNode>()) {
|
||||||
Value* cond = state.allocValue();
|
Value* cond = state.allocValue();
|
||||||
eval_node(n->cond, *cond, env);
|
eval_node(n->cond, *cond, env);
|
||||||
force(cond);
|
force(cond);
|
||||||
|
|
@ -446,7 +468,8 @@ struct Evaluator::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_node(n->body, v, env);
|
eval_node(n->body, v, env);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -455,9 +478,10 @@ struct Evaluator::Impl {
|
||||||
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
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
@ -21,7 +21,8 @@ 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,
|
||||||
|
nix::Value& result,
|
||||||
IREnvironment* env = nullptr);
|
IREnvironment* env = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -29,6 +30,6 @@ private:
|
||||||
std::unique_ptr<Impl> pImpl;
|
std::unique_ptr<Impl> pImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#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 {
|
||||||
|
|
||||||
|
|
@ -31,15 +31,14 @@ void NameResolver::exit_scope() {
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -52,8 +51,7 @@ uint32_t NameResolver::resolve(const std::string& name) {
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
@ -76,8 +74,7 @@ struct IRGenerator::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
|
@ -219,4 +216,4 @@ std::shared_ptr<Node> IRGenerator::generate(const std::shared_ptr<Node>& ast) {
|
||||||
return pImpl->convert(ast);
|
return pImpl->convert(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
#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 {
|
||||||
|
|
||||||
|
|
@ -40,6 +40,6 @@ private:
|
||||||
std::unique_ptr<Impl> pImpl;
|
std::unique_ptr<Impl> pImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
#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 {
|
||||||
|
|
||||||
|
|
@ -127,7 +127,7 @@ int run_decompile(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include <array>
|
#include <iostream>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <regex>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <regex>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
namespace nix_irc {
|
namespace nix_irc {
|
||||||
|
|
||||||
static std::string trim(const std::string& s) {
|
static std::string trim(const std::string& s) {
|
||||||
size_t start = s.find_first_not_of(" \t\n\r");
|
size_t start = s.find_first_not_of(" \t\n\r");
|
||||||
if (start == std::string::npos)
|
if (start == std::string::npos) return "";
|
||||||
return "";
|
|
||||||
size_t end = s.find_last_not_of(" \t\n\r");
|
size_t end = s.find_last_not_of(" \t\n\r");
|
||||||
return s.substr(start, end - start + 1);
|
return s.substr(start, end - start + 1);
|
||||||
}
|
}
|
||||||
|
|
@ -42,8 +41,7 @@ static std::pair<std::string, std::string> run_command(const std::string& cmd) {
|
||||||
std::string error;
|
std::string error;
|
||||||
|
|
||||||
FILE* pipe = popen(cmd.c_str(), "r");
|
FILE* pipe = popen(cmd.c_str(), "r");
|
||||||
if (!pipe)
|
if (!pipe) throw std::runtime_error("popen failed");
|
||||||
throw std::runtime_error("popen failed");
|
|
||||||
|
|
||||||
while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
|
while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {
|
||||||
result += buffer.data();
|
result += buffer.data();
|
||||||
|
|
@ -58,51 +56,14 @@ static std::pair<std::string, std::string> run_command(const std::string& cmd) {
|
||||||
|
|
||||||
struct Token {
|
struct Token {
|
||||||
enum Type {
|
enum Type {
|
||||||
LPAREN,
|
LPAREN, RPAREN, LBRACE, RBRACE, LBRACKET, RBRACKET,
|
||||||
RPAREN,
|
IDENT, STRING, STRING_INTERP, PATH, INT, BOOL,
|
||||||
LBRACE,
|
LET, IN, REC, IF, THEN, ELSE, ASSERT, WITH, INHERIT,
|
||||||
RBRACE,
|
DOT, SEMICOLON, COLON, EQUALS, AT, COMMA, QUESTION, ELLIPSIS,
|
||||||
LBRACKET,
|
|
||||||
RBRACKET,
|
|
||||||
IDENT,
|
|
||||||
STRING,
|
|
||||||
STRING_INTERP,
|
|
||||||
PATH,
|
|
||||||
INT,
|
|
||||||
BOOL,
|
|
||||||
LET,
|
|
||||||
IN,
|
|
||||||
REC,
|
|
||||||
IF,
|
|
||||||
THEN,
|
|
||||||
ELSE,
|
|
||||||
ASSERT,
|
|
||||||
WITH,
|
|
||||||
INHERIT,
|
|
||||||
DOT,
|
|
||||||
SEMICOLON,
|
|
||||||
COLON,
|
|
||||||
EQUALS,
|
|
||||||
AT,
|
|
||||||
COMMA,
|
|
||||||
QUESTION,
|
|
||||||
ELLIPSIS,
|
|
||||||
// Operators
|
// Operators
|
||||||
PLUS,
|
PLUS, MINUS, STAR, SLASH, CONCAT,
|
||||||
MINUS,
|
EQEQ, NE, LT, GT, LE, GE,
|
||||||
STAR,
|
AND, OR, IMPL, NOT,
|
||||||
SLASH,
|
|
||||||
CONCAT,
|
|
||||||
EQEQ,
|
|
||||||
NE,
|
|
||||||
LT,
|
|
||||||
GT,
|
|
||||||
LE,
|
|
||||||
GE,
|
|
||||||
AND,
|
|
||||||
OR,
|
|
||||||
IMPL,
|
|
||||||
NOT,
|
|
||||||
EOF_
|
EOF_
|
||||||
} type;
|
} type;
|
||||||
std::string value;
|
std::string value;
|
||||||
|
|
@ -115,123 +76,98 @@ public:
|
||||||
Lexer(const std::string& input) : input(input), pos(0), line(1), col(1) {}
|
Lexer(const std::string& input) : input(input), pos(0), line(1), col(1) {}
|
||||||
|
|
||||||
std::vector<Token> tokenize() {
|
std::vector<Token> tokenize() {
|
||||||
#define TOKEN(t) \
|
#define TOKEN(t) Token{Token::t, "", line, col}
|
||||||
Token { Token::t, "", line, col }
|
|
||||||
|
|
||||||
while (pos < input.size()) {
|
while (pos < input.size()) {
|
||||||
skip_whitespace();
|
skip_whitespace();
|
||||||
if (pos >= input.size())
|
if (pos >= input.size()) break;
|
||||||
break;
|
|
||||||
|
|
||||||
char c = input[pos];
|
char c = input[pos];
|
||||||
|
|
||||||
if (c == '(') {
|
if (c == '(') { emit(TOKEN(LPAREN)); }
|
||||||
emit(TOKEN(LPAREN));
|
else if (c == ')') { emit(TOKEN(RPAREN)); }
|
||||||
} else if (c == ')') {
|
else if (c == '{') { emit(TOKEN(LBRACE)); }
|
||||||
emit(TOKEN(RPAREN));
|
else if (c == '}') { emit(TOKEN(RBRACE)); }
|
||||||
} else if (c == '{') {
|
else if (c == '[') { emit(TOKEN(LBRACKET)); }
|
||||||
emit(TOKEN(LBRACE));
|
else if (c == ']') { emit(TOKEN(RBRACKET)); }
|
||||||
} else if (c == '}') {
|
else if (c == ';') { emit(TOKEN(SEMICOLON)); }
|
||||||
emit(TOKEN(RBRACE));
|
else if (c == ':') { emit(TOKEN(COLON)); }
|
||||||
} else if (c == '[') {
|
else if (c == '@') { emit(TOKEN(AT)); }
|
||||||
emit(TOKEN(LBRACKET));
|
else if (c == ',') { emit(TOKEN(COMMA)); }
|
||||||
} else if (c == ']') {
|
else if (c == '"') { tokenize_string(); }
|
||||||
emit(TOKEN(RBRACKET));
|
|
||||||
} else if (c == ';') {
|
|
||||||
emit(TOKEN(SEMICOLON));
|
|
||||||
} else if (c == ':') {
|
|
||||||
emit(TOKEN(COLON));
|
|
||||||
} else if (c == '@') {
|
|
||||||
emit(TOKEN(AT));
|
|
||||||
} else if (c == ',') {
|
|
||||||
emit(TOKEN(COMMA));
|
|
||||||
} else if (c == '"') {
|
|
||||||
tokenize_string();
|
|
||||||
}
|
|
||||||
// Two-char operators
|
// Two-char operators
|
||||||
else if (c == '=' && pos + 1 < input.size() && input[pos + 1] == '=') {
|
else if (c == '=' && pos + 1 < input.size() && input[pos + 1] == '=') {
|
||||||
tokens.push_back(TOKEN(EQEQ));
|
tokens.push_back(TOKEN(EQEQ));
|
||||||
pos += 2;
|
pos += 2; col += 2;
|
||||||
col += 2;
|
}
|
||||||
} else if (c == '=') {
|
else if (c == '=') { emit(TOKEN(EQUALS)); }
|
||||||
emit(TOKEN(EQUALS));
|
else if (c == '!' && pos + 1 < input.size() && input[pos + 1] == '=') {
|
||||||
} else if (c == '!' && pos + 1 < input.size() && input[pos + 1] == '=') {
|
|
||||||
tokens.push_back(TOKEN(NE));
|
tokens.push_back(TOKEN(NE));
|
||||||
pos += 2;
|
pos += 2; col += 2;
|
||||||
col += 2;
|
}
|
||||||
} else if (c == '<' && pos + 1 < input.size() && input[pos + 1] == '=') {
|
else if (c == '<' && pos + 1 < input.size() && input[pos + 1] == '=') {
|
||||||
tokens.push_back(TOKEN(LE));
|
tokens.push_back(TOKEN(LE));
|
||||||
pos += 2;
|
pos += 2; col += 2;
|
||||||
col += 2;
|
}
|
||||||
} else if (c == '>' && pos + 1 < input.size() && input[pos + 1] == '=') {
|
else if (c == '>' && pos + 1 < input.size() && input[pos + 1] == '=') {
|
||||||
tokens.push_back(TOKEN(GE));
|
tokens.push_back(TOKEN(GE));
|
||||||
pos += 2;
|
pos += 2; col += 2;
|
||||||
col += 2;
|
}
|
||||||
} else if (c == '+' && pos + 1 < input.size() && input[pos + 1] == '+') {
|
else if (c == '+' && pos + 1 < input.size() && input[pos + 1] == '+') {
|
||||||
tokens.push_back(TOKEN(CONCAT));
|
tokens.push_back(TOKEN(CONCAT));
|
||||||
pos += 2;
|
pos += 2; col += 2;
|
||||||
col += 2;
|
}
|
||||||
} else if (c == '&' && pos + 1 < input.size() && input[pos + 1] == '&') {
|
else if (c == '&' && pos + 1 < input.size() && input[pos + 1] == '&') {
|
||||||
tokens.push_back(TOKEN(AND));
|
tokens.push_back(TOKEN(AND));
|
||||||
pos += 2;
|
pos += 2; col += 2;
|
||||||
col += 2;
|
}
|
||||||
} else if (c == '|' && pos + 1 < input.size() && input[pos + 1] == '|') {
|
else if (c == '|' && pos + 1 < input.size() && input[pos + 1] == '|') {
|
||||||
tokens.push_back(TOKEN(OR));
|
tokens.push_back(TOKEN(OR));
|
||||||
pos += 2;
|
pos += 2; col += 2;
|
||||||
col += 2;
|
}
|
||||||
} else if (c == '-' && pos + 1 < input.size() && input[pos + 1] == '>') {
|
else if (c == '-' && pos + 1 < input.size() && input[pos + 1] == '>') {
|
||||||
tokens.push_back(TOKEN(IMPL));
|
tokens.push_back(TOKEN(IMPL));
|
||||||
pos += 2;
|
pos += 2; col += 2;
|
||||||
col += 2;
|
|
||||||
}
|
}
|
||||||
// Single-char operators
|
// Single-char operators
|
||||||
else if (c == '+') {
|
else if (c == '+') { emit(TOKEN(PLUS)); }
|
||||||
emit(TOKEN(PLUS));
|
else if (c == '*') { emit(TOKEN(STAR)); }
|
||||||
} else if (c == '*') {
|
else if (c == '/') {
|
||||||
emit(TOKEN(STAR));
|
|
||||||
} else if (c == '/') {
|
|
||||||
// Check if it's a path or division
|
// Check if it's a path or division
|
||||||
if (pos + 1 < input.size() && (isalnum(input[pos + 1]) || input[pos + 1] == '.')) {
|
if (pos + 1 < input.size() && (isalnum(input[pos + 1]) || input[pos + 1] == '.')) {
|
||||||
tokenize_path();
|
tokenize_path();
|
||||||
} else {
|
} else {
|
||||||
emit(TOKEN(SLASH));
|
emit(TOKEN(SLASH));
|
||||||
}
|
}
|
||||||
} else if (c == '<') {
|
}
|
||||||
emit(TOKEN(LT));
|
else if (c == '<') { emit(TOKEN(LT)); }
|
||||||
} else if (c == '>') {
|
else if (c == '>') { emit(TOKEN(GT)); }
|
||||||
emit(TOKEN(GT));
|
else if (c == '!') { emit(TOKEN(NOT)); }
|
||||||
} else if (c == '!') {
|
else if (c == '.') {
|
||||||
emit(TOKEN(NOT));
|
|
||||||
} else if (c == '.') {
|
|
||||||
// Check for ellipsis (...)
|
// Check for ellipsis (...)
|
||||||
if (pos + 2 < input.size() && input[pos + 1] == '.' && input[pos + 2] == '.') {
|
if (pos + 2 < input.size() && input[pos + 1] == '.' && input[pos + 2] == '.') {
|
||||||
tokens.push_back(TOKEN(ELLIPSIS));
|
tokens.push_back(TOKEN(ELLIPSIS));
|
||||||
pos += 3;
|
pos += 3; col += 3;
|
||||||
col += 3;
|
|
||||||
} else {
|
} else {
|
||||||
emit(TOKEN(DOT));
|
emit(TOKEN(DOT));
|
||||||
}
|
}
|
||||||
} else if (c == '?') {
|
}
|
||||||
emit(TOKEN(QUESTION));
|
else if (c == '?') { emit(TOKEN(QUESTION)); }
|
||||||
} else if (c == '-') {
|
else if (c == '-') {
|
||||||
// Check if it's a negative number or minus operator
|
// Check if it's a negative number or minus operator
|
||||||
if (pos + 1 < input.size() && isdigit(input[pos + 1])) {
|
if (pos + 1 < input.size() && isdigit(input[pos + 1])) {
|
||||||
tokenize_int();
|
tokenize_int();
|
||||||
} else {
|
} else {
|
||||||
emit(TOKEN(MINUS));
|
emit(TOKEN(MINUS));
|
||||||
}
|
}
|
||||||
} else if (isdigit(c)) {
|
|
||||||
tokenize_int();
|
|
||||||
} else if (isalpha(c) || c == '_') {
|
|
||||||
tokenize_ident();
|
|
||||||
} else {
|
|
||||||
pos++;
|
|
||||||
col++;
|
|
||||||
}
|
}
|
||||||
|
else if (isdigit(c)) { tokenize_int(); }
|
||||||
|
else if (isalpha(c) || c == '_') { tokenize_ident(); }
|
||||||
|
else { pos++; col++; }
|
||||||
}
|
}
|
||||||
tokens.push_back({Token::EOF_, "", line, col});
|
tokens.push_back({Token::EOF_, "", line, col});
|
||||||
|
|
||||||
#undef TOKEN
|
#undef TOKEN
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,16 +188,11 @@ private:
|
||||||
while (pos < input.size()) {
|
while (pos < input.size()) {
|
||||||
char c = input[pos];
|
char c = input[pos];
|
||||||
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
||||||
if (c == '\n') {
|
if (c == '\n') { line++; col = 1; }
|
||||||
line++;
|
else { col++; }
|
||||||
col = 1;
|
|
||||||
} else {
|
|
||||||
col++;
|
|
||||||
}
|
|
||||||
pos++;
|
pos++;
|
||||||
} else if (c == '#') {
|
} else if (c == '#') {
|
||||||
while (pos < input.size() && input[pos] != '\n')
|
while (pos < input.size() && input[pos] != '\n') pos++;
|
||||||
pos++;
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -277,27 +208,13 @@ private:
|
||||||
if (input[pos] == '\\' && pos + 1 < input.size()) {
|
if (input[pos] == '\\' && pos + 1 < input.size()) {
|
||||||
pos++;
|
pos++;
|
||||||
switch (input[pos]) {
|
switch (input[pos]) {
|
||||||
case 'n':
|
case 'n': s += '\n'; break;
|
||||||
s += '\n';
|
case 't': s += '\t'; break;
|
||||||
break;
|
case 'r': s += '\r'; break;
|
||||||
case 't':
|
case '"': s += '"'; break;
|
||||||
s += '\t';
|
case '\\': s += '\\'; break;
|
||||||
break;
|
case '$': s += '$'; break; // Escaped $
|
||||||
case 'r':
|
default: s += input[pos]; break;
|
||||||
s += '\r';
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
s += '"';
|
|
||||||
break;
|
|
||||||
case '\\':
|
|
||||||
s += '\\';
|
|
||||||
break;
|
|
||||||
case '$':
|
|
||||||
s += '$';
|
|
||||||
break; // Escaped $
|
|
||||||
default:
|
|
||||||
s += input[pos];
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
pos++;
|
pos++;
|
||||||
} else if (input[pos] == '$' && pos + 1 < input.size() && input[pos + 1] == '{') {
|
} else if (input[pos] == '$' && pos + 1 < input.size() && input[pos + 1] == '{') {
|
||||||
|
|
@ -319,8 +236,10 @@ private:
|
||||||
|
|
||||||
void tokenize_path() {
|
void tokenize_path() {
|
||||||
size_t start = pos;
|
size_t start = pos;
|
||||||
while (pos < input.size() && !isspace(input[pos]) && input[pos] != '(' && input[pos] != ')' &&
|
while (pos < input.size() && !isspace(input[pos]) &&
|
||||||
input[pos] != '{' && input[pos] != '}' && input[pos] != '[' && input[pos] != ']') {
|
input[pos] != '(' && input[pos] != ')' &&
|
||||||
|
input[pos] != '{' && input[pos] != '}' &&
|
||||||
|
input[pos] != '[' && input[pos] != ']') {
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
std::string path = input.substr(start, pos - start);
|
std::string path = input.substr(start, pos - start);
|
||||||
|
|
@ -330,10 +249,8 @@ private:
|
||||||
|
|
||||||
void tokenize_int() {
|
void tokenize_int() {
|
||||||
size_t start = pos;
|
size_t start = pos;
|
||||||
if (input[pos] == '-')
|
if (input[pos] == '-') pos++;
|
||||||
pos++;
|
while (pos < input.size() && isdigit(input[pos])) pos++;
|
||||||
while (pos < input.size() && isdigit(input[pos]))
|
|
||||||
pos++;
|
|
||||||
std::string num = input.substr(start, pos - start);
|
std::string num = input.substr(start, pos - start);
|
||||||
tokens.push_back({Token::INT, num, line, col});
|
tokens.push_back({Token::INT, num, line, col});
|
||||||
col += num.size();
|
col += num.size();
|
||||||
|
|
@ -341,33 +258,21 @@ private:
|
||||||
|
|
||||||
void tokenize_ident() {
|
void tokenize_ident() {
|
||||||
size_t start = pos;
|
size_t start = pos;
|
||||||
while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-'))
|
while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-')) pos++;
|
||||||
pos++;
|
|
||||||
std::string ident = input.substr(start, pos - start);
|
std::string ident = input.substr(start, pos - start);
|
||||||
|
|
||||||
Token::Type type = Token::IDENT;
|
Token::Type type = Token::IDENT;
|
||||||
if (ident == "let")
|
if (ident == "let") type = Token::LET;
|
||||||
type = Token::LET;
|
else if (ident == "in") type = Token::IN;
|
||||||
else if (ident == "in")
|
else if (ident == "rec") type = Token::REC;
|
||||||
type = Token::IN;
|
else if (ident == "if") type = Token::IF;
|
||||||
else if (ident == "rec")
|
else if (ident == "then") type = Token::THEN;
|
||||||
type = Token::REC;
|
else if (ident == "else") type = Token::ELSE;
|
||||||
else if (ident == "if")
|
else if (ident == "assert") type = Token::ASSERT;
|
||||||
type = Token::IF;
|
else if (ident == "with") type = Token::WITH;
|
||||||
else if (ident == "then")
|
else if (ident == "inherit") type = Token::INHERIT;
|
||||||
type = Token::THEN;
|
else if (ident == "true") type = Token::BOOL;
|
||||||
else if (ident == "else")
|
else if (ident == "false") type = Token::BOOL;
|
||||||
type = Token::ELSE;
|
|
||||||
else if (ident == "assert")
|
|
||||||
type = Token::ASSERT;
|
|
||||||
else if (ident == "with")
|
|
||||||
type = Token::WITH;
|
|
||||||
else if (ident == "inherit")
|
|
||||||
type = Token::INHERIT;
|
|
||||||
else if (ident == "true")
|
|
||||||
type = Token::BOOL;
|
|
||||||
else if (ident == "false")
|
|
||||||
type = Token::BOOL;
|
|
||||||
|
|
||||||
tokens.push_back({type, ident, line, col});
|
tokens.push_back({type, ident, line, col});
|
||||||
col += ident.size();
|
col += ident.size();
|
||||||
|
|
@ -381,8 +286,7 @@ public:
|
||||||
std::string current_file;
|
std::string current_file;
|
||||||
|
|
||||||
const Token& current() {
|
const Token& current() {
|
||||||
if (pos < tokens.size())
|
if (pos < tokens.size()) return tokens[pos];
|
||||||
return tokens[pos];
|
|
||||||
static Token eof{Token::EOF_, "", 0, 0};
|
static Token eof{Token::EOF_, "", 0, 0};
|
||||||
return eof;
|
return eof;
|
||||||
}
|
}
|
||||||
|
|
@ -399,8 +303,8 @@ public:
|
||||||
|
|
||||||
bool expect(Token::Type type) {
|
bool expect(Token::Type type) {
|
||||||
if (current().type != type) {
|
if (current().type != type) {
|
||||||
std::cerr << "Expected token " << type << " but got " << current().type << " at "
|
std::cerr << "Expected token " << type << " but got " << current().type
|
||||||
<< current().line << ":" << current().col << "\n";
|
<< " at " << current().line << ":" << current().col << "\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
advance();
|
advance();
|
||||||
|
|
@ -410,74 +314,43 @@ public:
|
||||||
// Get operator precedence (higher = tighter binding)
|
// Get operator precedence (higher = tighter binding)
|
||||||
int get_precedence(Token::Type type) {
|
int get_precedence(Token::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Token::OR:
|
case Token::OR: return 1;
|
||||||
return 1;
|
case Token::AND: return 2;
|
||||||
case Token::AND:
|
case Token::IMPL: return 3;
|
||||||
return 2;
|
case Token::EQEQ: case Token::NE: return 4;
|
||||||
case Token::IMPL:
|
case Token::LT: case Token::GT: case Token::LE: case Token::GE: return 5;
|
||||||
return 3;
|
case Token::CONCAT: return 6;
|
||||||
case Token::EQEQ:
|
case Token::PLUS: case Token::MINUS: return 7;
|
||||||
case Token::NE:
|
case Token::STAR: case Token::SLASH: return 8;
|
||||||
return 4;
|
default: return 0;
|
||||||
case Token::LT:
|
|
||||||
case Token::GT:
|
|
||||||
case Token::LE:
|
|
||||||
case Token::GE:
|
|
||||||
return 5;
|
|
||||||
case Token::CONCAT:
|
|
||||||
return 6;
|
|
||||||
case Token::PLUS:
|
|
||||||
case Token::MINUS:
|
|
||||||
return 7;
|
|
||||||
case Token::STAR:
|
|
||||||
case Token::SLASH:
|
|
||||||
return 8;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert token type to binary operator
|
// Convert token type to binary operator
|
||||||
BinaryOp token_to_binop(Token::Type type) {
|
BinaryOp token_to_binop(Token::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Token::PLUS:
|
case Token::PLUS: return BinaryOp::ADD;
|
||||||
return BinaryOp::ADD;
|
case Token::MINUS: return BinaryOp::SUB;
|
||||||
case Token::MINUS:
|
case Token::STAR: return BinaryOp::MUL;
|
||||||
return BinaryOp::SUB;
|
case Token::SLASH: return BinaryOp::DIV;
|
||||||
case Token::STAR:
|
case Token::CONCAT: return BinaryOp::CONCAT;
|
||||||
return BinaryOp::MUL;
|
case Token::EQEQ: return BinaryOp::EQ;
|
||||||
case Token::SLASH:
|
case Token::NE: return BinaryOp::NE;
|
||||||
return BinaryOp::DIV;
|
case Token::LT: return BinaryOp::LT;
|
||||||
case Token::CONCAT:
|
case Token::GT: return BinaryOp::GT;
|
||||||
return BinaryOp::CONCAT;
|
case Token::LE: return BinaryOp::LE;
|
||||||
case Token::EQEQ:
|
case Token::GE: return BinaryOp::GE;
|
||||||
return BinaryOp::EQ;
|
case Token::AND: return BinaryOp::AND;
|
||||||
case Token::NE:
|
case Token::OR: return BinaryOp::OR;
|
||||||
return BinaryOp::NE;
|
case Token::IMPL: return BinaryOp::IMPL;
|
||||||
case Token::LT:
|
default: throw std::runtime_error("Invalid binary operator");
|
||||||
return BinaryOp::LT;
|
|
||||||
case Token::GT:
|
|
||||||
return BinaryOp::GT;
|
|
||||||
case Token::LE:
|
|
||||||
return BinaryOp::LE;
|
|
||||||
case Token::GE:
|
|
||||||
return BinaryOp::GE;
|
|
||||||
case Token::AND:
|
|
||||||
return BinaryOp::AND;
|
|
||||||
case Token::OR:
|
|
||||||
return BinaryOp::OR;
|
|
||||||
case Token::IMPL:
|
|
||||||
return BinaryOp::IMPL;
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("Invalid binary operator");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_expr() {
|
std::shared_ptr<Node> parse_expr() {
|
||||||
// Try to parse lambda
|
// Try to parse lambda
|
||||||
auto lambda = try_parse_lambda();
|
auto lambda = try_parse_lambda();
|
||||||
if (lambda)
|
if (lambda) return lambda;
|
||||||
return lambda;
|
|
||||||
|
|
||||||
if (consume(Token::IF)) {
|
if (consume(Token::IF)) {
|
||||||
auto cond = parse_expr();
|
auto cond = parse_expr();
|
||||||
|
|
@ -520,7 +393,9 @@ public:
|
||||||
return parse_expr1();
|
return parse_expr1();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_expr1() { return parse_binary_op(0); }
|
std::shared_ptr<Node> parse_expr1() {
|
||||||
|
return parse_binary_op(0);
|
||||||
|
}
|
||||||
|
|
||||||
// Precedence climbing for binary operators
|
// Precedence climbing for binary operators
|
||||||
std::shared_ptr<Node> parse_binary_op(int min_prec) {
|
std::shared_ptr<Node> parse_binary_op(int min_prec) {
|
||||||
|
|
@ -528,14 +403,17 @@ public:
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
int prec = get_precedence(current().type);
|
int prec = get_precedence(current().type);
|
||||||
if (prec == 0 || prec < min_prec)
|
if (prec == 0 || prec < min_prec) break;
|
||||||
break;
|
|
||||||
|
|
||||||
Token op_token = current();
|
Token op_token = current();
|
||||||
advance();
|
advance();
|
||||||
|
|
||||||
auto right = parse_binary_op(prec + 1);
|
auto right = parse_binary_op(prec + 1);
|
||||||
left = std::make_shared<Node>(BinaryOpNode(token_to_binop(op_token.type), left, right));
|
left = std::make_shared<Node>(BinaryOpNode(
|
||||||
|
token_to_binop(op_token.type),
|
||||||
|
left,
|
||||||
|
right
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return left;
|
return left;
|
||||||
|
|
@ -562,16 +440,16 @@ public:
|
||||||
Token n = current();
|
Token n = current();
|
||||||
expect(Token::IDENT);
|
expect(Token::IDENT);
|
||||||
auto a = std::make_shared<Node>(ConstStringNode(n.value));
|
auto a = std::make_shared<Node>(ConstStringNode(n.value));
|
||||||
curr->attr =
|
curr->attr = std::make_shared<Node>(AppNode(
|
||||||
std::make_shared<Node>(AppNode(std::make_shared<Node>(AppNode(curr->attr, a)),
|
std::make_shared<Node>(AppNode(curr->attr, a)),
|
||||||
std::make_shared<Node>(ConstNullNode())));
|
std::make_shared<Node>(ConstNullNode())
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} else if (consume(Token::LBRACE)) {
|
} else if (consume(Token::LBRACE)) {
|
||||||
auto result = std::make_shared<Node>(
|
auto result = std::make_shared<Node>(SelectNode(left, std::make_shared<Node>(ConstStringNode(name.value))));
|
||||||
SelectNode(left, std::make_shared<Node>(ConstStringNode(name.value))));
|
|
||||||
parse_expr_attrs(result);
|
parse_expr_attrs(result);
|
||||||
expect(Token::RBRACE);
|
expect(Token::RBRACE);
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -698,8 +576,10 @@ public:
|
||||||
|
|
||||||
if (source) {
|
if (source) {
|
||||||
// inherit (expr) x → x = expr.x
|
// inherit (expr) x → x = expr.x
|
||||||
auto select = std::make_shared<Node>(
|
auto select = std::make_shared<Node>(SelectNode(
|
||||||
SelectNode(source, std::make_shared<Node>(ConstStringNode(name.value))));
|
source,
|
||||||
|
std::make_shared<Node>(ConstStringNode(name.value))
|
||||||
|
));
|
||||||
attrs.attrs.push_back({name.value, select});
|
attrs.attrs.push_back({name.value, select});
|
||||||
} else {
|
} else {
|
||||||
// inherit x → x = x
|
// inherit x → x = x
|
||||||
|
|
@ -727,10 +607,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consume(Token::COMMA))
|
if (consume(Token::COMMA)) continue;
|
||||||
continue;
|
if (consume(Token::SEMICOLON)) continue;
|
||||||
if (consume(Token::SEMICOLON))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// If we get here and haven't handled the token, break
|
// If we get here and haven't handled the token, break
|
||||||
if (current().type != Token::RBRACE && current().type != Token::EOF_) {
|
if (current().type != Token::RBRACE && current().type != Token::EOF_) {
|
||||||
|
|
@ -752,15 +630,18 @@ public:
|
||||||
std::vector<std::shared_ptr<Node>> elements;
|
std::vector<std::shared_ptr<Node>> elements;
|
||||||
while (current().type != Token::RBRACKET) {
|
while (current().type != Token::RBRACKET) {
|
||||||
elements.push_back(parse_expr());
|
elements.push_back(parse_expr());
|
||||||
if (!consume(Token::COMMA))
|
if (!consume(Token::COMMA)) break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
expect(Token::RBRACKET);
|
expect(Token::RBRACKET);
|
||||||
|
|
||||||
for (auto it = elements.rbegin(); it != elements.rend(); ++it) {
|
for (auto it = elements.rbegin(); it != elements.rend(); ++it) {
|
||||||
list = std::make_shared<Node>(AppNode(
|
list = std::make_shared<Node>(AppNode(
|
||||||
std::make_shared<Node>(AppNode(std::make_shared<Node>(VarNode(0, "__list")), *it)),
|
std::make_shared<Node>(AppNode(
|
||||||
list));
|
std::make_shared<Node>(VarNode(0, "__list")),
|
||||||
|
*it
|
||||||
|
)),
|
||||||
|
list
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
|
@ -785,8 +666,10 @@ public:
|
||||||
|
|
||||||
if (source) {
|
if (source) {
|
||||||
// inherit (expr) x → x = expr.x
|
// inherit (expr) x → x = expr.x
|
||||||
auto select = std::make_shared<Node>(
|
auto select = std::make_shared<Node>(SelectNode(
|
||||||
SelectNode(source, std::make_shared<Node>(ConstStringNode(name.value))));
|
source,
|
||||||
|
std::make_shared<Node>(ConstStringNode(name.value))
|
||||||
|
));
|
||||||
bindings.push_back({name.value, select});
|
bindings.push_back({name.value, select});
|
||||||
} else {
|
} else {
|
||||||
// inherit x → x = x
|
// inherit x → x = x
|
||||||
|
|
@ -799,8 +682,7 @@ public:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current().type != Token::IDENT)
|
if (current().type != Token::IDENT) break;
|
||||||
break;
|
|
||||||
Token key = current();
|
Token key = current();
|
||||||
advance();
|
advance();
|
||||||
|
|
||||||
|
|
@ -814,8 +696,7 @@ public:
|
||||||
bindings.push_back({key.value, value});
|
bindings.push_back({key.value, value});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!consume(Token::SEMICOLON))
|
if (!consume(Token::SEMICOLON)) break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -858,8 +739,7 @@ public:
|
||||||
while (current().type != Token::RBRACE && current().type != Token::EOF_) {
|
while (current().type != Token::RBRACE && current().type != Token::EOF_) {
|
||||||
if (consume(Token::ELLIPSIS)) {
|
if (consume(Token::ELLIPSIS)) {
|
||||||
has_ellipsis = true;
|
has_ellipsis = true;
|
||||||
if (consume(Token::COMMA))
|
if (consume(Token::COMMA)) continue;
|
||||||
continue;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -877,8 +757,7 @@ public:
|
||||||
|
|
||||||
fields.push_back(field);
|
fields.push_back(field);
|
||||||
|
|
||||||
if (consume(Token::COMMA))
|
if (consume(Token::COMMA)) continue;
|
||||||
continue;
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
@ -910,14 +789,22 @@ public:
|
||||||
|
|
||||||
for (const auto& field : fields) {
|
for (const auto& field : fields) {
|
||||||
// Create arg.field selection
|
// Create arg.field selection
|
||||||
auto select = std::make_shared<Node>(
|
auto select = std::make_shared<Node>(SelectNode(
|
||||||
SelectNode(arg_var, std::make_shared<Node>(ConstStringNode(field.name))));
|
arg_var,
|
||||||
|
std::make_shared<Node>(ConstStringNode(field.name))
|
||||||
|
));
|
||||||
|
|
||||||
if (field.default_val) {
|
if (field.default_val) {
|
||||||
// if arg ? field then arg.field else default
|
// if arg ? field then arg.field else default
|
||||||
auto has_attr = std::make_shared<Node>(
|
auto has_attr = std::make_shared<Node>(HasAttrNode(
|
||||||
HasAttrNode(arg_var, std::make_shared<Node>(ConstStringNode(field.name))));
|
arg_var,
|
||||||
auto if_node = std::make_shared<Node>(IfNode(has_attr, select, *field.default_val));
|
std::make_shared<Node>(ConstStringNode(field.name))
|
||||||
|
));
|
||||||
|
auto if_node = std::make_shared<Node>(IfNode(
|
||||||
|
has_attr,
|
||||||
|
select,
|
||||||
|
*field.default_val
|
||||||
|
));
|
||||||
bindings.push_back({field.name, if_node});
|
bindings.push_back({field.name, if_node});
|
||||||
} else {
|
} else {
|
||||||
bindings.push_back({field.name, select});
|
bindings.push_back({field.name, select});
|
||||||
|
|
@ -977,14 +864,13 @@ public:
|
||||||
depth--;
|
depth--;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (raw[i] == string_quote && (i == 0 || raw[i - 1] != '\\')) {
|
if (raw[i] == string_quote && (i == 0 || raw[i-1] != '\\')) {
|
||||||
in_string = false;
|
in_string = false;
|
||||||
} else if (raw[i] == '\\') {
|
} else if (raw[i] == '\\') {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (depth > 0)
|
if (depth > 0) i++;
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth > 0) {
|
if (depth > 0) {
|
||||||
|
|
@ -1061,4 +947,4 @@ std::shared_ptr<Node> Parser::parse_file(const std::string& path) {
|
||||||
return parse(content, path);
|
return parse(content, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
#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 {
|
||||||
|
|
||||||
|
|
@ -20,6 +20,6 @@ private:
|
||||||
std::unique_ptr<Impl> pImpl;
|
std::unique_ptr<Impl> pImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
#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 {
|
||||||
|
|
||||||
|
|
@ -22,20 +22,17 @@ struct Resolver::Impl {
|
||||||
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 from_dir = fs::path(from_file).parent_path();
|
||||||
fs::path candidate = from_dir / p;
|
fs::path candidate = from_dir / p;
|
||||||
if (fs::exists(candidate))
|
if (fs::exists(candidate)) return candidate.string();
|
||||||
return candidate.string();
|
|
||||||
|
|
||||||
for (const auto& search : config.search_paths) {
|
for (const auto& search : config.search_paths) {
|
||||||
candidate = fs::path(search) / p;
|
candidate = fs::path(search) / p;
|
||||||
if (fs::exists(candidate))
|
if (fs::exists(candidate)) return candidate.string();
|
||||||
return candidate.string();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
|
@ -92,7 +89,7 @@ ImportResult Resolver::resolve_import(const Node& import_node, const std::string
|
||||||
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;
|
||||||
|
|
@ -111,4 +108,4 @@ std::string normalize_path(const std::string& path) {
|
||||||
return fs::absolute(p).string();
|
return fs::absolute(p).string();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
#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 {
|
||||||
|
|
||||||
|
|
@ -43,6 +43,6 @@ private:
|
||||||
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
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#include "serializer.h"
|
#include "serializer.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace nix_irc {
|
namespace nix_irc {
|
||||||
|
|
||||||
|
|
@ -21,7 +21,9 @@ struct Serializer::Impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_u8(uint8_t val) { buffer.push_back(val); }
|
void write_u8(uint8_t val) {
|
||||||
|
buffer.push_back(val);
|
||||||
|
}
|
||||||
|
|
||||||
void write_string(const std::string& str) {
|
void write_string(const std::string& str) {
|
||||||
write_u32(str.size());
|
write_u32(str.size());
|
||||||
|
|
@ -29,42 +31,24 @@ struct Serializer::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeType get_node_type(const Node& node) {
|
NodeType get_node_type(const Node& node) {
|
||||||
if (node.holds<ConstIntNode>())
|
if (node.holds<ConstIntNode>()) return NodeType::CONST_INT;
|
||||||
return NodeType::CONST_INT;
|
if (node.holds<ConstStringNode>()) return NodeType::CONST_STRING;
|
||||||
if (node.holds<ConstStringNode>())
|
if (node.holds<ConstPathNode>()) return NodeType::CONST_PATH;
|
||||||
return NodeType::CONST_STRING;
|
if (node.holds<ConstBoolNode>()) return NodeType::CONST_BOOL;
|
||||||
if (node.holds<ConstPathNode>())
|
if (node.holds<ConstNullNode>()) return NodeType::CONST_NULL;
|
||||||
return NodeType::CONST_PATH;
|
if (node.holds<VarNode>()) return NodeType::VAR;
|
||||||
if (node.holds<ConstBoolNode>())
|
if (node.holds<LambdaNode>()) return NodeType::LAMBDA;
|
||||||
return NodeType::CONST_BOOL;
|
if (node.holds<AppNode>()) return NodeType::APP;
|
||||||
if (node.holds<ConstNullNode>())
|
if (node.holds<BinaryOpNode>()) return NodeType::BINARY_OP;
|
||||||
return NodeType::CONST_NULL;
|
if (node.holds<UnaryOpNode>()) return NodeType::UNARY_OP;
|
||||||
if (node.holds<VarNode>())
|
if (node.holds<AttrsetNode>()) return NodeType::ATTRSET;
|
||||||
return NodeType::VAR;
|
if (node.holds<SelectNode>()) return NodeType::SELECT;
|
||||||
if (node.holds<LambdaNode>())
|
if (node.holds<HasAttrNode>()) return NodeType::HAS_ATTR;
|
||||||
return NodeType::LAMBDA;
|
if (node.holds<WithNode>()) return NodeType::WITH;
|
||||||
if (node.holds<AppNode>())
|
if (node.holds<IfNode>()) return NodeType::IF;
|
||||||
return NodeType::APP;
|
if (node.holds<LetNode>()) return NodeType::LET;
|
||||||
if (node.holds<BinaryOpNode>())
|
if (node.holds<LetRecNode>()) return NodeType::LETREC;
|
||||||
return NodeType::BINARY_OP;
|
if (node.holds<AssertNode>()) return NodeType::ASSERT;
|
||||||
if (node.holds<UnaryOpNode>())
|
|
||||||
return NodeType::UNARY_OP;
|
|
||||||
if (node.holds<AttrsetNode>())
|
|
||||||
return NodeType::ATTRSET;
|
|
||||||
if (node.holds<SelectNode>())
|
|
||||||
return NodeType::SELECT;
|
|
||||||
if (node.holds<HasAttrNode>())
|
|
||||||
return NodeType::HAS_ATTR;
|
|
||||||
if (node.holds<WithNode>())
|
|
||||||
return NodeType::WITH;
|
|
||||||
if (node.holds<IfNode>())
|
|
||||||
return NodeType::IF;
|
|
||||||
if (node.holds<LetNode>())
|
|
||||||
return NodeType::LET;
|
|
||||||
if (node.holds<LetRecNode>())
|
|
||||||
return NodeType::LETREC;
|
|
||||||
if (node.holds<AssertNode>())
|
|
||||||
return NodeType::ASSERT;
|
|
||||||
return NodeType::ERROR;
|
return NodeType::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,36 +74,27 @@ struct Serializer::Impl {
|
||||||
write_u32(n->index);
|
write_u32(n->index);
|
||||||
} else if (auto* n = node.get_if<LambdaNode>()) {
|
} else if (auto* n = node.get_if<LambdaNode>()) {
|
||||||
write_u32(n->arity);
|
write_u32(n->arity);
|
||||||
if (n->body)
|
if (n->body) write_node(*n->body);
|
||||||
write_node(*n->body);
|
|
||||||
} else if (auto* n = node.get_if<AppNode>()) {
|
} else if (auto* n = node.get_if<AppNode>()) {
|
||||||
if (n->func)
|
if (n->func) write_node(*n->func);
|
||||||
write_node(*n->func);
|
if (n->arg) write_node(*n->arg);
|
||||||
if (n->arg)
|
|
||||||
write_node(*n->arg);
|
|
||||||
} else if (auto* n = node.get_if<BinaryOpNode>()) {
|
} else if (auto* n = node.get_if<BinaryOpNode>()) {
|
||||||
write_u8(static_cast<uint8_t>(n->op));
|
write_u8(static_cast<uint8_t>(n->op));
|
||||||
if (n->left)
|
if (n->left) write_node(*n->left);
|
||||||
write_node(*n->left);
|
if (n->right) write_node(*n->right);
|
||||||
if (n->right)
|
|
||||||
write_node(*n->right);
|
|
||||||
} else if (auto* n = node.get_if<UnaryOpNode>()) {
|
} else if (auto* n = node.get_if<UnaryOpNode>()) {
|
||||||
write_u8(static_cast<uint8_t>(n->op));
|
write_u8(static_cast<uint8_t>(n->op));
|
||||||
if (n->operand)
|
if (n->operand) write_node(*n->operand);
|
||||||
write_node(*n->operand);
|
|
||||||
} else if (auto* n = node.get_if<AttrsetNode>()) {
|
} else if (auto* n = node.get_if<AttrsetNode>()) {
|
||||||
write_u8(n->recursive ? 1 : 0);
|
write_u8(n->recursive ? 1 : 0);
|
||||||
write_u32(n->attrs.size());
|
write_u32(n->attrs.size());
|
||||||
for (const auto& [key, val] : n->attrs) {
|
for (const auto& [key, val] : n->attrs) {
|
||||||
write_string(key);
|
write_string(key);
|
||||||
if (val)
|
if (val) write_node(*val);
|
||||||
write_node(*val);
|
|
||||||
}
|
}
|
||||||
} else if (auto* n = node.get_if<SelectNode>()) {
|
} else if (auto* n = node.get_if<SelectNode>()) {
|
||||||
if (n->expr)
|
if (n->expr) write_node(*n->expr);
|
||||||
write_node(*n->expr);
|
if (n->attr) write_node(*n->attr);
|
||||||
if (n->attr)
|
|
||||||
write_node(*n->attr);
|
|
||||||
if (n->default_expr && *n->default_expr) {
|
if (n->default_expr && *n->default_expr) {
|
||||||
write_u8(1);
|
write_u8(1);
|
||||||
write_node(**n->default_expr);
|
write_node(**n->default_expr);
|
||||||
|
|
@ -127,45 +102,32 @@ struct Serializer::Impl {
|
||||||
write_u8(0);
|
write_u8(0);
|
||||||
}
|
}
|
||||||
} else if (auto* n = node.get_if<HasAttrNode>()) {
|
} else if (auto* n = node.get_if<HasAttrNode>()) {
|
||||||
if (n->expr)
|
if (n->expr) write_node(*n->expr);
|
||||||
write_node(*n->expr);
|
if (n->attr) write_node(*n->attr);
|
||||||
if (n->attr)
|
|
||||||
write_node(*n->attr);
|
|
||||||
} else if (auto* n = node.get_if<WithNode>()) {
|
} else if (auto* n = node.get_if<WithNode>()) {
|
||||||
if (n->attrs)
|
if (n->attrs) write_node(*n->attrs);
|
||||||
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>()) {
|
} else if (auto* n = node.get_if<IfNode>()) {
|
||||||
if (n->cond)
|
if (n->cond) write_node(*n->cond);
|
||||||
write_node(*n->cond);
|
if (n->then_branch) write_node(*n->then_branch);
|
||||||
if (n->then_branch)
|
if (n->else_branch) write_node(*n->else_branch);
|
||||||
write_node(*n->then_branch);
|
|
||||||
if (n->else_branch)
|
|
||||||
write_node(*n->else_branch);
|
|
||||||
} else if (auto* n = node.get_if<LetNode>()) {
|
} else if (auto* n = node.get_if<LetNode>()) {
|
||||||
write_u32(n->bindings.size());
|
write_u32(n->bindings.size());
|
||||||
for (const auto& [key, val] : n->bindings) {
|
for (const auto& [key, val] : n->bindings) {
|
||||||
write_string(key);
|
write_string(key);
|
||||||
if (val)
|
if (val) write_node(*val);
|
||||||
write_node(*val);
|
|
||||||
}
|
}
|
||||||
if (n->body)
|
if (n->body) write_node(*n->body);
|
||||||
write_node(*n->body);
|
|
||||||
} else if (auto* n = node.get_if<LetRecNode>()) {
|
} else if (auto* n = node.get_if<LetRecNode>()) {
|
||||||
write_u32(n->bindings.size());
|
write_u32(n->bindings.size());
|
||||||
for (const auto& [key, val] : n->bindings) {
|
for (const auto& [key, val] : n->bindings) {
|
||||||
write_string(key);
|
write_string(key);
|
||||||
if (val)
|
if (val) write_node(*val);
|
||||||
write_node(*val);
|
|
||||||
}
|
}
|
||||||
if (n->body)
|
if (n->body) write_node(*n->body);
|
||||||
write_node(*n->body);
|
|
||||||
} else if (auto* n = node.get_if<AssertNode>()) {
|
} else if (auto* n = node.get_if<AssertNode>()) {
|
||||||
if (n->cond)
|
if (n->cond) write_node(*n->cond);
|
||||||
write_node(*n->cond);
|
if (n->body) write_node(*n->body);
|
||||||
if (n->body)
|
|
||||||
write_node(*n->body);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -220,9 +182,9 @@ struct Deserializer::Impl {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
@ -230,13 +192,15 @@ struct Deserializer::Impl {
|
||||||
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;
|
pos += 8;
|
||||||
return val;
|
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();
|
||||||
|
|
@ -425,4 +389,4 @@ IRModule Deserializer::deserialize(const std::vector<uint8_t>& data) {
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
#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 {
|
||||||
|
|
||||||
|
|
@ -34,6 +34,6 @@ private:
|
||||||
std::unique_ptr<Impl> pImpl;
|
std::unique_ptr<Impl> pImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
@ -40,9 +40,15 @@ enum class NodeType : uint8_t {
|
||||||
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;
|
||||||
|
|
@ -188,20 +194,42 @@ struct ForceNode {
|
||||||
// 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,20 +254,23 @@ 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;
|
||||||
|
|
@ -256,5 +286,5 @@ struct IRModule {
|
||||||
std::unordered_map<std::string, uint32_t> string_table;
|
std::unordered_map<std::string, uint32_t> string_table;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nix_irc
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue