From d82bbb3bea1b3505201a4014c612be322bb82ff1 Mon Sep 17 00:00:00 2001
From: nikic <+@ni-po.com>
Date: Fri, 3 Jun 2011 17:44:23 +0200
Subject: [PATCH] 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.
---
README.md | 34 ++++++---------
grammar/php.kmyacc | 37 +++++++++-------
grammar/preprocessor.php | 8 +++-
grammar/y.output | 72 +++++++++++++++----------------
grammar/zend_language_parser.phpy | 2 +-
lib/Lexer.php | 26 ++++++++++-
lib/Node/Stmt/Class.php | 17 +++-----
lib/ParseErrorException.php | 70 ++++++++++++++++++++++++++++++
lib/Parser.php | 38 ++++++++--------
lib/ParserDebug.php | 38 ++++++++--------
test/testAgainstDirectory.php | 47 ++++++++------------
test/testExpressions.php | 13 +++---
12 files changed, 246 insertions(+), 156 deletions(-)
create mode 100644 lib/ParseErrorException.php
diff --git a/README.md b/README.md
index ba5e596..d940837 100644
--- a/README.md
+++ b/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 = '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'
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 '
' . htmlspecialchars($nodeDumper->dump($stmts)) . '
';
+ } catch (ParseErrorException $e) {
+ echo 'Parse Error: ', $e->getMessage();
}
This script will have an output similar to the following:
diff --git a/grammar/php.kmyacc b/grammar/php.kmyacc
index 24f0dd8..195bffc 100644
--- a/grammar/php.kmyacc
+++ b/grammar/php.kmyacc
@@ -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)
diff --git a/grammar/preprocessor.php b/grammar/preprocessor.php
index f38a200..67d417a 100644
--- a/grammar/preprocessor.php
+++ b/grammar/preprocessor.php
@@ -71,7 +71,7 @@ function resolveNodes($code) {
function resolveMacros($code) {
return preg_replace_callback(
- '~(?init|push|pushNormalizing|toArray|parse(?:Var|Encapsed|LNumber|DNumber))' . ARGS . '~',
+ '~(?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) . ')';
}
diff --git a/grammar/y.output b/grammar/y.output
index 46b1132..69c5714 100644
--- a/grammar/y.output
+++ b/grammar/y.output
@@ -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
diff --git a/grammar/zend_language_parser.phpy b/grammar/zend_language_parser.phpy
index c27cc52..9ef6710 100644
--- a/grammar/zend_language_parser.phpy
+++ b/grammar/zend_language_parser.phpy
@@ -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:
diff --git a/lib/Lexer.php b/lib/Lexer.php
index 209b6ca..2d223b7 100644
--- a/lib/Lexer.php
+++ b/lib/Lexer.php
@@ -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]);
+ }
}
/**
diff --git a/lib/Node/Stmt/Class.php b/lib/Node/Stmt/Class.php
index b0dc902..fe5e2fc 100644
--- a/lib/Node/Stmt/Class.php
+++ b/lib/Node/Stmt/Class.php
@@ -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"');
}
}
}
\ No newline at end of file
diff --git a/lib/ParseErrorException.php b/lib/ParseErrorException.php
new file mode 100644
index 0000000..f6ca88c
--- /dev/null
+++ b/lib/ParseErrorException.php
@@ -0,0 +1,70 @@
+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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/Parser.php b/lib/Parser.php
index be22cb2..ecfdeb6 100644
--- a/lib/Parser.php
+++ b/lib/Parser.php
@@ -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() {
diff --git a/lib/ParserDebug.php b/lib/ParserDebug.php
index 8c238e4..c72f5cc 100644
--- a/lib/ParserDebug.php
+++ b/lib/ParserDebug.php
@@ -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() {
diff --git a/test/testAgainstDirectory.php b/test/testAgainstDirectory.php
index c29196e..0fba13a 100644
--- a/test/testAgainstDirectory.php
+++ b/test/testAgainstDirectory.php
@@ -22,9 +22,11 @@ echo '
$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 = '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 '
PASS |
FAIL |
|
- ';
+
+ ' . $e->getMessage() . ' |
';
++$ppFail;
}
- } else {
+ } catch (ParseErrorException $e) {
echo '
FAIL |
|
|
- ' . $errMsg . ' |
';
+ ' . $e->getMessage() . ' |
';
++$parseFail;
}
diff --git a/test/testExpressions.php b/test/testExpressions.php
index 78d52c5..9c4239b 100644
--- a/test/testExpressions.php
+++ b/test/testExpressions.php
@@ -31,16 +31,13 @@ foreach (explode("\n", $exprs) as $expr) {
continue;
}
- if ($parser->parse(
- new Lexer('parse(new Lexer('' . $expr . ' | PASS | ';
- } else {
+ } catch (ParseErrorException $e) {
echo '' . $expr . ' | FAIL |
';
+ echo '' . $e->getMessage() . ' |
';
}
}