Skip to content

Commit 46890e3

Browse files
committed
glayzzle#75 fix precedence resolution
1 parent 771a2d4 commit 46890e3

File tree

4 files changed

+80
-97
lines changed

4 files changed

+80
-97
lines changed

src/ast.js

Lines changed: 80 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,85 @@ AST.prototype.position = function(parser) {
124124
);
125125
};
126126

127+
128+
// operators in ascending order of precedence
129+
AST.precedence = {};
130+
var binOperatorsPrecedence = [
131+
['or'],
132+
['xor'],
133+
['and'],
134+
// TODO: assignment / not sure that PHP allows this with expressions
135+
['retif'],
136+
['??'],
137+
['||'],
138+
['&&'],
139+
['|'],
140+
['^'],
141+
['&'],
142+
['==', '!=', '===', '!==', /* '<>', */ '<=>'],
143+
['<', '<=', '>', '>='],
144+
['<<', '>>'],
145+
['+', '-', '.'],
146+
['*', '/', '%'],
147+
['!'],
148+
['instanceof'],
149+
// TODO: typecasts
150+
// TODO: [ (array)
151+
// TODO: clone, new
152+
].forEach(function (list, index) {
153+
list.forEach(function (operator) {
154+
AST.precedence[operator] = index + 1;
155+
});
156+
});
157+
158+
159+
/**
160+
* Check and fix precence, by default using right
161+
*/
162+
AST.prototype.resolvePrecedence = function(result) {
163+
var buffer;
164+
// handling precendence
165+
if (result.kind === 'bin') {
166+
if (result.right && result.right.kind === 'bin') {
167+
var lLevel = AST.precedence[result.type];
168+
var rLevel = AST.precedence[result.right.type];
169+
if (lLevel && rLevel && rLevel <= lLevel) {
170+
// https://github.com/glayzzle/php-parser/issues/79
171+
// shift precedence
172+
buffer = result.right;
173+
result.right = result.right.left;
174+
buffer.left = this.resolvePrecedence(result);
175+
result = buffer;
176+
}
177+
}
178+
} else if (result.kind === 'unary') {
179+
// https://github.com/glayzzle/php-parser/issues/75
180+
if (result.what) {
181+
// unary precedence is allways lower
182+
if (result.what.kind === 'bin') {
183+
buffer = result.what;
184+
result.what = result.what.left;
185+
buffer.left = this.resolvePrecedence(result);
186+
result = buffer;
187+
} else if (result.what.kind === 'retif') {
188+
buffer = result.what;
189+
result.what = result.what.test;
190+
buffer.test = this.resolvePrecedence(result);
191+
result = buffer;
192+
}
193+
}
194+
} else if (result.kind === 'retif') {
195+
// https://github.com/glayzzle/php-parser/issues/77
196+
if (result.falseExpr && result.falseExpr.kind === 'retif') {
197+
buffer = result.falseExpr;
198+
result.falseExpr = buffer.test;
199+
buffer.test = this.resolvePrecedence(result);
200+
result = buffer;
201+
}
202+
}
203+
return result;
204+
};
205+
127206
/**
128207
* Prepares an AST node
129208
* @param {String|null} kind - Defines the node type
@@ -172,22 +251,7 @@ AST.prototype.prepare = function(kind, parser) {
172251
}
173252
var result = Object.create(node.prototype);
174253
node.apply(result, args);
175-
if (
176-
result.kind === 'bin' &&
177-
result.right &&
178-
typeof result.right.precedence === 'function'
179-
) {
180-
var out = result.right.precedence(result);
181-
if (out) { // shift with precedence
182-
result = out;
183-
}
184-
} else if (result.kind === 'unary' && result.what) {
185-
var out = result.precedence(result.what);
186-
if (out) { // shift with precedence
187-
result = out;
188-
}
189-
}
190-
return result;
254+
return self.resolvePrecedence(result);
191255
};
192256
};
193257

src/ast/bin.js

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,6 @@
77

88
var Operation = require('./operation');
99
var KIND = 'bin';
10-
11-
// operators in ascending order of precedence
12-
var binOperatorsPrecedence = [
13-
['or'],
14-
['xor'],
15-
['and'],
16-
// TODO: assignment / not sure that PHP allows this with expressions
17-
['retif'],
18-
['??'],
19-
['||'],
20-
['&&'],
21-
['|'],
22-
['^'],
23-
['&'],
24-
['==', '!=', '===', '!==', /* '<>', */ '<=>'],
25-
['<', '<=', '>', '>='],
26-
['<<', '>>'],
27-
['+', '-', '.'],
28-
['*', '/', '%'],
29-
['!'],
30-
['instanceof'],
31-
// TODO: typecasts
32-
// TODO: [ (array)
33-
// TODO: clone, new
34-
];
35-
36-
/*
37-
x OP1 (y OP2 z)
38-
z OP1 (x OP2 y)
39-
z OP2 (x OP1 y)
40-
*/
4110
/**
4211
* Binary operations
4312
* @constructor Bin
@@ -53,23 +22,4 @@ var Bin = Operation.extends(function Bin(type, left, right, location) {
5322
this.right = right;
5423
});
5524

56-
Bin.prototype.precedence = function(node) {
57-
var lLevel = Bin.precedence[node.type];
58-
var rLevel = Bin.precedence[this.type];
59-
if (lLevel && rLevel && rLevel < lLevel) {
60-
// shift precedence
61-
node.right = this.left;
62-
this.left = node;
63-
return this;
64-
}
65-
};
66-
67-
// define nodes shifting
68-
Bin.precedence = {};
69-
binOperatorsPrecedence.forEach(function (list, index) {
70-
list.forEach(function (operator) {
71-
Bin.precedence[operator] = index + 1;
72-
});
73-
});
74-
7525
module.exports = Bin;

src/ast/retif.js

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
var Statement = require('./statement');
99
var KIND = 'retif';
10-
var Bin = require('./bin');
11-
var PRECEDENCE = Bin.precedence[KIND];
1210

1311
/**
1412
* Defines a short if statement that returns a value
@@ -25,21 +23,4 @@ var RetIf = Statement.extends(function RetIf(test, trueExpr, falseExpr, location
2523
this.falseExpr = falseExpr;
2624
});
2725

28-
/**
29-
* Handles precedence over items
30-
*/
31-
RetIf.prototype.precedence = function(node) {
32-
var what = node.kind === 'bin' ? node.type : node.kind;
33-
var lLevel = Bin.precedence[what];
34-
if (lLevel && PRECEDENCE < lLevel) {
35-
if (node.kind === 'bin') {
36-
node.right = this.test;
37-
this.test = node;
38-
return this;
39-
} else {
40-
throw new Error('@todo ' + node.kind);
41-
}
42-
}
43-
};
44-
4526
module.exports = RetIf;

src/ast/unary.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,4 @@ var Unary = Operation.extends(function Unary(type, what, location) {
2121
this.what = what;
2222
});
2323

24-
Unary.prototype.precedence = function(node) {
25-
if (node.kind === 'bin') {
26-
this.what = node.left;
27-
node.left = this;
28-
return node;
29-
} else if (node.kind === 'retif') {
30-
this.what = node.test;
31-
node.test = this;
32-
return node;
33-
}
34-
};
35-
3624
module.exports = Unary;

0 commit comments

Comments
 (0)