various: format with clang-format
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ib9abc9d2dcd036d3680c5aa3dc919bfa6a6a6964
This commit is contained in:
parent
63a9eddc49
commit
98fd1bfc52
12 changed files with 1923 additions and 1819 deletions
|
|
@ -9,7 +9,7 @@ namespace nix {
|
|||
class EvalState;
|
||||
class Value;
|
||||
class PosIdx;
|
||||
}
|
||||
} // namespace nix
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
|
|
@ -18,18 +18,17 @@ class IREnvironment;
|
|||
|
||||
class Evaluator {
|
||||
public:
|
||||
explicit Evaluator(nix::EvalState& state);
|
||||
~Evaluator();
|
||||
explicit Evaluator(nix::EvalState& state);
|
||||
~Evaluator();
|
||||
|
||||
void eval_to_nix(const std::shared_ptr<Node>& ir_node,
|
||||
nix::Value& result,
|
||||
IREnvironment* env = nullptr);
|
||||
void eval_to_nix(const std::shared_ptr<Node>& ir_node, nix::Value& result,
|
||||
IREnvironment* env = nullptr);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,219 +1,222 @@
|
|||
#include "ir_gen.h"
|
||||
#include <algorithm>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
struct NameResolver::Impl {
|
||||
std::vector<std::unordered_map<std::string, uint32_t>> scopes;
|
||||
std::vector<std::vector<std::string>> scope_names;
|
||||
std::vector<std::unordered_map<std::string, uint32_t>> scopes;
|
||||
std::vector<std::vector<std::string>> scope_names;
|
||||
|
||||
Impl() {
|
||||
scopes.push_back({});
|
||||
scope_names.push_back({});
|
||||
}
|
||||
Impl() {
|
||||
scopes.push_back({});
|
||||
scope_names.push_back({});
|
||||
}
|
||||
};
|
||||
|
||||
NameResolver::NameResolver() : pImpl(std::make_unique<Impl>()) {}
|
||||
NameResolver::~NameResolver() = default;
|
||||
|
||||
void NameResolver::enter_scope() {
|
||||
pImpl->scopes.push_back({});
|
||||
pImpl->scope_names.push_back({});
|
||||
pImpl->scopes.push_back({});
|
||||
pImpl->scope_names.push_back({});
|
||||
}
|
||||
|
||||
void NameResolver::exit_scope() {
|
||||
if (!pImpl->scopes.empty()) {
|
||||
pImpl->scopes.pop_back();
|
||||
pImpl->scope_names.pop_back();
|
||||
}
|
||||
if (!pImpl->scopes.empty()) {
|
||||
pImpl->scopes.pop_back();
|
||||
pImpl->scope_names.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void NameResolver::bind(const std::string& name) {
|
||||
if (pImpl->scopes.empty()) return;
|
||||
uint32_t idx = pImpl->scope_names.back().size();
|
||||
pImpl->scopes.back()[name] = idx;
|
||||
pImpl->scope_names.back().push_back(name);
|
||||
if (pImpl->scopes.empty())
|
||||
return;
|
||||
uint32_t idx = pImpl->scope_names.back().size();
|
||||
pImpl->scopes.back()[name] = idx;
|
||||
pImpl->scope_names.back().push_back(name);
|
||||
}
|
||||
|
||||
uint32_t NameResolver::resolve(const std::string& name) {
|
||||
for (int i = (int)pImpl->scopes.size() - 1; i >= 0; --i) {
|
||||
auto it = pImpl->scopes[i].find(name);
|
||||
if (it != pImpl->scopes[i].end()) {
|
||||
uint32_t depth = pImpl->scopes.size() - 1 - i;
|
||||
uint32_t offset = it->second;
|
||||
return depth << 16 | offset;
|
||||
}
|
||||
for (int i = (int) pImpl->scopes.size() - 1; i >= 0; --i) {
|
||||
auto it = pImpl->scopes[i].find(name);
|
||||
if (it != pImpl->scopes[i].end()) {
|
||||
uint32_t depth = pImpl->scopes.size() - 1 - i;
|
||||
uint32_t offset = it->second;
|
||||
return depth << 16 | offset;
|
||||
}
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool NameResolver::is_bound(const std::string& name) const {
|
||||
for (auto it = pImpl->scopes.rbegin(); it != pImpl->scopes.rend(); ++it) {
|
||||
if (it->count(name)) return true;
|
||||
}
|
||||
return false;
|
||||
for (auto it = pImpl->scopes.rbegin(); it != pImpl->scopes.rend(); ++it) {
|
||||
if (it->count(name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct IRGenerator::Impl {
|
||||
std::unordered_map<std::string, uint32_t> string_table;
|
||||
uint32_t next_string_id = 0;
|
||||
NameResolver name_resolver;
|
||||
std::unordered_map<std::string, uint32_t> string_table;
|
||||
uint32_t next_string_id = 0;
|
||||
NameResolver name_resolver;
|
||||
|
||||
Impl() {}
|
||||
Impl() {}
|
||||
|
||||
uint32_t add_string(const std::string& str) {
|
||||
auto it = string_table.find(str);
|
||||
if (it != string_table.end()) {
|
||||
return it->second;
|
||||
}
|
||||
uint32_t id = next_string_id++;
|
||||
string_table[str] = id;
|
||||
return id;
|
||||
uint32_t add_string(const std::string& str) {
|
||||
auto it = string_table.find(str);
|
||||
if (it != string_table.end()) {
|
||||
return it->second;
|
||||
}
|
||||
uint32_t id = next_string_id++;
|
||||
string_table[str] = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> convert(const std::shared_ptr<Node>& node_ptr) {
|
||||
if (!node_ptr) return std::make_shared<Node>(ConstNullNode{});
|
||||
std::shared_ptr<Node> convert(const std::shared_ptr<Node>& node_ptr) {
|
||||
if (!node_ptr)
|
||||
return std::make_shared<Node>(ConstNullNode{});
|
||||
|
||||
const Node& node = *node_ptr;
|
||||
const Node& node = *node_ptr;
|
||||
|
||||
if (auto* n = node.get_if<ConstIntNode>()) {
|
||||
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<ConstIntNode>()) {
|
||||
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{});
|
||||
}
|
||||
};
|
||||
|
||||
IRGenerator::IRGenerator() : pImpl(std::make_unique<Impl>()) {}
|
||||
IRGenerator::~IRGenerator() = default;
|
||||
|
||||
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) {
|
||||
return pImpl->add_string(str);
|
||||
return pImpl->add_string(str);
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> IRGenerator::generate(const std::shared_ptr<Node>& ast) {
|
||||
return pImpl->convert(ast);
|
||||
return pImpl->convert(ast);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
|
|
|
|||
|
|
@ -2,44 +2,44 @@
|
|||
#define NIX_IRC_IR_GEN_H
|
||||
|
||||
#include "types.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
class IRGenerator {
|
||||
public:
|
||||
IRGenerator();
|
||||
~IRGenerator();
|
||||
|
||||
void set_string_table(const std::unordered_map<std::string, uint32_t>& table);
|
||||
uint32_t add_string(const std::string& str);
|
||||
|
||||
std::shared_ptr<Node> generate(const std::shared_ptr<Node>& ast);
|
||||
|
||||
IRGenerator();
|
||||
~IRGenerator();
|
||||
|
||||
void set_string_table(const std::unordered_map<std::string, uint32_t>& table);
|
||||
uint32_t add_string(const std::string& str);
|
||||
|
||||
std::shared_ptr<Node> generate(const std::shared_ptr<Node>& ast);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
};
|
||||
|
||||
class NameResolver {
|
||||
public:
|
||||
NameResolver();
|
||||
~NameResolver();
|
||||
|
||||
void enter_scope();
|
||||
void exit_scope();
|
||||
void bind(const std::string& name);
|
||||
uint32_t resolve(const std::string& name);
|
||||
bool is_bound(const std::string& name) const;
|
||||
|
||||
NameResolver();
|
||||
~NameResolver();
|
||||
|
||||
void enter_scope();
|
||||
void exit_scope();
|
||||
void bind(const std::string& name);
|
||||
uint32_t resolve(const std::string& name);
|
||||
bool is_bound(const std::string& name) const;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
|
||||
#endif
|
||||
248
src/irc/main.cpp
248
src/irc/main.cpp
|
|
@ -1,150 +1,150 @@
|
|||
#include <iostream>
|
||||
#include "ir_gen.h"
|
||||
#include "parser.h"
|
||||
#include "resolver.h"
|
||||
#include "ir_gen.h"
|
||||
#include "serializer.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
void print_usage(const char* prog) {
|
||||
std::cout << "Usage: " << prog << " [options] <input.nix> [output.nixir]\n"
|
||||
<< "\nOptions:\n"
|
||||
<< " -I <path> Add search path for imports\n"
|
||||
<< " --no-imports Disable import resolution\n"
|
||||
<< " --help Show this help\n";
|
||||
std::cout << "Usage: " << prog << " [options] <input.nix> [output.nixir]\n"
|
||||
<< "\nOptions:\n"
|
||||
<< " -I <path> Add search path for imports\n"
|
||||
<< " --no-imports Disable import resolution\n"
|
||||
<< " --help Show this help\n";
|
||||
}
|
||||
|
||||
int run_compile(int argc, char** argv) {
|
||||
std::string input_file;
|
||||
std::string output_file;
|
||||
std::vector<std::string> search_paths;
|
||||
bool resolve_imports = true;
|
||||
|
||||
int i = 1;
|
||||
while (i < argc) {
|
||||
std::string arg = argv[i];
|
||||
if (arg == "-I") {
|
||||
if (i + 1 >= argc) {
|
||||
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;
|
||||
}
|
||||
|
||||
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";
|
||||
auto ast = parser.parse_file(input_file);
|
||||
|
||||
if (!ast) {
|
||||
std::cerr << "Error: Failed to parse input\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Resolving imports...\n";
|
||||
|
||||
IRGenerator ir_gen;
|
||||
|
||||
std::cout << "Generating IR...\n";
|
||||
auto ir = ir_gen.generate(ast);
|
||||
std::string input_file;
|
||||
std::string output_file;
|
||||
std::vector<std::string> search_paths;
|
||||
bool resolve_imports = true;
|
||||
|
||||
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";
|
||||
int i = 1;
|
||||
while (i < argc) {
|
||||
std::string arg = argv[i];
|
||||
if (arg == "-I") {
|
||||
if (i + 1 >= argc) {
|
||||
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;
|
||||
}
|
||||
|
||||
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";
|
||||
auto ast = parser.parse_file(input_file);
|
||||
|
||||
if (!ast) {
|
||||
std::cerr << "Error: Failed to parse input\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Resolving imports...\n";
|
||||
|
||||
IRGenerator ir_gen;
|
||||
|
||||
std::cout << "Generating IR...\n";
|
||||
auto ir = ir_gen.generate(ast);
|
||||
|
||||
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) {
|
||||
std::cout << "Usage: " << prog << " decompile <input.nixir>\n";
|
||||
std::cout << "Usage: " << prog << " decompile <input.nixir>\n";
|
||||
}
|
||||
|
||||
int run_decompile(int argc, char** argv) {
|
||||
if (argc < 3) {
|
||||
print_decompile_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string input_file = argv[2];
|
||||
|
||||
try {
|
||||
Deserializer deserializer;
|
||||
auto module = deserializer.deserialize(input_file);
|
||||
|
||||
std::cout << "IR Version: " << module.version << "\n";
|
||||
std::cout << "Sources: " << module.sources.size() << "\n";
|
||||
std::cout << "Imports: " << module.imports.size() << "\n";
|
||||
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
if (argc < 3) {
|
||||
print_decompile_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string input_file = argv[2];
|
||||
|
||||
try {
|
||||
Deserializer deserializer;
|
||||
auto module = deserializer.deserialize(input_file);
|
||||
|
||||
std::cout << "IR Version: " << module.version << "\n";
|
||||
std::cout << "Sources: " << module.sources.size() << "\n";
|
||||
std::cout << "Imports: " << module.imports.size() << "\n";
|
||||
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
nix_irc::print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string cmd = argv[1];
|
||||
|
||||
if (cmd == "compile" || cmd == "c") {
|
||||
return nix_irc::run_compile(argc - 1, argv + 1);
|
||||
} else if (cmd == "decompile" || cmd == "d") {
|
||||
return nix_irc::run_decompile(argc, argv);
|
||||
} else if (cmd == "help" || cmd == "--help" || cmd == "-h") {
|
||||
nix_irc::print_usage(argv[0]);
|
||||
return 0;
|
||||
} else {
|
||||
return nix_irc::run_compile(argc, argv);
|
||||
}
|
||||
if (argc < 2) {
|
||||
nix_irc::print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string cmd = argv[1];
|
||||
|
||||
if (cmd == "compile" || cmd == "c") {
|
||||
return nix_irc::run_compile(argc - 1, argv + 1);
|
||||
} else if (cmd == "decompile" || cmd == "d") {
|
||||
return nix_irc::run_decompile(argc, argv);
|
||||
} else if (cmd == "help" || cmd == "--help" || cmd == "-h") {
|
||||
nix_irc::print_usage(argv[0]);
|
||||
return 0;
|
||||
} else {
|
||||
return nix_irc::run_compile(argc, argv);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1798
src/irc/parser.cpp
1798
src/irc/parser.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -2,24 +2,24 @@
|
|||
#define NIX_IRC_PARSER_H
|
||||
|
||||
#include "types.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser();
|
||||
~Parser();
|
||||
|
||||
std::shared_ptr<Node> parse(const std::string& source, const std::string& path = "<stdin>");
|
||||
std::shared_ptr<Node> parse_file(const std::string& path);
|
||||
|
||||
Parser();
|
||||
~Parser();
|
||||
|
||||
std::shared_ptr<Node> parse(const std::string& source, const std::string& path = "<stdin>");
|
||||
std::shared_ptr<Node> parse_file(const std::string& path);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
|
||||
#endif
|
||||
|
|
@ -1,111 +1,114 @@
|
|||
#include "resolver.h"
|
||||
#include "parser.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
struct Resolver::Impl {
|
||||
ResolverConfig config;
|
||||
std::vector<std::pair<std::string, std::string>> resolved_imports;
|
||||
std::unordered_set<std::string> visited;
|
||||
Parser parser;
|
||||
|
||||
Impl(const ResolverConfig& cfg) : config(cfg) {}
|
||||
|
||||
std::string resolve_path(const std::string& path, const std::string& from_file) {
|
||||
fs::path p(path);
|
||||
|
||||
if (p.is_absolute()) {
|
||||
if (fs::exists(p)) return path;
|
||||
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 "";
|
||||
ResolverConfig config;
|
||||
std::vector<std::pair<std::string, std::string>> resolved_imports;
|
||||
std::unordered_set<std::string> visited;
|
||||
Parser parser;
|
||||
|
||||
Impl(const ResolverConfig& cfg) : config(cfg) {}
|
||||
|
||||
std::string resolve_path(const std::string& path, const std::string& from_file) {
|
||||
fs::path p(path);
|
||||
|
||||
if (p.is_absolute()) {
|
||||
if (fs::exists(p))
|
||||
return path;
|
||||
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};
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
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() = default;
|
||||
|
||||
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) {
|
||||
pImpl->config.search_paths = paths;
|
||||
pImpl->config.search_paths = paths;
|
||||
}
|
||||
|
||||
ImportResult Resolver::resolve_import(const std::string& path, const std::string& from_file) {
|
||||
auto result = pImpl->do_resolve(path, from_file);
|
||||
if (result.success && result.ast) {
|
||||
pImpl->resolved_imports.push_back({path, result.path});
|
||||
}
|
||||
return result;
|
||||
auto result = pImpl->do_resolve(path, from_file);
|
||||
if (result.success && result.ast) {
|
||||
pImpl->resolved_imports.push_back({path, result.path});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ImportResult Resolver::resolve_import(const Node& import_node, const std::string& from_file) {
|
||||
const ConstPathNode* path_node = import_node.get_if<ConstPathNode>();
|
||||
if (!path_node) {
|
||||
return {false, "", "Dynamic import not supported", nullptr};
|
||||
}
|
||||
return resolve_import(path_node->value, from_file);
|
||||
const ConstPathNode* path_node = import_node.get_if<ConstPathNode>();
|
||||
if (!path_node) {
|
||||
return {false, "", "Dynamic import not supported", nullptr};
|
||||
}
|
||||
return resolve_import(path_node->value, from_file);
|
||||
}
|
||||
|
||||
std::vector<std::string> Resolver::get_resolved_files() const {
|
||||
std::vector<std::string> files;
|
||||
for (const auto& [orig, resolved] : pImpl->resolved_imports) {
|
||||
(void)orig;
|
||||
files.push_back(resolved);
|
||||
}
|
||||
return files;
|
||||
std::vector<std::string> files;
|
||||
for (const auto& [orig, resolved] : pImpl->resolved_imports) {
|
||||
(void) orig;
|
||||
files.push_back(resolved);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
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) {
|
||||
return node.holds<ConstPathNode>();
|
||||
return node.holds<ConstPathNode>();
|
||||
}
|
||||
|
||||
std::string normalize_path(const std::string& path) {
|
||||
fs::path p(path);
|
||||
return fs::absolute(p).string();
|
||||
fs::path p(path);
|
||||
return fs::absolute(p).string();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
|
|
|
|||
|
|
@ -2,47 +2,47 @@
|
|||
#define NIX_IRC_RESOLVER_H
|
||||
|
||||
#include "types.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
struct ImportResult {
|
||||
bool success;
|
||||
std::string path;
|
||||
std::string error;
|
||||
std::shared_ptr<Node> ast;
|
||||
bool success;
|
||||
std::string path;
|
||||
std::string error;
|
||||
std::shared_ptr<Node> ast;
|
||||
};
|
||||
|
||||
struct ResolverConfig {
|
||||
std::vector<std::string> search_paths;
|
||||
bool resolve_imports = true;
|
||||
std::vector<std::string> search_paths;
|
||||
bool resolve_imports = true;
|
||||
};
|
||||
|
||||
class Resolver {
|
||||
public:
|
||||
Resolver(const ResolverConfig& config = {});
|
||||
~Resolver();
|
||||
|
||||
void add_search_path(const std::string& path);
|
||||
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 Node& import_node, const std::string& from_file);
|
||||
|
||||
std::vector<std::string> get_resolved_files() const;
|
||||
std::vector<std::pair<std::string, std::string>> get_imports() const;
|
||||
|
||||
Resolver(const ResolverConfig& config = {});
|
||||
~Resolver();
|
||||
|
||||
void add_search_path(const std::string& path);
|
||||
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 Node& import_node, const std::string& from_file);
|
||||
|
||||
std::vector<std::string> get_resolved_files() const;
|
||||
std::vector<std::pair<std::string, std::string>> get_imports() const;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
};
|
||||
|
||||
bool is_static_import(const Node& node);
|
||||
std::string normalize_path(const std::string& path);
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
|
||||
#endif
|
||||
|
|
@ -1,392 +1,428 @@
|
|||
#include "serializer.h"
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
struct Serializer::Impl {
|
||||
std::vector<uint8_t> buffer;
|
||||
std::vector<uint8_t> buffer;
|
||||
|
||||
void write_u32(uint32_t val) {
|
||||
buffer.push_back((val >> 0) & 0xFF);
|
||||
buffer.push_back((val >> 8) & 0xFF);
|
||||
buffer.push_back((val >> 16) & 0xFF);
|
||||
buffer.push_back((val >> 24) & 0xFF);
|
||||
void write_u32(uint32_t val) {
|
||||
buffer.push_back((val >> 0) & 0xFF);
|
||||
buffer.push_back((val >> 8) & 0xFF);
|
||||
buffer.push_back((val >> 16) & 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_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_string(const std::string& str) {
|
||||
write_u32(str.size());
|
||||
buffer.insert(buffer.end(), str.begin(), str.end());
|
||||
}
|
||||
|
||||
NodeType get_node_type(const Node& node) {
|
||||
if (node.holds<ConstIntNode>()) return NodeType::CONST_INT;
|
||||
if (node.holds<ConstStringNode>()) return NodeType::CONST_STRING;
|
||||
if (node.holds<ConstPathNode>()) return NodeType::CONST_PATH;
|
||||
if (node.holds<ConstBoolNode>()) return NodeType::CONST_BOOL;
|
||||
if (node.holds<ConstNullNode>()) return NodeType::CONST_NULL;
|
||||
if (node.holds<VarNode>()) return NodeType::VAR;
|
||||
if (node.holds<LambdaNode>()) return NodeType::LAMBDA;
|
||||
if (node.holds<AppNode>()) return NodeType::APP;
|
||||
if (node.holds<BinaryOpNode>()) return NodeType::BINARY_OP;
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t get_node_line(const Node& node) {
|
||||
return std::visit([](const auto& n) { return n.line; }, node.data);
|
||||
}
|
||||
|
||||
void write_node(const Node& node) {
|
||||
write_u8(static_cast<uint8_t>(get_node_type(node)));
|
||||
write_u32(get_node_line(node));
|
||||
|
||||
if (auto* n = node.get_if<ConstIntNode>()) {
|
||||
write_u64(static_cast<uint64_t>(n->value));
|
||||
} else if (auto* n = node.get_if<ConstStringNode>()) {
|
||||
write_string(n->value);
|
||||
} else if (auto* n = node.get_if<ConstPathNode>()) {
|
||||
write_string(n->value);
|
||||
} else if (auto* n = node.get_if<ConstBoolNode>()) {
|
||||
write_u8(n->value ? 1 : 0);
|
||||
} else if (auto* n = node.get_if<ConstNullNode>()) {
|
||||
// No data for null
|
||||
} else if (auto* n = node.get_if<VarNode>()) {
|
||||
write_u32(n->index);
|
||||
} else if (auto* n = node.get_if<LambdaNode>()) {
|
||||
write_u32(n->arity);
|
||||
if (n->body) write_node(*n->body);
|
||||
} else if (auto* n = node.get_if<AppNode>()) {
|
||||
if (n->func) write_node(*n->func);
|
||||
if (n->arg) write_node(*n->arg);
|
||||
} else if (auto* n = node.get_if<BinaryOpNode>()) {
|
||||
write_u8(static_cast<uint8_t>(n->op));
|
||||
if (n->left) write_node(*n->left);
|
||||
if (n->right) write_node(*n->right);
|
||||
} else if (auto* n = node.get_if<UnaryOpNode>()) {
|
||||
write_u8(static_cast<uint8_t>(n->op));
|
||||
if (n->operand) write_node(*n->operand);
|
||||
} else if (auto* n = node.get_if<AttrsetNode>()) {
|
||||
write_u8(n->recursive ? 1 : 0);
|
||||
write_u32(n->attrs.size());
|
||||
for (const auto& [key, val] : n->attrs) {
|
||||
write_string(key);
|
||||
if (val) write_node(*val);
|
||||
}
|
||||
} else if (auto* n = node.get_if<SelectNode>()) {
|
||||
if (n->expr) write_node(*n->expr);
|
||||
if (n->attr) write_node(*n->attr);
|
||||
if (n->default_expr && *n->default_expr) {
|
||||
write_u8(1);
|
||||
write_node(**n->default_expr);
|
||||
} else {
|
||||
write_u8(0);
|
||||
}
|
||||
} else if (auto* n = node.get_if<HasAttrNode>()) {
|
||||
if (n->expr) write_node(*n->expr);
|
||||
if (n->attr) write_node(*n->attr);
|
||||
} else if (auto* n = node.get_if<WithNode>()) {
|
||||
if (n->attrs) write_node(*n->attrs);
|
||||
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);
|
||||
}
|
||||
void write_u8(uint8_t val) { buffer.push_back(val); }
|
||||
|
||||
void write_string(const std::string& str) {
|
||||
write_u32(str.size());
|
||||
buffer.insert(buffer.end(), str.begin(), str.end());
|
||||
}
|
||||
|
||||
NodeType get_node_type(const Node& node) {
|
||||
if (node.holds<ConstIntNode>())
|
||||
return NodeType::CONST_INT;
|
||||
if (node.holds<ConstStringNode>())
|
||||
return NodeType::CONST_STRING;
|
||||
if (node.holds<ConstPathNode>())
|
||||
return NodeType::CONST_PATH;
|
||||
if (node.holds<ConstBoolNode>())
|
||||
return NodeType::CONST_BOOL;
|
||||
if (node.holds<ConstNullNode>())
|
||||
return NodeType::CONST_NULL;
|
||||
if (node.holds<VarNode>())
|
||||
return NodeType::VAR;
|
||||
if (node.holds<LambdaNode>())
|
||||
return NodeType::LAMBDA;
|
||||
if (node.holds<AppNode>())
|
||||
return NodeType::APP;
|
||||
if (node.holds<BinaryOpNode>())
|
||||
return NodeType::BINARY_OP;
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t get_node_line(const Node& node) {
|
||||
return std::visit([](const auto& n) { return n.line; }, node.data);
|
||||
}
|
||||
|
||||
void write_node(const Node& node) {
|
||||
write_u8(static_cast<uint8_t>(get_node_type(node)));
|
||||
write_u32(get_node_line(node));
|
||||
|
||||
if (auto* n = node.get_if<ConstIntNode>()) {
|
||||
write_u64(static_cast<uint64_t>(n->value));
|
||||
} else if (auto* n = node.get_if<ConstStringNode>()) {
|
||||
write_string(n->value);
|
||||
} else if (auto* n = node.get_if<ConstPathNode>()) {
|
||||
write_string(n->value);
|
||||
} else if (auto* n = node.get_if<ConstBoolNode>()) {
|
||||
write_u8(n->value ? 1 : 0);
|
||||
} else if (auto* n = node.get_if<ConstNullNode>()) {
|
||||
// No data for null
|
||||
} else if (auto* n = node.get_if<VarNode>()) {
|
||||
write_u32(n->index);
|
||||
} else if (auto* n = node.get_if<LambdaNode>()) {
|
||||
write_u32(n->arity);
|
||||
if (n->body)
|
||||
write_node(*n->body);
|
||||
} else if (auto* n = node.get_if<AppNode>()) {
|
||||
if (n->func)
|
||||
write_node(*n->func);
|
||||
if (n->arg)
|
||||
write_node(*n->arg);
|
||||
} else if (auto* n = node.get_if<BinaryOpNode>()) {
|
||||
write_u8(static_cast<uint8_t>(n->op));
|
||||
if (n->left)
|
||||
write_node(*n->left);
|
||||
if (n->right)
|
||||
write_node(*n->right);
|
||||
} else if (auto* n = node.get_if<UnaryOpNode>()) {
|
||||
write_u8(static_cast<uint8_t>(n->op));
|
||||
if (n->operand)
|
||||
write_node(*n->operand);
|
||||
} else if (auto* n = node.get_if<AttrsetNode>()) {
|
||||
write_u8(n->recursive ? 1 : 0);
|
||||
write_u32(n->attrs.size());
|
||||
for (const auto& [key, val] : n->attrs) {
|
||||
write_string(key);
|
||||
if (val)
|
||||
write_node(*val);
|
||||
}
|
||||
} else if (auto* n = node.get_if<SelectNode>()) {
|
||||
if (n->expr)
|
||||
write_node(*n->expr);
|
||||
if (n->attr)
|
||||
write_node(*n->attr);
|
||||
if (n->default_expr && *n->default_expr) {
|
||||
write_u8(1);
|
||||
write_node(**n->default_expr);
|
||||
} else {
|
||||
write_u8(0);
|
||||
}
|
||||
} else if (auto* n = node.get_if<HasAttrNode>()) {
|
||||
if (n->expr)
|
||||
write_node(*n->expr);
|
||||
if (n->attr)
|
||||
write_node(*n->attr);
|
||||
} else if (auto* n = node.get_if<WithNode>()) {
|
||||
if (n->attrs)
|
||||
write_node(*n->attrs);
|
||||
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() = default;
|
||||
|
||||
void Serializer::serialize(const IRModule& module, const std::string& path) {
|
||||
auto bytes = serialize_to_bytes(module);
|
||||
std::ofstream out(path, std::ios::binary);
|
||||
out.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||
auto bytes = serialize_to_bytes(module);
|
||||
std::ofstream out(path, std::ios::binary);
|
||||
out.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||
}
|
||||
|
||||
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_VERSION);
|
||||
pImpl->write_u32(IR_MAGIC);
|
||||
pImpl->write_u32(IR_VERSION);
|
||||
|
||||
pImpl->write_u32(module.sources.size());
|
||||
for (const auto& src : module.sources) {
|
||||
pImpl->write_string(src.path);
|
||||
pImpl->write_string(src.content);
|
||||
}
|
||||
pImpl->write_u32(module.sources.size());
|
||||
for (const auto& src : module.sources) {
|
||||
pImpl->write_string(src.path);
|
||||
pImpl->write_string(src.content);
|
||||
}
|
||||
|
||||
pImpl->write_u32(module.imports.size());
|
||||
for (const auto& [from, to] : module.imports) {
|
||||
pImpl->write_string(from);
|
||||
pImpl->write_string(to);
|
||||
}
|
||||
pImpl->write_u32(module.imports.size());
|
||||
for (const auto& [from, to] : module.imports) {
|
||||
pImpl->write_string(from);
|
||||
pImpl->write_string(to);
|
||||
}
|
||||
|
||||
pImpl->write_u32(module.string_table.size());
|
||||
for (const auto& [str, id] : module.string_table) {
|
||||
pImpl->write_string(str);
|
||||
pImpl->write_u32(id);
|
||||
}
|
||||
pImpl->write_u32(module.string_table.size());
|
||||
for (const auto& [str, id] : module.string_table) {
|
||||
pImpl->write_string(str);
|
||||
pImpl->write_u32(id);
|
||||
}
|
||||
|
||||
if (module.entry && module.entry != nullptr) {
|
||||
pImpl->write_u8(1);
|
||||
pImpl->write_node(*module.entry);
|
||||
} else {
|
||||
pImpl->write_u8(0);
|
||||
}
|
||||
if (module.entry && module.entry != nullptr) {
|
||||
pImpl->write_u8(1);
|
||||
pImpl->write_node(*module.entry);
|
||||
} else {
|
||||
pImpl->write_u8(0);
|
||||
}
|
||||
|
||||
return pImpl->buffer;
|
||||
return pImpl->buffer;
|
||||
}
|
||||
|
||||
struct Deserializer::Impl {
|
||||
std::vector<uint8_t> buffer;
|
||||
size_t pos = 0;
|
||||
std::vector<uint8_t> buffer;
|
||||
size_t pos = 0;
|
||||
|
||||
uint32_t read_u32() {
|
||||
uint32_t val = 0;
|
||||
val |= buffer[pos + 0];
|
||||
val |= (uint32_t)buffer[pos + 1] << 8;
|
||||
val |= (uint32_t)buffer[pos + 2] << 16;
|
||||
val |= (uint32_t)buffer[pos + 3] << 24;
|
||||
pos += 4;
|
||||
return val;
|
||||
uint32_t read_u32() {
|
||||
uint32_t val = 0;
|
||||
val |= buffer[pos + 0];
|
||||
val |= (uint32_t) buffer[pos + 1] << 8;
|
||||
val |= (uint32_t) buffer[pos + 2] << 16;
|
||||
val |= (uint32_t) buffer[pos + 3] << 24;
|
||||
pos += 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
uint64_t read_u64() {
|
||||
uint64_t val = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
val |= (uint64_t) buffer[pos + i] << (i * 8);
|
||||
}
|
||||
pos += 8;
|
||||
return val;
|
||||
}
|
||||
|
||||
uint64_t read_u64() {
|
||||
uint64_t val = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
val |= (uint64_t)buffer[pos + i] << (i * 8);
|
||||
}
|
||||
pos += 8;
|
||||
return val;
|
||||
uint8_t read_u8() { return buffer[pos++]; }
|
||||
|
||||
std::string read_string() {
|
||||
uint32_t len = read_u32();
|
||||
std::string str(reinterpret_cast<const char*>(&buffer[pos]), len);
|
||||
pos += len;
|
||||
return str;
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> read_node() {
|
||||
NodeType type = static_cast<NodeType>(read_u8());
|
||||
uint32_t line = read_u32();
|
||||
|
||||
switch (type) {
|
||||
case NodeType::CONST_INT: {
|
||||
int64_t val = static_cast<int64_t>(read_u64());
|
||||
return std::make_shared<Node>(ConstIntNode(val, line));
|
||||
}
|
||||
|
||||
uint8_t read_u8() {
|
||||
return buffer[pos++];
|
||||
case NodeType::CONST_STRING: {
|
||||
std::string val = read_string();
|
||||
return std::make_shared<Node>(ConstStringNode(val, line));
|
||||
}
|
||||
|
||||
std::string read_string() {
|
||||
uint32_t len = read_u32();
|
||||
std::string str(reinterpret_cast<const char*>(&buffer[pos]), len);
|
||||
pos += len;
|
||||
return str;
|
||||
case NodeType::CONST_PATH: {
|
||||
std::string val = read_string();
|
||||
return std::make_shared<Node>(ConstPathNode(val, line));
|
||||
}
|
||||
|
||||
std::shared_ptr<Node> read_node() {
|
||||
NodeType type = static_cast<NodeType>(read_u8());
|
||||
uint32_t line = read_u32();
|
||||
|
||||
switch (type) {
|
||||
case NodeType::CONST_INT: {
|
||||
int64_t val = static_cast<int64_t>(read_u64());
|
||||
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_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() = default;
|
||||
|
||||
IRModule Deserializer::deserialize(const std::string& path) {
|
||||
std::ifstream in(path, std::ios::binary | std::ios::ate);
|
||||
size_t size = in.tellg();
|
||||
in.seekg(0);
|
||||
pImpl->buffer.resize(size);
|
||||
in.read(reinterpret_cast<char*>(pImpl->buffer.data()), size);
|
||||
pImpl->pos = 0;
|
||||
return deserialize(pImpl->buffer);
|
||||
std::ifstream in(path, std::ios::binary | std::ios::ate);
|
||||
size_t size = in.tellg();
|
||||
in.seekg(0);
|
||||
pImpl->buffer.resize(size);
|
||||
in.read(reinterpret_cast<char*>(pImpl->buffer.data()), size);
|
||||
pImpl->pos = 0;
|
||||
return deserialize(pImpl->buffer);
|
||||
}
|
||||
|
||||
IRModule Deserializer::deserialize(const std::vector<uint8_t>& data) {
|
||||
pImpl->buffer = data;
|
||||
pImpl->pos = 0;
|
||||
pImpl->buffer = data;
|
||||
pImpl->pos = 0;
|
||||
|
||||
IRModule module;
|
||||
IRModule module;
|
||||
|
||||
uint32_t magic = pImpl->read_u32();
|
||||
if (magic != IR_MAGIC) {
|
||||
throw std::runtime_error("Invalid IR file");
|
||||
}
|
||||
uint32_t magic = pImpl->read_u32();
|
||||
if (magic != IR_MAGIC) {
|
||||
throw std::runtime_error("Invalid IR file");
|
||||
}
|
||||
|
||||
uint32_t version = pImpl->read_u32();
|
||||
if (version != IR_VERSION) {
|
||||
throw std::runtime_error("Unsupported IR version");
|
||||
}
|
||||
uint32_t version = pImpl->read_u32();
|
||||
if (version != IR_VERSION) {
|
||||
throw std::runtime_error("Unsupported IR version");
|
||||
}
|
||||
|
||||
uint32_t num_sources = pImpl->read_u32();
|
||||
for (uint32_t i = 0; i < num_sources; i++) {
|
||||
SourceFile src;
|
||||
src.path = pImpl->read_string();
|
||||
src.content = pImpl->read_string();
|
||||
module.sources.push_back(src);
|
||||
}
|
||||
uint32_t num_sources = pImpl->read_u32();
|
||||
for (uint32_t i = 0; i < num_sources; i++) {
|
||||
SourceFile src;
|
||||
src.path = pImpl->read_string();
|
||||
src.content = pImpl->read_string();
|
||||
module.sources.push_back(src);
|
||||
}
|
||||
|
||||
uint32_t num_imports = pImpl->read_u32();
|
||||
for (uint32_t i = 0; i < num_imports; i++) {
|
||||
module.imports.push_back({pImpl->read_string(), pImpl->read_string()});
|
||||
}
|
||||
uint32_t num_imports = pImpl->read_u32();
|
||||
for (uint32_t i = 0; i < num_imports; i++) {
|
||||
module.imports.push_back({pImpl->read_string(), pImpl->read_string()});
|
||||
}
|
||||
|
||||
uint32_t num_strings = pImpl->read_u32();
|
||||
for (uint32_t i = 0; i < num_strings; i++) {
|
||||
std::string str = pImpl->read_string();
|
||||
uint32_t id = pImpl->read_u32();
|
||||
module.string_table[str] = id;
|
||||
}
|
||||
uint32_t num_strings = pImpl->read_u32();
|
||||
for (uint32_t i = 0; i < num_strings; i++) {
|
||||
std::string str = pImpl->read_string();
|
||||
uint32_t id = pImpl->read_u32();
|
||||
module.string_table[str] = id;
|
||||
}
|
||||
|
||||
if (pImpl->read_u8()) {
|
||||
module.entry = pImpl->read_node();
|
||||
}
|
||||
if (pImpl->read_u8()) {
|
||||
module.entry = pImpl->read_node();
|
||||
}
|
||||
|
||||
return module;
|
||||
return module;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
|
|
|
|||
|
|
@ -2,38 +2,38 @@
|
|||
#define NIX_IRC_SERIALIZER_H
|
||||
|
||||
#include "types.h"
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
class Serializer {
|
||||
public:
|
||||
Serializer();
|
||||
~Serializer();
|
||||
|
||||
void serialize(const IRModule& module, const std::string& path);
|
||||
std::vector<uint8_t> serialize_to_bytes(const IRModule& module);
|
||||
|
||||
Serializer();
|
||||
~Serializer();
|
||||
|
||||
void serialize(const IRModule& module, const std::string& path);
|
||||
std::vector<uint8_t> serialize_to_bytes(const IRModule& module);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
};
|
||||
|
||||
class Deserializer {
|
||||
public:
|
||||
Deserializer();
|
||||
~Deserializer();
|
||||
|
||||
IRModule deserialize(const std::string& path);
|
||||
IRModule deserialize(const std::vector<uint8_t>& data);
|
||||
|
||||
Deserializer();
|
||||
~Deserializer();
|
||||
|
||||
IRModule deserialize(const std::string& path);
|
||||
IRModule deserialize(const std::vector<uint8_t>& data);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> pImpl;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
|
||||
#endif
|
||||
296
src/irc/types.h
296
src/irc/types.h
|
|
@ -2,14 +2,14 @@
|
|||
#define NIX_IRC_TYPES_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace nix_irc {
|
||||
|
||||
|
|
@ -17,219 +17,191 @@ constexpr uint32_t IR_MAGIC = 0x4E495258;
|
|||
constexpr uint32_t IR_VERSION = 2;
|
||||
|
||||
enum class NodeType : uint8_t {
|
||||
CONST_INT = 0x01,
|
||||
CONST_STRING = 0x02,
|
||||
CONST_PATH = 0x03,
|
||||
CONST_BOOL = 0x04,
|
||||
CONST_NULL = 0x05,
|
||||
VAR = 0x10,
|
||||
LAMBDA = 0x20,
|
||||
APP = 0x21,
|
||||
BINARY_OP = 0x22,
|
||||
UNARY_OP = 0x23,
|
||||
ATTRSET = 0x30,
|
||||
SELECT = 0x31,
|
||||
HAS_ATTR = 0x34,
|
||||
WITH = 0x32,
|
||||
IF = 0x40,
|
||||
LET = 0x50,
|
||||
LETREC = 0x51,
|
||||
ASSERT = 0x52,
|
||||
THUNK = 0x60,
|
||||
FORCE = 0x61,
|
||||
ERROR = 0xFF
|
||||
CONST_INT = 0x01,
|
||||
CONST_STRING = 0x02,
|
||||
CONST_PATH = 0x03,
|
||||
CONST_BOOL = 0x04,
|
||||
CONST_NULL = 0x05,
|
||||
VAR = 0x10,
|
||||
LAMBDA = 0x20,
|
||||
APP = 0x21,
|
||||
BINARY_OP = 0x22,
|
||||
UNARY_OP = 0x23,
|
||||
ATTRSET = 0x30,
|
||||
SELECT = 0x31,
|
||||
HAS_ATTR = 0x34,
|
||||
WITH = 0x32,
|
||||
IF = 0x40,
|
||||
LET = 0x50,
|
||||
LETREC = 0x51,
|
||||
ASSERT = 0x52,
|
||||
THUNK = 0x60,
|
||||
FORCE = 0x61,
|
||||
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
|
||||
class Node;
|
||||
|
||||
struct ConstIntNode {
|
||||
int64_t value;
|
||||
uint32_t line = 0;
|
||||
ConstIntNode(int64_t v = 0, uint32_t l = 0) : value(v), line(l) {}
|
||||
int64_t value;
|
||||
uint32_t line = 0;
|
||||
ConstIntNode(int64_t v = 0, uint32_t l = 0) : value(v), line(l) {}
|
||||
};
|
||||
|
||||
struct ConstStringNode {
|
||||
std::string value;
|
||||
uint32_t line = 0;
|
||||
ConstStringNode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}
|
||||
std::string value;
|
||||
uint32_t line = 0;
|
||||
ConstStringNode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}
|
||||
};
|
||||
|
||||
struct ConstPathNode {
|
||||
std::string value;
|
||||
uint32_t line = 0;
|
||||
ConstPathNode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}
|
||||
std::string value;
|
||||
uint32_t line = 0;
|
||||
ConstPathNode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}
|
||||
};
|
||||
|
||||
struct ConstBoolNode {
|
||||
bool value;
|
||||
uint32_t line = 0;
|
||||
ConstBoolNode(bool v = false, uint32_t l = 0) : value(v), line(l) {}
|
||||
bool value;
|
||||
uint32_t line = 0;
|
||||
ConstBoolNode(bool v = false, uint32_t l = 0) : value(v), line(l) {}
|
||||
};
|
||||
|
||||
struct ConstNullNode {
|
||||
uint32_t line = 0;
|
||||
ConstNullNode(uint32_t l = 0) : line(l) {}
|
||||
uint32_t line = 0;
|
||||
ConstNullNode(uint32_t l = 0) : line(l) {}
|
||||
};
|
||||
|
||||
struct VarNode {
|
||||
uint32_t index = 0;
|
||||
std::optional<std::string> name;
|
||||
uint32_t line = 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) {}
|
||||
uint32_t index = 0;
|
||||
std::optional<std::string> name;
|
||||
uint32_t line = 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) {}
|
||||
};
|
||||
|
||||
struct LambdaNode {
|
||||
uint32_t arity = 1;
|
||||
std::shared_ptr<Node> body;
|
||||
std::optional<std::string> param_name;
|
||||
bool strict_pattern = true;
|
||||
uint32_t line = 0;
|
||||
LambdaNode(uint32_t a, std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
uint32_t arity = 1;
|
||||
std::shared_ptr<Node> body;
|
||||
std::optional<std::string> param_name;
|
||||
bool strict_pattern = true;
|
||||
uint32_t line = 0;
|
||||
LambdaNode(uint32_t a, std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct AppNode {
|
||||
std::shared_ptr<Node> func;
|
||||
std::shared_ptr<Node> arg;
|
||||
uint32_t line = 0;
|
||||
AppNode(std::shared_ptr<Node> f, std::shared_ptr<Node> a, uint32_t l = 0);
|
||||
std::shared_ptr<Node> func;
|
||||
std::shared_ptr<Node> arg;
|
||||
uint32_t line = 0;
|
||||
AppNode(std::shared_ptr<Node> f, std::shared_ptr<Node> a, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct BinaryOpNode {
|
||||
BinaryOp op;
|
||||
std::shared_ptr<Node> left;
|
||||
std::shared_ptr<Node> right;
|
||||
uint32_t line = 0;
|
||||
BinaryOpNode(BinaryOp o, std::shared_ptr<Node> l, std::shared_ptr<Node> r, uint32_t ln = 0);
|
||||
BinaryOp op;
|
||||
std::shared_ptr<Node> left;
|
||||
std::shared_ptr<Node> right;
|
||||
uint32_t line = 0;
|
||||
BinaryOpNode(BinaryOp o, std::shared_ptr<Node> l, std::shared_ptr<Node> r, uint32_t ln = 0);
|
||||
};
|
||||
|
||||
struct UnaryOpNode {
|
||||
UnaryOp op;
|
||||
std::shared_ptr<Node> operand;
|
||||
uint32_t line = 0;
|
||||
UnaryOpNode(UnaryOp o, std::shared_ptr<Node> operand, uint32_t l = 0);
|
||||
UnaryOp op;
|
||||
std::shared_ptr<Node> operand;
|
||||
uint32_t line = 0;
|
||||
UnaryOpNode(UnaryOp o, std::shared_ptr<Node> operand, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct AttrsetNode {
|
||||
std::vector<std::pair<std::string, std::shared_ptr<Node>>> attrs;
|
||||
bool recursive = false;
|
||||
uint32_t line = 0;
|
||||
AttrsetNode(bool rec = false, uint32_t l = 0) : recursive(rec), line(l) {}
|
||||
std::vector<std::pair<std::string, std::shared_ptr<Node>>> attrs;
|
||||
bool recursive = false;
|
||||
uint32_t line = 0;
|
||||
AttrsetNode(bool rec = false, uint32_t l = 0) : recursive(rec), line(l) {}
|
||||
};
|
||||
|
||||
struct SelectNode {
|
||||
std::shared_ptr<Node> expr;
|
||||
std::shared_ptr<Node> attr;
|
||||
std::optional<std::shared_ptr<Node>> default_expr;
|
||||
uint32_t line = 0;
|
||||
SelectNode(std::shared_ptr<Node> e, std::shared_ptr<Node> a, uint32_t l = 0);
|
||||
std::shared_ptr<Node> expr;
|
||||
std::shared_ptr<Node> attr;
|
||||
std::optional<std::shared_ptr<Node>> default_expr;
|
||||
uint32_t line = 0;
|
||||
SelectNode(std::shared_ptr<Node> e, std::shared_ptr<Node> a, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct HasAttrNode {
|
||||
std::shared_ptr<Node> expr;
|
||||
std::shared_ptr<Node> attr;
|
||||
uint32_t line = 0;
|
||||
HasAttrNode(std::shared_ptr<Node> e, std::shared_ptr<Node> a, uint32_t l = 0);
|
||||
std::shared_ptr<Node> expr;
|
||||
std::shared_ptr<Node> attr;
|
||||
uint32_t line = 0;
|
||||
HasAttrNode(std::shared_ptr<Node> e, std::shared_ptr<Node> a, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct WithNode {
|
||||
std::shared_ptr<Node> attrs;
|
||||
std::shared_ptr<Node> body;
|
||||
uint32_t line = 0;
|
||||
WithNode(std::shared_ptr<Node> a, std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
std::shared_ptr<Node> attrs;
|
||||
std::shared_ptr<Node> body;
|
||||
uint32_t line = 0;
|
||||
WithNode(std::shared_ptr<Node> a, std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct IfNode {
|
||||
std::shared_ptr<Node> cond;
|
||||
std::shared_ptr<Node> then_branch;
|
||||
std::shared_ptr<Node> else_branch;
|
||||
uint32_t line = 0;
|
||||
IfNode(std::shared_ptr<Node> c, std::shared_ptr<Node> t, std::shared_ptr<Node> e, uint32_t l = 0);
|
||||
std::shared_ptr<Node> cond;
|
||||
std::shared_ptr<Node> then_branch;
|
||||
std::shared_ptr<Node> else_branch;
|
||||
uint32_t line = 0;
|
||||
IfNode(std::shared_ptr<Node> c, std::shared_ptr<Node> t, std::shared_ptr<Node> e, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct LetNode {
|
||||
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
|
||||
std::shared_ptr<Node> body;
|
||||
uint32_t line = 0;
|
||||
LetNode(std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
|
||||
std::shared_ptr<Node> body;
|
||||
uint32_t line = 0;
|
||||
LetNode(std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct LetRecNode {
|
||||
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
|
||||
std::shared_ptr<Node> body;
|
||||
uint32_t line = 0;
|
||||
LetRecNode(std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;
|
||||
std::shared_ptr<Node> body;
|
||||
uint32_t line = 0;
|
||||
LetRecNode(std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct AssertNode {
|
||||
std::shared_ptr<Node> cond;
|
||||
std::shared_ptr<Node> body;
|
||||
uint32_t line = 0;
|
||||
AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
std::shared_ptr<Node> cond;
|
||||
std::shared_ptr<Node> body;
|
||||
uint32_t line = 0;
|
||||
AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct ThunkNode {
|
||||
std::shared_ptr<Node> expr;
|
||||
uint32_t line = 0;
|
||||
ThunkNode(std::shared_ptr<Node> e, uint32_t l = 0);
|
||||
std::shared_ptr<Node> expr;
|
||||
uint32_t line = 0;
|
||||
ThunkNode(std::shared_ptr<Node> e, uint32_t l = 0);
|
||||
};
|
||||
|
||||
struct ForceNode {
|
||||
std::shared_ptr<Node> expr;
|
||||
uint32_t line = 0;
|
||||
ForceNode(std::shared_ptr<Node> e, uint32_t l = 0);
|
||||
std::shared_ptr<Node> expr;
|
||||
uint32_t line = 0;
|
||||
ForceNode(std::shared_ptr<Node> e, uint32_t l = 0);
|
||||
};
|
||||
|
||||
// Node wraps a variant for type-safe AST
|
||||
class Node {
|
||||
public:
|
||||
using Variant = std::variant<
|
||||
ConstIntNode,
|
||||
ConstStringNode,
|
||||
ConstPathNode,
|
||||
ConstBoolNode,
|
||||
ConstNullNode,
|
||||
VarNode,
|
||||
LambdaNode,
|
||||
AppNode,
|
||||
BinaryOpNode,
|
||||
UnaryOpNode,
|
||||
AttrsetNode,
|
||||
SelectNode,
|
||||
HasAttrNode,
|
||||
WithNode,
|
||||
IfNode,
|
||||
LetNode,
|
||||
LetRecNode,
|
||||
AssertNode,
|
||||
ThunkNode,
|
||||
ForceNode
|
||||
>;
|
||||
using Variant = std::variant<ConstIntNode, ConstStringNode, 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
|
||||
|
|
@ -239,7 +211,8 @@ 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)
|
||||
: func(f), arg(a), line(l) {}
|
||||
|
||||
inline BinaryOpNode::BinaryOpNode(BinaryOp o, std::shared_ptr<Node> l, std::shared_ptr<Node> r, uint32_t ln)
|
||||
inline BinaryOpNode::BinaryOpNode(BinaryOp o, std::shared_ptr<Node> l, std::shared_ptr<Node> r,
|
||||
uint32_t ln)
|
||||
: op(o), left(l), right(r), line(ln) {}
|
||||
|
||||
inline UnaryOpNode::UnaryOpNode(UnaryOp o, std::shared_ptr<Node> operand, uint32_t l)
|
||||
|
|
@ -254,37 +227,34 @@ 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)
|
||||
: attrs(a), body(b), line(l) {}
|
||||
|
||||
inline IfNode::IfNode(std::shared_ptr<Node> c, std::shared_ptr<Node> t, std::shared_ptr<Node> e, uint32_t l)
|
||||
inline IfNode::IfNode(std::shared_ptr<Node> c, std::shared_ptr<Node> t, std::shared_ptr<Node> e,
|
||||
uint32_t 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)
|
||||
: 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 {
|
||||
std::string path;
|
||||
std::string content;
|
||||
std::shared_ptr<Node> ast;
|
||||
std::string path;
|
||||
std::string content;
|
||||
std::shared_ptr<Node> ast;
|
||||
};
|
||||
|
||||
struct IRModule {
|
||||
uint32_t version = IR_VERSION;
|
||||
std::vector<SourceFile> sources;
|
||||
std::vector<std::pair<std::string, std::string>> imports;
|
||||
std::shared_ptr<Node> entry;
|
||||
std::unordered_map<std::string, uint32_t> string_table;
|
||||
uint32_t version = IR_VERSION;
|
||||
std::vector<SourceFile> sources;
|
||||
std::vector<std::pair<std::string, std::string>> imports;
|
||||
std::shared_ptr<Node> entry;
|
||||
std::unordered_map<std::string, uint32_t> string_table;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace nix_irc
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,20 +5,14 @@
|
|||
#include "nix/expr/eval.hh"
|
||||
#include "nix/expr/primops.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/parser.h"
|
||||
#include "irc/resolver.h"
|
||||
#include "irc/serializer.h"
|
||||
#include "irc/types.h"
|
||||
#include "irc/evaluator.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace nix_ir_plugin {
|
||||
|
||||
|
|
@ -29,11 +23,9 @@ using namespace nix_irc;
|
|||
* Load and evaluate a pre-compiled IR bundle
|
||||
* 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(
|
||||
*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);
|
||||
|
||||
|
|
@ -42,25 +34,19 @@ static void prim_loadIR(EvalState &state, const PosIdx pos, Value **args,
|
|||
|
||||
try {
|
||||
module = deserializer.deserialize(pathStr);
|
||||
} catch (const std::exception &e) {
|
||||
state.error<EvalError>("failed to deserialize IR bundle: %s", e.what())
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
} catch (const std::exception& e) {
|
||||
state.error<EvalError>("failed to deserialize IR bundle: %s", e.what()).atPos(pos).debugThrow();
|
||||
}
|
||||
|
||||
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 {
|
||||
Evaluator evaluator(state);
|
||||
evaluator.eval_to_nix(module.entry, v);
|
||||
} catch (const std::exception &e) {
|
||||
state.error<EvalError>("failed to evaluate IR: %s", e.what())
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
} catch (const std::exception& e) {
|
||||
state.error<EvalError>("failed to evaluate IR: %s", e.what()).atPos(pos).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,11 +54,9 @@ static void prim_loadIR(EvalState &state, const PosIdx pos, Value **args,
|
|||
* Compile Nix source to IR on-the-fly
|
||||
* 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(
|
||||
*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);
|
||||
|
||||
|
|
@ -81,9 +65,7 @@ static void prim_compileNix(EvalState &state, const PosIdx pos, Value **args,
|
|||
auto ast = parser.parse(sourceStr, "<inline>");
|
||||
|
||||
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;
|
||||
|
|
@ -92,10 +74,8 @@ static void prim_compileNix(EvalState &state, const PosIdx pos, Value **args,
|
|||
Evaluator evaluator(state);
|
||||
evaluator.eval_to_nix(ir, v);
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
state.error<EvalError>("IR compilation failed: %s", e.what())
|
||||
.atPos(pos)
|
||||
.debugThrow();
|
||||
} catch (const std::exception& e) {
|
||||
state.error<EvalError>("IR compilation failed: %s", e.what()).atPos(pos).debugThrow();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,19 +83,18 @@ static void prim_compileNix(EvalState &state, const PosIdx pos, Value **args,
|
|||
* Get information about the IR plugin
|
||||
* 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);
|
||||
|
||||
Value *vName = state.allocValue();
|
||||
Value* vName = state.allocValue();
|
||||
vName->mkString("nix-ir-plugin");
|
||||
bindings.insert(state.symbols.create("name"), vName);
|
||||
|
||||
Value *vVersion = state.allocValue();
|
||||
Value* vVersion = state.allocValue();
|
||||
vVersion->mkString("0.1.0");
|
||||
bindings.insert(state.symbols.create("version"), vVersion);
|
||||
|
||||
Value *vStatus = state.allocValue();
|
||||
Value* vStatus = state.allocValue();
|
||||
vStatus->mkString("runtime-active");
|
||||
bindings.insert(state.symbols.create("status"), vStatus);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue