Build node attributes inside semantic action methods

Minor performance improvement for parsing, also allows to access
attributes with higher granulity in the parser, though this is not
currently done.

* #n can now be used to access the stack position of a token. $n
  is the same as $this->semStack[#n]. (Post-translate $n will
  actually be the stack position.)
* $attributeStack is now $this->startAttributeStack and
  $endAttributes is now $this->endAttributes.
* Attributes for a node are now computed inside the individual
  reduction methods, instead of being passed as a parameter.
  Accessible through the attributes() macro.
This commit is contained in:
Nikita Popov 2015-04-22 15:57:29 +02:00
parent e30c3ac01b
commit 6996db1e3a
5 changed files with 732 additions and 719 deletions

View File

@ -2,8 +2,8 @@
$meta #
#semval($) $this->semValue
#semval($,%t) $this->semValue
#semval(%n) $this->semStack[$this->stackPos-(%l-%n)]
#semval(%n,%t) $this->semStack[$this->stackPos-(%l-%n)]
#semval(%n) $this->stackPos-(%l-%n)
#semval(%n,%t) $this->stackPos-(%l-%n)
#include;
namespace PhpParser;
@ -87,7 +87,7 @@ class Parser extends ParserAbstract
#endif
#reduce
protected function reduceRule%n($attributes) {
protected function reduceRule%n() {
%b
}
#noact

View File

@ -43,6 +43,7 @@ $grammarCode = resolveConstants($grammarCode);
$grammarCode = resolveNodes($grammarCode);
$grammarCode = resolveMacros($grammarCode);
$grammarCode = resolveArrays($grammarCode);
$grammarCode = resolveStackAccess($grammarCode);
file_put_contents($tmpGrammarFile, $grammarCode);
@ -83,7 +84,7 @@ function resolveNodes($code) {
$paramCode .= $param . ', ';
}
return 'new Node\\' . $matches['name'] . '(' . $paramCode . '$attributes)';
return 'new Node\\' . $matches['name'] . '(' . $paramCode . 'attributes())';
},
$code
);
@ -102,6 +103,11 @@ function resolveMacros($code) {
$matches['args']
);
if ('attributes' == $name) {
assertArgs(0, $args, $name);
return '$this->startAttributeStack[#0] + $this->endAttributes';
}
if ('init' == $name) {
return '$$ = array(' . implode(', ', $args) . ')';
}
@ -185,6 +191,12 @@ function resolveArrays($code) {
);
}
function resolveStackAccess($code) {
$code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code);
$code = preg_replace('/#(\d+)/', '$$1', $code);
return $code;
}
function moveFileWithDirCheck($fromPath, $toPath) {
$dir = dirname($toPath);
if (!is_dir($dir)) {

View File

@ -176,7 +176,7 @@ inner_statement:
| function_declaration_statement { $$ = $1; }
| class_declaration_statement { $$ = $1; }
| T_HALT_COMPILER
{ throw new Error('__HALT_COMPILER() can only be used from the outermost scope', $attributes); }
{ throw new Error('__HALT_COMPILER() can only be used from the outermost scope', attributes()); }
;
statement:

File diff suppressed because it is too large Load Diff

View File

@ -78,10 +78,14 @@ abstract class ParserAbstract
protected $lexer;
/** @var mixed Temporary value containing the result of last semantic action (reduction) */
protected $semValue;
/** @var array Semantic value stack (contains values of tokens and semantic action results) */
protected $semStack;
/** @var int Position in stacks (state stack, semantic value stack, attribute stack) */
protected $stackPos;
/** @var array Semantic value stack (contains values of tokens and semantic action results) */
protected $semStack;
/** @var array[] Start attribute stack */
protected $startAttributeStack;
/** @var array End attributes of last *shifted* token */
protected $endAttributes;
/** @var bool Whether to throw on first error */
protected $throwOnError;
@ -131,11 +135,11 @@ abstract class ParserAbstract
// From the first token only the startAttributes are taken and from the last only
// the endAttributes. Both are merged using the array union operator (+).
$startAttributes = array('startLine' => 1);
$endAttributes = array();
$this->endAttributes = array();
// In order to figure out the attributes for the starting token, we have to keep
// them in a stack
$attributeStack = array($startAttributes);
$this->startAttributeStack = array($startAttributes);
// Start off in the initial state and keep a stack of previous states
$state = 0;
@ -174,7 +178,7 @@ abstract class ParserAbstract
));
}
$attributeStack[$this->stackPos] = $startAttributes;
$this->startAttributeStack[$this->stackPos] = $startAttributes;
//$this->traceRead($symbol);
}
@ -197,10 +201,10 @@ abstract class ParserAbstract
//$this->traceShift($symbol);
++$this->stackPos;
$stateStack[$this->stackPos] = $state = $action;
$stateStack[$this->stackPos] = $state = $action;
$this->semStack[$this->stackPos] = $tokenValue;
$attributeStack[$this->stackPos] = $startAttributes;
$endAttributes = $nextEndAttributes;
$this->startAttributeStack[$this->stackPos] = $startAttributes;
$this->endAttributes = $nextEndAttributes;
$symbol = self::SYMBOL_NONE;
if ($errorState) {
@ -231,10 +235,7 @@ abstract class ParserAbstract
//$this->traceReduce($rule);
try {
$this->{'reduceRule' . $rule}(
$attributeStack[$this->stackPos - $this->ruleToLength[$rule]]
+ $endAttributes
);
$this->{'reduceRule' . $rule}();
} catch (Error $e) {
if (-1 === $e->getStartLine() && isset($startAttributes['startLine'])) {
$e->setStartLine($startAttributes['startLine']);
@ -256,7 +257,7 @@ abstract class ParserAbstract
++$this->stackPos;
$stateStack[$this->stackPos] = $state;
$this->semStack[$this->stackPos] = $this->semValue;
$attributeStack[$this->stackPos] = $startAttributes;
$this->startAttributeStack[$this->stackPos] = $startAttributes;
} else {
/* error */
switch ($errorState) {