mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2024-11-26 20:14:46 +01:00
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:
parent
0146a41132
commit
d82bbb3bea
34
README.md
34
README.md
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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) . ')';
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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"');
|
||||
}
|
||||
}
|
||||
}
|
70
lib/ParseErrorException.php
Normal file
70
lib/ParseErrorException.php
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>';
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user