Slightly optimize the parser and improve the API

This commit is contained in:
nikic 2011-04-20 13:40:29 +02:00
parent aea5e43349
commit e00b460125
4 changed files with 86 additions and 126 deletions

25
README
View File

@ -5,23 +5,20 @@ This project is work in progress, but it basically works and was tested on a lar
Usage:
$parser = new Parser();
$parser->yyparse(
$stmts = $parser->yyparse(
new Lexer('<?php // some code'),
function($stmt) {
// this is the accept callback
// it is fired in case of a successful parse
// $stmts contains an array of nodes
// to print them do:
foreach ($stmts as $stmt) {
echo $stmt, "\n";
}
},
function($msg) {
// this is the error callback
// it is fired in case of an error (currently
// errors abort the parse)
// this is the error callback, which is fired in case of
// a parse error. It is passed the error message.
echo $msg, "\n";
}
);
// see example usage in test.php
// the return value of Parser->yyparse will either be false (which
// signifies that an error occured) or an array of statements (Nodes)
if (false !== $stmts) {
// dump the AST
foreach ($stmts as $stmt) {
echo $stmt, "\n";
}
}

View File

@ -114,65 +114,51 @@ class YYParser
);
}
protected function accept() {
$acceptCallback = $this->acceptCallback;
$acceptCallback($this->yyval);
}
protected function yyaccept() {
$this->yyaccept = 1;
}
protected function yyabort() {
$this->yyaccept = 2;
}
@if -t
/* Traditional Debug Mode */
private function YYTRACE_NEWSTATE($state, $sym) {
if ($this->yydebug) {
$this->yyprintln("% State " . $state . ", Lookahead "
. ($sym < 0 ? "--none--" : self::$yyterminals[$sym]));
$this->yyprintln('% State ' . $state . ', Lookahead '
. ($sym < 0 ? '--none--' : self::$yyterminals[$sym]));
}
}
private function YYTRACE_READ($sym) {
if ($this->yydebug)
$this->yyprintln("% Reading " . self::$yyterminals[$sym]);
$this->yyprintln('% Reading ' . self::$yyterminals[$sym]);
}
private function YYTRACE_SHIFT($sym) {
if ($this->yydebug)
$this->yyprintln("% Shift " . self::$yyterminals[$sym]);
$this->yyprintln('% Shift ' . self::$yyterminals[$sym]);
}
private function YYTRACE_ACCEPT() {
if ($this->yydebug)
$this->yyprintln("% Accepted.");
$this->yyprintln('% Accepted.');
}
private function YYTRACE_REDUCE($n) {
if ($this->yydebug)
$this->yyprintln("% Reduce by (" . $n . ") " . self::$yyproduction[$n]);
$this->yyprintln('% Reduce by (' . $n . ') ' . self::$yyproduction[$n]);
}
private function YYTRACE_POP($state) {
if ($this->yydebug)
$this->yyprintln("% Recovering, uncovers state " . $state);
$this->yyprintln('% Recovering, uncovers state ' . $state);
}
private function YYTRACE_DISCARD($sym) {
if ($this->yydebug)
$this->yyprintln("% Discard " . self::$yyterminals[$sym]);
$this->yyprintln('% Discard ' . self::$yyterminals[$sym]);
}
@endif
/**
* Parser entry point
*/
public function yyparse($lex, $acceptCallback, $errorCallback) {
public function yyparse($lex, $errorCallback) {
$this->lex = $lex;
$this->acceptCallback = $acceptCallback;
$this->errorCallback = $errorCallback;
$this->yyastk = array();
@ -222,14 +208,14 @@ class YYParser
@if -t
$this->YYTRACE_SHIFT($yychar);
@endif
$this->yysp++;
++$this->yysp;
$yysstk[$this->yysp] = $yystate = $yyn;
$this->yyastk[$this->yysp] = $yylval;
$yychar = -1;
if ($yyerrflag > 0)
$yyerrflag--;
--$yyerrflag;
if ($yyn < self::YYNLSTATES)
continue;
@ -250,36 +236,29 @@ class YYParser
@if -t
$this->YYTRACE_ACCEPT();
@endif
$this->accept();
return $this->yyaccept - 1;
return $this->yyval;
} elseif ($yyn != self::YYUNEXPECTED) {
/* reduce */
$yyl = self::$yylen[$yyn];
$n = $this->yysp-$yyl+1;
$yyval = isset($this->yyastk[$n]) ? $this->yyastk[$n] : null;
@if -t
$this->YYTRACE_REDUCE($yyn);
@endif
$this->{'yyn' . $yyn}();
if ($this->yyaccept) {
$yyn = self::YYNLSTATES;
/* Goto - shift nonterminal */
$this->yysp -= self::$yylen[$yyn];
$yyn = self::$yylhs[$yyn];
if (($yyp = self::$yygbase[$yyn] + $yysstk[$this->yysp]) >= 0
&& $yyp < self::YYGLAST
&& self::$yygcheck[$yyp] == $yyn) {
$yystate = self::$yygoto[$yyp];
} else {
/* Goto - shift nonterminal */
$this->yysp -= $yyl;
$yyn = self::$yylhs[$yyn];
if (($yyp = self::$yygbase[$yyn] + $yysstk[$this->yysp]) >= 0
&& $yyp < self::YYGLAST
&& self::$yygcheck[$yyp] == $yyn) {
$yystate = self::$yygoto[$yyp];
} else {
$yystate = self::$yygdefault[$yyn];
}
$this->yysp++;
$yysstk[$this->yysp] = $yystate;
$this->yyastk[$this->yysp] = $this->yyval;
$yystate = self::$yygdefault[$yyn];
}
++$this->yysp;
$yysstk[$this->yysp] = $yystate;
$this->yyastk[$this->yysp] = $this->yyval;
} else {
/* error */
switch ($yyerrflag) {
@ -297,7 +276,7 @@ class YYParser
&& $yyn < self::YYLAST
&& self::$yycheck[$yyn] == self::YYINTERRTOK))) {
if ($this->yysp <= 0) {
return 1;
return false;
}
$yystate = $yysstk[--$this->yysp];
@if -t
@ -316,7 +295,7 @@ class YYParser
$this->YYTRACE_DISCARD($yychar);
@endif
if ($yychar == 0) {
return 1;
return false;
}
$yychar = -1;
break;
@ -336,7 +315,10 @@ class YYParser
%b
}
@noact
private function yyn%n() {}
private function yyn%n() {
$this->yyval = $this->yyastk[$this->yysp];
}
@endreduce
}
@tailcode;

View File

@ -145,7 +145,6 @@ class Parser
protected $yyval;
protected $yyastk;
protected $yysp;
protected $yyaccept;
/** Debug mode flag **/
public $yydebug = true;
@ -1254,63 +1253,49 @@ class Parser
);
}
protected function accept() {
$acceptCallback = $this->acceptCallback;
$acceptCallback($this->yyval);
}
protected function yyaccept() {
$this->yyaccept = 1;
}
protected function yyabort() {
$this->yyaccept = 2;
}
/* Traditional Debug Mode */
private function YYTRACE_NEWSTATE($state, $sym) {
if ($this->yydebug) {
$this->yyprintln("% State " . $state . ", Lookahead "
. ($sym < 0 ? "--none--" : self::$yyterminals[$sym]));
$this->yyprintln('% State ' . $state . ', Lookahead '
. ($sym < 0 ? '--none--' : self::$yyterminals[$sym]));
}
}
private function YYTRACE_READ($sym) {
if ($this->yydebug)
$this->yyprintln("% Reading " . self::$yyterminals[$sym]);
$this->yyprintln('% Reading ' . self::$yyterminals[$sym]);
}
private function YYTRACE_SHIFT($sym) {
if ($this->yydebug)
$this->yyprintln("% Shift " . self::$yyterminals[$sym]);
$this->yyprintln('% Shift ' . self::$yyterminals[$sym]);
}
private function YYTRACE_ACCEPT() {
if ($this->yydebug)
$this->yyprintln("% Accepted.");
$this->yyprintln('% Accepted.');
}
private function YYTRACE_REDUCE($n) {
if ($this->yydebug)
$this->yyprintln("% Reduce by (" . $n . ") " . self::$yyproduction[$n]);
$this->yyprintln('% Reduce by (' . $n . ') ' . self::$yyproduction[$n]);
}
private function YYTRACE_POP($state) {
if ($this->yydebug)
$this->yyprintln("% Recovering, uncovers state " . $state);
$this->yyprintln('% Recovering, uncovers state ' . $state);
}
private function YYTRACE_DISCARD($sym) {
if ($this->yydebug)
$this->yyprintln("% Discard " . self::$yyterminals[$sym]);
$this->yyprintln('% Discard ' . self::$yyterminals[$sym]);
}
/**
* Parser entry point
*/
public function yyparse($lex, $acceptCallback, $errorCallback) {
public function yyparse($lex, $errorCallback) {
$this->lex = $lex;
$this->acceptCallback = $acceptCallback;
$this->errorCallback = $errorCallback;
$this->yyastk = array();
@ -1354,14 +1339,14 @@ class Parser
if ($yyn > 0) {
/* shift */
$this->YYTRACE_SHIFT($yychar);
$this->yysp++;
++$this->yysp;
$yysstk[$this->yysp] = $yystate = $yyn;
$this->yyastk[$this->yysp] = $yylval;
$yychar = -1;
if ($yyerrflag > 0)
$yyerrflag--;
--$yyerrflag;
if ($yyn < self::YYNLSTATES)
continue;
@ -1376,38 +1361,30 @@ class Parser
}
for (;;) {
/* reduce/error */
if ($yyn == 0) {
/* accept */
$this->YYTRACE_ACCEPT();
$this->accept();
return $this->yyaccept - 1;
return $this->yyval;
} elseif ($yyn != self::YYUNEXPECTED) {
/* reduce */
$yyl = self::$yylen[$yyn];
$n = $this->yysp-$yyl+1;
$yyval = isset($this->yyastk[$n]) ? $this->yyastk[$n] : null;
$this->YYTRACE_REDUCE($yyn);
$this->{'yyn' . $yyn}();
if ($this->yyaccept) {
$yyn = self::YYNLSTATES;
/* Goto - shift nonterminal */
$this->yysp -= self::$yylen[$yyn];
$yyn = self::$yylhs[$yyn];
if (($yyp = self::$yygbase[$yyn] + $yysstk[$this->yysp]) >= 0
&& $yyp < self::YYGLAST
&& self::$yygcheck[$yyp] == $yyn) {
$yystate = self::$yygoto[$yyp];
} else {
/* Goto - shift nonterminal */
$this->yysp -= $yyl;
$yyn = self::$yylhs[$yyn];
if (($yyp = self::$yygbase[$yyn] + $yysstk[$this->yysp]) >= 0
&& $yyp < self::YYGLAST
&& self::$yygcheck[$yyp] == $yyn) {
$yystate = self::$yygoto[$yyp];
} else {
$yystate = self::$yygdefault[$yyn];
}
$this->yysp++;
$yysstk[$this->yysp] = $yystate;
$this->yyastk[$this->yysp] = $this->yyval;
$yystate = self::$yygdefault[$yyn];
}
++$this->yysp;
$yysstk[$this->yysp] = $yystate;
$this->yyastk[$this->yysp] = $this->yyval;
} else {
/* error */
switch ($yyerrflag) {
@ -1425,7 +1402,7 @@ class Parser
&& $yyn < self::YYLAST
&& self::$yycheck[$yyn] == self::YYINTERRTOK))) {
if ($this->yysp <= 0) {
return 1;
return false;
}
$yystate = $yysstk[--$this->yysp];
$this->YYTRACE_POP($yystate);
@ -1438,7 +1415,7 @@ class Parser
case 3:
$this->YYTRACE_DISCARD($yychar);
if ($yychar == 0) {
return 1;
return false;
}
$yychar = -1;
break;
@ -1452,7 +1429,10 @@ class Parser
}
}
}
private function yyn0() {}
private function yyn0() {
$this->yyval = $this->yyastk[$this->yysp];
}
private function yyn1() {
$this->yyval = $this->yyastk[$this->yysp-(1-1)];

View File

@ -10,25 +10,26 @@ $parser = new Parser();
$parser->yydebug = false;
// Output Demo
$parser->yyparse(new Lexer(
$stmts = $parser->yyparse(new Lexer(
'<?php
echo HI;
hallo();
blaBlub();'
),
function($stmts) {
foreach ($stmts as $stmt) {
echo htmlspecialchars($stmt), "\n";
}
},
function($msg) {
echo $msg, "\n";
}
);
if (false !== $stmts) {
foreach ($stmts as $stmt) {
echo htmlspecialchars($stmt), "\n";
}
}
echo "\n\n";
// Correctness Demo
$GST = microtime(true);
foreach (new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('.'),
RecursiveIteratorIterator::LEAVES_ONLY)
@ -40,16 +41,16 @@ foreach (new RecursiveIteratorIterator(
set_time_limit(5);
$startTime = microtime(true);
$result = $parser->yyparse(
$stmts = $parser->yyparse(
new Lexer(file_get_contents($file)),
function($stmts) { },
function($msg) {
echo $msg, "\n";
}
);
$endTime = microtime(true);
echo str_pad($file . ': ', 120, ' '), ($result == -1 ? 'successful' : 'ERROR'), ' (', $endTime - $startTime, ')', "\n";
echo str_pad($file . ': ', 120, ' '), (false !== $stmts ? 'successful' : 'ERROR'), ' (', $endTime - $startTime, ')', "\n";
flush();
}
}
echo microtime(true) - $GST;