irc: support merge operator
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Icfb0cc81542e637d4b91c6a5788370fb6a6a6964
This commit is contained in:
parent
38c13de01d
commit
59fcd3ef92
4 changed files with 71 additions and 18 deletions
|
|
@ -117,13 +117,13 @@ struct Evaluator::Impl {
|
||||||
v.mkPath(state.rootPath(CanonPath(n->value)));
|
v.mkPath(state.rootPath(CanonPath(n->value)));
|
||||||
} else if (auto* n = node->get_if<ConstBoolNode>()) {
|
} else if (auto* n = node->get_if<ConstBoolNode>()) {
|
||||||
v.mkBool(n->value);
|
v.mkBool(n->value);
|
||||||
} else if (auto* n = node->get_if<ConstNullNode>()) { // NOLINT(bugprone-branch-clone)
|
} else if (auto* n = node->get_if<ConstNullNode>()) { // NOLINT(bugprone-branch-clone)
|
||||||
v.mkNull();
|
v.mkNull();
|
||||||
} else if (auto* n = node->get_if<ConstURINode>()) {
|
} else if (auto* n = node->get_if<ConstURINode>()) {
|
||||||
// Parse and validate URI, then create string with URI context
|
// Parse and validate URI, then create string with URI context
|
||||||
auto parsed = parseURL(n->value, true);
|
auto parsed = parseURL(n->value, true);
|
||||||
// Store URI with context - the parsed URL string
|
// Store URI with context - use simple mkString with context
|
||||||
v.mkString(parsed.to_string(), nix::NixStringContext{}, state.mem);
|
v.mkString(parsed.to_string(), nix::NixStringContext{});
|
||||||
} 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()) {
|
||||||
|
|
@ -298,6 +298,32 @@ struct Evaluator::Impl {
|
||||||
// ++ is list concatenation in Nix; string concat uses ADD (+)
|
// ++ is list concatenation in Nix; string concat uses ADD (+)
|
||||||
state.error<EvalError>("list concatenation not yet implemented").debugThrow();
|
state.error<EvalError>("list concatenation not yet implemented").debugThrow();
|
||||||
break;
|
break;
|
||||||
|
case BinaryOp::MERGE: {
|
||||||
|
// // is attrset merge - right overrides left
|
||||||
|
if (left->type() != nAttrs || right->type() != nAttrs) {
|
||||||
|
state.error<EvalError>("attrset merge requires two attrsets").debugThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a map of right attrs first (these have priority)
|
||||||
|
std::unordered_map<Symbol, Value*> right_attrs;
|
||||||
|
for (auto& attr : *right->attrs()) {
|
||||||
|
right_attrs[attr.name] = attr.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy right attrs to result
|
||||||
|
auto builder = state.buildBindings(left->attrs()->size() + right->attrs()->size());
|
||||||
|
for (auto& attr : *right->attrs()) {
|
||||||
|
builder.insert(attr.name, attr.value);
|
||||||
|
}
|
||||||
|
// Add left attrs that don't exist in right
|
||||||
|
for (auto& attr : *left->attrs()) {
|
||||||
|
if (right_attrs.find(attr.name) == right_attrs.end()) {
|
||||||
|
builder.insert(attr.name, attr.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.mkAttrs(builder.finish());
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
state.error<EvalError>("unknown binary operator").debugThrow();
|
state.error<EvalError>("unknown binary operator").debugThrow();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,8 @@ struct IRGenerator::Impl {
|
||||||
name_resolver.bind(key);
|
name_resolver.bind(key);
|
||||||
}
|
}
|
||||||
std::vector<std::pair<std::string, std::shared_ptr<Node>>> new_bindings;
|
std::vector<std::pair<std::string, std::shared_ptr<Node>>> new_bindings;
|
||||||
for (const auto& [key, val] : n->bindings) {
|
new_bindings.reserve(n->bindings.size());
|
||||||
|
for (const auto& [key, val] : n->bindings) {
|
||||||
new_bindings.push_back({key, convert(val)});
|
new_bindings.push_back({key, convert(val)});
|
||||||
}
|
}
|
||||||
auto body = convert(n->body);
|
auto body = convert(n->body);
|
||||||
|
|
@ -177,7 +178,8 @@ struct IRGenerator::Impl {
|
||||||
name_resolver.bind(key);
|
name_resolver.bind(key);
|
||||||
}
|
}
|
||||||
std::vector<std::pair<std::string, std::shared_ptr<Node>>> new_bindings;
|
std::vector<std::pair<std::string, std::shared_ptr<Node>>> new_bindings;
|
||||||
for (const auto& [key, val] : n->bindings) {
|
new_bindings.reserve(n->bindings.size());
|
||||||
|
for (const auto& [key, val] : n->bindings) {
|
||||||
new_bindings.push_back({key, convert(val)});
|
new_bindings.push_back({key, convert(val)});
|
||||||
}
|
}
|
||||||
auto body = convert(n->body);
|
auto body = convert(n->body);
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ struct Token {
|
||||||
STAR,
|
STAR,
|
||||||
SLASH,
|
SLASH,
|
||||||
CONCAT,
|
CONCAT,
|
||||||
|
MERGE,
|
||||||
EQEQ,
|
EQEQ,
|
||||||
NE,
|
NE,
|
||||||
LT,
|
LT,
|
||||||
|
|
@ -173,6 +174,10 @@ public:
|
||||||
tokens.push_back(TOKEN(CONCAT));
|
tokens.push_back(TOKEN(CONCAT));
|
||||||
pos += 2;
|
pos += 2;
|
||||||
col += 2;
|
col += 2;
|
||||||
|
} else if (c == '/' && pos + 1 < input.size() && input[pos + 1] == '/') {
|
||||||
|
tokens.push_back(TOKEN(MERGE));
|
||||||
|
pos += 2;
|
||||||
|
col += 2;
|
||||||
} else if (c == '&' && pos + 1 < input.size() && input[pos + 1] == '&') {
|
} else if (c == '&' && pos + 1 < input.size() && input[pos + 1] == '&') {
|
||||||
tokens.push_back(TOKEN(AND));
|
tokens.push_back(TOKEN(AND));
|
||||||
pos += 2;
|
pos += 2;
|
||||||
|
|
@ -237,13 +242,13 @@ public:
|
||||||
} else if (isalpha(c)) {
|
} else if (isalpha(c)) {
|
||||||
// Check if it's a URI (contains ://) - look ahead
|
// Check if it's a URI (contains ://) - look ahead
|
||||||
size_t lookahead = pos;
|
size_t lookahead = pos;
|
||||||
while (lookahead < input.size() && (isalnum(input[lookahead]) || input[lookahead] == '_' ||
|
while (lookahead < input.size() &&
|
||||||
input[lookahead] == '-' || input[lookahead] == '+' ||
|
(isalnum(input[lookahead]) || input[lookahead] == '_' || input[lookahead] == '-' ||
|
||||||
input[lookahead] == '.'))
|
input[lookahead] == '+' || input[lookahead] == '.'))
|
||||||
lookahead++;
|
lookahead++;
|
||||||
std::string potential_scheme = input.substr(pos, lookahead - pos);
|
std::string potential_scheme = input.substr(pos, lookahead - pos);
|
||||||
if (lookahead + 2 < input.size() && input[lookahead] == ':' && input[lookahead + 1] == '/' &&
|
if (lookahead + 2 < input.size() && input[lookahead] == ':' &&
|
||||||
input[lookahead + 2] == '/') {
|
input[lookahead + 1] == '/' && input[lookahead + 2] == '/') {
|
||||||
// It's a URI, consume the whole thing
|
// It's a URI, consume the whole thing
|
||||||
tokenize_uri();
|
tokenize_uri();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -267,7 +272,7 @@ private:
|
||||||
size_t line;
|
size_t line;
|
||||||
size_t col;
|
size_t col;
|
||||||
|
|
||||||
void emit(Token t) {
|
void emit(const Token& t) {
|
||||||
tokens.push_back(t);
|
tokens.push_back(t);
|
||||||
pos++;
|
pos++;
|
||||||
col++;
|
col++;
|
||||||
|
|
@ -394,7 +399,7 @@ private:
|
||||||
void tokenize_ident() {
|
void tokenize_ident() {
|
||||||
size_t start = pos;
|
size_t start = pos;
|
||||||
while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-' ||
|
while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-' ||
|
||||||
input[pos] == '+' || input[pos] == '.'))
|
input[pos] == '+' || input[pos] == '.'))
|
||||||
pos++;
|
pos++;
|
||||||
std::string ident = input.substr(start, pos - start);
|
std::string ident = input.substr(start, pos - start);
|
||||||
|
|
||||||
|
|
@ -471,6 +476,8 @@ public:
|
||||||
// Get operator precedence (higher = tighter binding)
|
// Get operator precedence (higher = tighter binding)
|
||||||
int get_precedence(Token::Type type) {
|
int get_precedence(Token::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case Token::MERGE:
|
||||||
|
return 1; // Low precedence - binds loosely, but must be > 0 to be recognized as binary op
|
||||||
case Token::OR:
|
case Token::OR:
|
||||||
return 1;
|
return 1;
|
||||||
case Token::AND:
|
case Token::AND:
|
||||||
|
|
@ -511,6 +518,8 @@ public:
|
||||||
return BinaryOp::DIV;
|
return BinaryOp::DIV;
|
||||||
case Token::CONCAT:
|
case Token::CONCAT:
|
||||||
return BinaryOp::CONCAT;
|
return BinaryOp::CONCAT;
|
||||||
|
case Token::MERGE:
|
||||||
|
return BinaryOp::MERGE;
|
||||||
case Token::EQEQ:
|
case Token::EQEQ:
|
||||||
return BinaryOp::EQ;
|
return BinaryOp::EQ;
|
||||||
case Token::NE:
|
case Token::NE:
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,23 @@ enum class NodeType : uint8_t {
|
||||||
ERROR = 0xFF
|
ERROR = 0xFF
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class BinaryOp : uint8_t { ADD, SUB, MUL, DIV, CONCAT, EQ, NE, LT, GT, LE, GE, AND, OR, IMPL };
|
enum class BinaryOp : uint8_t {
|
||||||
|
ADD,
|
||||||
|
SUB,
|
||||||
|
MUL,
|
||||||
|
DIV,
|
||||||
|
CONCAT,
|
||||||
|
EQ,
|
||||||
|
NE,
|
||||||
|
LT,
|
||||||
|
GT,
|
||||||
|
LE,
|
||||||
|
GE,
|
||||||
|
AND,
|
||||||
|
OR,
|
||||||
|
IMPL,
|
||||||
|
MERGE
|
||||||
|
};
|
||||||
|
|
||||||
enum class UnaryOp : uint8_t { NEG, NOT };
|
enum class UnaryOp : uint8_t { NEG, NOT };
|
||||||
|
|
||||||
|
|
@ -203,11 +219,11 @@ struct ForceNode {
|
||||||
// Node wraps a variant for type-safe AST
|
// Node wraps a variant for type-safe AST
|
||||||
class Node {
|
class Node {
|
||||||
public:
|
public:
|
||||||
using Variant = std::variant<ConstIntNode, ConstFloatNode, ConstStringNode, ConstPathNode,
|
using Variant =
|
||||||
ConstBoolNode, ConstNullNode, ConstURINode, VarNode, LambdaNode,
|
std::variant<ConstIntNode, ConstFloatNode, ConstStringNode, ConstPathNode, ConstBoolNode,
|
||||||
AppNode, BinaryOpNode, UnaryOpNode, AttrsetNode, SelectNode,
|
ConstNullNode, ConstURINode, VarNode, LambdaNode, AppNode, BinaryOpNode,
|
||||||
HasAttrNode, WithNode, IfNode, LetNode, LetRecNode, AssertNode,
|
UnaryOpNode, AttrsetNode, SelectNode, HasAttrNode, WithNode, IfNode, LetNode,
|
||||||
ThunkNode, ForceNode>;
|
LetRecNode, AssertNode, ThunkNode, ForceNode>;
|
||||||
|
|
||||||
Variant data;
|
Variant data;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue