mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2025-01-22 05:11:39 +01:00
Enable basic error recovery
Adding only a single recovery rule for now. The API is now: * throwOnError parser option must be disabled. * List of Errors is available through $parser->getErrors(). This method is available either way. * If no recovery is possible $parser->parse() will return null. (Obviously only if throwOnError is disabled).
This commit is contained in:
parent
a35c2a2067
commit
3b7d8e8b5d
@ -215,6 +215,7 @@ statement:
|
||||
| T_THROW expr ';' { $$ = Stmt\Throw_[$2]; }
|
||||
| T_GOTO T_STRING ';' { $$ = Stmt\Goto_[$2]; }
|
||||
| T_STRING ':' { $$ = Stmt\Label[$1]; }
|
||||
| error { $$ = array(); /* means: no statement */ }
|
||||
;
|
||||
|
||||
catches:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -96,7 +96,7 @@ abstract class ParserAbstract
|
||||
* Creates a parser instance.
|
||||
*
|
||||
* @param Lexer $lexer A lexer
|
||||
* @param array $options Options array. The boolean 'throwOnError' option determined whether an exception should be
|
||||
* @param array $options Options array. The boolean 'throwOnError' option determines whether an exception should be
|
||||
* thrown on first error, or if the parser should try to continue parsing the remaining code
|
||||
* and build a partial AST.
|
||||
*/
|
||||
@ -109,7 +109,7 @@ abstract class ParserAbstract
|
||||
/**
|
||||
* Get array of errors that occurred during the last parse.
|
||||
*
|
||||
* This method may only return multiple errors if the throwOnError option is disabled.
|
||||
* This method may only return multiple errors if the 'throwOnError' option is disabled.
|
||||
*
|
||||
* @return Error[]
|
||||
*/
|
||||
@ -122,7 +122,8 @@ abstract class ParserAbstract
|
||||
*
|
||||
* @param string $code The source code to parse
|
||||
*
|
||||
* @return Node[] Array of statements
|
||||
* @return Node[]|null Array of statements (or null if the 'throwOnError' option is disabled and the parser was
|
||||
* unable to recover from an error).
|
||||
*/
|
||||
public function parse($code) {
|
||||
$this->lexer->startLexing($code);
|
||||
@ -244,7 +245,13 @@ abstract class ParserAbstract
|
||||
$e->setStartLine($startAttributes['startLine']);
|
||||
}
|
||||
|
||||
$this->errors[] = $e;
|
||||
if ($this->throwOnError) {
|
||||
throw $e;
|
||||
} else {
|
||||
// Currently can't recover from "special" errors
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* Goto - shift nonterminal */
|
||||
@ -284,7 +291,8 @@ abstract class ParserAbstract
|
||||
&& $idx < $this->actionTableSize && $this->actionCheck[$idx] == $this->errorSymbol)
|
||||
) || ($action = $this->action[$idx]) == $this->defaultAction) { // Not totally sure about this
|
||||
if ($this->stackPos <= 0) {
|
||||
throw new Error('Could not recover from error');
|
||||
// Could not recover from error
|
||||
return null;
|
||||
}
|
||||
$state = $stateStack[--$this->stackPos];
|
||||
//$this->tracePop($state);
|
||||
@ -296,8 +304,8 @@ abstract class ParserAbstract
|
||||
|
||||
case 3:
|
||||
if ($symbol === 0) {
|
||||
// Reached EOF
|
||||
throw new Error('Could not recover from error');
|
||||
// Reached EOF without recovering from error
|
||||
return null;
|
||||
}
|
||||
|
||||
//$this->traceDiscard($symbol);
|
||||
@ -314,6 +322,8 @@ abstract class ParserAbstract
|
||||
$rule = $state - $this->YYNLSTATES;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Reached end of parser loop');
|
||||
}
|
||||
|
||||
protected function getErrorMessage($symbol, $state) {
|
||||
|
@ -30,24 +30,36 @@ class ParserTest extends CodeTestAbstract
|
||||
/**
|
||||
* @dataProvider provideTestParseFail
|
||||
*/
|
||||
public function testParseFail($name, $code, $expectedMsg) {
|
||||
public function testParseFail($name, $code, $expected) {
|
||||
$lexer = new Lexer\Emulative(array('usedAttributes' => array(
|
||||
'startLine', 'endLine', 'startFilePos', 'endFilePos'
|
||||
)));
|
||||
$parser = new Parser($lexer);
|
||||
$parser = new Parser($lexer, array(
|
||||
'throwOnError' => false,
|
||||
));
|
||||
|
||||
try {
|
||||
$parser->parse($code);
|
||||
$stmts = $parser->parse($code);
|
||||
$errors = $parser->getErrors();
|
||||
|
||||
$this->fail(sprintf('"%s": Expected Error', $name));
|
||||
} catch (Error $e) {
|
||||
$output = '';
|
||||
foreach ($errors as $error) {
|
||||
$output .= $this->formatErrorMessage($error, $code) . "\n";
|
||||
}
|
||||
|
||||
if (null !== $stmts) {
|
||||
$dumper = new NodeDumper;
|
||||
$output .= $dumper->dump($stmts);
|
||||
}
|
||||
|
||||
$this->assertSame($this->canonicalize($expected), $this->canonicalize($output), $name);
|
||||
}
|
||||
|
||||
private function formatErrorMessage(Error $e, $code) {
|
||||
if ($e->hasColumnInfo()) {
|
||||
$msg = $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code)
|
||||
return $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code)
|
||||
. ' to ' . $e->getEndLine() . ':' . $e->getEndColumn($code);
|
||||
} else {
|
||||
$msg = $e->getMessage();
|
||||
}
|
||||
$this->assertSame($expectedMsg, $msg, $name);
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,11 @@ Error positions
|
||||
<?php foo
|
||||
-----
|
||||
Syntax error, unexpected EOF from 1:10 to 1:10
|
||||
array(
|
||||
)
|
||||
-----
|
||||
<?php foo /* bar */
|
||||
-----
|
||||
Syntax error, unexpected EOF from 1:20 to 1:20
|
||||
array(
|
||||
)
|
||||
|
@ -4,3 +4,5 @@ New without a class
|
||||
new;
|
||||
-----
|
||||
Syntax error, unexpected ';' from 2:4 to 2:4
|
||||
array(
|
||||
)
|
||||
|
@ -25,8 +25,20 @@ Multiple final modifiers are not allowed on line 1
|
||||
Cannot use the final modifier on an abstract class member on line 1
|
||||
-----
|
||||
<?php abstract final class A { }
|
||||
// Type in the partial parse could conceivably be any of 0, 16 or 32
|
||||
-----
|
||||
Syntax error, unexpected T_FINAL, expecting T_CLASS from 1:16 to 1:20
|
||||
array(
|
||||
0: Stmt_Class(
|
||||
type: 32
|
||||
name: A
|
||||
extends: null
|
||||
implements: array(
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
-----
|
||||
<?php class A { abstract $a; }
|
||||
-----
|
||||
|
@ -11,6 +11,8 @@ Cannot use 'PARENT' as class name as it is reserved on line 1
|
||||
<?php class static {}
|
||||
-----
|
||||
Syntax error, unexpected T_STATIC, expecting T_STRING from 1:13 to 1:18
|
||||
array(
|
||||
)
|
||||
-----
|
||||
<?php class A extends self {}
|
||||
-----
|
||||
@ -23,6 +25,8 @@ Cannot use 'PARENT' as class name as it is reserved from 1:23 to 1:28
|
||||
<?php class A extends static {}
|
||||
-----
|
||||
Syntax error, unexpected T_STATIC, expecting T_STRING or T_NAMESPACE or T_NS_SEPARATOR from 1:23 to 1:28
|
||||
array(
|
||||
)
|
||||
-----
|
||||
<?php class A implements self {}
|
||||
-----
|
||||
@ -35,6 +39,8 @@ Cannot use 'PARENT' as interface name as it is reserved from 1:26 to 1:31
|
||||
<?php class A implements static {}
|
||||
-----
|
||||
Syntax error, unexpected T_STATIC, expecting T_STRING or T_NAMESPACE or T_NS_SEPARATOR from 1:26 to 1:31
|
||||
array(
|
||||
)
|
||||
-----
|
||||
<?php interface self {}
|
||||
-----
|
||||
@ -47,6 +53,8 @@ Cannot use 'PARENT' as class name as it is reserved on line 1
|
||||
<?php interface static {}
|
||||
-----
|
||||
Syntax error, unexpected T_STATIC, expecting T_STRING from 1:17 to 1:22
|
||||
array(
|
||||
)
|
||||
-----
|
||||
<?php interface A extends self {}
|
||||
-----
|
||||
@ -59,3 +67,5 @@ Cannot use 'PARENT' as interface name as it is reserved from 1:27 to 1:32
|
||||
<?php interface A extends static {}
|
||||
-----
|
||||
Syntax error, unexpected T_STATIC, expecting T_STRING or T_NAMESPACE or T_NS_SEPARATOR from 1:27 to 1:32
|
||||
array(
|
||||
)
|
||||
|
@ -11,6 +11,8 @@ Cannot use 'PARENT' as namespace name from 1:17 to 1:22
|
||||
<?php namespace static;
|
||||
-----
|
||||
Syntax error, unexpected T_STATIC, expecting T_STRING or T_NS_SEPARATOR or '{' from 1:17 to 1:22
|
||||
array(
|
||||
)
|
||||
-----
|
||||
<?php use A as self;
|
||||
-----
|
||||
@ -23,3 +25,5 @@ Cannot use B as PARENT because 'PARENT' is a special class name on line 1
|
||||
<?php use C as static;
|
||||
-----
|
||||
Syntax error, unexpected T_STATIC, expecting T_STRING from 1:16 to 1:21
|
||||
array(
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user