diff --git a/grammar/php5.y b/grammar/php5.y index fcd1686..0c58887 100644 --- a/grammar/php5.y +++ b/grammar/php5.y @@ -72,13 +72,13 @@ use_type: /* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */ group_use_declaration: T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}' - { $$ = Stmt\GroupUse[Name[$3], $6, $2]; } + { $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, $2]; } | T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}' - { $$ = Stmt\GroupUse[Name[$4], $7, $2]; } + { $$ = Stmt\GroupUse[new Name($4, stackAttributes(#4)), $7, $2]; } | T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}' - { $$ = Stmt\GroupUse[Name[$2], $5, Stmt\Use_::TYPE_UNKNOWN]; } + { $$ = Stmt\GroupUse[new Name($2, stackAttributes(#2)), $5, Stmt\Use_::TYPE_UNKNOWN]; } | T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}' - { $$ = Stmt\GroupUse[Name[$3], $6, Stmt\Use_::TYPE_UNKNOWN]; } + { $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, Stmt\Use_::TYPE_UNKNOWN]; } ; unprefixed_use_declarations: diff --git a/grammar/php7.y b/grammar/php7.y index 47fe2e7..20b76e9 100644 --- a/grammar/php7.y +++ b/grammar/php7.y @@ -72,13 +72,13 @@ use_type: /* Using namespace_name_parts here to avoid s/r conflict on T_NS_SEPARATOR */ group_use_declaration: T_USE use_type namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}' - { $$ = Stmt\GroupUse[Name[$3], $6, $2]; } + { $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, $2]; } | T_USE use_type T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' unprefixed_use_declarations '}' - { $$ = Stmt\GroupUse[Name[$4], $7, $2]; } + { $$ = Stmt\GroupUse[new Name($4, stackAttributes(#4)), $7, $2]; } | T_USE namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}' - { $$ = Stmt\GroupUse[Name[$2], $5, Stmt\Use_::TYPE_UNKNOWN]; } + { $$ = Stmt\GroupUse[new Name($2, stackAttributes(#2)), $5, Stmt\Use_::TYPE_UNKNOWN]; } | T_USE T_NS_SEPARATOR namespace_name_parts T_NS_SEPARATOR '{' inline_use_declarations '}' - { $$ = Stmt\GroupUse[Name[$3], $6, Stmt\Use_::TYPE_UNKNOWN]; } + { $$ = Stmt\GroupUse[new Name($3, stackAttributes(#3)), $6, Stmt\Use_::TYPE_UNKNOWN]; } ; unprefixed_use_declarations: diff --git a/grammar/rebuildParsers.php b/grammar/rebuildParsers.php index 5b538ce..0959981 100644 --- a/grammar/rebuildParsers.php +++ b/grammar/rebuildParsers.php @@ -124,6 +124,12 @@ function resolveMacros($code) { return '$this->startAttributeStack[#1] + $this->endAttributes'; } + if ('stackAttributes' == $name) { + assertArgs(1, $args, $name); + return '$this->startAttributeStack[' . $args[0] . ']' + . ' + $this->endAttributeStack[' . $args[0] . ']'; + } + if ('init' == $name) { return '$$ = array(' . implode(', ', $args) . ')'; } diff --git a/lib/PhpParser/Parser/Php5.php b/lib/PhpParser/Parser/Php5.php index aea69ad..c2ed4c0 100644 --- a/lib/PhpParser/Parser/Php5.php +++ b/lib/PhpParser/Parser/Php5.php @@ -1303,19 +1303,19 @@ class Php5 extends \PhpParser\ParserAbstract } protected function reduceRule98() { - $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-6)], $this->semStack[$this->stackPos-(7-2)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes); + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-3)] + $this->endAttributeStack[$this->stackPos-(7-3)]), $this->semStack[$this->stackPos-(7-6)], $this->semStack[$this->stackPos-(7-2)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes); } protected function reduceRule99() { - $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(8-4)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(8-7)], $this->semStack[$this->stackPos-(8-2)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes); + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(8-4)], $this->startAttributeStack[$this->stackPos-(8-4)] + $this->endAttributeStack[$this->stackPos-(8-4)]), $this->semStack[$this->stackPos-(8-7)], $this->semStack[$this->stackPos-(8-2)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes); } protected function reduceRule100() { - $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(6-2)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes); + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(6-2)], $this->startAttributeStack[$this->stackPos-(6-2)] + $this->endAttributeStack[$this->stackPos-(6-2)]), $this->semStack[$this->stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes); } protected function reduceRule101() { - $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-6)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes); + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-3)] + $this->endAttributeStack[$this->stackPos-(7-3)]), $this->semStack[$this->stackPos-(7-6)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes); } protected function reduceRule102() { diff --git a/lib/PhpParser/Parser/Php7.php b/lib/PhpParser/Parser/Php7.php index c9d40c4..9e71dff 100644 --- a/lib/PhpParser/Parser/Php7.php +++ b/lib/PhpParser/Parser/Php7.php @@ -1188,19 +1188,19 @@ class Php7 extends \PhpParser\ParserAbstract } protected function reduceRule98() { - $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-6)], $this->semStack[$this->stackPos-(7-2)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes); + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-3)] + $this->endAttributeStack[$this->stackPos-(7-3)]), $this->semStack[$this->stackPos-(7-6)], $this->semStack[$this->stackPos-(7-2)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes); } protected function reduceRule99() { - $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(8-4)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(8-7)], $this->semStack[$this->stackPos-(8-2)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes); + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(8-4)], $this->startAttributeStack[$this->stackPos-(8-4)] + $this->endAttributeStack[$this->stackPos-(8-4)]), $this->semStack[$this->stackPos-(8-7)], $this->semStack[$this->stackPos-(8-2)], $this->startAttributeStack[$this->stackPos-(8-1)] + $this->endAttributes); } protected function reduceRule100() { - $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(6-2)], $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes); + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(6-2)], $this->startAttributeStack[$this->stackPos-(6-2)] + $this->endAttributeStack[$this->stackPos-(6-2)]), $this->semStack[$this->stackPos-(6-5)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(6-1)] + $this->endAttributes); } protected function reduceRule101() { - $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(7-6)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes); + $this->semValue = new Stmt\GroupUse(new Name($this->semStack[$this->stackPos-(7-3)], $this->startAttributeStack[$this->stackPos-(7-3)] + $this->endAttributeStack[$this->stackPos-(7-3)]), $this->semStack[$this->stackPos-(7-6)], Stmt\Use_::TYPE_UNKNOWN, $this->startAttributeStack[$this->stackPos-(7-1)] + $this->endAttributes); } protected function reduceRule102() { diff --git a/lib/PhpParser/ParserAbstract.php b/lib/PhpParser/ParserAbstract.php index 4ba2890..928f731 100644 --- a/lib/PhpParser/ParserAbstract.php +++ b/lib/PhpParser/ParserAbstract.php @@ -86,6 +86,8 @@ abstract class ParserAbstract implements Parser protected $semStack; /** @var array[] Start attribute stack */ protected $startAttributeStack; + /** @var array[] End attribute stack */ + protected $endAttributeStack; /** @var array End attributes of last *shifted* token */ protected $endAttributes; /** @var array Start attributes of last *read* token */ @@ -149,9 +151,9 @@ abstract class ParserAbstract implements Parser $endAttributes = '*POISON'; $this->endAttributes = $endAttributes; - // In order to figure out the attributes for the starting token, we have to keep - // them in a stack + // Keep stack of start and end attributes $this->startAttributeStack = array(); + $this->endAttributeStack = array($endAttributes); // Start off in the initial state and keep a stack of previous states $state = 0; @@ -193,6 +195,7 @@ abstract class ParserAbstract implements Parser // This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get // the attributes of the next token, even though they don't contain it themselves. $this->startAttributeStack[$this->stackPos+1] = $startAttributes; + $this->endAttributeStack[$this->stackPos+1] = $endAttributes; $this->lookaheadStartAttributes = $startAttributes; //$this->traceRead($symbol); @@ -219,6 +222,7 @@ abstract class ParserAbstract implements Parser $stateStack[$this->stackPos] = $state = $action; $this->semStack[$this->stackPos] = $tokenValue; $this->startAttributeStack[$this->stackPos] = $startAttributes; + $this->endAttributeStack[$this->stackPos] = $endAttributes; $this->endAttributes = $endAttributes; $symbol = self::SYMBOL_NONE; @@ -266,6 +270,7 @@ abstract class ParserAbstract implements Parser } /* Goto - shift nonterminal */ + $lastEndAttributes = $this->endAttributeStack[$this->stackPos]; $this->stackPos -= $this->ruleToLength[$rule]; $nonTerminal = $this->ruleToNonTerminal[$rule]; $idx = $this->gotoBase[$nonTerminal] + $stateStack[$this->stackPos]; @@ -278,6 +283,7 @@ abstract class ParserAbstract implements Parser ++$this->stackPos; $stateStack[$this->stackPos] = $state; $this->semStack[$this->stackPos] = $this->semValue; + $this->endAttributeStack[$this->stackPos] = $lastEndAttributes; } else { /* error */ switch ($this->errorState) { diff --git a/test/PhpParser/ParserTest.php b/test/PhpParser/ParserTest.php index 1295ae0..5f32c31 100644 --- a/test/PhpParser/ParserTest.php +++ b/test/PhpParser/ParserTest.php @@ -171,6 +171,17 @@ EOC; array("?>\nFoo", ['hasLeadingNewline' => true]), ); } + + public function testGroupUsePrefixFileOffsets() { + $parser = $this->getParser(new Lexer( + ['usedAttributes' => ['startFilePos', 'endFilePos']] + )); + $stmts = $parser->parse('assertSame(10, $groupUse->prefix->getAttribute('startFilePos')); + $this->assertSame(16, $groupUse->prefix->getAttribute('endFilePos')); + } } class InvalidTokenLexer extends Lexer {