Skip to content

Commit 127c967

Browse files
committed
fix the graceful mode
1 parent ee9b875 commit 127c967

File tree

5 files changed

+99
-88
lines changed

5 files changed

+99
-88
lines changed

src/parser.js

Lines changed: 29 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,6 @@ function isNumber(n) {
1212
}
1313

1414

15-
/**
16-
* Graceful decorator
17-
*/
18-
var _gracefulDecorator = function(fn) {
19-
try {
20-
this._currentNode = this._gracefulProxy[fn].apply(
21-
this,
22-
Array.prototype.slice.call(arguments, 1)
23-
);
24-
return this._currentNode;
25-
} catch(e) {
26-
if (this.lastError) {
27-
this.next(); // ignore token & go next
28-
var errorNode = [
29-
'error',
30-
this.lastError.tokenName,
31-
this.lastError.expected,
32-
this.lastError.line
33-
];
34-
// force to append the error node
35-
if (this.ast.length < 3) {
36-
this.ast.push([]);
37-
}
38-
this.ast[2].push(errorNode);
39-
// return the node
40-
return errorNode;
41-
} else {
42-
throw e; // not a parsing error
43-
}
44-
}
45-
};
46-
4715
/**
4816
* The PHP Parser class
4917
*
@@ -184,40 +152,11 @@ parser.prototype.getTokenName = function(token) {
184152
}
185153
};
186154

187-
/**
188-
* enable / disable the graceful mode
189-
*/
190-
parser.prototype.graceful = function(mode) {
191-
if (this._graceful !== mode) {
192-
if (mode) {
193-
// enable the graceful mode
194-
this._gracefulProxy = {};
195-
for(var i in this) {
196-
var cb = this[i];
197-
if (typeof cb === 'function') {
198-
this._gracefulProxy[i] = cb;
199-
this[i] = _gracefulDecorator.bind(this, i);
200-
}
201-
}
202-
} else {
203-
// disable the graceful mode
204-
for(var i in this._gracefulProxy) {
205-
this[i] = this._gracefulProxy[i];
206-
}
207-
}
208-
this._graceful = mode;
209-
}
210-
return this;
211-
};
212-
213155
/**
214156
* main entry point : converts a source code to AST
215157
*/
216158
parser.prototype.parse = function(code) {
217159
this.lastError = false;
218-
if (this.suppressErrors) {
219-
this.graceful(this.suppressErrors);
220-
}
221160
this.currentNamespace = [''];
222161
this.lexer.setInput(code);
223162
this.lexer.comment_tokens = this.extractDoc;
@@ -244,6 +183,19 @@ parser.prototype.parse = function(code) {
244183
* Raise an error
245184
*/
246185
parser.prototype.raiseError = function(message, msgExpect, expect, token) {
186+
if (!this.suppressErrors) {
187+
throw new Error(message);
188+
}
189+
if (!this.firstError) {
190+
this.firstError = {
191+
token: this.token,
192+
tokenName: token,
193+
expected: expect,
194+
messageExpected: msgExpect,
195+
message: message,
196+
line: this.lexer.yylloc.first_line
197+
};
198+
}
247199
this.lastError = {
248200
token: this.token,
249201
tokenName: token,
@@ -252,9 +204,18 @@ parser.prototype.raiseError = function(message, msgExpect, expect, token) {
252204
message: message,
253205
line: this.lexer.yylloc.first_line
254206
};
255-
if (!this.suppressErrors) {
256-
throw new Error(this.lastError.message);
207+
if (this.ast.length === 2) {
208+
this.ast.push([]);
257209
}
210+
// Error node :
211+
var node = [
212+
'error',
213+
this.token,
214+
message,
215+
this.lexer.yylloc.first_line
216+
];
217+
this.ast[2].push(node);
218+
return node;
258219
};
259220

260221
/**
@@ -355,7 +316,7 @@ parser.prototype.expectEndOfStatement = function() {
355316
};
356317

357318
/** outputs some debug information on current token **/
358-
var ignoreStack = ['parser.next', '_gracefulDecorator'];
319+
var ignoreStack = ['parser.next', 'parser.nextWithComments'];
359320
parser.prototype.showlog = function() {
360321
var stack = (new Error()).stack.split('\n');
361322
var line;
@@ -386,14 +347,12 @@ parser.prototype.showlog = function() {
386347

387348
/** force to expect specified token **/
388349
parser.prototype.expect = function(token) {
389-
if (!this.lastError) {
390-
if (Array.isArray(token)) {
391-
if (token.indexOf(this.token) === -1) {
392-
this.error(token);
393-
}
394-
} else if (this.token != token) {
350+
if (Array.isArray(token)) {
351+
if (token.indexOf(this.token) === -1) {
395352
this.error(token);
396353
}
354+
} else if (this.token != token) {
355+
this.error(token);
397356
}
398357
return this;
399358
};

src/parser/class.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ module.exports = {
167167
this.tok.T_VARIABLE,
168168
this.tok.T_FUNCTION
169169
]);
170+
this.next(); // ignore token
170171
}
171172
}
172173
this.expect('}').nextWithComments();
@@ -245,7 +246,6 @@ module.exports = {
245246
if (this.is('T_MEMBER_FLAGS')) {
246247
var idx = 0, val = 0;
247248
do {
248-
249249
switch(this.token) {
250250
case this.tok.T_PUBLIC: idx = 0; val = 0; break;
251251
case this.tok.T_PROTECTED: idx = 0; val = 1; break;
@@ -256,13 +256,21 @@ module.exports = {
256256
}
257257
if (asInterface) {
258258
if (idx == 0 && val == 2) {
259+
// an interface can't be private
259260
this.expect([this.tok.T_PUBLIC, this.tok.T_PROTECTED]);
261+
val = -1;
260262
} else if (idx == 2 && val == 1) {
263+
// an interface cant be abstract
261264
this.error();
265+
val = -1;
262266
}
263267
}
264-
if (result[idx] != -1) this.error();
265-
result[idx] = val;
268+
if (result[idx] !== -1) {
269+
// already defined flag
270+
this.error();
271+
} else if (val !== -1) {
272+
result[idx] = val;
273+
}
266274
} while(this.next().is('T_MEMBER_FLAGS'));
267275
}
268276

@@ -374,6 +382,7 @@ module.exports = {
374382
this.tok.T_CONST,
375383
this.tok.T_FUNCTION
376384
]);
385+
this.next();
377386
}
378387
}
379388
this.expect('}').next();

src/parser/expr.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,8 @@ module.exports = {
321321
}
322322
}
323323
} else {
324-
this.error('EXPR');
324+
expr = this.error('EXPR');
325+
this.next();
325326
}
326327

327328
// returns variable | scalar

src/parser/statement.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ module.exports = {
4848
case this.tok.T_INTERFACE:
4949
return this.read_interface(flag);
5050
default:
51-
this.error([this.tok.T_CLASS, this.tok.T_INTERFACE]);
51+
var err = this.error([this.tok.T_CLASS, this.tok.T_INTERFACE]);
52+
this.next();
53+
return err;
5254
}
5355
case this.tok.T_CLASS:
5456
return this.read_class(0);

test/functional/gracefulTests.js

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,64 @@ var parser = require('../../main');
44
describe('Test graceful mode', function() {
55

66
describe('to suppress errors', function() {
7-
7+
88
// init a new parser instance
9-
var test = parser.create();
10-
test.parser.graceful(true);
11-
12-
// Get result from parser
13-
var ast = test.parseEval([
14-
'$var = ', // 1.
15-
'function() {', // 2.
16-
'$foo = ', // 3. <-- missing expr
17-
'}', // 4.
18-
'}' // 5. <-- extra '}' token here
19-
].join('\n'));
9+
var test = parser.create({
10+
parser: {
11+
suppressErrors: true
12+
}
13+
});
2014

2115
it('should contain 2 errors', function () {
16+
// Get result from parser
17+
var ast = test.parseEval([
18+
'$var = ', // 1.
19+
'function() {', // 2.
20+
'$foo = ', // 3. <-- missing expr
21+
'}', // 4.
22+
'}' // 5. <-- extra '}' token here
23+
].join('\n'));
24+
ast[2].length.should.be.exactly(2);
25+
ast[1][0][2][6][0][2][0].should.be.exactly('error');
26+
});
27+
28+
it('test expr', function () {
29+
var ast = test.parseEval('$a = $b -; $foo = $a;');
30+
2231
ast[2].length.should.be.exactly(2);
32+
ast[1].length.should.be.exactly(2);
33+
34+
ast[1][0][2][0].should.be.exactly('bin');
35+
ast[1][0][2][1].should.be.exactly('-');
36+
ast[1][0][2][3][0].should.be.exactly('error');
37+
38+
ast[1][1][0].should.be.exactly('set');
39+
ast[1][1][1][0].should.be.exactly('var');
40+
ast[1][1][1][1].should.be.exactly('$foo');
41+
});
42+
43+
it('test class', function () {
44+
var ast = test.parseEval('class foo { foo const A = 1 ');
45+
46+
ast[2].length.should.be.exactly(3);
47+
ast[1].length.should.be.exactly(1);
48+
49+
ast[1][0][0].should.be.exactly('class');
50+
ast[1][0][1].should.be.exactly('foo');
51+
ast[1][0][5].constants.length.should.be.exactly(1);
52+
ast[1][0][5].constants[0][0].should.be.exactly('A');
53+
ast[1][0][5].constants[0][1][1].should.be.exactly('1');
54+
55+
});
56+
57+
it('test flags', function () {
58+
var ast = test.parseEval('final final interface foo { abstract function func() ');
59+
ast[2].length.should.be.exactly(4);
60+
ast[1].length.should.be.exactly(2);
61+
ast[1][0][0].should.be.exactly('error');
62+
ast[1][1][0].should.be.exactly('interface');
2363
});
2464

2565
});
2666

27-
});
67+
});

0 commit comments

Comments
 (0)