Skip to content

Commit f00b11e

Browse files
authored
Merge pull request glayzzle#485 from czosel/fix-right-associative-operators
fix: handle right-associative operators ** and ??
2 parents 85ed983 + cb84748 commit f00b11e

File tree

4 files changed

+65
-11
lines changed

4 files changed

+65
-11
lines changed

src/ast.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ AST.precedence = {};
164164
["*", "/", "%"],
165165
["!"],
166166
["instanceof"],
167-
["cast", "silent"]
167+
["cast", "silent"],
168+
["**"]
168169
// TODO: [ (array)
169170
// TODO: clone, new
170171
].forEach(function(list, index) {
@@ -173,6 +174,10 @@ AST.precedence = {};
173174
});
174175
});
175176

177+
AST.prototype.isRightAssociative = function(operator) {
178+
return operator === "**" || operator === "??";
179+
};
180+
176181
/**
177182
* Change parent node informations after swapping childs
178183
*/
@@ -230,7 +235,12 @@ AST.prototype.resolvePrecedence = function(result, parser) {
230235
if (result.right.kind === "bin") {
231236
lLevel = AST.precedence[result.type];
232237
rLevel = AST.precedence[result.right.type];
233-
if (lLevel && rLevel && rLevel <= lLevel) {
238+
if (
239+
lLevel &&
240+
rLevel &&
241+
rLevel <= lLevel &&
242+
!this.isRightAssociative(result.type)
243+
) {
234244
// https://github.com/glayzzle/php-parser/issues/79
235245
// shift precedence
236246
buffer = result.right;

test/precedence.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ describe("Test precedence", function() {
8686
it("test ??", function() {
8787
shouldBeSame("1 || 2 ?? 3", "(1 || 2) ?? 3");
8888
});
89+
it("test ?? right-associative", function() {
90+
shouldBeSame("1 ?? 2 ?? 3", "1 ?? (2 ?? 3)");
91+
});
92+
it("test ** right-associative", function() {
93+
shouldBeSame("1 ** 2 ** 3", "1 ** (2 ** 3)");
94+
});
8995
it("test ?:", function() {
9096
shouldBeSame("1 ?? 2 ? 3 : 5", "(1 ?? 2) ? 3 : 5");
9197
shouldBeSame("1 and 2 ? 3 : 5", "1 and (2 ? 3 : 5)");

test/snapshot/__snapshots__/bin.test.js.snap

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,41 @@ Program {
182182
}
183183
`;
184184

185+
exports[`bin ** right-associative 1`] = `
186+
Program {
187+
"children": Array [
188+
ExpressionStatement {
189+
"expression": Bin {
190+
"kind": "bin",
191+
"left": Variable {
192+
"curly": false,
193+
"kind": "variable",
194+
"name": "a",
195+
},
196+
"right": Bin {
197+
"kind": "bin",
198+
"left": Variable {
199+
"curly": false,
200+
"kind": "variable",
201+
"name": "b",
202+
},
203+
"right": Variable {
204+
"curly": false,
205+
"kind": "variable",
206+
"name": "c",
207+
},
208+
"type": "**",
209+
},
210+
"type": "**",
211+
},
212+
"kind": "expressionstatement",
213+
},
214+
],
215+
"errors": Array [],
216+
"kind": "program",
217+
}
218+
`;
219+
185220
exports[`bin + 1`] = `
186221
Program {
187222
"children": Array [
@@ -567,25 +602,25 @@ Program {
567602
ExpressionStatement {
568603
"expression": Bin {
569604
"kind": "bin",
570-
"left": Bin {
605+
"left": Variable {
606+
"curly": false,
607+
"kind": "variable",
608+
"name": "a",
609+
},
610+
"right": Bin {
571611
"kind": "bin",
572612
"left": Variable {
573613
"curly": false,
574614
"kind": "variable",
575-
"name": "foo",
615+
"name": "b",
576616
},
577617
"right": Variable {
578618
"curly": false,
579619
"kind": "variable",
580-
"name": "bar",
620+
"name": "c",
581621
},
582622
"type": "??",
583623
},
584-
"right": Variable {
585-
"curly": false,
586-
"kind": "variable",
587-
"name": "bazz",
588-
},
589624
"type": "??",
590625
},
591626
"kind": "expressionstatement",

test/snapshot/bin.test.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,11 @@ describe("bin", () => {
103103
it("??", () => {
104104
expect(parser.parseEval("$foo ?? $bar;")).toMatchSnapshot();
105105
});
106+
it("** right-associative", () => {
107+
expect(parser.parseEval("$a ** $b ** $c;")).toMatchSnapshot();
108+
});
106109
it("?? right-associative", () => {
107-
expect(parser.parseEval("$foo ?? $bar ?? $bazz;")).toMatchSnapshot();
110+
expect(parser.parseEval("$a ?? $b ?? $c;")).toMatchSnapshot();
108111
});
109112
it("?? (php < 7)", function() {
110113
const astErr = parser.parseEval(`$var ?? $var;`, {

0 commit comments

Comments
 (0)