clone
ssh://git@git.notashelf.dev:33/notashelf/nixir.gitCommit ea20aaab005a
tarball12 files changed+1923-1819
@@ -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;- }- }- return 0xFFFFFFFF;+ 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; } 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;-- 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;- }-- 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;-- 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{});+ std::unordered_map<std::string, uint32_t> string_table;+ uint32_t next_string_id = 0;+ NameResolver name_resolver;++ 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;+ }++ 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;++ 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\ No newline at end of file@@ -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]);+ 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; }- - if (output_file.empty()) {- output_file = input_file + "r";+ 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 + "ir";+ }++ try {+ Parser parser;+ Resolver resolver;++ for (const auto& path : search_paths) {+ resolver.add_search_path(path); }- - 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;++ 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);+ } }@@ -1,950 +1,1064 @@ #include "parser.h"-#include <iostream>+#include <array> #include <cstdio> #include <cstdlib>+#include <iostream> #include <memory>-#include <stdexcept>+#include <regex> #include <sstream>+#include <stdexcept> #include <vector>-#include <regex>-#include <array> namespace nix_irc { static std::string trim(const std::string& s) {- size_t start = s.find_first_not_of(" \t\n\r");- if (start == std::string::npos) return "";- size_t end = s.find_last_not_of(" \t\n\r");- return s.substr(start, end - start + 1);+ size_t start = s.find_first_not_of(" \t\n\r");+ if (start == std::string::npos)+ return "";+ size_t end = s.find_last_not_of(" \t\n\r");+ return s.substr(start, end - start + 1); } static std::string read_file(const std::string& path) {- FILE* f = fopen(path.c_str(), "r");- if (!f) {- throw std::runtime_error("Cannot open file: " + path);- }- fseek(f, 0, SEEK_END);- long size = ftell(f);- fseek(f, 0, SEEK_SET);- std::string content(size, '\0');- if (fread(content.data(), 1, size, f) != static_cast<size_t>(size)) {- fclose(f);- throw std::runtime_error("Failed to read file: " + path);- }+ FILE* f = fopen(path.c_str(), "r");+ if (!f) {+ throw std::runtime_error("Cannot open file: " + path);+ }+ fseek(f, 0, SEEK_END);+ long size = ftell(f);+ fseek(f, 0, SEEK_SET);+ std::string content(size, '\0');+ if (fread(content.data(), 1, size, f) != static_cast<size_t>(size)) { fclose(f);- return content;+ throw std::runtime_error("Failed to read file: " + path);+ }+ fclose(f);+ return content; } static std::pair<std::string, std::string> run_command(const std::string& cmd) {- std::array<char, 256> buffer;- std::string result;- std::string error;-- FILE* pipe = popen(cmd.c_str(), "r");- if (!pipe) throw std::runtime_error("popen failed");-- while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {- result += buffer.data();- }-- int status = pclose(pipe);- if (status != 0) {- throw std::runtime_error("Command failed: " + cmd);- }- return {result, error};+ std::array<char, 256> buffer;+ std::string result;+ std::string error;++ FILE* pipe = popen(cmd.c_str(), "r");+ if (!pipe)+ throw std::runtime_error("popen failed");++ while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) {+ result += buffer.data();+ }++ int status = pclose(pipe);+ if (status != 0) {+ throw std::runtime_error("Command failed: " + cmd);+ }+ return {result, error}; } struct Token {- enum Type {- LPAREN, RPAREN, LBRACE, RBRACE, 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- PLUS, MINUS, STAR, SLASH, CONCAT,- EQEQ, NE, LT, GT, LE, GE,- AND, OR, IMPL, NOT,- EOF_- } type;- std::string value;- size_t line;- size_t col;+ enum Type {+ LPAREN,+ RPAREN,+ LBRACE,+ RBRACE,+ 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+ PLUS,+ MINUS,+ STAR,+ SLASH,+ CONCAT,+ EQEQ,+ NE,+ LT,+ GT,+ LE,+ GE,+ AND,+ OR,+ IMPL,+ NOT,+ EOF_+ } type;+ std::string value;+ size_t line;+ size_t col; }; class Lexer { public:- Lexer(const std::string& input) : input(input), pos(0), line(1), col(1) {}-- std::vector<Token> tokenize() {- #define TOKEN(t) Token{Token::t, "", line, col}-- while (pos < input.size()) {- skip_whitespace();- if (pos >= input.size()) break;-- char c = input[pos];-- if (c == '(') { emit(TOKEN(LPAREN)); }- else if (c == ')') { emit(TOKEN(RPAREN)); }- else if (c == '{') { emit(TOKEN(LBRACE)); }- else if (c == '}') { emit(TOKEN(RBRACE)); }- else if (c == '[') { emit(TOKEN(LBRACKET)); }- else if (c == ']') { 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- else if (c == '=' && pos + 1 < input.size() && input[pos + 1] == '=') {- tokens.push_back(TOKEN(EQEQ));- pos += 2; col += 2;- }- else if (c == '=') { emit(TOKEN(EQUALS)); }- else if (c == '!' && pos + 1 < input.size() && input[pos + 1] == '=') {- tokens.push_back(TOKEN(NE));- pos += 2; col += 2;- }- else if (c == '<' && pos + 1 < input.size() && input[pos + 1] == '=') {- tokens.push_back(TOKEN(LE));- pos += 2; col += 2;- }- else if (c == '>' && pos + 1 < input.size() && input[pos + 1] == '=') {- tokens.push_back(TOKEN(GE));- pos += 2; col += 2;- }- else if (c == '+' && pos + 1 < input.size() && input[pos + 1] == '+') {- tokens.push_back(TOKEN(CONCAT));- pos += 2; col += 2;- }- else if (c == '&' && pos + 1 < input.size() && input[pos + 1] == '&') {- tokens.push_back(TOKEN(AND));- pos += 2; col += 2;- }- else if (c == '|' && pos + 1 < input.size() && input[pos + 1] == '|') {- tokens.push_back(TOKEN(OR));- pos += 2; col += 2;- }- else if (c == '-' && pos + 1 < input.size() && input[pos + 1] == '>') {- tokens.push_back(TOKEN(IMPL));- pos += 2; col += 2;- }- // Single-char operators- else if (c == '+') { emit(TOKEN(PLUS)); }- else if (c == '*') { emit(TOKEN(STAR)); }- else if (c == '/') {- // Check if it's a path or division- if (pos + 1 < input.size() && (isalnum(input[pos + 1]) || input[pos + 1] == '.')) {- tokenize_path();- } else {- emit(TOKEN(SLASH));- }- }- else if (c == '<') { emit(TOKEN(LT)); }- else if (c == '>') { emit(TOKEN(GT)); }- else if (c == '!') { emit(TOKEN(NOT)); }- else if (c == '.') {- // Check for ellipsis (...)- if (pos + 2 < input.size() && input[pos + 1] == '.' && input[pos + 2] == '.') {- tokens.push_back(TOKEN(ELLIPSIS));- pos += 3; col += 3;- } else {- emit(TOKEN(DOT));- }- }- else if (c == '?') { emit(TOKEN(QUESTION)); }- else if (c == '-') {- // Check if it's a negative number or minus operator- if (pos + 1 < input.size() && isdigit(input[pos + 1])) {- tokenize_int();- } else {- emit(TOKEN(MINUS));- }- }- else if (isdigit(c)) { tokenize_int(); }- else if (isalpha(c) || c == '_') { tokenize_ident(); }- else { pos++; col++; }+ Lexer(const std::string& input) : input(input), pos(0), line(1), col(1) {}++ std::vector<Token> tokenize() {+#define TOKEN(t) \+ Token { Token::t, "", line, col }++ while (pos < input.size()) {+ skip_whitespace();+ if (pos >= input.size())+ break;++ char c = input[pos];++ if (c == '(') {+ emit(TOKEN(LPAREN));+ } else if (c == ')') {+ emit(TOKEN(RPAREN));+ } else if (c == '{') {+ emit(TOKEN(LBRACE));+ } else if (c == '}') {+ emit(TOKEN(RBRACE));+ } else if (c == '[') {+ emit(TOKEN(LBRACKET));+ } else if (c == ']') {+ 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+ else if (c == '=' && pos + 1 < input.size() && input[pos + 1] == '=') {+ tokens.push_back(TOKEN(EQEQ));+ pos += 2;+ col += 2;+ } else if (c == '=') {+ emit(TOKEN(EQUALS));+ } else if (c == '!' && pos + 1 < input.size() && input[pos + 1] == '=') {+ tokens.push_back(TOKEN(NE));+ pos += 2;+ col += 2;+ } else if (c == '<' && pos + 1 < input.size() && input[pos + 1] == '=') {+ tokens.push_back(TOKEN(LE));+ pos += 2;+ col += 2;+ } else if (c == '>' && pos + 1 < input.size() && input[pos + 1] == '=') {+ tokens.push_back(TOKEN(GE));+ pos += 2;+ col += 2;+ } else if (c == '+' && pos + 1 < input.size() && input[pos + 1] == '+') {+ tokens.push_back(TOKEN(CONCAT));+ pos += 2;+ col += 2;+ } else if (c == '&' && pos + 1 < input.size() && input[pos + 1] == '&') {+ tokens.push_back(TOKEN(AND));+ pos += 2;+ col += 2;+ } else if (c == '|' && pos + 1 < input.size() && input[pos + 1] == '|') {+ tokens.push_back(TOKEN(OR));+ pos += 2;+ col += 2;+ } else if (c == '-' && pos + 1 < input.size() && input[pos + 1] == '>') {+ tokens.push_back(TOKEN(IMPL));+ pos += 2;+ col += 2;+ }+ // Single-char operators+ else if (c == '+') {+ emit(TOKEN(PLUS));+ } else if (c == '*') {+ emit(TOKEN(STAR));+ } else if (c == '/') {+ // Check if it's a path or division+ if (pos + 1 < input.size() && (isalnum(input[pos + 1]) || input[pos + 1] == '.')) {+ tokenize_path();+ } else {+ emit(TOKEN(SLASH)); }- tokens.push_back({Token::EOF_, "", line, col});-- #undef TOKEN- return tokens;- }--private:- std::vector<Token> tokens;- const std::string& input;- size_t pos;- size_t line;- size_t col;-- void emit(Token t) {- tokens.push_back(t);+ } else if (c == '<') {+ emit(TOKEN(LT));+ } else if (c == '>') {+ emit(TOKEN(GT));+ } else if (c == '!') {+ emit(TOKEN(NOT));+ } else if (c == '.') {+ // Check for ellipsis (...)+ if (pos + 2 < input.size() && input[pos + 1] == '.' && input[pos + 2] == '.') {+ tokens.push_back(TOKEN(ELLIPSIS));+ pos += 3;+ col += 3;+ } else {+ emit(TOKEN(DOT));+ }+ } else if (c == '?') {+ emit(TOKEN(QUESTION));+ } else if (c == '-') {+ // Check if it's a negative number or minus operator+ if (pos + 1 < input.size() && isdigit(input[pos + 1])) {+ tokenize_int();+ } else {+ emit(TOKEN(MINUS));+ }+ } else if (isdigit(c)) {+ tokenize_int();+ } else if (isalpha(c) || c == '_') {+ tokenize_ident();+ } else { pos++; col++;+ } }+ tokens.push_back({Token::EOF_, "", line, col}); - void skip_whitespace() {- while (pos < input.size()) {- char c = input[pos];- if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {- if (c == '\n') { line++; col = 1; }- else { col++; }- pos++;- } else if (c == '#') {- while (pos < input.size() && input[pos] != '\n') pos++;- } else {- break;- }+#undef TOKEN+ return tokens;+ }++private:+ std::vector<Token> tokens;+ const std::string& input;+ size_t pos;+ size_t line;+ size_t col;++ void emit(Token t) {+ tokens.push_back(t);+ pos++;+ col++;+ }++ void skip_whitespace() {+ while (pos < input.size()) {+ char c = input[pos];+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {+ if (c == '\n') {+ line++;+ col = 1;+ } else {+ col++; }+ pos++;+ } else if (c == '#') {+ while (pos < input.size() && input[pos] != '\n')+ pos++;+ } else {+ break;+ } }+ } - void tokenize_string() {+ void tokenize_string() {+ pos++;+ std::string s;+ bool has_interp = false;++ while (pos < input.size() && input[pos] != '"') {+ if (input[pos] == '\\' && pos + 1 < input.size()) { pos++;- std::string s;- bool has_interp = false;-- while (pos < input.size() && input[pos] != '"') {- if (input[pos] == '\\' && pos + 1 < input.size()) {- pos++;- switch (input[pos]) {- case 'n': s += '\n'; break;- case 't': s += '\t'; break;- case 'r': s += '\r'; break;- case '"': s += '"'; break;- case '\\': s += '\\'; break;- case '$': s += '$'; break; // Escaped $- default: s += input[pos]; break;- }- pos++;- } else if (input[pos] == '$' && pos + 1 < input.size() && input[pos + 1] == '{') {- // Found interpolation marker- has_interp = true;- s += input[pos]; // Keep $ in raw string- pos++;- } else {- s += input[pos];- pos++;- }+ switch (input[pos]) {+ case 'n':+ s += '\n';+ break;+ case 't':+ s += '\t';+ break;+ case 'r':+ s += '\r';+ break;+ case '"':+ s += '"';+ break;+ case '\\':+ s += '\\';+ break;+ case '$':+ s += '$';+ break; // Escaped $+ default:+ s += input[pos];+ break; } pos++;-- Token::Type type = has_interp ? Token::STRING_INTERP : Token::STRING;- tokens.push_back({type, s, line, col});- col += s.size() + 2;+ } else if (input[pos] == '$' && pos + 1 < input.size() && input[pos + 1] == '{') {+ // Found interpolation marker+ has_interp = true;+ s += input[pos]; // Keep $ in raw string+ pos++;+ } else {+ s += input[pos];+ pos++;+ } }-- void tokenize_path() {- size_t start = pos;- while (pos < input.size() && !isspace(input[pos]) &&- input[pos] != '(' && input[pos] != ')' &&- input[pos] != '{' && input[pos] != '}' &&- input[pos] != '[' && input[pos] != ']') {- pos++;- }- std::string path = input.substr(start, pos - start);- tokens.push_back({Token::PATH, path, line, col});- col += path.size();- }-- void tokenize_int() {- size_t start = pos;- if (input[pos] == '-') pos++;- while (pos < input.size() && isdigit(input[pos])) pos++;- std::string num = input.substr(start, pos - start);- tokens.push_back({Token::INT, num, line, col});- col += num.size();- }-- void tokenize_ident() {- size_t start = pos;- while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-')) pos++;- std::string ident = input.substr(start, pos - start);-- Token::Type type = Token::IDENT;- if (ident == "let") type = Token::LET;- else if (ident == "in") type = Token::IN;- else if (ident == "rec") type = Token::REC;- else if (ident == "if") type = Token::IF;- else if (ident == "then") type = Token::THEN;- else if (ident == "else") 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});- col += ident.size();+ pos++;++ Token::Type type = has_interp ? Token::STRING_INTERP : Token::STRING;+ tokens.push_back({type, s, line, col});+ col += s.size() + 2;+ }++ void tokenize_path() {+ size_t start = pos;+ while (pos < input.size() && !isspace(input[pos]) && input[pos] != '(' && input[pos] != ')' &&+ input[pos] != '{' && input[pos] != '}' && input[pos] != '[' && input[pos] != ']') {+ pos++; }+ std::string path = input.substr(start, pos - start);+ tokens.push_back({Token::PATH, path, line, col});+ col += path.size();+ }++ void tokenize_int() {+ size_t start = pos;+ if (input[pos] == '-')+ pos++;+ while (pos < input.size() && isdigit(input[pos]))+ pos++;+ std::string num = input.substr(start, pos - start);+ tokens.push_back({Token::INT, num, line, col});+ col += num.size();+ }++ void tokenize_ident() {+ size_t start = pos;+ while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-'))+ pos++;+ std::string ident = input.substr(start, pos - start);++ Token::Type type = Token::IDENT;+ if (ident == "let")+ type = Token::LET;+ else if (ident == "in")+ type = Token::IN;+ else if (ident == "rec")+ type = Token::REC;+ else if (ident == "if")+ type = Token::IF;+ else if (ident == "then")+ type = Token::THEN;+ else if (ident == "else")+ 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});+ col += ident.size();+ } }; class Parser::Impl { public:- std::vector<Token> tokens;- size_t pos = 0;- std::string current_file;-- const Token& current() {- if (pos < tokens.size()) return tokens[pos];- static Token eof{Token::EOF_, "", 0, 0};- return eof;+ std::vector<Token> tokens;+ size_t pos = 0;+ std::string current_file;++ const Token& current() {+ if (pos < tokens.size())+ return tokens[pos];+ static Token eof{Token::EOF_, "", 0, 0};+ return eof;+ }++ void advance() { pos++; }++ bool consume(Token::Type type) {+ if (current().type == type) {+ advance();+ return true;+ }+ return false;+ }++ bool expect(Token::Type type) {+ if (current().type != type) {+ std::cerr << "Expected token " << type << " but got " << current().type << " at "+ << current().line << ":" << current().col << "\n";+ return false;+ }+ advance();+ return true;+ }++ // Get operator precedence (higher = tighter binding)+ int get_precedence(Token::Type type) {+ switch (type) {+ case Token::OR:+ return 1;+ case Token::AND:+ return 2;+ case Token::IMPL:+ return 3;+ case Token::EQEQ:+ case Token::NE:+ return 4;+ 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+ BinaryOp token_to_binop(Token::Type type) {+ switch (type) {+ case Token::PLUS:+ return BinaryOp::ADD;+ case Token::MINUS:+ return BinaryOp::SUB;+ case Token::STAR:+ return BinaryOp::MUL;+ case Token::SLASH:+ return BinaryOp::DIV;+ case Token::CONCAT:+ return BinaryOp::CONCAT;+ case Token::EQEQ:+ return BinaryOp::EQ;+ case Token::NE:+ return BinaryOp::NE;+ case Token::LT:+ 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() {+ // Try to parse lambda+ auto lambda = try_parse_lambda();+ if (lambda)+ return lambda;++ if (consume(Token::IF)) {+ auto cond = parse_expr();+ expect(Token::THEN);+ auto then = parse_expr();+ expect(Token::ELSE);+ auto else_ = parse_expr();+ return std::make_shared<Node>(IfNode(cond, then, else_));+ }+ if (consume(Token::LET)) {+ bool is_rec = consume(Token::REC);+ std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;+ parse_bindings(bindings);+ expect(Token::IN);+ auto body = parse_expr();++ if (is_rec) {+ auto letrec = LetRecNode(body);+ letrec.bindings = std::move(bindings);+ return std::make_shared<Node>(std::move(letrec));+ } else {+ auto let = LetNode(body);+ let.bindings = std::move(bindings);+ return std::make_shared<Node>(std::move(let));+ }+ }+ if (consume(Token::ASSERT)) {+ auto cond = parse_expr();+ expect(Token::SEMICOLON);+ auto body = parse_expr();+ return std::make_shared<Node>(AssertNode(cond, body));+ }+ if (consume(Token::WITH)) {+ auto attrs = parse_expr();+ expect(Token::SEMICOLON);+ auto body = parse_expr();+ return std::make_shared<Node>(WithNode(attrs, body)); } - void advance() { pos++; }+ return parse_expr1();+ } - bool consume(Token::Type type) {- if (current().type == type) {- advance();- return true;- }- return false;+ std::shared_ptr<Node> parse_expr1() { return parse_binary_op(0); }++ // Precedence climbing for binary operators+ std::shared_ptr<Node> parse_binary_op(int min_prec) {+ auto left = parse_selection();++ while (true) {+ int prec = get_precedence(current().type);+ if (prec == 0 || prec < min_prec)+ break;++ Token op_token = current();+ advance();++ auto right = parse_binary_op(prec + 1);+ left = std::make_shared<Node>(BinaryOpNode(token_to_binop(op_token.type), left, right)); } - bool expect(Token::Type type) {- if (current().type != type) {- std::cerr << "Expected token " << type << " but got " << current().type- << " at " << current().line << ":" << current().col << "\n";- return false;- }+ return left;+ }++ std::shared_ptr<Node> parse_selection() {+ auto left = parse_expr2();++ while (current().type == Token::DOT) {+ advance();+ Token name = current();+ if (name.type == Token::IDENT) { advance();- return true;- }-- // Get operator precedence (higher = tighter binding)- int get_precedence(Token::Type type) {- switch (type) {- case Token::OR: return 1;- case Token::AND: return 2;- case Token::IMPL: return 3;- case Token::EQEQ: case Token::NE: return 4;- 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;- }- }+ auto attr = std::make_shared<Node>(ConstStringNode(name.value));+ auto result = std::make_shared<Node>(SelectNode(left, attr)); - // Convert token type to binary operator- BinaryOp token_to_binop(Token::Type type) {- switch (type) {- case Token::PLUS: return BinaryOp::ADD;- case Token::MINUS: return BinaryOp::SUB;- case Token::STAR: return BinaryOp::MUL;- case Token::SLASH: return BinaryOp::DIV;- case Token::CONCAT: return BinaryOp::CONCAT;- case Token::EQEQ: return BinaryOp::EQ;- case Token::NE: return BinaryOp::NE;- case Token::LT: 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");+ if (consume(Token::DOT)) {+ Token name2 = current();+ if (name2.type == Token::IDENT) {+ advance();+ auto attr2 = std::make_shared<Node>(ConstStringNode(name2.value));+ auto* curr = result->get_if<SelectNode>();+ while (curr && consume(Token::DOT)) {+ Token n = current();+ expect(Token::IDENT);+ auto a = std::make_shared<Node>(ConstStringNode(n.value));+ curr->attr =+ std::make_shared<Node>(AppNode(std::make_shared<Node>(AppNode(curr->attr, a)),+ std::make_shared<Node>(ConstNullNode())));+ }+ } }+ return result;+ } else if (consume(Token::LBRACE)) {+ auto result = std::make_shared<Node>(+ SelectNode(left, std::make_shared<Node>(ConstStringNode(name.value))));+ parse_expr_attrs(result);+ expect(Token::RBRACE);+ return result;+ }+ return left; } - std::shared_ptr<Node> parse_expr() {- // Try to parse lambda- auto lambda = try_parse_lambda();- if (lambda) return lambda;+ return left;+ } - if (consume(Token::IF)) {- auto cond = parse_expr();- expect(Token::THEN);- auto then = parse_expr();- expect(Token::ELSE);- auto else_ = parse_expr();- return std::make_shared<Node>(IfNode(cond, then, else_));- }- if (consume(Token::LET)) {- bool is_rec = consume(Token::REC);- std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;- parse_bindings(bindings);- expect(Token::IN);- auto body = parse_expr();-- if (is_rec) {- auto letrec = LetRecNode(body);- letrec.bindings = std::move(bindings);- return std::make_shared<Node>(std::move(letrec));- } else {- auto let = LetNode(body);- let.bindings = std::move(bindings);- return std::make_shared<Node>(std::move(let));- }- }- if (consume(Token::ASSERT)) {- auto cond = parse_expr();- expect(Token::SEMICOLON);- auto body = parse_expr();- return std::make_shared<Node>(AssertNode(cond, body));- }- if (consume(Token::WITH)) {- auto attrs = parse_expr();- expect(Token::SEMICOLON);- auto body = parse_expr();- return std::make_shared<Node>(WithNode(attrs, body));- }+ void parse_expr_attrs(std::shared_ptr<Node>&) {+ // Extended selection syntax+ } - return parse_expr1();- }+ std::shared_ptr<Node> parse_expr2() {+ std::shared_ptr<Node> left = parse_expr3(); - std::shared_ptr<Node> parse_expr1() {- return parse_binary_op(0);+ while (true) {+ if (current().type == Token::LBRACKET) {+ advance();+ auto arg = parse_expr();+ expect(Token::RBRACKET);+ left = std::make_shared<Node>(AppNode(left, arg));+ } else if (current().type == Token::STRING) {+ Token s = current();+ advance();+ auto arg = std::make_shared<Node>(ConstStringNode(s.value));+ left = std::make_shared<Node>(AppNode(left, arg));+ } else {+ break;+ } } - // Precedence climbing for binary operators- std::shared_ptr<Node> parse_binary_op(int min_prec) {- auto left = parse_selection();+ return left;+ } - while (true) {- int prec = get_precedence(current().type);- if (prec == 0 || prec < min_prec) break;+ std::shared_ptr<Node> parse_expr3() {+ // Handle unary operators+ if (consume(Token::MINUS)) {+ auto operand = parse_expr3();+ return std::make_shared<Node>(UnaryOpNode(UnaryOp::NEG, operand));+ } - Token op_token = current();- advance();+ if (consume(Token::NOT)) {+ auto operand = parse_expr3();+ return std::make_shared<Node>(UnaryOpNode(UnaryOp::NOT, operand));+ } - auto right = parse_binary_op(prec + 1);- left = std::make_shared<Node>(BinaryOpNode(- token_to_binop(op_token.type),- left,- right- ));- }+ if (consume(Token::LPAREN)) {+ auto expr = parse_expr();+ expect(Token::RPAREN);+ return expr;+ } - return left;+ if (consume(Token::LBRACE)) {+ return parse_attrs(); } - std::shared_ptr<Node> parse_selection() {- auto left = parse_expr2();+ if (consume(Token::LBRACKET)) {+ return parse_list();+ } - while (current().type == Token::DOT) {- advance();- Token name = current();- if (name.type == Token::IDENT) {- advance();- auto attr = std::make_shared<Node>(ConstStringNode(name.value));- auto result = std::make_shared<Node>(SelectNode(left, attr));-- if (consume(Token::DOT)) {- Token name2 = current();- if (name2.type == Token::IDENT) {- advance();- auto attr2 = std::make_shared<Node>(ConstStringNode(name2.value));- auto* curr = result->get_if<SelectNode>();- while (curr && consume(Token::DOT)) {- Token n = current();- expect(Token::IDENT);- auto a = std::make_shared<Node>(ConstStringNode(n.value));- curr->attr = std::make_shared<Node>(AppNode(- std::make_shared<Node>(AppNode(curr->attr, a)),- std::make_shared<Node>(ConstNullNode())- ));- }- }- }- return result;- } else if (consume(Token::LBRACE)) {- auto result = std::make_shared<Node>(SelectNode(left, std::make_shared<Node>(ConstStringNode(name.value))));- parse_expr_attrs(result);- expect(Token::RBRACE);- return result;- }- return left;- }+ Token t = current(); - return left;+ if (t.type == Token::IDENT) {+ advance();+ return std::make_shared<Node>(VarNode(0, t.value)); } - void parse_expr_attrs(std::shared_ptr<Node>&) {- // Extended selection syntax+ if (t.type == Token::INT) {+ advance();+ return std::make_shared<Node>(ConstIntNode(std::stoll(t.value))); } - std::shared_ptr<Node> parse_expr2() {- std::shared_ptr<Node> left = parse_expr3();+ if (t.type == Token::STRING) {+ advance();+ return std::make_shared<Node>(ConstStringNode(t.value));+ } - while (true) {- if (current().type == Token::LBRACKET) {- advance();- auto arg = parse_expr();- expect(Token::RBRACKET);- left = std::make_shared<Node>(AppNode(left, arg));- } else if (current().type == Token::STRING) {- Token s = current();- advance();- auto arg = std::make_shared<Node>(ConstStringNode(s.value));- left = std::make_shared<Node>(AppNode(left, arg));- } else {- break;- }- }+ if (t.type == Token::STRING_INTERP) {+ Token str_token = current();+ advance();+ return parse_string_interp(str_token.value);+ } - return left;+ if (t.type == Token::PATH) {+ advance();+ return std::make_shared<Node>(ConstPathNode(t.value)); } - std::shared_ptr<Node> parse_expr3() {- // Handle unary operators- if (consume(Token::MINUS)) {- auto operand = parse_expr3();- return std::make_shared<Node>(UnaryOpNode(UnaryOp::NEG, operand));- }+ if (t.type == Token::BOOL) {+ advance();+ return std::make_shared<Node>(ConstBoolNode(t.value == "true"));+ } - if (consume(Token::NOT)) {- auto operand = parse_expr3();- return std::make_shared<Node>(UnaryOpNode(UnaryOp::NOT, operand));- }+ std::cerr << "Unknown token: " << t.value << " (type " << t.type << ")\n";+ advance();+ return std::make_shared<Node>(ConstNullNode());+ }++ std::shared_ptr<Node> parse_attrs() {+ auto attrs = AttrsetNode(false);++ while (current().type != Token::RBRACE && current().type != Token::EOF_) {+ if (consume(Token::REC)) {+ attrs.recursive = true;+ continue;+ } + // Handle inherit keyword+ if (consume(Token::INHERIT)) {+ std::shared_ptr<Node> source;++ // Check for (expr) form if (consume(Token::LPAREN)) {- auto expr = parse_expr();- expect(Token::RPAREN);- return expr;+ source = parse_expr();+ expect(Token::RPAREN); } - if (consume(Token::LBRACE)) {- return parse_attrs();+ // Parse identifier list+ while (current().type == Token::IDENT) {+ Token name = current();+ advance();++ if (source) {+ // inherit (expr) x → x = expr.x+ auto select = std::make_shared<Node>(+ SelectNode(source, std::make_shared<Node>(ConstStringNode(name.value))));+ attrs.attrs.push_back({name.value, select});+ } else {+ // inherit x → x = x+ auto var = std::make_shared<Node>(VarNode(0, name.value));+ attrs.attrs.push_back({name.value, var});+ } } - if (consume(Token::LBRACKET)) {- return parse_list();+ expect(Token::SEMICOLON);+ continue;+ }++ if (current().type == Token::IDENT || current().type == Token::STRING) {+ Token key = current();+ advance();+ std::string key_str = key.value;++ if (consume(Token::EQUALS)) {+ auto value = parse_expr();+ attrs.attrs.push_back({key_str, value});+ } else if (consume(Token::AT)) {+ auto pattern = parse_expr();+ auto value = parse_expr();+ attrs.attrs.push_back({key_str, value}); }+ } - Token t = current();+ if (consume(Token::COMMA))+ continue;+ if (consume(Token::SEMICOLON))+ continue; - if (t.type == Token::IDENT) {- advance();- return std::make_shared<Node>(VarNode(0, t.value));- }+ // If we get here and haven't handled the token, break+ if (current().type != Token::RBRACE && current().type != Token::EOF_) {+ break;+ }+ } - if (t.type == Token::INT) {- advance();- return std::make_shared<Node>(ConstIntNode(std::stoll(t.value)));- }+ expect(Token::RBRACE);+ return std::make_shared<Node>(std::move(attrs));+ } - if (t.type == Token::STRING) {- advance();- return std::make_shared<Node>(ConstStringNode(t.value));- }+ std::shared_ptr<Node> parse_list() {+ std::shared_ptr<Node> list = std::make_shared<Node>(ConstNullNode()); - if (t.type == Token::STRING_INTERP) {- Token str_token = current();- advance();- return parse_string_interp(str_token.value);- }+ if (consume(Token::RBRACKET)) {+ return list;+ } - if (t.type == Token::PATH) {- advance();- return std::make_shared<Node>(ConstPathNode(t.value));+ std::vector<std::shared_ptr<Node>> elements;+ while (current().type != Token::RBRACKET) {+ elements.push_back(parse_expr());+ if (!consume(Token::COMMA))+ break;+ }+ expect(Token::RBRACKET);++ for (auto it = elements.rbegin(); it != elements.rend(); ++it) {+ list = std::make_shared<Node>(AppNode(+ std::make_shared<Node>(AppNode(std::make_shared<Node>(VarNode(0, "__list")), *it)),+ list));+ }++ return list;+ }++ void parse_bindings(std::vector<std::pair<std::string, std::shared_ptr<Node>>>& bindings) {+ while (current().type == Token::IDENT || current().type == Token::INHERIT) {+ // Handle inherit keyword+ if (consume(Token::INHERIT)) {+ std::shared_ptr<Node> source;++ // Check for (expr) form+ if (consume(Token::LPAREN)) {+ source = parse_expr();+ expect(Token::RPAREN); } - if (t.type == Token::BOOL) {- advance();- return std::make_shared<Node>(ConstBoolNode(t.value == "true"));+ // Parse identifier list+ while (current().type == Token::IDENT) {+ Token name = current();+ advance();++ if (source) {+ // inherit (expr) x → x = expr.x+ auto select = std::make_shared<Node>(+ SelectNode(source, std::make_shared<Node>(ConstStringNode(name.value))));+ bindings.push_back({name.value, select});+ } else {+ // inherit x → x = x+ auto var = std::make_shared<Node>(VarNode(0, name.value));+ bindings.push_back({name.value, var});+ } } - std::cerr << "Unknown token: " << t.value << " (type " << t.type << ")\n";- advance();- return std::make_shared<Node>(ConstNullNode());+ expect(Token::SEMICOLON);+ continue;+ }++ if (current().type != Token::IDENT)+ break;+ Token key = current();+ advance();++ if (consume(Token::AT)) {+ auto pattern = parse_expr();+ auto value = parse_expr();+ bindings.push_back({key.value, value});+ } else {+ expect(Token::EQUALS);+ auto value = parse_expr();+ bindings.push_back({key.value, value});+ }++ if (!consume(Token::SEMICOLON))+ break;+ }+ }++ // Try to parse lambda, return nullptr if not a lambda+ std::shared_ptr<Node> try_parse_lambda() {+ size_t saved_pos = pos;++ // Check for named pattern: arg@{ ... }:+ std::optional<std::string> named_arg;+ if (current().type == Token::IDENT) {+ Token name = current();+ advance();+ if (consume(Token::AT)) {+ named_arg = name.value;+ } else if (consume(Token::COLON)) {+ // Simple lambda: x: body+ auto body = parse_expr();+ auto lambda = LambdaNode(1, body);+ lambda.param_name = name.value;+ return std::make_shared<Node>(std::move(lambda));+ } else {+ // Not a lambda, restore position+ pos = saved_pos;+ return nullptr;+ } } - std::shared_ptr<Node> parse_attrs() {- auto attrs = AttrsetNode(false);+ // Check for pattern: { ... }:+ if (current().type == Token::LBRACE) {+ advance();++ // Parse pattern fields+ struct Field {+ std::string name;+ std::optional<std::shared_ptr<Node>> default_val;+ };+ std::vector<Field> fields;+ bool has_ellipsis = false;++ while (current().type != Token::RBRACE && current().type != Token::EOF_) {+ if (consume(Token::ELLIPSIS)) {+ has_ellipsis = true;+ if (consume(Token::COMMA))+ continue;+ break;+ } - while (current().type != Token::RBRACE && current().type != Token::EOF_) {- if (consume(Token::REC)) {- attrs.recursive = true;- continue;- }+ if (current().type == Token::IDENT) {+ Token field_name = current();+ advance(); - // Handle inherit keyword- if (consume(Token::INHERIT)) {- std::shared_ptr<Node> source;-- // Check for (expr) form- if (consume(Token::LPAREN)) {- source = parse_expr();- expect(Token::RPAREN);- }-- // Parse identifier list- while (current().type == Token::IDENT) {- Token name = current();- advance();-- if (source) {- // inherit (expr) x → x = expr.x- auto select = std::make_shared<Node>(SelectNode(- source,- std::make_shared<Node>(ConstStringNode(name.value))- ));- attrs.attrs.push_back({name.value, select});- } else {- // inherit x → x = x- auto var = std::make_shared<Node>(VarNode(0, name.value));- attrs.attrs.push_back({name.value, var});- }- }-- expect(Token::SEMICOLON);- continue;- }+ Field field;+ field.name = field_name.value; - if (current().type == Token::IDENT || current().type == Token::STRING) {- Token key = current();- advance();- std::string key_str = key.value;-- if (consume(Token::EQUALS)) {- auto value = parse_expr();- attrs.attrs.push_back({key_str, value});- } else if (consume(Token::AT)) {- auto pattern = parse_expr();- auto value = parse_expr();- attrs.attrs.push_back({key_str, value});- }- }+ // Check for default value+ if (consume(Token::QUESTION)) {+ field.default_val = parse_expr();+ } - if (consume(Token::COMMA)) continue;- if (consume(Token::SEMICOLON)) continue;+ fields.push_back(field); - // If we get here and haven't handled the token, break- if (current().type != Token::RBRACE && current().type != Token::EOF_) {- break;- }+ if (consume(Token::COMMA))+ continue;+ break;+ } else {+ break; }+ } - expect(Token::RBRACE);- return std::make_shared<Node>(std::move(attrs));- }+ if (!consume(Token::RBRACE)) {+ // Not a lambda pattern, restore+ pos = saved_pos;+ return nullptr;+ } - std::shared_ptr<Node> parse_list() {- std::shared_ptr<Node> list = std::make_shared<Node>(ConstNullNode());+ if (!consume(Token::COLON)) {+ // Not a lambda, restore+ pos = saved_pos;+ return nullptr;+ } - if (consume(Token::RBRACKET)) {- return list;- }+ // Parse body+ auto body = parse_expr(); - std::vector<std::shared_ptr<Node>> elements;- while (current().type != Token::RBRACKET) {- elements.push_back(parse_expr());- if (!consume(Token::COMMA)) break;- }- expect(Token::RBRACKET);+ // Desugar pattern to lambda with let bindings+ // { a, b ? x }: body → arg: let a = arg.a; b = if arg ? a then arg.a else x; in body - for (auto it = elements.rbegin(); it != elements.rend(); ++it) {- list = std::make_shared<Node>(AppNode(- std::make_shared<Node>(AppNode(- std::make_shared<Node>(VarNode(0, "__list")),- *it- )),- list- ));- }+ std::string arg_name = named_arg.value_or("_arg");+ auto arg_var = std::make_shared<Node>(VarNode(0, arg_name)); - return list;- }-- void parse_bindings(std::vector<std::pair<std::string, std::shared_ptr<Node>>>& bindings) {- while (current().type == Token::IDENT || current().type == Token::INHERIT) {- // Handle inherit keyword- if (consume(Token::INHERIT)) {- std::shared_ptr<Node> source;-- // Check for (expr) form- if (consume(Token::LPAREN)) {- source = parse_expr();- expect(Token::RPAREN);- }-- // Parse identifier list- while (current().type == Token::IDENT) {- Token name = current();- advance();-- if (source) {- // inherit (expr) x → x = expr.x- auto select = std::make_shared<Node>(SelectNode(- source,- std::make_shared<Node>(ConstStringNode(name.value))- ));- bindings.push_back({name.value, select});- } else {- // inherit x → x = x- auto var = std::make_shared<Node>(VarNode(0, name.value));- bindings.push_back({name.value, var});- }- }-- expect(Token::SEMICOLON);- continue;- }+ std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings; - if (current().type != Token::IDENT) break;- Token key = current();- advance();+ for (const auto& field : fields) {+ // Create arg.field selection+ auto select = std::make_shared<Node>(+ SelectNode(arg_var, std::make_shared<Node>(ConstStringNode(field.name)))); - if (consume(Token::AT)) {- auto pattern = parse_expr();- auto value = parse_expr();- bindings.push_back({key.value, value});- } else {- expect(Token::EQUALS);- auto value = parse_expr();- bindings.push_back({key.value, value});- }-- if (!consume(Token::SEMICOLON)) break;+ if (field.default_val) {+ // if arg ? field then arg.field else default+ auto has_attr = std::make_shared<Node>(+ HasAttrNode(arg_var, 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});+ } else {+ bindings.push_back({field.name, select}); }+ }++ // If named pattern, also bind the argument name+ if (named_arg) {+ bindings.push_back({*named_arg, arg_var});+ }++ // Create let expression+ auto let = LetNode(body);+ let.bindings = std::move(bindings);+ auto let_node = std::make_shared<Node>(std::move(let));++ // Create lambda+ auto lambda = LambdaNode(1, let_node);+ lambda.param_name = arg_name;+ lambda.strict_pattern = !has_ellipsis;+ return std::make_shared<Node>(std::move(lambda)); } - // Try to parse lambda, return nullptr if not a lambda- std::shared_ptr<Node> try_parse_lambda() {- size_t saved_pos = pos;+ // Not a lambda+ pos = saved_pos;+ return nullptr;+ } - // Check for named pattern: arg@{ ... }:- std::optional<std::string> named_arg;- if (current().type == Token::IDENT) {- Token name = current();- advance();- if (consume(Token::AT)) {- named_arg = name.value;- } else if (consume(Token::COLON)) {- // Simple lambda: x: body- auto body = parse_expr();- auto lambda = LambdaNode(1, body);- lambda.param_name = name.value;- return std::make_shared<Node>(std::move(lambda));- } else {- // Not a lambda, restore position- pos = saved_pos;- return nullptr;- }- }+ std::shared_ptr<Node> parse_string_interp(const std::string& raw) {+ std::vector<std::shared_ptr<Node>> parts;+ size_t i = 0;+ std::string current_str; - // Check for pattern: { ... }:- if (current().type == Token::LBRACE) {- advance();+ while (i < raw.size()) {+ if (raw[i] == '$' && i + 1 < raw.size() && raw[i + 1] == '{') {+ // Save current string part if any+ if (!current_str.empty()) {+ parts.push_back(std::make_shared<Node>(ConstStringNode(current_str)));+ current_str.clear();+ } - // Parse pattern fields- struct Field {- std::string name;- std::optional<std::shared_ptr<Node>> default_val;- };- std::vector<Field> fields;- bool has_ellipsis = false;-- while (current().type != Token::RBRACE && current().type != Token::EOF_) {- if (consume(Token::ELLIPSIS)) {- has_ellipsis = true;- if (consume(Token::COMMA)) continue;- break;- }-- if (current().type == Token::IDENT) {- Token field_name = current();- advance();-- Field field;- field.name = field_name.value;-- // Check for default value- if (consume(Token::QUESTION)) {- field.default_val = parse_expr();- }-- fields.push_back(field);-- if (consume(Token::COMMA)) continue;- break;- } else {- break;- }+ // Find matching }+ i += 2; // Skip ${+ int depth = 1;+ size_t expr_start = i;+ bool in_string = false;+ char string_quote = 0;++ while (i < raw.size() && depth > 0) {+ if (!in_string) {+ if (raw[i] == '"' || raw[i] == '\'') {+ in_string = true;+ string_quote = raw[i];+ } else if (raw[i] == '{') {+ depth++;+ } else if (raw[i] == '}') {+ depth--; }-- if (!consume(Token::RBRACE)) {- // Not a lambda pattern, restore- pos = saved_pos;- return nullptr;+ } else {+ if (raw[i] == string_quote && (i == 0 || raw[i - 1] != '\\')) {+ in_string = false;+ } else if (raw[i] == '\\') {+ i++; }+ }+ if (depth > 0)+ i++;+ } - if (!consume(Token::COLON)) {- // Not a lambda, restore- pos = saved_pos;- return nullptr;- }+ if (depth > 0) {+ throw std::runtime_error("unterminated ${ in string interpolation");+ } - // Parse body- auto body = parse_expr();-- // Desugar pattern to lambda with let bindings- // { a, b ? x }: body → arg: let a = arg.a; b = if arg ? a then arg.a else x; in body-- std::string arg_name = named_arg.value_or("_arg");- auto arg_var = std::make_shared<Node>(VarNode(0, arg_name));-- std::vector<std::pair<std::string, std::shared_ptr<Node>>> bindings;-- for (const auto& field : fields) {- // Create arg.field selection- auto select = std::make_shared<Node>(SelectNode(- arg_var,- std::make_shared<Node>(ConstStringNode(field.name))- ));-- if (field.default_val) {- // if arg ? field then arg.field else default- auto has_attr = std::make_shared<Node>(HasAttrNode(- arg_var,- 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});- } else {- bindings.push_back({field.name, select});- }- }+ // Parse the expression+ std::string expr_str = raw.substr(expr_start, i - expr_start); - // If named pattern, also bind the argument name- if (named_arg) {- bindings.push_back({*named_arg, arg_var});- }+ // Tokenize and parse the expression+ Lexer lexer(expr_str);+ auto expr_tokens = lexer.tokenize(); - // Create let expression- auto let = LetNode(body);- let.bindings = std::move(bindings);- auto let_node = std::make_shared<Node>(std::move(let));+ // Save current state+ auto saved_tokens = tokens;+ auto saved_pos = pos; - // Create lambda- auto lambda = LambdaNode(1, let_node);- lambda.param_name = arg_name;- lambda.strict_pattern = !has_ellipsis;- return std::make_shared<Node>(std::move(lambda));- }+ // Parse expression+ tokens = expr_tokens;+ pos = 0;+ auto expr = parse_expr(); - // Not a lambda+ // Restore state+ tokens = saved_tokens; pos = saved_pos;- return nullptr;- } - std::shared_ptr<Node> parse_string_interp(const std::string& raw) {- std::vector<std::shared_ptr<Node>> parts;- size_t i = 0;- std::string current_str;-- while (i < raw.size()) {- if (raw[i] == '$' && i + 1 < raw.size() && raw[i + 1] == '{') {- // Save current string part if any- if (!current_str.empty()) {- parts.push_back(std::make_shared<Node>(ConstStringNode(current_str)));- current_str.clear();- }-- // Find matching }- i += 2; // Skip ${- int depth = 1;- size_t expr_start = i;- bool in_string = false;- char string_quote = 0;-- while (i < raw.size() && depth > 0) {- if (!in_string) {- if (raw[i] == '"' || raw[i] == '\'') {- in_string = true;- string_quote = raw[i];- } else if (raw[i] == '{') {- depth++;- } else if (raw[i] == '}') {- depth--;- }- } else {- if (raw[i] == string_quote && (i == 0 || raw[i-1] != '\\')) {- in_string = false;- } else if (raw[i] == '\\') {- i++;- }- }- if (depth > 0) i++;- }-- if (depth > 0) {- throw std::runtime_error("unterminated ${ in string interpolation");- }-- // Parse the expression- std::string expr_str = raw.substr(expr_start, i - expr_start);-- // Tokenize and parse the expression- Lexer lexer(expr_str);- auto expr_tokens = lexer.tokenize();-- // Save current state- auto saved_tokens = tokens;- auto saved_pos = pos;-- // Parse expression- tokens = expr_tokens;- pos = 0;- auto expr = parse_expr();-- // Restore state- tokens = saved_tokens;- pos = saved_pos;-- // Convert to string using toString builtin- auto to_string = std::make_shared<Node>(VarNode(0, "toString"));- auto str_expr = std::make_shared<Node>(AppNode(to_string, expr));- parts.push_back(str_expr);-- i++; // Skip }- } else {- current_str += raw[i];- i++;- }- }+ // Convert to string using toString builtin+ auto to_string = std::make_shared<Node>(VarNode(0, "toString"));+ auto str_expr = std::make_shared<Node>(AppNode(to_string, expr));+ parts.push_back(str_expr); - // Add remaining string part- if (!current_str.empty()) {- parts.push_back(std::make_shared<Node>(ConstStringNode(current_str)));- }+ i++; // Skip }+ } else {+ current_str += raw[i];+ i++;+ }+ } - // Build concatenation tree- if (parts.empty()) {- return std::make_shared<Node>(ConstStringNode(""));- }+ // Add remaining string part+ if (!current_str.empty()) {+ parts.push_back(std::make_shared<Node>(ConstStringNode(current_str)));+ } - auto result = parts[0];- for (size_t j = 1; j < parts.size(); j++) {- // Use ADD (+) for string concatenation; CONCAT (++) is Nix list concatenation- result = std::make_shared<Node>(BinaryOpNode(BinaryOp::ADD, result, parts[j]));- }+ // Build concatenation tree+ if (parts.empty()) {+ return std::make_shared<Node>(ConstStringNode(""));+ } - return result;+ auto result = parts[0];+ for (size_t j = 1; j < parts.size(); j++) {+ // Use ADD (+) for string concatenation; CONCAT (++) is Nix list concatenation+ result = std::make_shared<Node>(BinaryOpNode(BinaryOp::ADD, result, parts[j])); }++ return result;+ } }; Parser::Parser() : pImpl(std::make_unique<Impl>()) {} Parser::~Parser() = default; std::shared_ptr<Node> Parser::parse(const std::string& source, const std::string& path) {- pImpl->current_file = path;+ pImpl->current_file = path; - Lexer lexer(source);- pImpl->tokens = lexer.tokenize();- pImpl->pos = 0;+ Lexer lexer(source);+ pImpl->tokens = lexer.tokenize();+ pImpl->pos = 0; - return pImpl->parse_expr();+ return pImpl->parse_expr(); } std::shared_ptr<Node> Parser::parse_file(const std::string& path) {- std::string content = read_file(path);- return parse(content, path);+ std::string content = read_file(path);+ return parse(content, path); } -}+} // namespace nix_irc@@ -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\ No newline at end of file@@ -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 <filesystem> #include <string>-#include <vector> #include <unordered_set>-#include <filesystem>+#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\ No newline at end of file@@ -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;-- 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_u8(uint8_t val) {- buffer.push_back(val);+ 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_u64(uint64_t val) {+ for (int i = 0; i < 8; i++) {+ buffer.push_back((val >> (i * 8)) & 0xFF); }-- 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->write_u32(IR_MAGIC);- pImpl->write_u32(IR_VERSION);+ pImpl->buffer.clear();++ 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.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);+ }++ if (module.entry && module.entry != nullptr) {+ pImpl->write_u8(1);+ pImpl->write_node(*module.entry);+ } else {+ pImpl->write_u8(0);+ }++ return pImpl->buffer;+} - pImpl->write_u32(module.sources.size());- for (const auto& src : module.sources) {- pImpl->write_string(src.path);- pImpl->write_string(src.content);+struct Deserializer::Impl {+ 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;+ }++ uint64_t read_u64() {+ uint64_t val = 0;+ for (int i = 0; i < 8; i++) {+ val |= (uint64_t) buffer[pos + i] << (i * 8); }-- pImpl->write_u32(module.imports.size());- for (const auto& [from, to] : module.imports) {- pImpl->write_string(from);- pImpl->write_string(to);+ 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)); }-- pImpl->write_u32(module.string_table.size());- for (const auto& [str, id] : module.string_table) {- pImpl->write_string(str);- pImpl->write_u32(id);+ case NodeType::CONST_STRING: {+ std::string val = read_string();+ return std::make_shared<Node>(ConstStringNode(val, line)); }-- if (module.entry && module.entry != nullptr) {- pImpl->write_u8(1);- pImpl->write_node(*module.entry);- } else {- pImpl->write_u8(0);+ case NodeType::CONST_PATH: {+ std::string val = read_string();+ return std::make_shared<Node>(ConstPathNode(val, line)); }-- return pImpl->buffer;-}--struct Deserializer::Impl {- 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;+ case NodeType::CONST_BOOL: {+ bool val = read_u8() != 0;+ return std::make_shared<Node>(ConstBoolNode(val, line)); }-- 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;+ 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)); }-- uint8_t read_u8() {- return buffer[pos++];+ case NodeType::LAMBDA: {+ uint32_t arity = read_u32();+ auto body = read_node();+ return std::make_shared<Node>(LambdaNode(arity, body, 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::APP: {+ auto func = read_node();+ auto arg = read_node();+ return std::make_shared<Node>(AppNode(func, arg, 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::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;-- IRModule module;-- 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 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_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();- }-- return module;+ pImpl->buffer = data;+ pImpl->pos = 0;++ IRModule module;++ 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 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_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();+ }++ 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\ No newline at end of file@@ -2,14 +2,14 @@ #define NIX_IRC_TYPES_H #include <cstdint>+#include <fstream>+#include <memory>+#include <optional>+#include <sstream> #include <string>-#include <vector> #include <unordered_map>-#include <optional>-#include <memory> #include <variant>-#include <fstream>-#include <sstream>+#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- >;-- Variant data;-- 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>- const T* get_if() const { return std::get_if<T>(&data); }-- template<typename T>- bool holds() const { return std::holds_alternative<T>(data); }+ 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;++ 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> const T* get_if() const { return std::get_if<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);