1<?php 2 3declare(strict_types=1); 4 5namespace Antlr\Antlr4\Runtime\Tree; 6 7use Antlr\Antlr4\Runtime\ParserRuleContext; 8 9class ParseTreeWalker 10{ 11 public static function default() : self 12 { 13 static $instance; 14 15 return $instance ?? ($instance = new self()); 16 } 17 18 public function walk(ParseTreeListener $listener, ParseTree $tree) : void 19 { 20 if ($tree instanceof ErrorNode) { 21 $listener->visitErrorNode($tree); 22 23 return; 24 } 25 26 if ($tree instanceof TerminalNode) { 27 $listener->visitTerminal($tree); 28 29 return; 30 } 31 32 if (!$tree instanceof RuleNode) { 33 throw new \RuntimeException('Unexpected tree type.'); 34 } 35 36 $this->enterRule($listener, $tree); 37 38 $count = $tree->getChildCount(); 39 40 for ($i = 0; $i < $count; $i++) { 41 $child = $tree->getChild($i); 42 43 if ($child !== null) { 44 $this->walk($listener, $child); 45 } 46 } 47 48 $this->exitRule($listener, $tree); 49 } 50 51 /** 52 * The discovery of a rule node, involves sending two events: the generic 53 * {@see ParseTreeListener::enterEveryRule()} and a 54 * {@see RuleContext}-specific event. First we trigger the generic and then 55 * the rule specific. We to them in reverse order upon finishing the node. 56 */ 57 protected function enterRule(ParseTreeListener $listener, RuleNode $ruleNode) : void 58 { 59 /** @var ParserRuleContext $ctx */ 60 $ctx = $ruleNode->getRuleContext(); 61 62 $listener->enterEveryRule($ctx); 63 64 $ctx->enterRule($listener); 65 } 66 67 protected function exitRule(ParseTreeListener $listener, RuleNode $ruleNode) : void 68 { 69 /** @var ParserRuleContext $ctx */ 70 $ctx = $ruleNode->getRuleContext(); 71 72 $ctx->exitRule($listener); 73 74 $listener->exitEveryRule($ctx); 75 } 76} 77