Throw ParseErrorException on error instead of error callback

As long as the parser isn't reentrant having an error callback doesn't really make sense and only complicates everything.
This commit is contained in:
nikic 2011-06-03 17:44:23 +02:00
parent 0146a41132
commit d82bbb3bea
12 changed files with 246 additions and 156 deletions

View File

@ -19,20 +19,17 @@ This package currently bundles several components:
Parser and ParserDebug
----------------------
Parsing is performed using `Parser->parse()`. This method accepts a `Lexer` as the first parameter
and a error callback, i.e. a function that will be passed a message in case of an error, as
second parameter. The parser returns an array of statement nodes. If an error occurs the parser
instead returns `false` and sends an error message to the error callback.
Parsing is performed using `Parser->parse()`. This method accepts a `Lexer` as the only parameter
and returns an array of statement nodes. If an error occurs it throws a ParseErrorException.
$code = '<?php // some code';
$parser = new Parser;
$stmts = $parser->parse(
new Lexer($code),
function ($msg) {
echo $msg;
}
);
try {
$parser = new Parser;
$stmts = $parser->parse(new Lexer($code));
} catch (ParseErrorException $e) {
echo 'Parse Error: ', $e->getMessage();
}
The `ParserDebug` class also parses a PHP code, but outputs a debug trace while doing so.
@ -59,7 +56,7 @@ respective files.
NodeDumper
----------
Nodes can be dumped into a string representation using the `NodeDumper->dump` method:
Nodes can be dumped into a string representation using the `NodeDumper->dump()` method:
$code = <<<'CODE'
<?php
@ -70,17 +67,14 @@ Nodes can be dumped into a string representation using the `NodeDumper->dump` me
printLine('Hallo World!!!');
CODE;
$parser = new Parser;
$stmts = $parser->parse(
new Lexer($code),
function ($msg) {
echo $msg;
}
);
try {
$parser = new Parser;
$stmts = $parser->parse(new Lexer($code));
if (false !== $stmts) {
$nodeDumper = new NodeDumper;
echo '<pre>' . htmlspecialchars($nodeDumper->dump($stmts)) . '</pre>';
} catch (ParseErrorException $e) {
echo 'Parse Error: ', $e->getMessage();
}
This script will have an output similar to the following:

View File

@ -142,12 +142,11 @@ class YYParser
* Parses PHP code into a node tree and prints out debugging information.
#endif
*
* @param Lexer $lex A lexer
* @param callback $errorCallback Function to be passed a message in case of an error.
* @param Lexer $lex A lexer
*
* @return array Array of statements
*/
public function parse(Lexer $lex, $errorCallback) {
public function parse(Lexer $lex) {
$this->yyastk = array();
$yysstk = array();
$this->yysp = 0;
@ -201,8 +200,8 @@ class YYParser
$this->yyastk[$this->yysp] = $yylval;
$yychar = -1;
if ($yyerrflag > 0)
--$yyerrflag;
/*if ($yyerrflag > 0)
--$yyerrflag;*/
if ($yyn < self::YYNLSTATES)
continue;
@ -229,7 +228,13 @@ class YYParser
#if -t
$this->YYTRACE_REDUCE($yyn);
#endif
$this->{'yyn' . $yyn}();
try {
$this->{'yyn' . $yyn}();
} catch (ParseErrorException $e) {
$e->setRawLine($lex->getLine());
throw $e;
}
/* Goto - shift nonterminal */
$this->yysp -= self::$yylen[$yyn];
@ -248,18 +253,18 @@ class YYParser
$this->yyastk[$this->yysp] = $this->yyval;
} else {
/* error */
switch ($yyerrflag) {
case 0:
$errorCallback(
'Parse error:'
. ' Unexpected token ' . self::$yyterminals[$yychar]
. ' on line ' . $lex->getLine()
/*switch ($yyerrflag) {
case 0:*/
#endif
throw new ParseErrorException(
'Unexpected token ' . self::$yyterminals[$yychar],
$lex->getLine()
);
case 1:
/*case 1:
case 2:
$yyerrflag = 3;
$yyerrflag = 3;*/
/* Pop until error-expecting state uncovered */
while (!(($yyn = self::$yybase[$yystate] + self::YYINTERRTOK) >= 0
/*while (!(($yyn = self::$yybase[$yystate] + self::YYINTERRTOK) >= 0
&& $yyn < self::YYLAST
&& self::$yycheck[$yyn] == self::YYINTERRTOK
|| ($yystate < self::YY2TBLSTATE
@ -290,7 +295,7 @@ class YYParser
}
$yychar = -1;
break;
}
}*/
}
if ($yystate < self::YYNLSTATES)

View File

@ -71,7 +71,7 @@ function resolveNodes($code) {
function resolveMacros($code) {
return preg_replace_callback(
'~(?<name>init|push|pushNormalizing|toArray|parse(?:Var|Encapsed|LNumber|DNumber))' . ARGS . '~',
'~(?<name>error|init|push|pushNormalizing|toArray|parse(?:Var|Encapsed|LNumber|DNumber))' . ARGS . '~',
function($matches) {
// recurse
$matches['args'] = resolveMacros($matches['args']);
@ -82,6 +82,12 @@ function resolveMacros($code) {
$matches['args']
);
if ('error' == $name) {
assertArgs(1, $args, $name);
return 'throw new ParseErrorException(' . $args[0] . ')';
}
if ('init' == $name) {
return '$$ = array(' . implode(', ', $args) . ')';
}

View File

@ -924,8 +924,8 @@ state 9
. error
state 10
(24) inner_statement_list : inner_statement_list . inner_statement
(233) expr : T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars '{' inner_statement_list . '}'
(24) inner_statement_list : inner_statement_list . inner_statement
T_INCLUDE shift 56
T_INCLUDE_ONCE shift 57
@ -1227,8 +1227,8 @@ state 12
. error
state 13
(24) inner_statement_list : inner_statement_list . inner_statement
(136) method_body : '{' inner_statement_list . '}'
(24) inner_statement_list : inner_statement_list . inner_statement
T_INCLUDE shift 56
T_INCLUDE_ONCE shift 57
@ -10246,7 +10246,6 @@ state 134
. error
state 135
(92) case_list : case_list T_CASE expr . case_separator inner_statement_list
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10274,6 +10273,7 @@ state 135
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(92) case_list : case_list T_CASE expr . case_separator inner_statement_list
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -10307,7 +10307,6 @@ state 135
. error
state 136
(47) statement : expr . ';'
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10335,6 +10334,7 @@ state 136
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(47) statement : expr . ';'
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -10366,7 +10366,6 @@ state 136
. error
state 137
(38) statement : T_BREAK expr . ';'
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10394,6 +10393,7 @@ state 137
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(38) statement : T_BREAK expr . ';'
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -10425,7 +10425,6 @@ state 137
. error
state 138
(40) statement : T_CONTINUE expr . ';'
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10453,6 +10452,7 @@ state 138
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(40) statement : T_CONTINUE expr . ';'
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -10484,7 +10484,6 @@ state 138
. error
state 139
(42) statement : T_RETURN expr . ';'
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10512,6 +10511,7 @@ state 139
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(42) statement : T_RETURN expr . ';'
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -10543,7 +10543,6 @@ state 139
. error
state 140
(55) statement : T_THROW expr . ';'
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10571,6 +10570,7 @@ state 140
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(55) statement : T_THROW expr . ';'
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -10779,8 +10779,6 @@ state 143
. error
state 144
(31) statement : T_IF '(' expr . ')' statement elseif_list else_single
(32) statement : T_IF '(' expr . ')' ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10808,6 +10806,8 @@ state 144
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(31) statement : T_IF '(' expr . ')' statement elseif_list else_single
(32) statement : T_IF '(' expr . ')' ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -10839,7 +10839,6 @@ state 144
. error
state 145
(33) statement : T_WHILE '(' expr . ')' while_statement
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10867,6 +10866,7 @@ state 145
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(33) statement : T_WHILE '(' expr . ')' while_statement
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -10898,9 +10898,6 @@ state 145
. error
state 146
(49) statement : T_FOREACH '(' expr . T_AS variable ')' foreach_statement
(50) statement : T_FOREACH '(' expr . T_AS '&' variable ')' foreach_statement
(51) statement : T_FOREACH '(' expr . T_AS variable T_DOUBLE_ARROW optional_ref variable ')' foreach_statement
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10928,6 +10925,9 @@ state 146
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(49) statement : T_FOREACH '(' expr . T_AS variable ')' foreach_statement
(50) statement : T_FOREACH '(' expr . T_AS '&' variable ')' foreach_statement
(51) statement : T_FOREACH '(' expr . T_AS variable T_DOUBLE_ARROW optional_ref variable ')' foreach_statement
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -10959,7 +10959,6 @@ state 146
. error
state 147
(36) statement : T_SWITCH '(' expr . ')' switch_case_list
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -10987,6 +10986,7 @@ state 147
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(36) statement : T_SWITCH '(' expr . ')' switch_case_list
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -11610,7 +11610,6 @@ state 157
. error
state 158
(34) statement : T_DO statement T_WHILE '(' expr . ')' ';'
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -11638,6 +11637,7 @@ state 158
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(34) statement : T_DO statement T_WHILE '(' expr . ')' ';'
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -11907,7 +11907,6 @@ state 162
. error
state 163
(99) elseif_list : elseif_list T_ELSEIF '(' expr . ')' statement
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -11935,6 +11934,7 @@ state 163
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(99) elseif_list : elseif_list T_ELSEIF '(' expr . ')' statement
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -11966,7 +11966,6 @@ state 163
. error
state 164
(101) new_elseif_list : new_elseif_list T_ELSEIF '(' expr . ')' ':' inner_statement_list
(181) expr : expr . T_BOOLEAN_OR expr
(182) expr : expr . T_BOOLEAN_AND expr
(183) expr : expr . T_LOGICAL_OR expr
@ -11994,6 +11993,7 @@ state 164
(209) expr : expr . T_INSTANCEOF class_name_reference
(211) expr : expr . '?' expr ':' expr
(212) expr : expr . '?' ':' expr
(101) new_elseif_list : new_elseif_list T_ELSEIF '(' expr . ')' ':' inner_statement_list
T_LOGICAL_OR shift 83
T_LOGICAL_XOR shift 84
@ -14905,8 +14905,8 @@ state 226
. reduce (208)
state 227
(67) class_declaration_statement : T_INTERFACE T_STRING interface_extends_list '{' class_statement_list . '}'
(130) class_statement_list : class_statement_list . class_statement
(67) class_declaration_statement : T_INTERFACE T_STRING interface_extends_list '{' class_statement_list . '}'
(139) method_modifiers : .
T_CONST shift 483
@ -14927,8 +14927,8 @@ state 227
. reduce (139)
state 228
(66) class_declaration_statement : class_entry_type T_STRING extends_from implements_list '{' class_statement_list . '}'
(130) class_statement_list : class_statement_list . class_statement
(66) class_declaration_statement : class_entry_type T_STRING extends_from implements_list '{' class_statement_list . '}'
(139) method_modifiers : .
T_CONST shift 483
@ -15972,10 +15972,10 @@ state 277
. error
state 278
(246) name : T_NAMESPACE . T_NS_SEPARATOR namespace_name
(11) top_statement : T_NAMESPACE . namespace_name ';'
(12) top_statement : T_NAMESPACE . namespace_name '{' top_statement_list '}'
(13) top_statement : T_NAMESPACE . '{' top_statement_list '}'
(246) name : T_NAMESPACE . T_NS_SEPARATOR namespace_name
T_STRING shift 568 and reduce (5)
T_NS_SEPARATOR shift 340
@ -16164,10 +16164,10 @@ state 294
. error
state 295
(49) statement : T_FOREACH '(' expr T_AS variable . ')' foreach_statement
(51) statement : T_FOREACH '(' expr T_AS variable . T_DOUBLE_ARROW optional_ref variable ')' foreach_statement
(302) object_access : variable . T_OBJECT_OPERATOR object_property '(' function_call_argument_list ')'
(303) object_access_arrayable : variable . T_OBJECT_OPERATOR object_property
(49) statement : T_FOREACH '(' expr T_AS variable . ')' foreach_statement
(51) statement : T_FOREACH '(' expr T_AS variable . T_DOUBLE_ARROW optional_ref variable ')' foreach_statement
T_OBJECT_OPERATOR shift 264
T_DOUBLE_ARROW shift 359
@ -16325,16 +16325,16 @@ state 311
. reduce (311)
state 312
(45) statement : T_ECHO expr_list . ';'
(155) expr_list : expr_list . ',' expr
(45) statement : T_ECHO expr_list . ';'
',' shift 122
';' shift 605 and reduce (45)
. error
state 313
(65) function_declaration_statement : T_FUNCTION optional_ref . T_STRING '(' parameter_list ')' '{' inner_statement_list '}'
(233) expr : T_FUNCTION optional_ref . '(' parameter_list ')' lexical_vars '{' inner_statement_list '}'
(65) function_declaration_statement : T_FUNCTION optional_ref . T_STRING '(' parameter_list ')' '{' inner_statement_list '}'
T_STRING shift 413
'(' shift 256
@ -16349,17 +16349,17 @@ state 314
. error
state 315
(43) statement : T_GLOBAL global_var_list . ';'
(121) global_var_list : global_var_list . ',' global_var
(43) statement : T_GLOBAL global_var_list . ';'
',' shift 303
';' shift 612 and reduce (43)
. error
state 316
(44) statement : T_STATIC static_var_list . ';'
(126) static_var_list : static_var_list . ',' T_VARIABLE
(127) static_var_list : static_var_list . ',' T_VARIABLE '=' static_scalar
(44) statement : T_STATIC static_var_list . ';'
',' shift 417
';' shift 613 and reduce (44)
@ -16428,8 +16428,8 @@ state 323
. error
state 324
(62) variables_list : variables_list . ',' variable
(213) expr : T_ISSET '(' variables_list . ')'
(62) variables_list : variables_list . ',' variable
',' shift 246
')' shift 648 and reduce (213)
@ -16507,9 +16507,9 @@ state 332
. error
state 333
(50) statement : T_FOREACH '(' expr T_AS '&' variable . ')' foreach_statement
(302) object_access : variable . T_OBJECT_OPERATOR object_property '(' function_call_argument_list ')'
(303) object_access_arrayable : variable . T_OBJECT_OPERATOR object_property
(50) statement : T_FOREACH '(' expr T_AS '&' variable . ')' foreach_statement
T_OBJECT_OPERATOR shift 264
')' shift 22
@ -16533,9 +16533,9 @@ state 335
. error
state 336
(51) statement : T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW optional_ref variable . ')' foreach_statement
(302) object_access : variable . T_OBJECT_OPERATOR object_property '(' function_call_argument_list ')'
(303) object_access_arrayable : variable . T_OBJECT_OPERATOR object_property
(51) statement : T_FOREACH '(' expr T_AS variable T_DOUBLE_ARROW optional_ref variable . ')' foreach_statement
T_OBJECT_OPERATOR shift 264
')' shift 24
@ -16600,8 +16600,8 @@ state 343
. reduce (257)
state 344
(65) function_declaration_statement : T_FUNCTION . optional_ref T_STRING '(' parameter_list ')' '{' inner_statement_list '}'
(233) expr : T_FUNCTION . optional_ref '(' parameter_list ')' lexical_vars '{' inner_statement_list '}'
(65) function_declaration_statement : T_FUNCTION . optional_ref T_STRING '(' parameter_list ')' '{' inner_statement_list '}'
(63) optional_ref : .
'&' shift 589 and reduce (64)
@ -16609,8 +16609,8 @@ state 344
. reduce (63)
state 345
(44) statement : T_STATIC . static_var_list ';'
(243) class_name : T_STATIC .
(44) statement : T_STATIC . static_var_list ';'
T_VARIABLE shift 402
static_var_list goto 316
@ -17151,9 +17151,9 @@ state 417
. error
state 418
(61) variables_list : variable .
(302) object_access : variable . T_OBJECT_OPERATOR object_property '(' function_call_argument_list ')'
(303) object_access_arrayable : variable . T_OBJECT_OPERATOR object_property
(61) variables_list : variable .
T_OBJECT_OPERATOR shift 264
. reduce (61)
@ -17423,9 +17423,9 @@ state 447
. error
state 448
(106) parameter_list : non_empty_parameter_list .
(110) non_empty_parameter_list : non_empty_parameter_list . ',' optional_class_type optional_ref T_VARIABLE
(111) non_empty_parameter_list : non_empty_parameter_list . ',' optional_class_type optional_ref T_VARIABLE '=' static_scalar
(106) parameter_list : non_empty_parameter_list .
',' shift 262
. reduce (106)
@ -17567,9 +17567,9 @@ state 469
. error
state 470
(62) variables_list : variables_list ',' variable .
(302) object_access : variable . T_OBJECT_OPERATOR object_property '(' function_call_argument_list ')'
(303) object_access_arrayable : variable . T_OBJECT_OPERATOR object_property
(62) variables_list : variables_list ',' variable .
T_OBJECT_OPERATOR shift 264
. reduce (62)
@ -19468,4 +19468,4 @@ Statistics for zend_language_parser.phpy:
3821 items
1130 lookahead sets used
13448+800=14248 action entries
228656 bytes used
228688 bytes used

View File

@ -159,7 +159,7 @@ inner_statement:
statement { $$ = $1; }
| function_declaration_statement { $$ = $1; }
| class_declaration_statement { $$ = $1; }
| T_HALT_COMPILER '(' ')' ';' { error('__halt_compiler() can only be used from the outermost scope'); }
| T_HALT_COMPILER '(' ')' ';' { throw new ParseErrorException('__halt_compiler() can only be used from the outermost scope'); }
;
statement:

View File

@ -18,8 +18,32 @@ class Lexer
public function __construct($code) {
self::initTokenMap();
$this->tokens = token_get_all($code);
// Reset the error message in error_get_last()
// Still hoping for a better solution to be found.
@$errorGetLastResetUndefinedVariable;
$this->tokens = @token_get_all($code);
$this->pos = -1;
$error = error_get_last();
if (preg_match(
'~^(Unterminated comment) starting line ([0-9]+)$~',
$error['message'],
$matches
)
) {
throw new ParseErrorException($matches[1], $matches[2]);
}
if (preg_match(
'~^(Unexpected character in input:\s+\'(.)\' \(ASCII=[0-9]+\))~s',
$error['message'],
$matches
)
) {
throw new ParseErrorException($matches[1]);
}
}
/**

View File

@ -17,29 +17,24 @@ class Node_Stmt_Class extends Node_Stmt
const MODIFIER_FINAL = 32;
public static function verifyModifier($a, $b) {
// TODO: Actually throw errors
if ($a & 7 && $b & 7) {
('Multiple access type modifiers are not allowed');
throw new ParseErrorException('Multiple access type modifiers are not allowed');
}
if ($a & self::MODIFIER_ABSTRACT && $b & self::MODIFIER_ABSTRACT) {
('Multiple abstract modifiers are not allowed');
throw new ParseErrorException('Multiple abstract modifiers are not allowed');
}
if ($a & self::MODIFIER_STATIC && $b & self::MODIFIER_STATIC) {
('Multiple static modifiers are not allowed');
throw new ParseErrorException('Multiple static modifiers are not allowed');
}
if ($a & self::MODIFIER_FINAL && $b & self::MODIFIER_FINAL) {
('Multiple final modifiers are not allowed');
throw new ParseErrorException('Multiple final modifiers are not allowed');
}
if (($a | $b)
& (self::MODIFIER_ABSTRACT | self::MODIFIER_FINAL)
== self::MODIFIER_ABSTRACT | self::MODIFIER_FINAL
) {
('Cannot use the final modifier on an abstract class member"');
if ($a & 48 && $b & 48) {
throw new ParseErrorException('Cannot use the final modifier on an abstract class member"');
}
}
}

View File

@ -0,0 +1,70 @@
<?php
class ParseErrorException extends RuntimeException
{
protected $rawMessage;
protected $rawLine;
/**
* Creates an Exception signifying a parse error.
*
* @param string $message Error message
* @param int $line Error line in PHP file
*/
public function __construct($message, $line = -1) {
$this->rawMessage = (string) $message;
$this->rawLine = (int) $line;
$this->updateMessage();
}
/**
* Gets the error message
*
* @return string Error message
*/
public function getRawMessage() {
return $this->message;
}
/**
* Sets the line of the PHP file the error occurred in.
*
* @param string $message Error message
*/
public function setRawMessage($message) {
$this->message = (string) $message;
$this->updateMessage();
}
/**
* Gets the error line in the PHP file.
*
* @return int Error line in the PHP file
*/
public function getRawLine() {
return $this->rawLine;
}
/**
* Sets the line of the PHP file the error occurred in.
*
* @param int $line Error line in the PHP file
*/
public function setRawLine($line) {
$this->rawLine = (int) $line;
$this->updateMessage();
}
/**
* Updates the exception message after a change to rawMessage or rawLine.
*/
protected function updateMessage() {
$this->message = $this->rawMessage;
if (-1 === $this->rawLine) {
$this->message .= ' on unknown line';
} else {
$this->message .= ' on line ' . $this->rawLine;
}
}
}

View File

@ -887,12 +887,11 @@ class Parser
/**
* Parses PHP code into a node tree.
*
* @param Lexer $lex A lexer
* @param callback $errorCallback Function to be passed a message in case of an error.
* @param Lexer $lex A lexer
*
* @return array Array of statements
*/
public function parse(Lexer $lex, $errorCallback) {
public function parse(Lexer $lex) {
$this->yyastk = array();
$yysstk = array();
$this->yysp = 0;
@ -937,8 +936,8 @@ class Parser
$this->yyastk[$this->yysp] = $yylval;
$yychar = -1;
if ($yyerrflag > 0)
--$yyerrflag;
/*if ($yyerrflag > 0)
--$yyerrflag;*/
if ($yyn < self::YYNLSTATES)
continue;
@ -959,7 +958,13 @@ class Parser
return $this->yyval;
} elseif ($yyn != self::YYUNEXPECTED) {
/* reduce */
$this->{'yyn' . $yyn}();
try {
$this->{'yyn' . $yyn}();
} catch (ParseErrorException $e) {
$e->setRawLine($lex->getLine());
throw $e;
}
/* Goto - shift nonterminal */
$this->yysp -= self::$yylen[$yyn];
@ -978,18 +983,17 @@ class Parser
$this->yyastk[$this->yysp] = $this->yyval;
} else {
/* error */
switch ($yyerrflag) {
case 0:
$errorCallback(
'Parse error:'
. ' Unexpected token ' . self::$yyterminals[$yychar]
. ' on line ' . $lex->getLine()
/*switch ($yyerrflag) {
case 0:*/
throw new ParseErrorException(
'Unexpected token ' . self::$yyterminals[$yychar],
$lex->getLine()
);
case 1:
/*case 1:
case 2:
$yyerrflag = 3;
$yyerrflag = 3;*/
/* Pop until error-expecting state uncovered */
while (!(($yyn = self::$yybase[$yystate] + self::YYINTERRTOK) >= 0
/*while (!(($yyn = self::$yybase[$yystate] + self::YYINTERRTOK) >= 0
&& $yyn < self::YYLAST
&& self::$yycheck[$yyn] == self::YYINTERRTOK
|| ($yystate < self::YY2TBLSTATE
@ -1011,7 +1015,7 @@ class Parser
}
$yychar = -1;
break;
}
}*/
}
if ($yystate < self::YYNLSTATES)
@ -1139,7 +1143,7 @@ class Parser
}
private function yyn29() {
error('__halt_compiler() can only be used from the outermost scope');
throw new ParseErrorException('__halt_compiler() can only be used from the outermost scope');
}
private function yyn30() {

View File

@ -1281,12 +1281,11 @@ class ParserDebug
/**
* Parses PHP code into a node tree and prints out debugging information.
*
* @param Lexer $lex A lexer
* @param callback $errorCallback Function to be passed a message in case of an error.
* @param Lexer $lex A lexer
*
* @return array Array of statements
*/
public function parse(Lexer $lex, $errorCallback) {
public function parse(Lexer $lex) {
$this->yyastk = array();
$yysstk = array();
$this->yysp = 0;
@ -1334,8 +1333,8 @@ class ParserDebug
$this->yyastk[$this->yysp] = $yylval;
$yychar = -1;
if ($yyerrflag > 0)
--$yyerrflag;
/*if ($yyerrflag > 0)
--$yyerrflag;*/
if ($yyn < self::YYNLSTATES)
continue;
@ -1358,7 +1357,13 @@ class ParserDebug
} elseif ($yyn != self::YYUNEXPECTED) {
/* reduce */
$this->YYTRACE_REDUCE($yyn);
$this->{'yyn' . $yyn}();
try {
$this->{'yyn' . $yyn}();
} catch (ParseErrorException $e) {
$e->setRawLine($lex->getLine());
throw $e;
}
/* Goto - shift nonterminal */
$this->yysp -= self::$yylen[$yyn];
@ -1377,18 +1382,17 @@ class ParserDebug
$this->yyastk[$this->yysp] = $this->yyval;
} else {
/* error */
switch ($yyerrflag) {
case 0:
$errorCallback(
'Parse error:'
. ' Unexpected token ' . self::$yyterminals[$yychar]
. ' on line ' . $lex->getLine()
/*switch ($yyerrflag) {
case 0:*/
throw new ParseErrorException(
'Unexpected token ' . self::$yyterminals[$yychar],
$lex->getLine()
);
case 1:
/*case 1:
case 2:
$yyerrflag = 3;
$yyerrflag = 3;*/
/* Pop until error-expecting state uncovered */
while (!(($yyn = self::$yybase[$yystate] + self::YYINTERRTOK) >= 0
/*while (!(($yyn = self::$yybase[$yystate] + self::YYINTERRTOK) >= 0
&& $yyn < self::YYLAST
&& self::$yycheck[$yyn] == self::YYINTERRTOK
|| ($yystate < self::YY2TBLSTATE
@ -1413,7 +1417,7 @@ class ParserDebug
}
$yychar = -1;
break;
}
}*/
}
if ($yystate < self::YYNLSTATES)
@ -1541,7 +1545,7 @@ class ParserDebug
}
private function yyn29() {
error('__halt_compiler() can only be used from the outermost scope');
throw new ParseErrorException('__halt_compiler() can only be used from the outermost scope');
}
private function yyn30() {

View File

@ -22,9 +22,11 @@ echo '<table>
$parseFail = $parseCount = $ppFail = $ppCount = $compareFail = $compareCount = 0;
$totalStartTime = microtime(true);
$parseTime = $ppTime = $compareTime = 0;
$totalStartTime = microtime(true);
foreach (new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($DIR),
RecursiveIteratorIterator::LEAVES_ONLY)
@ -39,36 +41,24 @@ foreach (new RecursiveIteratorIterator(
set_time_limit(10);
$errMsg = '';
try {
++$parseCount;
$startTime = microtime(true);
$stmts = $parser->parse(new Lexer(file_get_contents($file)));
$parseTime += microtime(true) - $startTime;
++$parseCount;
$parseTime -= microtime(true);
$stmts = $parser->parse(
new Lexer(file_get_contents($file)),
function ($msg) use (&$errMsg) {
$errMsg = $msg;
}
);
$parseTime += microtime(true);
if (false !== $stmts) {
++$ppCount;
$ppTime -= microtime(true);
$startTime = microtime(true);
$code = '<?php' . "\n" . $prettyPrinter->prettyPrint($stmts);
$ppTime += microtime(true);
$ppTime += microtime(true) - $startTime;
$ppStmts = $parser->parse(
new Lexer($code),
function ($msg) use (&$errMsg) {
$errMsg = $msg;
}
);
try {
$ppStmts = $parser->parse(new Lexer($code));
if (false !== $ppStmts) {
++$compareCount;
$compareTime -= microtime(true);
$startTime = microtime(true);
$same = $nodeDumper->dump($stmts) == $nodeDumper->dump($ppStmts);
$compareTime += microtime(true);
$compareTime += microtime(true) - $startTime;
if ($same) {
echo '
@ -85,22 +75,23 @@ foreach (new RecursiveIteratorIterator(
++$compareFail;
}
} else {
} catch (ParseErrorException $e) {
echo '
<td class="pass">PASS</td>
<td class="fail">FAIL</td>
<td></td>
</tr>';
</tr>
<tr class="failReason"><td colspan="4">' . $e->getMessage() . '</td></tr>';
++$ppFail;
}
} else {
} catch (ParseErrorException $e) {
echo '
<td class="fail">FAIL</td>
<td></td>
<td></td>
</tr>
<tr class="failReason"><td colspan="4">' . $errMsg . '</td></tr>';
<tr class="failReason"><td colspan="4">' . $e->getMessage() . '</td></tr>';
++$parseFail;
}

View File

@ -31,16 +31,13 @@ foreach (explode("\n", $exprs) as $expr) {
continue;
}
if ($parser->parse(
new Lexer('<?php ' . $expr . ';'),
function ($msg) use(&$errMsg) {
$errMsg = $msg;
}
)
) {
try {
$parser->parse(new Lexer('<?php ' . $expr . ';'));
echo '<tr><td>' . $expr . '</td><td class="pass">PASS</td></tr>';
} else {
} catch (ParseErrorException $e) {
echo '<tr><td>' . $expr . '</td><td class="fail">FAIL</td></tr>';
echo '<tr><td colspan="2">' . $e->getMessage() . '</td></tr>';
}
}