From cc604bea49833236df6211942e382be73b06b853 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath <2+defunkt@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:27:18 -0800 Subject: [PATCH] fix dot.get + thing --- src/parser/parser2.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/parser/parser2.ts b/src/parser/parser2.ts index 50a453d..f11579b 100644 --- a/src/parser/parser2.ts +++ b/src/parser/parser2.ts @@ -126,6 +126,10 @@ export class Parser { if (expr.type.is('ParenExpr') && !this.isExprEnd()) expr = this.functionCall(expr) + // if dotget is followed by binary operator, continue parsing as binary expression + if (expr.type.is('DotGet') && this.is($T.Operator) && !this.is($T.Operator, '|')) + expr = this.dotGetBinOp(expr) + // one | echo if (allowPipe && this.isPipe()) return this.pipe(expr) @@ -535,10 +539,32 @@ export class Parser { return nodes ? node.push(left, nodes!) : node.push(left, ...parts) } + // continue parsing dotget/word binary operation + dotGetBinOp(left: SyntaxNode): SyntaxNode { + while (this.is($T.Operator) && !this.is($T.Operator, '|')) { + const op = this.current().value! + const bp = precedence[op] + if (bp === undefined) break + + const opNode = this.op() + const right = this.exprWithPrecedence(bp + 1) + + const nodeType = conditionals.has(op) ? 'ConditionalOp' : 'BinOp' + const node = new SyntaxNode(nodeType, left.from, right.to) + node.push(left, opNode, right) + left = node + } + return left + } + // dotget in a statement/expression (something.blah) or (something.blah arg1) dotGetFunctionCall(): SyntaxNode { const dotGet = this.dotGet() + // if followed by a binary operator (not pipe), return dotGet/Word as-is for expression parser + if (this.is($T.Operator) && !this.is($T.Operator, '|')) + return dotGet + // dotget not in scope, regular Word if (dotGet.type.is('Word')) return dotGet