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() {
|
||||
size_t start = pos;
|
||||
while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-' ||
|
||||
input[pos] == '+' || input[pos] == '.'))
|
||||
// Note: Don't include '.' here - it's used for selection (a.b.c)
|
||||
// URIs are handled separately by checking for '://' pattern
|
||||
while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-'))
|
||||
pos++;
|
||||
std::string ident = input.substr(start, pos - start);
|
||||
|
||||
|
|
@ -927,16 +928,25 @@ public:
|
|||
std::shared_ptr<Node> left = parse_expr3();
|
||||
|
||||
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) {
|
||||
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 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 {
|
||||
break;
|
||||
}
|
||||
|
|
@ -969,6 +979,16 @@ public:
|
|||
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)) {
|
||||
return parse_attrs();
|
||||
}
|
||||
|
|
@ -1151,6 +1171,35 @@ public:
|
|||
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::vector<std::shared_ptr<Node>> elements;
|
||||
|
||||
|
|
@ -1158,18 +1207,14 @@ public:
|
|||
return std::make_shared<Node>(ListNode(elements));
|
||||
}
|
||||
|
||||
while (current().type != Token::RBRACKET) {
|
||||
elements.push_back(parse_expr());
|
||||
if (!consume(Token::RBRACKET)) {
|
||||
// Elements are whitespace-separated in Nix, no comma required
|
||||
// But we'll continue parsing until we hit ]
|
||||
} else {
|
||||
// Found closing bracket
|
||||
return std::make_shared<Node>(ListNode(elements));
|
||||
while (current().type != Token::RBRACKET && current().type != Token::EOF_) {
|
||||
elements.push_back(parse_list_element());
|
||||
if (current().type == Token::RBRACKET) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Unreachable, but for safety
|
||||
expect(Token::RBRACKET);
|
||||
return std::make_shared<Node>(ListNode(elements));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue