From 6d1f77132c54b13872f0d212b9637ae2702b375d Mon Sep 17 00:00:00 2001 From: nikic Date: Mon, 21 Apr 2014 12:30:55 +0200 Subject: [PATCH] Move Stmt\Namespace_::postprocess() to parser --- grammar/zend_language_parser.phpy | 2 +- lib/PhpParser/Node/Stmt/Namespace_.php | 79 ----------------------- lib/PhpParser/Parser.php | 2 +- lib/PhpParser/ParserAbstract.php | 87 ++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 81 deletions(-) diff --git a/grammar/zend_language_parser.phpy b/grammar/zend_language_parser.phpy index f324e61..404512e 100644 --- a/grammar/zend_language_parser.phpy +++ b/grammar/zend_language_parser.phpy @@ -112,7 +112,7 @@ %% start: - top_statement_list { $$ = Stmt\Namespace_::postprocess($1); } + top_statement_list { $$ = $this->handleNamespaces($1); } ; top_statement_list: diff --git a/lib/PhpParser/Node/Stmt/Namespace_.php b/lib/PhpParser/Node/Stmt/Namespace_.php index a329e4d..71a9629 100644 --- a/lib/PhpParser/Node/Stmt/Namespace_.php +++ b/lib/PhpParser/Node/Stmt/Namespace_.php @@ -45,83 +45,4 @@ class Namespace_ extends Node\Stmt } } } - - public static function postprocess(array $stmts) { - // null = not in namespace, false = semicolon style, true = bracket style - $bracketed = null; - - // whether any statements that aren't allowed before a namespace declaration are encountered - // (the only valid statement currently is a declare) - $hasNotAllowedStmts = false; - - // offsets for semicolon style namespaces - // (required for transplanting the following statements into their ->stmts property) - $nsOffsets = array(); - - foreach ($stmts as $i => $stmt) { - if ($stmt instanceof self) { - // ->stmts is null if semicolon style is used - $currentBracketed = null !== $stmt->stmts; - - // if no namespace statement has been encountered yet - if (!isset($bracketed)) { - // set the namespacing style - $bracketed = $currentBracketed; - - // and ensure that it isn't preceded by a not allowed statement - if ($hasNotAllowedStmts) { - throw new Error('Namespace declaration statement has to be the very first statement in the script', $stmt->getLine()); - } - // otherwise ensure that the style of the current namespace matches the style of - // namespaceing used before in this document - } elseif ($bracketed !== $currentBracketed) { - throw new Error('Cannot mix bracketed namespace declarations with unbracketed namespace declarations', $stmt->getLine()); - } - - // for semicolon style namespaces remember the offset - if (!$bracketed) { - $nsOffsets[] = $i; - } - // declare() and __halt_compiler() are the only valid statements outside of namespace declarations - } elseif (!$stmt instanceof Declare_ - && !$stmt instanceof HaltCompiler - ) { - if (true === $bracketed) { - throw new Error('No code may exist outside of namespace {}', $stmt->getLine()); - } - - $hasNotAllowedStmts = true; - } - } - - // if bracketed namespaces were used or no namespaces were used at all just return the - // original statements - if (!isset($bracketed) || true === $bracketed) { - return $stmts; - // for semicolon style transplant statements - } else { - // take all statements preceding the first namespace - $newStmts = array_slice($stmts, 0, $nsOffsets[0]); - - // iterate over all following namespaces - for ($i = 0, $c = count($nsOffsets); $i < $c; ++$i) { - $newStmts[] = $nsStmt = $stmts[$nsOffsets[$i]]; - - // the last namespace takes all statements after it - if ($c === $i + 1) { - $nsStmt->stmts = array_slice($stmts, $nsOffsets[$i] + 1); - - // if the last statement is __halt_compiler() put it outside the namespace - if (end($nsStmt->stmts) instanceof HaltCompiler) { - $newStmts[] = array_pop($nsStmt->stmts); - } - // and all the others take all statements between the current and the following one - } else { - $nsStmt->stmts = array_slice($stmts, $nsOffsets[$i] + 1, $nsOffsets[$i + 1] - $nsOffsets[$i] - 1); - } - } - - return $newStmts; - } - } } diff --git a/lib/PhpParser/Parser.php b/lib/PhpParser/Parser.php index c195c84..416ca6e 100644 --- a/lib/PhpParser/Parser.php +++ b/lib/PhpParser/Parser.php @@ -1001,7 +1001,7 @@ class Parser extends ParserAbstract } protected function reduceRule1($attributes) { - $this->semValue = Node\Stmt\Namespace_::postprocess($this->semStack[$this->stackPos-(1-1)]); + $this->semValue = $this->handleNamespaces($this->semStack[$this->stackPos-(1-1)]); } protected function reduceRule2($attributes) { diff --git a/lib/PhpParser/ParserAbstract.php b/lib/PhpParser/ParserAbstract.php index 5f47d43..6e5e15c 100644 --- a/lib/PhpParser/ParserAbstract.php +++ b/lib/PhpParser/ParserAbstract.php @@ -273,6 +273,10 @@ abstract class ParserAbstract return $expected; } + /* + * Tracing functions used for debugging the parser. + */ + protected function traceNewState($state, $symbol) { echo '% State ' . $state . ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n"; @@ -293,4 +297,87 @@ abstract class ParserAbstract protected function traceReduce($n) { echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n"; } + + /* + * Helper functions invoked by semantic actions + */ + + protected function handleNamespaces(array $stmts) { + // null = not in namespace, false = semicolon style, true = bracket style + $bracketed = null; + + // whether any statements that aren't allowed before a namespace declaration are encountered + // (the only valid statement currently is a declare) + $hasNotAllowedStmts = false; + + // offsets for semicolon style namespaces + // (required for transplanting the following statements into their ->stmts property) + $nsOffsets = array(); + + foreach ($stmts as $i => $stmt) { + if ($stmt instanceof Node\Stmt\Namespace_) { + // ->stmts is null if semicolon style is used + $currentBracketed = null !== $stmt->stmts; + + // if no namespace statement has been encountered yet + if (!isset($bracketed)) { + // set the namespacing style + $bracketed = $currentBracketed; + + // and ensure that it isn't preceded by a not allowed statement + if ($hasNotAllowedStmts) { + throw new Error('Namespace declaration statement has to be the very first statement in the script', $stmt->getLine()); + } + // otherwise ensure that the style of the current namespace matches the style of + // namespaceing used before in this document + } elseif ($bracketed !== $currentBracketed) { + throw new Error('Cannot mix bracketed namespace declarations with unbracketed namespace declarations', $stmt->getLine()); + } + + // for semicolon style namespaces remember the offset + if (!$bracketed) { + $nsOffsets[] = $i; + } + // declare() and __halt_compiler() are the only valid statements outside of namespace declarations + } elseif (!$stmt instanceof Node\Stmt\Declare_ + && !$stmt instanceof Node\Stmt\HaltCompiler + ) { + if (true === $bracketed) { + throw new Error('No code may exist outside of namespace {}', $stmt->getLine()); + } + + $hasNotAllowedStmts = true; + } + } + + // if bracketed namespaces were used or no namespaces were used at all just return the + // original statements + if (!isset($bracketed) || true === $bracketed) { + return $stmts; + // for semicolon style transplant statements + } else { + // take all statements preceding the first namespace + $newStmts = array_slice($stmts, 0, $nsOffsets[0]); + + // iterate over all following namespaces + for ($i = 0, $c = count($nsOffsets); $i < $c; ++$i) { + $newStmts[] = $nsStmt = $stmts[$nsOffsets[$i]]; + + // the last namespace takes all statements after it + if ($c === $i + 1) { + $nsStmt->stmts = array_slice($stmts, $nsOffsets[$i] + 1); + + // if the last statement is __halt_compiler() put it outside the namespace + if (end($nsStmt->stmts) instanceof Node\Stmt\HaltCompiler) { + $newStmts[] = array_pop($nsStmt->stmts); + } + // and all the others take all statements between the current and the following one + } else { + $nsStmt->stmts = array_slice($stmts, $nsOffsets[$i] + 1, $nsOffsets[$i + 1] - $nsOffsets[$i] - 1); + } + } + + return $newStmts; + } + } }