irc/parser: fix list parsing and function application
Fixes bug where `concat [1 2 3] [4 5 6]` tried to apply integer 1 as a function. Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I6f373dd83bcac9e59286b0448472200b6a6a6964
This commit is contained in:
parent
8bce6c27b5
commit
6587d07833
1 changed files with 62 additions and 17 deletions
|
|
@ -636,8 +636,9 @@ 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] == '-' ||
|
// Note: Don't include '.' here - it's used for selection (a.b.c)
|
||||||
input[pos] == '+' || input[pos] == '.'))
|
// URIs are handled separately by checking for '://' pattern
|
||||||
|
while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-'))
|
||||||
pos++;
|
pos++;
|
||||||
std::string ident = input.substr(start, pos - start);
|
std::string ident = input.substr(start, pos - start);
|
||||||
|
|
||||||
|
|
@ -927,16 +928,25 @@ public:
|
||||||
std::shared_ptr<Node> left = parse_expr3();
|
std::shared_ptr<Node> left = parse_expr3();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (current().type == Token::LBRACKET) {
|
if (current().type == Token::STRING) {
|
||||||
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();
|
Token s = current();
|
||||||
advance();
|
advance();
|
||||||
auto arg = std::make_shared<Node>(ConstStringNode(s.value));
|
auto arg = std::make_shared<Node>(ConstStringNode(s.value));
|
||||||
left = std::make_shared<Node>(AppNode(left, arg));
|
left = std::make_shared<Node>(AppNode(left, arg));
|
||||||
|
} else if (current().type == Token::LPAREN) {
|
||||||
|
// Function application with parenthesized argument: func (expr)
|
||||||
|
advance();
|
||||||
|
auto arg = parse_expr();
|
||||||
|
expect(Token::RPAREN);
|
||||||
|
left = std::make_shared<Node>(AppNode(left, arg));
|
||||||
|
} else if (current().type == Token::IDENT || current().type == Token::INT ||
|
||||||
|
current().type == Token::FLOAT || current().type == Token::BOOL ||
|
||||||
|
current().type == Token::PATH || current().type == Token::LOOKUP_PATH ||
|
||||||
|
current().type == Token::URI || current().type == Token::LBRACKET) {
|
||||||
|
// Juxtaposition application: f x
|
||||||
|
// Parse the argument as a primary expression (which handles lists, etc.)
|
||||||
|
auto arg = parse_expr3();
|
||||||
|
left = std::make_shared<Node>(AppNode(left, arg));
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -969,6 +979,16 @@ public:
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle rec { ... } syntax
|
||||||
|
if (consume(Token::REC)) {
|
||||||
|
expect(Token::LBRACE);
|
||||||
|
auto attrs = parse_attrs();
|
||||||
|
if (auto* attrset = attrs->get_if<AttrsetNode>()) {
|
||||||
|
attrset->recursive = true;
|
||||||
|
}
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
|
||||||
if (consume(Token::LBRACE)) {
|
if (consume(Token::LBRACE)) {
|
||||||
return parse_attrs();
|
return parse_attrs();
|
||||||
}
|
}
|
||||||
|
|
@ -1151,6 +1171,35 @@ public:
|
||||||
return std::make_shared<Node>(std::move(attrs));
|
return std::make_shared<Node>(std::move(attrs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse a list element: supports selections but NOT juxtaposition application
|
||||||
|
// This prevents [1 2 3] from being parsed as ((1 2) 3)
|
||||||
|
std::shared_ptr<Node> parse_list_element() {
|
||||||
|
auto left = parse_expr3();
|
||||||
|
|
||||||
|
// Handle selections (a.b.c)
|
||||||
|
while (current().type == Token::DOT) {
|
||||||
|
advance();
|
||||||
|
Token name = current();
|
||||||
|
if (name.type == Token::IDENT) {
|
||||||
|
advance();
|
||||||
|
auto attr = std::make_shared<Node>(ConstStringNode(name.value));
|
||||||
|
left = std::make_shared<Node>(SelectNode(left, attr));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for 'or' default value
|
||||||
|
if (left->get_if<SelectNode>() && current().type == Token::IDENT && current().value == "or") {
|
||||||
|
advance();
|
||||||
|
auto default_expr = parse_expr3();
|
||||||
|
auto* select = left->get_if<SelectNode>();
|
||||||
|
select->default_expr = default_expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_list() {
|
std::shared_ptr<Node> parse_list() {
|
||||||
std::vector<std::shared_ptr<Node>> elements;
|
std::vector<std::shared_ptr<Node>> elements;
|
||||||
|
|
||||||
|
|
@ -1158,18 +1207,14 @@ public:
|
||||||
return std::make_shared<Node>(ListNode(elements));
|
return std::make_shared<Node>(ListNode(elements));
|
||||||
}
|
}
|
||||||
|
|
||||||
while (current().type != Token::RBRACKET) {
|
while (current().type != Token::RBRACKET && current().type != Token::EOF_) {
|
||||||
elements.push_back(parse_expr());
|
elements.push_back(parse_list_element());
|
||||||
if (!consume(Token::RBRACKET)) {
|
if (current().type == Token::RBRACKET) {
|
||||||
// Elements are whitespace-separated in Nix, no comma required
|
break;
|
||||||
// But we'll continue parsing until we hit ]
|
|
||||||
} else {
|
|
||||||
// Found closing bracket
|
|
||||||
return std::make_shared<Node>(ListNode(elements));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unreachable, but for safety
|
expect(Token::RBRACKET);
|
||||||
return std::make_shared<Node>(ListNode(elements));
|
return std::make_shared<Node>(ListNode(elements));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue