irc: support lookup paths and import keyword
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I0d16726646aef82ce675c4f8d209029a6a6a6964
This commit is contained in:
parent
3c1ce0fd31
commit
a6aade6c11
4 changed files with 116 additions and 60 deletions
|
|
@ -5,10 +5,7 @@
|
||||||
#include "evaluator.h"
|
#include "evaluator.h"
|
||||||
#include "nix/expr/eval.hh"
|
#include "nix/expr/eval.hh"
|
||||||
#include "nix/expr/value.hh"
|
#include "nix/expr/value.hh"
|
||||||
#include "nix/util/error.hh"
|
|
||||||
#include "nix/util/url.hh"
|
#include "nix/util/url.hh"
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace nix_irc {
|
namespace nix_irc {
|
||||||
|
|
@ -67,11 +64,7 @@ struct Evaluator::Impl {
|
||||||
|
|
||||||
explicit Impl(EvalState& s) : state(s) {}
|
explicit Impl(EvalState& s) : state(s) {}
|
||||||
|
|
||||||
~Impl() {
|
// Destructor not needed - unique_ptr handles cleanup automatically
|
||||||
for (auto& env : environments) {
|
|
||||||
delete env.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IREnvironment* make_env(IREnvironment* parent = nullptr) {
|
IREnvironment* make_env(IREnvironment* parent = nullptr) {
|
||||||
auto env = new IREnvironment(parent);
|
auto env = new IREnvironment(parent);
|
||||||
|
|
@ -124,6 +117,11 @@ struct Evaluator::Impl {
|
||||||
auto parsed = parseURL(n->value, true);
|
auto parsed = parseURL(n->value, true);
|
||||||
// Store URI with context - use simple mkString with context
|
// Store URI with context - use simple mkString with context
|
||||||
v.mkString(parsed.to_string(), nix::NixStringContext{});
|
v.mkString(parsed.to_string(), nix::NixStringContext{});
|
||||||
|
} else if (auto* n = node->get_if<ConstLookupPathNode>()) {
|
||||||
|
// Lookup path like <nixpkgs>; resolve via Nix search path
|
||||||
|
// We can use EvalState's searchPath to resolve
|
||||||
|
auto path = state.findFile(n->value);
|
||||||
|
v.mkPath(path);
|
||||||
} else if (auto* n = node->get_if<VarNode>()) {
|
} else if (auto* n = node->get_if<VarNode>()) {
|
||||||
Value* bound = env ? env->lookup(n->index) : nullptr;
|
Value* bound = env ? env->lookup(n->index) : nullptr;
|
||||||
if (!bound && env && n->name.has_value()) {
|
if (!bound && env && n->name.has_value()) {
|
||||||
|
|
@ -369,17 +367,16 @@ struct Evaluator::Impl {
|
||||||
} else if (auto* n = node->get_if<LetNode>()) {
|
} else if (auto* n = node->get_if<LetNode>()) {
|
||||||
auto let_env = make_env(env);
|
auto let_env = make_env(env);
|
||||||
for (const auto& [name, expr] : n->bindings) {
|
for (const auto& [name, expr] : n->bindings) {
|
||||||
Value* val = make_thunk(expr, env);
|
// Create thunks in let_env so bindings can reference each other
|
||||||
|
Value* val = make_thunk(expr, let_env);
|
||||||
let_env->bind(val);
|
let_env->bind(val);
|
||||||
}
|
}
|
||||||
eval_node(n->body, v, let_env);
|
eval_node(n->body, v, let_env);
|
||||||
} else if (auto* n = node->get_if<LetRecNode>()) {
|
} else if (auto* n = node->get_if<LetRecNode>()) {
|
||||||
auto letrec_env = make_env(env);
|
auto letrec_env = make_env(env);
|
||||||
std::vector<Value*> thunk_vals;
|
|
||||||
|
|
||||||
for (const auto& [name, expr] : n->bindings) {
|
for (const auto& [name, expr] : n->bindings) {
|
||||||
Value* val = make_thunk(expr, letrec_env);
|
Value* val = make_thunk(expr, letrec_env);
|
||||||
thunk_vals.push_back(val);
|
|
||||||
letrec_env->bind(val);
|
letrec_env->bind(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -389,6 +386,8 @@ struct Evaluator::Impl {
|
||||||
|
|
||||||
IREnvironment* attr_env = env;
|
IREnvironment* attr_env = env;
|
||||||
if (n->recursive) {
|
if (n->recursive) {
|
||||||
|
// For recursive attrsets, create environment where all bindings can
|
||||||
|
// see each other
|
||||||
attr_env = make_env(env);
|
attr_env = make_env(env);
|
||||||
for (const auto& [key, val] : n->attrs) {
|
for (const auto& [key, val] : n->attrs) {
|
||||||
Value* thunk = make_thunk(val, attr_env);
|
Value* thunk = make_thunk(val, attr_env);
|
||||||
|
|
@ -396,13 +395,9 @@ struct Evaluator::Impl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attributes should be lazy, so store as thunks and not evaluated values
|
||||||
for (const auto& [key, val] : n->attrs) {
|
for (const auto& [key, val] : n->attrs) {
|
||||||
Value* attr_val = state.allocValue();
|
Value* attr_val = make_thunk(val, attr_env);
|
||||||
if (n->recursive) {
|
|
||||||
eval_node(val, *attr_val, attr_env);
|
|
||||||
} else {
|
|
||||||
eval_node(val, *attr_val, env);
|
|
||||||
}
|
|
||||||
bindings.insert(state.symbols.create(key), attr_val);
|
bindings.insert(state.symbols.create(key), attr_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -480,6 +475,21 @@ struct Evaluator::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_node(n->body, v, env);
|
eval_node(n->body, v, env);
|
||||||
|
} else if (auto* n = node->get_if<ImportNode>()) {
|
||||||
|
// Evaluate path expression to get the file path
|
||||||
|
Value* path_val = state.allocValue();
|
||||||
|
eval_node(n->path, *path_val, env);
|
||||||
|
force(path_val);
|
||||||
|
|
||||||
|
// Path should be a string or path type, convert to SourcePath
|
||||||
|
if (path_val->type() == nPath) {
|
||||||
|
state.evalFile(path_val->path(), v);
|
||||||
|
} else if (path_val->type() == nString) {
|
||||||
|
auto path = state.rootPath(CanonPath(path_val->c_str()));
|
||||||
|
state.evalFile(path, v);
|
||||||
|
} else {
|
||||||
|
state.error<EvalError>("import argument must be a path or string").debugThrow();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <regex>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
@ -24,22 +22,27 @@ static std::string read_file(const std::string& path) {
|
||||||
if (!f) {
|
if (!f) {
|
||||||
throw std::runtime_error("Cannot open file: " + path);
|
throw std::runtime_error("Cannot open file: " + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure FILE* is always closed
|
||||||
|
auto file_closer = [](FILE* fp) {
|
||||||
|
if (fp)
|
||||||
|
fclose(fp);
|
||||||
|
};
|
||||||
|
std::unique_ptr<FILE, decltype(file_closer)> file_guard(f, file_closer);
|
||||||
|
|
||||||
fseek(f, 0, SEEK_END);
|
fseek(f, 0, SEEK_END);
|
||||||
long size = ftell(f);
|
long size = ftell(f);
|
||||||
fseek(f, 0, SEEK_SET);
|
fseek(f, 0, SEEK_SET);
|
||||||
std::string content(size, '\0');
|
std::string content(size, '\0');
|
||||||
if (fread(content.data(), 1, size, f) != static_cast<size_t>(size)) {
|
if (fread(content.data(), 1, size, f) != static_cast<size_t>(size)) {
|
||||||
fclose(f);
|
|
||||||
throw std::runtime_error("Failed to read file: " + path);
|
throw std::runtime_error("Failed to read file: " + path);
|
||||||
}
|
}
|
||||||
fclose(f);
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<std::string, std::string> run_command(const std::string& cmd) {
|
static std::pair<std::string, std::string> run_command(const std::string& cmd) {
|
||||||
std::array<char, 256> buffer;
|
std::array<char, 256> buffer;
|
||||||
std::string result;
|
std::string result;
|
||||||
std::string error;
|
|
||||||
|
|
||||||
FILE* pipe = popen(cmd.c_str(), "r");
|
FILE* pipe = popen(cmd.c_str(), "r");
|
||||||
if (!pipe)
|
if (!pipe)
|
||||||
|
|
@ -53,7 +56,7 @@ static std::pair<std::string, std::string> run_command(const std::string& cmd) {
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
throw std::runtime_error("Command failed: " + cmd);
|
throw std::runtime_error("Command failed: " + cmd);
|
||||||
}
|
}
|
||||||
return {result, error};
|
return {result, ""};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Token {
|
struct Token {
|
||||||
|
|
@ -68,6 +71,7 @@ struct Token {
|
||||||
STRING,
|
STRING,
|
||||||
STRING_INTERP,
|
STRING_INTERP,
|
||||||
PATH,
|
PATH,
|
||||||
|
LOOKUP_PATH,
|
||||||
INT,
|
INT,
|
||||||
FLOAT,
|
FLOAT,
|
||||||
URI,
|
URI,
|
||||||
|
|
@ -81,6 +85,7 @@ struct Token {
|
||||||
ASSERT,
|
ASSERT,
|
||||||
WITH,
|
WITH,
|
||||||
INHERIT,
|
INHERIT,
|
||||||
|
IMPORT,
|
||||||
DOT,
|
DOT,
|
||||||
SEMICOLON,
|
SEMICOLON,
|
||||||
COLON,
|
COLON,
|
||||||
|
|
@ -204,7 +209,29 @@ public:
|
||||||
emit(TOKEN(SLASH));
|
emit(TOKEN(SLASH));
|
||||||
}
|
}
|
||||||
} else if (c == '<') {
|
} else if (c == '<') {
|
||||||
emit(TOKEN(LT));
|
// Check for lookup path <nixpkgs> vs comparison operator
|
||||||
|
size_t end = pos + 1;
|
||||||
|
bool is_lookup_path = false;
|
||||||
|
|
||||||
|
// Scan for valid lookup path characters until >
|
||||||
|
while (end < input.size() &&
|
||||||
|
(isalnum(input[end]) || input[end] == '-' || input[end] == '_' ||
|
||||||
|
input[end] == '/' || input[end] == '.')) {
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we found > and there's content, it's a lookup path
|
||||||
|
if (end < input.size() && input[end] == '>' && end > pos + 1) {
|
||||||
|
std::string path = input.substr(pos + 1, end - pos - 1);
|
||||||
|
tokens.push_back({Token::LOOKUP_PATH, path, line, col});
|
||||||
|
pos = end + 1;
|
||||||
|
col += (end - pos + 1);
|
||||||
|
is_lookup_path = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_lookup_path) {
|
||||||
|
emit(TOKEN(LT));
|
||||||
|
}
|
||||||
} else if (c == '>') {
|
} else if (c == '>') {
|
||||||
emit(TOKEN(GT));
|
emit(TOKEN(GT));
|
||||||
} else if (c == '!') {
|
} else if (c == '!') {
|
||||||
|
|
@ -430,6 +457,8 @@ private:
|
||||||
type = Token::WITH;
|
type = Token::WITH;
|
||||||
else if (ident == "inherit")
|
else if (ident == "inherit")
|
||||||
type = Token::INHERIT;
|
type = Token::INHERIT;
|
||||||
|
else if (ident == "import")
|
||||||
|
type = Token::IMPORT;
|
||||||
else if (ident == "true")
|
else if (ident == "true")
|
||||||
type = Token::BOOL;
|
type = Token::BOOL;
|
||||||
else if (ident == "false")
|
else if (ident == "false")
|
||||||
|
|
@ -620,42 +649,18 @@ public:
|
||||||
if (name.type == Token::IDENT) {
|
if (name.type == Token::IDENT) {
|
||||||
advance();
|
advance();
|
||||||
auto attr = std::make_shared<Node>(ConstStringNode(name.value));
|
auto attr = std::make_shared<Node>(ConstStringNode(name.value));
|
||||||
auto result = std::make_shared<Node>(SelectNode(left, attr));
|
left = std::make_shared<Node>(SelectNode(left, attr));
|
||||||
|
// Continue loop to handle multi-dot selections (a.b.c)
|
||||||
if (consume(Token::DOT)) {
|
continue;
|
||||||
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;
|
// If we get here, the token after DOT was not IDENT or LBRACE
|
||||||
|
// This is a parse error, but we'll just return what we have
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parse_expr_attrs(std::shared_ptr<Node>&) {
|
|
||||||
// Extended selection syntax
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_expr2() {
|
std::shared_ptr<Node> parse_expr2() {
|
||||||
std::shared_ptr<Node> left = parse_expr3();
|
std::shared_ptr<Node> left = parse_expr3();
|
||||||
|
|
||||||
|
|
@ -679,6 +684,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_expr3() {
|
std::shared_ptr<Node> parse_expr3() {
|
||||||
|
// Handle import expression
|
||||||
|
if (consume(Token::IMPORT)) {
|
||||||
|
auto path_expr = parse_expr3();
|
||||||
|
return std::make_shared<Node>(ImportNode(path_expr));
|
||||||
|
}
|
||||||
|
|
||||||
// Handle unary operators
|
// Handle unary operators
|
||||||
if (consume(Token::MINUS)) {
|
if (consume(Token::MINUS)) {
|
||||||
auto operand = parse_expr3();
|
auto operand = parse_expr3();
|
||||||
|
|
@ -742,6 +753,11 @@ public:
|
||||||
return std::make_shared<Node>(ConstPathNode(t.value));
|
return std::make_shared<Node>(ConstPathNode(t.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (t.type == Token::LOOKUP_PATH) {
|
||||||
|
advance();
|
||||||
|
return std::make_shared<Node>(ConstLookupPathNode(t.value));
|
||||||
|
}
|
||||||
|
|
||||||
if (t.type == Token::BOOL) {
|
if (t.type == Token::BOOL) {
|
||||||
advance();
|
advance();
|
||||||
return std::make_shared<Node>(ConstBoolNode(t.value == "true"));
|
return std::make_shared<Node>(ConstBoolNode(t.value == "true"));
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#include "serializer.h"
|
#include "serializer.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace nix_irc {
|
namespace nix_irc {
|
||||||
|
|
||||||
|
|
@ -43,6 +42,8 @@ struct Serializer::Impl {
|
||||||
return NodeType::CONST_NULL;
|
return NodeType::CONST_NULL;
|
||||||
if (node.holds<ConstURINode>())
|
if (node.holds<ConstURINode>())
|
||||||
return NodeType::CONST_URI;
|
return NodeType::CONST_URI;
|
||||||
|
if (node.holds<ConstLookupPathNode>())
|
||||||
|
return NodeType::CONST_LOOKUP_PATH;
|
||||||
if (node.holds<VarNode>())
|
if (node.holds<VarNode>())
|
||||||
return NodeType::VAR;
|
return NodeType::VAR;
|
||||||
if (node.holds<LambdaNode>())
|
if (node.holds<LambdaNode>())
|
||||||
|
|
@ -53,6 +54,8 @@ struct Serializer::Impl {
|
||||||
return NodeType::BINARY_OP;
|
return NodeType::BINARY_OP;
|
||||||
if (node.holds<UnaryOpNode>())
|
if (node.holds<UnaryOpNode>())
|
||||||
return NodeType::UNARY_OP;
|
return NodeType::UNARY_OP;
|
||||||
|
if (node.holds<ImportNode>())
|
||||||
|
return NodeType::IMPORT;
|
||||||
if (node.holds<AttrsetNode>())
|
if (node.holds<AttrsetNode>())
|
||||||
return NodeType::ATTRSET;
|
return NodeType::ATTRSET;
|
||||||
if (node.holds<SelectNode>())
|
if (node.holds<SelectNode>())
|
||||||
|
|
@ -97,6 +100,8 @@ struct Serializer::Impl {
|
||||||
// No data for null
|
// No data for null
|
||||||
} else if (auto* n = node.get_if<ConstURINode>()) {
|
} else if (auto* n = node.get_if<ConstURINode>()) {
|
||||||
write_string(n->value);
|
write_string(n->value);
|
||||||
|
} else if (auto* n = node.get_if<ConstLookupPathNode>()) {
|
||||||
|
write_string(n->value);
|
||||||
} else if (auto* n = node.get_if<VarNode>()) {
|
} else if (auto* n = node.get_if<VarNode>()) {
|
||||||
write_u32(n->index);
|
write_u32(n->index);
|
||||||
} else if (auto* n = node.get_if<LambdaNode>()) {
|
} else if (auto* n = node.get_if<LambdaNode>()) {
|
||||||
|
|
@ -118,6 +123,9 @@ struct Serializer::Impl {
|
||||||
write_u8(static_cast<uint8_t>(n->op));
|
write_u8(static_cast<uint8_t>(n->op));
|
||||||
if (n->operand)
|
if (n->operand)
|
||||||
write_node(*n->operand);
|
write_node(*n->operand);
|
||||||
|
} else if (auto* n = node.get_if<ImportNode>()) {
|
||||||
|
if (n->path)
|
||||||
|
write_node(*n->path);
|
||||||
} else if (auto* n = node.get_if<AttrsetNode>()) {
|
} else if (auto* n = node.get_if<AttrsetNode>()) {
|
||||||
write_u8(n->recursive ? 1 : 0);
|
write_u8(n->recursive ? 1 : 0);
|
||||||
write_u32(n->attrs.size());
|
write_u32(n->attrs.size());
|
||||||
|
|
@ -289,6 +297,10 @@ struct Deserializer::Impl {
|
||||||
std::string val = read_string();
|
std::string val = read_string();
|
||||||
return std::make_shared<Node>(ConstURINode(val, line));
|
return std::make_shared<Node>(ConstURINode(val, line));
|
||||||
}
|
}
|
||||||
|
case NodeType::CONST_LOOKUP_PATH: {
|
||||||
|
std::string val = read_string();
|
||||||
|
return std::make_shared<Node>(ConstLookupPathNode(val, line));
|
||||||
|
}
|
||||||
case NodeType::VAR: {
|
case NodeType::VAR: {
|
||||||
uint32_t index = read_u32();
|
uint32_t index = read_u32();
|
||||||
return std::make_shared<Node>(VarNode(index, "", line));
|
return std::make_shared<Node>(VarNode(index, "", line));
|
||||||
|
|
@ -314,6 +326,10 @@ struct Deserializer::Impl {
|
||||||
auto operand = read_node();
|
auto operand = read_node();
|
||||||
return std::make_shared<Node>(UnaryOpNode(op, operand, line));
|
return std::make_shared<Node>(UnaryOpNode(op, operand, line));
|
||||||
}
|
}
|
||||||
|
case NodeType::IMPORT: {
|
||||||
|
auto path = read_node();
|
||||||
|
return std::make_shared<Node>(ImportNode(path, line));
|
||||||
|
}
|
||||||
case NodeType::ATTRSET: {
|
case NodeType::ATTRSET: {
|
||||||
bool recursive = read_u8() != 0;
|
bool recursive = read_u8() != 0;
|
||||||
uint32_t num_attrs = read_u32();
|
uint32_t num_attrs = read_u32();
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,8 @@
|
||||||
#define NIX_IRC_TYPES_H
|
#define NIX_IRC_TYPES_H
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <fstream>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
@ -25,11 +23,13 @@ enum class NodeType : uint8_t {
|
||||||
CONST_BOOL = 0x04,
|
CONST_BOOL = 0x04,
|
||||||
CONST_NULL = 0x05,
|
CONST_NULL = 0x05,
|
||||||
CONST_URI = 0x07,
|
CONST_URI = 0x07,
|
||||||
|
CONST_LOOKUP_PATH = 0x08,
|
||||||
VAR = 0x10,
|
VAR = 0x10,
|
||||||
LAMBDA = 0x20,
|
LAMBDA = 0x20,
|
||||||
APP = 0x21,
|
APP = 0x21,
|
||||||
BINARY_OP = 0x22,
|
BINARY_OP = 0x22,
|
||||||
UNARY_OP = 0x23,
|
UNARY_OP = 0x23,
|
||||||
|
IMPORT = 0x24,
|
||||||
ATTRSET = 0x30,
|
ATTRSET = 0x30,
|
||||||
SELECT = 0x31,
|
SELECT = 0x31,
|
||||||
HAS_ATTR = 0x34,
|
HAS_ATTR = 0x34,
|
||||||
|
|
@ -107,6 +107,12 @@ struct ConstURINode {
|
||||||
ConstURINode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}
|
ConstURINode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ConstLookupPathNode {
|
||||||
|
std::string value; // e.g., "nixpkgs" or "nixpkgs/lib"
|
||||||
|
uint32_t line = 0;
|
||||||
|
ConstLookupPathNode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}
|
||||||
|
};
|
||||||
|
|
||||||
struct VarNode {
|
struct VarNode {
|
||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
std::optional<std::string> name;
|
std::optional<std::string> name;
|
||||||
|
|
@ -204,6 +210,12 @@ struct AssertNode {
|
||||||
AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l = 0);
|
AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ImportNode {
|
||||||
|
std::shared_ptr<Node> path; // Path expression to import
|
||||||
|
uint32_t line = 0;
|
||||||
|
ImportNode(std::shared_ptr<Node> p, uint32_t l = 0);
|
||||||
|
};
|
||||||
|
|
||||||
struct ThunkNode {
|
struct ThunkNode {
|
||||||
std::shared_ptr<Node> expr;
|
std::shared_ptr<Node> expr;
|
||||||
uint32_t line = 0;
|
uint32_t line = 0;
|
||||||
|
|
@ -221,9 +233,9 @@ class Node {
|
||||||
public:
|
public:
|
||||||
using Variant =
|
using Variant =
|
||||||
std::variant<ConstIntNode, ConstFloatNode, ConstStringNode, ConstPathNode, ConstBoolNode,
|
std::variant<ConstIntNode, ConstFloatNode, ConstStringNode, ConstPathNode, ConstBoolNode,
|
||||||
ConstNullNode, ConstURINode, VarNode, LambdaNode, AppNode, BinaryOpNode,
|
ConstNullNode, ConstURINode, ConstLookupPathNode, VarNode, LambdaNode, AppNode,
|
||||||
UnaryOpNode, AttrsetNode, SelectNode, HasAttrNode, WithNode, IfNode, LetNode,
|
BinaryOpNode, UnaryOpNode, ImportNode, AttrsetNode, SelectNode, HasAttrNode,
|
||||||
LetRecNode, AssertNode, ThunkNode, ForceNode>;
|
WithNode, IfNode, LetNode, LetRecNode, AssertNode, ThunkNode, ForceNode>;
|
||||||
|
|
||||||
Variant data;
|
Variant data;
|
||||||
|
|
||||||
|
|
@ -270,6 +282,8 @@ inline LetRecNode::LetRecNode(std::shared_ptr<Node> b, uint32_t l) : body(std::m
|
||||||
inline AssertNode::AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l)
|
inline AssertNode::AssertNode(std::shared_ptr<Node> c, std::shared_ptr<Node> b, uint32_t l)
|
||||||
: cond(std::move(c)), body(std::move(b)), line(l) {}
|
: cond(std::move(c)), body(std::move(b)), line(l) {}
|
||||||
|
|
||||||
|
inline ImportNode::ImportNode(std::shared_ptr<Node> p, uint32_t l) : path(std::move(p)), line(l) {}
|
||||||
|
|
||||||
inline ThunkNode::ThunkNode(std::shared_ptr<Node> e, uint32_t l) : expr(std::move(e)), line(l) {}
|
inline ThunkNode::ThunkNode(std::shared_ptr<Node> e, uint32_t l) : expr(std::move(e)), line(l) {}
|
||||||
|
|
||||||
inline ForceNode::ForceNode(std::shared_ptr<Node> e, uint32_t l) : expr(std::move(e)), line(l) {}
|
inline ForceNode::ForceNode(std::shared_ptr<Node> e, uint32_t l) : expr(std::move(e)), line(l) {}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue