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)));
|
||||
} else if (auto* n = node->get_if<ConstBoolNode>()) {
|
||||
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();
|
||||
} else if (auto* n = node->get_if<ConstURINode>()) {
|
||||
// Parse and validate URI, then create string with URI context
|
||||
auto parsed = parseURL(n->value, true);
|
||||
// Store URI with context - the parsed URL string
|
||||
v.mkString(parsed.to_string(), nix::NixStringContext{}, state.mem);
|
||||
// Store URI with context - use simple mkString with context
|
||||
v.mkString(parsed.to_string(), nix::NixStringContext{});
|
||||
} else if (auto* n = node->get_if<VarNode>()) {
|
||||
Value* bound = env ? env->lookup(n->index) : nullptr;
|
||||
if (!bound && env && n->name.has_value()) {
|
||||
|
|
@ -298,6 +298,32 @@ struct Evaluator::Impl {
|
|||
// ++ is list concatenation in Nix; string concat uses ADD (+)
|
||||
state.error<EvalError>("list concatenation not yet implemented").debugThrow();
|
||||
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:
|
||||
state.error<EvalError>("unknown binary operator").debugThrow();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,8 @@ struct IRGenerator::Impl {
|
|||
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.reserve(n->bindings.size());
|
||||
for (const auto& [key, val] : n->bindings) {
|
||||
new_bindings.push_back({key, convert(val)});
|
||||
}
|
||||
auto body = convert(n->body);
|
||||
|
|
@ -177,7 +178,8 @@ struct IRGenerator::Impl {
|
|||
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.reserve(n->bindings.size());
|
||||
for (const auto& [key, val] : n->bindings) {
|
||||
new_bindings.push_back({key, convert(val)});
|
||||
}
|
||||
auto body = convert(n->body);
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ struct Token {
|
|||
STAR,
|
||||
SLASH,
|
||||
CONCAT,
|
||||
MERGE,
|
||||
EQEQ,
|
||||
NE,
|
||||
LT,
|
||||
|
|
@ -173,6 +174,10 @@ public:
|
|||
tokens.push_back(TOKEN(CONCAT));
|
||||
pos += 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] == '&') {
|
||||
tokens.push_back(TOKEN(AND));
|
||||
pos += 2;
|
||||
|
|
@ -237,13 +242,13 @@ public:
|
|||
} else if (isalpha(c)) {
|
||||
// Check if it's a URI (contains ://) - look ahead
|
||||
size_t lookahead = pos;
|
||||
while (lookahead < input.size() && (isalnum(input[lookahead]) || input[lookahead] == '_' ||
|
||||
input[lookahead] == '-' || input[lookahead] == '+' ||
|
||||
input[lookahead] == '.'))
|
||||
while (lookahead < input.size() &&
|
||||
(isalnum(input[lookahead]) || input[lookahead] == '_' || input[lookahead] == '-' ||
|
||||
input[lookahead] == '+' || input[lookahead] == '.'))
|
||||
lookahead++;
|
||||
std::string potential_scheme = input.substr(pos, lookahead - pos);
|
||||
if (lookahead + 2 < input.size() && input[lookahead] == ':' && input[lookahead + 1] == '/' &&
|
||||
input[lookahead + 2] == '/') {
|
||||
if (lookahead + 2 < input.size() && input[lookahead] == ':' &&
|
||||
input[lookahead + 1] == '/' && input[lookahead + 2] == '/') {
|
||||
// It's a URI, consume the whole thing
|
||||
tokenize_uri();
|
||||
} else {
|
||||
|
|
@ -267,7 +272,7 @@ private:
|
|||
size_t line;
|
||||
size_t col;
|
||||
|
||||
void emit(Token t) {
|
||||
void emit(const Token& t) {
|
||||
tokens.push_back(t);
|
||||
pos++;
|
||||
col++;
|
||||
|
|
@ -394,7 +399,7 @@ private:
|
|||
void tokenize_ident() {
|
||||
size_t start = pos;
|
||||
while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-' ||
|
||||
input[pos] == '+' || input[pos] == '.'))
|
||||
input[pos] == '+' || input[pos] == '.'))
|
||||
pos++;
|
||||
std::string ident = input.substr(start, pos - start);
|
||||
|
||||
|
|
@ -471,6 +476,8 @@ public:
|
|||
// Get operator precedence (higher = tighter binding)
|
||||
int get_precedence(Token::Type 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:
|
||||
return 1;
|
||||
case Token::AND:
|
||||
|
|
@ -511,6 +518,8 @@ public:
|
|||
return BinaryOp::DIV;
|
||||
case Token::CONCAT:
|
||||
return BinaryOp::CONCAT;
|
||||
case Token::MERGE:
|
||||
return BinaryOp::MERGE;
|
||||
case Token::EQEQ:
|
||||
return BinaryOp::EQ;
|
||||
case Token::NE:
|
||||
|
|
|
|||
|
|
@ -43,7 +43,23 @@ enum class NodeType : uint8_t {
|
|||
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 };
|
||||
|
||||
|
|
@ -203,11 +219,11 @@ struct ForceNode {
|
|||
// Node wraps a variant for type-safe AST
|
||||
class Node {
|
||||
public:
|
||||
using Variant = std::variant<ConstIntNode, ConstFloatNode, ConstStringNode, ConstPathNode,
|
||||
ConstBoolNode, ConstNullNode, ConstURINode, VarNode, LambdaNode,
|
||||
AppNode, BinaryOpNode, UnaryOpNode, AttrsetNode, SelectNode,
|
||||
HasAttrNode, WithNode, IfNode, LetNode, LetRecNode, AssertNode,
|
||||
ThunkNode, ForceNode>;
|
||||
using Variant =
|
||||
std::variant<ConstIntNode, ConstFloatNode, ConstStringNode, ConstPathNode, ConstBoolNode,
|
||||
ConstNullNode, ConstURINode, VarNode, LambdaNode, AppNode, BinaryOpNode,
|
||||
UnaryOpNode, AttrsetNode, SelectNode, HasAttrNode, WithNode, IfNode, LetNode,
|
||||
LetRecNode, AssertNode, ThunkNode, ForceNode>;
|
||||
|
||||
Variant data;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue