Skip to content

Commit a07fe16

Browse files
committed
add a parenthesis node & implement operators precedence
1 parent b8e632d commit a07fe16

File tree

8 files changed

+95
-9
lines changed

8 files changed

+95
-9
lines changed

docs/AST.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
- [Pre](#pre)
3131
- [Post](#post)
3232
- [Bin](#bin)
33+
- [Parenthesis](#parenthesis)
3334
- [Bool](#Bool)
3435
- [Unary](#unary)
3536
- [Cast](#cast)
@@ -843,6 +844,16 @@ Defines a function parameter
843844
- `variadic` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
844845
- `nullable` **[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**
845846

847+
# Parenthesis
848+
849+
**Extends Operation**
850+
851+
Parenthesis encapsulation `(... expr ...)`
852+
853+
**Properties**
854+
855+
- `inner` **[Expression](#expression)**
856+
846857
# Position
847858

848859
Each Position object consists of a line number (1-indexed) and a column number (0-indexed):

src/ast.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var Position = require('./ast/position');
3636
* - [Pre](#pre)
3737
* - [Post](#post)
3838
* - [Bin](#bin)
39+
* - [Parenthesis](#parenthesis)
3940
* - [Bool](#Bool)
4041
* - [Unary](#unary)
4142
* - [Cast](#cast)
@@ -203,6 +204,7 @@ AST.prototype.prepare = function(kind, parser) {
203204
require('./ast/error'),
204205
require('./ast/eval'),
205206
require('./ast/exit'),
207+
require('./ast/expression'),
206208
require('./ast/for'),
207209
require('./ast/foreach'),
208210
require('./ast/function'),
@@ -222,9 +224,11 @@ AST.prototype.prepare = function(kind, parser) {
222224
require('./ast/method'),
223225
require('./ast/namespace'),
224226
require('./ast/new'),
227+
require('./ast/node'),
225228
require('./ast/number'),
226229
require('./ast/offsetlookup'),
227230
require('./ast/parameter'),
231+
require('./ast/parenthesis'),
228232
require('./ast/post'),
229233
require('./ast/pre'),
230234
require('./ast/print'),

src/ast/bin.js

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

11+
// define nodes shifting
12+
var precedence = {
13+
'+': 1,
14+
'-': 1,
15+
'.': 1,
16+
'*': 2,
17+
'/': 2,
18+
'%': 2
19+
};
20+
1121
/**
1222
* Binary operations
1323
* @constructor Bin
@@ -18,6 +28,20 @@ var KIND = 'bin';
1828
*/
1929
var Bin = Operation.extends(function Bin(type, left, right, location) {
2030
Operation.apply(this, [KIND, location]);
31+
if (right && right.kind === 'bin') {
32+
var lLevel = precedence[type];
33+
var rLevel = precedence[right.type];
34+
if (lLevel && rLevel && rLevel < lLevel) {
35+
// shift precedence
36+
var buffer = right.right;
37+
right.right = right.left;
38+
right.left = left;
39+
left = buffer;
40+
buffer = right.type;
41+
right.type = type;
42+
type = buffer;
43+
}
44+
}
2145
this.type = type;
2246
this.left = left;
2347
this.right = right;

src/ast/parenthesis.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*!
2+
* Copyright (C) 2017 Glayzzle (BSD3 License)
3+
* @authors https://github.com/glayzzle/php-parser/graphs/contributors
4+
* @url http://glayzzle.com
5+
*/
6+
"use strict";
7+
8+
var Operation = require('./operation');
9+
var KIND = 'parenthesis';
10+
11+
/**
12+
* Parenthesis encapsulation `(... expr ...)`
13+
* @constructor Parenthesis
14+
* @extends {Operation}
15+
* @property {Expression} inner
16+
*/
17+
var Parenthesis = Operation.extends(function Parenthesis(inner, location) {
18+
Operation.apply(this, [KIND, location]);
19+
this.inner = inner;
20+
});
21+
22+
module.exports = Parenthesis;

src/parser/expr.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,10 @@ module.exports = {
120120
}
121121

122122
if (this.token === '(') {
123+
var node = this.node('parenthesis');
123124
var expr = this.next().read_expr();
124125
this.expect(')') && this.next();
126+
expr = node(expr);
125127
// handle dereferencable
126128
if (this.token === this.tok.T_OBJECT_OPERATOR) {
127129
return this.recursive_variable_chain_scan(expr, false);

src/parser/statement.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,7 @@ module.exports = {
251251

252252
case this.tok.T_ECHO:
253253
var result = this.node('echo');
254-
var withParanthesis = (this.next().token === '(');
255-
withParanthesis && this.next();
256-
var args = this.read_list(this.read_expr, ',');
257-
if (withParanthesis) {
258-
this.expect(')') && this.next();
259-
}
254+
var args = this.next().read_list(this.read_expr, ',');
260255
this.expectEndOfStatement();
261256
return result(args);
262257

test/astTests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('Test AST structure', function() {
5656
});
5757
it('test echo, isset, unset, empty', function() {
5858
var ast = parser.parseEval([
59-
'echo(true, $var)',
59+
'echo ($expr) ? "ok" : "ko";',
6060
'print "some text"',
6161
'isset($foo, $bar)',
6262
'unset($var)',

test/exprTests.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ describe('Test expressions', function() {
142142
it('test exit', function() {
143143
var ast = parser.parseEval([
144144
'exit(1);',
145-
'exit();',
145+
'die();',
146146
'exit;'
147147
].join('\n'));
148148
ast.children[0].kind.should.be.exactly('exit');
@@ -188,7 +188,7 @@ describe('Test expressions', function() {
188188
'$a = new $foo();',
189189
'$a = new class extends foo implements bar { };',
190190
].join('\n'), {
191-
ast: { debug: false }
191+
parser: { debug: false }
192192
});
193193
ast.children[0].right.kind.should.be.exactly('new');
194194
ast.children[0].right.what.kind.should.be.exactly('identifier');
@@ -200,4 +200,32 @@ describe('Test expressions', function() {
200200
ast.children[2].right.what.kind.should.be.exactly('class');
201201
});
202202

203+
it('test nested expressions', function() {
204+
var ast = parser.parseEval([
205+
'$a = 5 * 2 + 1;', // same as (1 + (5 * 2))
206+
'$b = 5 * (2 + 1);',
207+
].join('\n'), {
208+
parser: { debug: false }
209+
});
210+
var aExpr = ast.children[0].right;
211+
aExpr.kind.should.be.exactly('bin');
212+
aExpr.left.value.should.be.exactly('1');
213+
aExpr.type.should.be.exactly('+');
214+
215+
aExpr.right.left.value.should.be.exactly('5');
216+
aExpr.right.type.should.be.exactly('*');
217+
aExpr.right.right.value.should.be.exactly('2');
218+
219+
var bExpr = ast.children[1].right;
220+
bExpr.kind.should.be.exactly('bin');
221+
bExpr.left.value.should.be.exactly('5');
222+
bExpr.type.should.be.exactly('*');
223+
224+
bExpr.right.kind.should.be.exactly('parenthesis');
225+
bExpr.right.inner.left.value.should.be.exactly('2');
226+
bExpr.right.inner.type.should.be.exactly('+');
227+
bExpr.right.inner.right.value.should.be.exactly('1');
228+
229+
});
230+
203231
});

0 commit comments

Comments
 (0)