Skip to content

Commit b921e37

Browse files
committed
Update AST for operators, unify bin/bool/coalesce nodes
Also begin fixing operator precedence.
1 parent d3e8582 commit b921e37

File tree

8 files changed

+98
-124
lines changed

8 files changed

+98
-124
lines changed

docs/AST.md

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,10 @@
2828
- [StaticLookup](#staticlookup)
2929
- [OffsetLookup](#offsetlookup)
3030
- [Operation](#operation)
31-
- [Coalesce](#coalesce)
3231
- [Pre](#pre)
3332
- [Post](#post)
3433
- [Bin](#bin)
3534
- [Parenthesis](#parenthesis)
36-
- [Bool](#Bool)
3735
- [Unary](#unary)
3836
- [Cast](#cast)
3937
- [Literal](#literal)
@@ -207,18 +205,6 @@ A block statement, i.e., a sequence of statements surrounded by braces.
207205

208206
- `children` **[Array](#array)<[Node](#node)>**
209207

210-
# Bool
211-
212-
**Extends Operation**
213-
214-
Boolean operations
215-
216-
**Properties**
217-
218-
- `type` **[String](#string)**
219-
- `left` **[Expression](#expression)**
220-
- `right` **[Expression](#expression)**
221-
222208
# Boolean
223209

224210
**Extends Literal**
@@ -341,18 +327,6 @@ Defines a closure
341327
- `nullable` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
342328
- `body` **([Block](#block) | null)**
343329

344-
# Coalesce
345-
346-
**Extends Operation**
347-
348-
Verify is the test property is defined and is not null, and returns
349-
is, otherwise returns the ifnull expression.
350-
351-
**Properties**
352-
353-
- `test` **[Expression](#expression)** The expression to be testes
354-
- `ifnull` **[Expression](#expression)** The returned expression if test is null
355-
356330
# Constant
357331

358332
**Extends Declaration**

src/ast.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,10 @@ var Position = require('./ast/position');
3434
* - [StaticLookup](#staticlookup)
3535
* - [OffsetLookup](#offsetlookup)
3636
* - [Operation](#operation)
37-
* - [Coalesce](#coalesce)
3837
* - [Pre](#pre)
3938
* - [Post](#post)
4039
* - [Bin](#bin)
4140
* - [Parenthesis](#parenthesis)
42-
* - [Bool](#Bool)
4341
* - [Unary](#unary)
4442
* - [Cast](#cast)
4543
* - [Literal](#literal)
@@ -184,7 +182,6 @@ AST.prototype.prepare = function(kind, parser) {
184182
require('./ast/assign'),
185183
require('./ast/bin'),
186184
require('./ast/block'),
187-
require('./ast/bool'),
188185
require('./ast/boolean'),
189186
require('./ast/break'),
190187
require('./ast/call'),
@@ -195,7 +192,6 @@ AST.prototype.prepare = function(kind, parser) {
195192
require('./ast/classconstant'),
196193
require('./ast/clone'),
197194
require('./ast/closure'),
198-
require('./ast/coalesce'),
199195
require('./ast/constant'),
200196
require('./ast/constref'),
201197
require('./ast/continue'),

src/ast/bin.js

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,44 @@
88
var Operation = require('./operation');
99
var KIND = 'bin';
1010

11+
// operators in ascending order of precedence
12+
var binOperatorsPrecedence = [
13+
['or'],
14+
['xor'],
15+
['and'],
16+
// TODO: assignment
17+
// TODO: ternary ? :
18+
['??'],
19+
['||'],
20+
['&&'],
21+
['|'],
22+
['^'],
23+
['&'],
24+
['==', '!=', '===', '!==', /* '<>', */ '<=>'],
25+
['<', '<=', '>', '>='],
26+
['<<', '>>'],
27+
['+', '-', '.'],
28+
['*', '/', '%'],
29+
// TODO: unary !
30+
['instanceof'],
31+
// TODO: unary ++, --, ~, @, typecasts
32+
// TODO: [ (array)
33+
// TODO: clone, new
34+
];
35+
1136
// define nodes shifting
12-
var precedence = {
13-
'+': 1,
14-
'-': 1,
15-
'.': 1,
16-
'*': 2,
17-
'/': 2,
18-
'%': 2
19-
};
37+
var precedence = {};
38+
binOperatorsPrecedence.forEach(function (list, index) {
39+
list.forEach(function (operator) {
40+
precedence[operator] = index + 1;
41+
});
42+
});
2043

44+
/*
45+
x OP1 (y OP2 z)
46+
z OP1 (x OP2 y)
47+
z OP2 (x OP1 y)
48+
*/
2149
/**
2250
* Binary operations
2351
* @constructor Bin
@@ -40,6 +68,9 @@ var Bin = Operation.extends(function Bin(type, left, right, location) {
4068
buffer = right.type;
4169
right.type = type;
4270
type = buffer;
71+
buffer = left;
72+
left = right;
73+
right = buffer;
4374
}
4475
}
4576
this.type = type;

src/ast/bool.js

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/ast/coalesce.js

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/parser/expr.js

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,42 +35,42 @@ module.exports = {
3535
return result('bin', '<<', expr, this.next().read_expr());
3636
if (this.token === this.tok.T_SR)
3737
return result('bin', '>>', expr, this.next().read_expr());
38-
// boolean operations
38+
// more binary operations (formerly bool)
3939
if (this.token === this.tok.T_BOOLEAN_OR)
40-
return result('bool', '|', expr, this.next().read_expr());
40+
return result('bin', '||', expr, this.next().read_expr());
4141
if (this.token === this.tok.T_LOGICAL_OR)
42-
return result('bool', '|', expr, this.next().read_expr());
42+
return result('bin', 'or', expr, this.next().read_expr());
4343
if (this.token === this.tok.T_BOOLEAN_AND)
44-
return result('bool', '&', expr, this.next().read_expr());
44+
return result('bin', '&&', expr, this.next().read_expr());
4545
if (this.token === this.tok.T_LOGICAL_AND)
46-
return result('bool', '&', expr, this.next().read_expr());
46+
return result('bin', 'and', expr, this.next().read_expr());
4747
if (this.token === this.tok.T_LOGICAL_XOR)
48-
return result('bool', '^', expr, this.next().read_expr());
48+
return result('bin', 'xor', expr, this.next().read_expr());
4949
if (this.token === this.tok.T_IS_IDENTICAL)
50-
return result('bool', '=', expr, this.next().read_expr());
50+
return result('bin', '===', expr, this.next().read_expr());
5151
if (this.token === this.tok.T_IS_NOT_IDENTICAL)
52-
return result('bool', '!=', expr, this.next().read_expr());
52+
return result('bin', '!==', expr, this.next().read_expr());
5353
if (this.token === this.tok.T_IS_EQUAL)
54-
return result('bool', '~', expr, this.next().read_expr());
54+
return result('bin', '==', expr, this.next().read_expr());
5555
if (this.token === this.tok.T_IS_NOT_EQUAL)
56-
return result('bool', '!~', expr, this.next().read_expr());
56+
return result('bin', '!=', expr, this.next().read_expr());
5757
if (this.token === '<')
58-
return result('bool', '<', expr, this.next().read_expr());
58+
return result('bin', '<', expr, this.next().read_expr());
5959
if (this.token === '>')
60-
return result('bool', '!~', expr, this.next().read_expr());
60+
return result('bin', '>', expr, this.next().read_expr());
6161
if (this.token === this.tok.T_IS_SMALLER_OR_EQUAL)
62-
return result('bool', '<=', expr, this.next().read_expr());
62+
return result('bin', '<=', expr, this.next().read_expr());
6363
if (this.token === this.tok.T_IS_GREATER_OR_EQUAL)
64-
return result('bool', '=>', expr, this.next().read_expr());
64+
return result('bin', '>=', expr, this.next().read_expr());
6565
if (this.token === this.tok.T_SPACESHIP)
66-
return result('bool', '<=>', expr, this.next().read_expr());
66+
return result('bin', '<=>', expr, this.next().read_expr());
6767
if (this.token === this.tok.T_INSTANCEOF)
68-
return result('bool', '?', expr, this.next().read_expr());
68+
return result('bin', 'instanceof', expr, this.next().read_expr());
6969

7070
// extra operations :
7171
// $username = $_GET['user'] ?? 'nobody';
7272
if (this.token === this.tok.T_COALESCE)
73-
return result('coalesce', expr, this.next().read_expr());
73+
return result('bin', '??', expr, this.next().read_expr());
7474

7575
// extra operations :
7676
// $username = $_GET['user'] ? true : false;

test/astTests.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,12 @@ describe('Test AST structure', function() {
100100
ast.children[0].status.value.should.be.exactly('-1');
101101
});
102102

103-
it('test coalesce', function() {
103+
it('test coalesce operator', function() {
104104
var ast = parser.parseEval('$var = $a ?? true;');
105-
ast.children[0].right.kind.should.be.exactly('coalesce');
106-
ast.children[0].right.test.kind.should.be.exactly('variable');
107-
ast.children[0].right.ifnull.kind.should.be.exactly('boolean');
105+
ast.children[0].right.kind.should.be.exactly('bin');
106+
ast.children[0].right.type.should.be.exactly('??');
107+
ast.children[0].right.left.kind.should.be.exactly('variable');
108+
ast.children[0].right.right.kind.should.be.exactly('boolean');
108109
});
109110

110111
it('test include / require', function() {

test/exprTests.js

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('Test expressions', function() {
3030
]);
3131
});
3232

33-
it('test boolean', function() {
33+
it('test more binary ops (formerly bool)', function() {
3434
var ast = parser.parseEval([
3535
'$a && $b;',
3636
'$a AND $b;',
@@ -50,14 +50,14 @@ describe('Test expressions', function() {
5050
].join('\n'));
5151

5252
ast.children.should.matchEach(function (node) {
53-
node.should.have.property('kind', 'bool');
53+
node.should.have.property('kind', 'bin');
5454
node.left.name.should.be.exactly('a');
5555
node.right.name.should.be.exactly('b');
5656
});
5757

5858
ast.children.map(function (node) { return node.type; })
5959
.should.deepEqual([
60-
'&', '&', '|', '|', '^', '=', '!=', '~', '!~', '!~', '<', '=>', '<=', '<=>', '?'
60+
'&&', 'and', '||', 'or', 'xor', '===', '!==', '==', '!=', '>', '<', '>=', '<=', '<=>', 'instanceof'
6161
]);
6262
});
6363

@@ -228,20 +228,21 @@ describe('Test expressions', function() {
228228

229229
it('test nested expressions precedence', function() {
230230
var ast = parser.parseEval([
231-
'$a = 5 * 2 + 1;', // same as (1 + (5 * 2))
231+
'$a = 5 * 2 + 1;', // same as ((5 * 2) + 1)
232232
'$b = 5 * (2 + 1);',
233-
'$c = 1 + 2 / 3 + 4;', // same as (1 + (4 + (2 / 3)))
233+
'$c = 1 + 2 / 3 + 4;', // same as (1 + ((2 / 3) + 4))
234+
'$d = 1 !== 2 && 3;', // same as (1 !== 2) && 3
234235
].join('\n'), {
235236
parser: { debug: false }
236237
});
237238
var aExpr = ast.children[0].right;
238239
aExpr.kind.should.be.exactly('bin');
239-
aExpr.left.value.should.be.exactly('1');
240+
aExpr.right.value.should.be.exactly('1');
240241
aExpr.type.should.be.exactly('+');
241242

242-
aExpr.right.left.value.should.be.exactly('5');
243-
aExpr.right.type.should.be.exactly('*');
244-
aExpr.right.right.value.should.be.exactly('2');
243+
aExpr.left.left.value.should.be.exactly('5');
244+
aExpr.left.type.should.be.exactly('*');
245+
aExpr.left.right.value.should.be.exactly('2');
245246

246247
var bExpr = ast.children[1].right;
247248
bExpr.kind.should.be.exactly('bin');
@@ -259,14 +260,36 @@ describe('Test expressions', function() {
259260
cExpr.type.should.be.exactly('+');
260261

261262
cExpr.right.kind.should.be.exactly('bin');
262-
cExpr.right.left.value.should.be.exactly('4');
263+
cExpr.right.right.value.should.be.exactly('4');
263264
cExpr.right.type.should.be.exactly('+');
264265

265-
cExpr.right.right.kind.should.be.exactly('bin');
266-
cExpr.right.right.left.value.should.be.exactly('2');
267-
cExpr.right.right.type.should.be.exactly('/');
268-
cExpr.right.right.right.value.should.be.exactly('3');
266+
cExpr.right.left.kind.should.be.exactly('bin');
267+
cExpr.right.left.left.value.should.be.exactly('2');
268+
cExpr.right.left.type.should.be.exactly('/');
269+
cExpr.right.left.right.value.should.be.exactly('3');
269270

271+
var dExpr = ast.children[3].right;
272+
dExpr.should.have.property('kind', 'bin');
273+
dExpr.should.deepEqual({
274+
kind: "bin",
275+
left: {
276+
kind: "bin",
277+
left: {
278+
kind: "number",
279+
value: "1"
280+
},
281+
right: {
282+
kind: "number",
283+
value: "2"
284+
},
285+
type: "!=="
286+
},
287+
right: {
288+
kind: "number",
289+
value: "3"
290+
},
291+
type: "&&"
292+
});
270293
});
271294

272295
});

0 commit comments

Comments
 (0)