1
0
mirror of https://github.com/danog/PHP-Parser.git synced 2024-11-26 20:04:48 +01:00

Further work on PrettyPrinter. Add possibility to test PrettyPrinter correctness

This commit is contained in:
nikic 2011-05-29 17:33:03 +02:00
parent 685171ddc5
commit 3c13dce680
18 changed files with 274 additions and 68 deletions

View File

@ -1,5 +1,5 @@
<?php
class Node_Expr extends NodeAbstract
abstract class Node_Expr extends NodeAbstract
{
}

View File

@ -1,5 +1,9 @@
<?php
/**
* @property Node_Variable $var Variable reference is assigned to
* @property Node_Variable $refVar Variable which is referenced
*/
class Node_Expr_AssignRef extends Node_Expr
{
}

View File

@ -1,5 +1,8 @@
<?php
/**
* @property null|Node_Expr $expr Expression
*/
class Node_Expr_Exit extends Node_Expr
{
}

View File

@ -1,5 +1,9 @@
<?php
/**
* @property array $assignList List of variables to assign to
* @property Node_Expr $expr Expression
*/
class Node_Expr_List extends Node_Expr
{
}

View File

@ -1,5 +1,10 @@
<?php
/**
* @property Node_Name|Node_Variable $class Class name
* @property string|Node_Variable $func Method name
* @property array $args Arguments
*/
class Node_Expr_StaticCall extends Node_Expr
{
}

View File

@ -1,5 +1,5 @@
<?php
class Node_Scalar extends NodeAbstract
abstract class Node_Scalar extends NodeAbstract
{
}

View File

@ -1,5 +1,5 @@
<?php
class Node_Stmt extends NodeAbstract
abstract class Node_Stmt extends NodeAbstract
{
}

View File

@ -1,5 +1,9 @@
<?php
/**
* @property null|Node_Expr $cond Condition (null for default)
* @property array $stmts Statements
*/
class Node_Stmt_Case extends Node_Stmt
{
}

View File

@ -1,5 +1,8 @@
<?php
/**
* @property array $consts Constant declarations
*/
class Node_Stmt_Const extends Node_Stmt
{
}

View File

@ -1,5 +1,9 @@
<?php
/**
* @property string $name Name
* @property Node_Expr $value Value
*/
class Node_Stmt_ConstConst extends Node_Stmt
{
}

View File

@ -1,5 +1,11 @@
<?php
/**
* @property array $init Init expression
* @property array $cond Loop condition
* @property array $loop Loop expression
* @property array $stmts Statements
*/
class Node_Stmt_For extends Node_Stmt
{
}

View File

@ -1,5 +1,9 @@
<?php
/**
* @property Node_Expr $cond Condition
* @property array $caseList Case list
*/
class Node_Stmt_Switch extends Node_Stmt
{
}

View File

@ -1,5 +1,8 @@
<?php
/**
* @property Node_Expr $expr Expression
*/
class Node_Stmt_Throw extends Node_Stmt
{
}

View File

@ -1,5 +1,9 @@
<?php
/**
* @property Node_Expr $cond Condition
* @property array $stmts Statements
*/
class Node_Stmt_While extends Node_Stmt
{
}

View File

@ -72,6 +72,10 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
return $this->p($node->var) . ' = ' . $this->p($node->expr);
}
public function pExpr_AssignRef(Node_Expr_AssignRef $node) {
return $this->p($node->var) . ' =& ' . $this->p($node->refVar);
}
public function pExpr_AssignPlus(Node_Expr_AssignPlus $node) {
return $this->p($node->var) . ' += ' . $this->p($node->expr);
}
@ -116,6 +120,19 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
return $this->p($node->var) . ' >>= ' . $this->p($node->expr);
}
public function pExpr_List(Node_Expr_List $node) {
$pAssignList = array();
foreach ($node->assignList as $element) {
if (null === $element) {
$pAssignList[] = '';
} else {
$pAssignList[] = $this->p($element);
}
}
return 'list(' . implode(', ', $pAssignList) . ') = ' . $this->p($node->expr);
}
// Binary expressions
public function pExpr_Plus(Node_Expr_Plus $node) {
@ -281,6 +298,12 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. '(' . $this->pCommaSeparated($node->args) . ')';
}
public function pExpr_StaticCall(Node_Expr_StaticCall $node) {
return $this->p($node->class) . '::'
. ($node->func instanceof Node_Variable ? $this->p($node->func) : $node->func)
. '(' . $this->pCommaSeparated($node->args) . ')';
}
public function pExpr_Empty(Node_Expr_Empty $node) {
return 'empty(' . $this->p($node->var) . ')';
}
@ -328,10 +351,7 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
}
public function pExpr_StaticPropertyFetch(Node_Expr_StaticPropertyFetch $node) {
// TODO
return $this->p($node->class) . '::'
. ($node->name instanceof Node_Expr ? $this->p($node->name) : '$' . $node->name);
return $this->p($node->class) . '::$' . $this->pObjectProperty($node->name);
}
public function pExpr_LambdaFunc(Node_Expr_LambdaFunc $node) {
@ -355,15 +375,11 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. ': ' . $this->p($node->else);
}
// Statements
public function pStmt_Break(Node_Stmt_Break $node) {
return 'break' . ($node->num !== null ? ' ' . $node->num : '');
public function pExpr_Exit(Node_Expr_Exit $node) {
return 'die' . (null !== $node->expr ? '(' . $this->p($node->expr) . ')' : '');
}
public function pStmt_Continue(Node_Stmt_Continue $node) {
return 'continue' . ($node->num !== null ? ' ' . $node->num : '');
}
// Declarations
public function pStmt_Class(Node_Stmt_Class $node) {
return $this->pModifiers($node->type)
@ -373,12 +389,13 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. "\n" . '{' . "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
}
public function pStmt_ClassConst(Node_Stmt_ClassConst $node) {
return 'const ' . $this->pCommaSeparated($node->consts);
public function pStmt_Property(Node_Stmt_Property $node) {
return $this->pModifiers($node->type) . $this->pCommaSeparated($node->props);
}
public function pStmt_ClassConstConst(Node_Stmt_ClassConstConst $node) {
return $node->name . ' = ' . $this->p($node->value);
public function pStmt_PropertyProperty(Node_Stmt_PropertyProperty $node) {
return '$' . $node->name
. (null !== $node->default ? ' = ' . $this->p($node->default) : '');
}
public function pStmt_ClassMethod(Node_Stmt_ClassMethod $node) {
@ -388,15 +405,12 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. "\n" . '{' . "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
}
public function pStmt_Echo(Node_Stmt_Echo $node) {
return 'echo ' . $this->pCommaSeparated($node->exprs);
public function pStmt_ClassConst(Node_Stmt_ClassConst $node) {
return 'const ' . $this->pCommaSeparated($node->consts);
}
public function pStmt_Foreach(Node_Stmt_Foreach $node) {
return 'foreach (' . $this->p($node->expr) . ' '
. (null !== $node->keyVar ? $this->p($node) . ' as ' : '')
. ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {' . "\n"
. $this->pIndent($this->pStmts($node->stmts)) . '}';
public function pStmt_ClassConstConst(Node_Stmt_ClassConstConst $node) {
return $node->name . ' = ' . $this->p($node->value);
}
public function pStmt_Func(Node_Stmt_Func $node) {
@ -412,6 +426,16 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. ($node->default ? ' = ' . $this->p($node->default) : '');
}
public function pStmt_Const(Node_Stmt_Const $node) {
return 'const ' . $this->pCommaSeparated($node->consts);
}
public function pStmt_ConstConst(Node_Stmt_ConstConst $node) {
return $node->name . ' = ' . $this->p($node->value);
}
// Control flow
public function pStmt_If(Node_Stmt_If $node) {
return 'if (' . $this->p($node->cond) . ') {' . "\n"
. $this->pIndent($this->pStmts($node->stmts)) . '}'
@ -420,7 +444,7 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
}
public function pStmt_Elseif(Node_Stmt_Elseif $node) {
return ' elseif {' . "\n"
return ' elseif (' . $this->p($node->cond) . ') {' . "\n"
. $this->pIndent($this->pStmts($node->stmts)) . '}';
}
@ -429,19 +453,62 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. $this->pIndent($this->pStmts($node->stmts)) . '}';
}
public function pStmt_Property(Node_Stmt_Property $node) {
return $this->pModifiers($node->type) . $this->pCommaSeparated($node->props);
public function pStmt_For(Node_Stmt_For $node) {
return 'for ('
. $this->pCommaSeparated($node->init) . ';'
. $this->pCommaSeparated($node->cond) . ';'
. $this->pCommaSeparated($node->loop)
. ') {' . "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
}
public function pStmt_PropertyProperty(Node_Stmt_PropertyProperty $node) {
return '$' . $node->name
. (null !== $node->default ? ' = ' . $this->p($node->default) : '');
public function pStmt_Foreach(Node_Stmt_Foreach $node) {
return 'foreach (' . $this->p($node->expr) . ' as '
. (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
. ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {' . "\n"
. $this->pIndent($this->pStmts($node->stmts)) . '}';
}
public function pStmt_While(Node_Stmt_While $node) {
return 'while (' . $this->p($node->cond) . ') {'
. "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
}
public function pStmt_Switch(Node_Stmt_Switch $node) {
return 'switch (' . $this->p($node->cond) . ') {'
. "\n" . $this->pIndent($this->pImplode($node->caseList)) . '}';
}
public function pStmt_Case(Node_Stmt_Case $node) {
return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':'
. "\n" . $this->pIndent($this->pStmts($node->stmts));
}
public function pStmt_Break(Node_Stmt_Break $node) {
return 'break' . ($node->num !== null ? ' ' . $node->num : '');
}
public function pStmt_Continue(Node_Stmt_Continue $node) {
return 'continue' . ($node->num !== null ? ' ' . $node->num : '');
}
public function pStmt_Return(Node_Stmt_Return $node) {
return 'return' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '');
}
public function pStmt_Throw(Node_Stmt_Throw $node) {
return 'throw' . (null !== $node->expr ? ' ' . $this->p($node->expr) : '');
}
// Other
public function pStmt_Echo(Node_Stmt_Echo $node) {
return 'echo ' . $this->pCommaSeparated($node->exprs);
}
public function pStmt_Noop(Node_Stmt_Noop $node) {
return '';
}
public function pStmt_Static(Node_Stmt_Static $node) {
return 'static ' . $this->pCommaSeparated($node->vars);
}
@ -451,7 +518,7 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. (null !== $node->default ? ' = ' . $this->p($node->default) : '');
}
// helpers
// Helpers
public function pObjectProperty($node) {
if ($node instanceof Node_Expr) {

View File

@ -78,11 +78,14 @@ abstract class PrettyPrinterAbstract
foreach ($nodes as $node) {
$return .= $this->p($node);
if ($node instanceof Node_Stmt_Func
|| $node instanceof Node_Stmt_Class
|| $node instanceof Node_Stmt_ClassMethod
|| $node instanceof Node_Stmt_Foreach
|| $node instanceof Node_Stmt_If
if ( $node instanceof Node_Stmt_Func
|| $node instanceof Node_Stmt_Class
|| $node instanceof Node_Stmt_ClassMethod
|| $node instanceof Node_Stmt_For
|| $node instanceof Node_Stmt_Foreach
|| $node instanceof Node_Stmt_If
|| $node instanceof Node_Stmt_Switch
|| $node instanceof Node_Stmt_While
) {
$return .= "\n";
} else {

View File

@ -23,6 +23,7 @@ $stmts = $parser->yyparse(new Lexer(
echo $msg;
}
);
if (false !== $stmts) {
foreach ($stmts as $stmt) {
echo htmlspecialchars($stmt), "\n";
@ -34,38 +35,9 @@ echo "\n\n";
$prettyPrinter = new PrettyPrinter_Zend;
echo htmlspecialchars($prettyPrinter->pStmts(
$parser->yyparse(
new Lexer(file_get_contents('./lib/Parser.php')),
new Lexer(file_get_contents('./grammar/analyzer.php')),
function ($msg) {
echo $msg;
}
)
));
echo "\n\n";
// Correctness Demo
$GST = microtime(true);
foreach (new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('.'),
RecursiveIteratorIterator::LEAVES_ONLY)
as $file) {
if ('.php' !== substr($file, -4)) {
continue;
}
set_time_limit(5);
$startTime = microtime(true);
$stmts = $parser->yyparse(
new Lexer(file_get_contents($file)),
function($msg) {
echo $msg, "\n";
}
);
$endTime = microtime(true);
echo str_pad($file . ': ', 120, ' '), (false !== $stmts ? 'successful' : 'ERROR'), ' (', $endTime - $startTime, ')', "\n";
flush();
}
echo microtime(true) - $GST;
));

View File

@ -0,0 +1,120 @@
<?php
$DIR = '..';
function __autoload($class) {
is_file($file = '../lib/' . strtr($class, '_', '/') . '.php') && require_once $file;
}
$parser = new Parser();
$parser->yydebug = false;
$prettyPrinter = new PrettyPrinter_Zend;
echo '<!DOCTYPE html>
<style>
body {
font-family: "Trebuchet MS", sans-serif;
}
.pass {
color: white;
background-color: green;
}
.fail {
color: white;
background-color: red;
}
.failReason {
background-color: rgba(255, 0, 0, 0.3);
}
</style>
<table>
<tr>
<td>File</td>
<td>Parse</td>
<td>Time</td>
<td>PrettyPrint</td>
<td>Same</td>
</tr>';
$GST = microtime(true);
foreach (new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($DIR),
RecursiveIteratorIterator::LEAVES_ONLY)
as $file) {
if ('.php' !== substr($file, -4)) {
continue;
}
echo '
<tr>
<td>' . $file . '</td>';
set_time_limit(5);
$errMsg = '';
$startTime = microtime(true);
$stmts = $parser->yyparse(
new Lexer(file_get_contents($file)),
function($msg) use (&$errMsg) {
$errMsg = $msg;
}
);
$time = microtime(true) - $startTime;
if (false !== $stmts) {
$code = '<?php' . "\n" . $prettyPrinter->pStmts($stmts);
$ppStmts = $parser->yyparse(
new Lexer($code),
function($msg) use (&$errMsg) {
$errMsg = $msg;
}
);
if (false !== $ppStmts) {
if ($stmts == $ppStmts) {
echo '
<td class="pass">PASS</td>
<td>' . $time . 's</td>
<td class="pass">PASS</td>
<td class="pass">PASS</td>
</tr>';
} else {
echo '
<td class="pass">PASS</td>
<td>' . $time . 's</td>
<td class="pass">PASS</td>
<td class="fail">FAIL</td>
</tr>';
}
} else {
echo '
<td class="pass">PASS</td>
<td>' . $time . 's</td>
<td class="fail">FAIL</td>
<td></td>
</tr>';
}
} else {
echo '
<td class="fail">FAIL</td>
<td>' . $time . 's</td>
<td></td>
<td></td>
</tr>
<tr class="failReason"><td colspan="5">' . $errMsg . '</td></tr>';
}
flush();
}
echo '
</table>';
echo 'Total time: ', microtime(true) - $GST;