/** * @inheritdoc */ public function parse(array $tokens) { $ast = new AbstractSyntaxTree(); /** @var Token $token */ foreach ($tokens as $token) { switch ($token->getType()) { case TokenTypes::T_EXPRESSION: $ast->getCurrentNode()->addChild(new ExpressionNode($token->getValue())); break; case TokenTypes::T_DIRECTIVE_OPEN: if (!$ast->getCurrentNode()->isRoot()) { $this->throwImproperlyNestedNodeException($token); } $childNode = new DirectiveNode(); $ast->getCurrentNode()->addChild($childNode); $ast->setCurrentNode($childNode); break; case TokenTypes::T_DIRECTIVE_CLOSE: if (!$ast->getCurrentNode()->isDirective()) { $this->throwUnopenedDelimiterException($token); } $ast->setCurrentNode($ast->getCurrentNode()->getParent()); break; case TokenTypes::T_DIRECTIVE_NAME: $ast->getCurrentNode()->addChild(new DirectiveNameNode($token->getValue())); break; case TokenTypes::T_SANITIZED_TAG_OPEN: if (!$ast->getCurrentNode()->isRoot()) { $this->throwImproperlyNestedNodeException($token); } $childNode = new SanitizedTagNode(); $ast->getCurrentNode()->addChild($childNode); $ast->setCurrentNode($childNode); break; case TokenTypes::T_SANITIZED_TAG_CLOSE: if (!$ast->getCurrentNode()->isSanitizedTag()) { $this->throwUnopenedDelimiterException($token); } $ast->setCurrentNode($ast->getCurrentNode()->getParent()); break; case TokenTypes::T_UNSANITIZED_TAG_OPEN: if (!$ast->getCurrentNode()->isRoot()) { $this->throwImproperlyNestedNodeException($token); } $childNode = new UnsanitizedTagNode(); $ast->getCurrentNode()->addChild($childNode); $ast->setCurrentNode($childNode); break; case TokenTypes::T_UNSANITIZED_TAG_CLOSE: if (!$ast->getCurrentNode()->isUnsanitizedTag()) { $this->throwUnopenedDelimiterException($token); } $ast->setCurrentNode($ast->getCurrentNode()->getParent()); break; case TokenTypes::T_COMMENT_OPEN: if (!$ast->getCurrentNode()->isRoot()) { $this->throwImproperlyNestedNodeException($token); } $childNode = new CommentNode(); $ast->getCurrentNode()->addChild($childNode); $ast->setCurrentNode($childNode); break; case TokenTypes::T_COMMENT_CLOSE: if (!$ast->getCurrentNode()->isComment()) { $this->throwUnopenedDelimiterException($token); } $ast->setCurrentNode($ast->getCurrentNode()->getParent()); break; case TokenTypes::T_PHP_TAG_OPEN: $ast->getCurrentNode()->addChild(new ExpressionNode($token->getValue())); break; case TokenTypes::T_PHP_TAG_CLOSE: $ast->getCurrentNode()->addChild(new ExpressionNode($token->getValue())); break; default: throw new RuntimeException(sprintf('Unknown token type "%s" with value "%s" near line %d', $token->getType(), $token->getValue(), $token->getLine())); } } if (!$ast->getCurrentNode()->isRoot()) { throw new RuntimeException(sprintf("Expected close delimiter, found %s", $ast->getCurrentNode()->getValue())); } return $ast; }
/** * Transpiles all nodes in an abstract syntax tree * * @param AbstractSyntaxTree $ast The abstract syntax tree to transpile * @return string The view with transpiled nodes * @throws RuntimeException Thrown if the nodes could not be transpiled */ protected function transpileNodes(AbstractSyntaxTree $ast) { $transpiledView = ""; $rootNode = $ast->getRootNode(); $previousNodeWasExpression = false; foreach ($rootNode->getChildren() as $childNode) { switch (get_class($childNode)) { case DirectiveNode::class: $transpiledView .= $this->transpileDirectiveNode($childNode); $previousNodeWasExpression = false; break; case SanitizedTagNode::class: $transpiledView .= $this->transpileSanitizedTagNode($childNode); $previousNodeWasExpression = false; break; case UnsanitizedTagNode::class: $transpiledView .= $this->transpileUnsanitizedTagNode($childNode); $previousNodeWasExpression = false; break; case CommentNode::class: $transpiledView .= $this->transpileCommentNode($childNode); $previousNodeWasExpression = false; break; case ExpressionNode::class: // To keep expressions from running against each other, we pad all expressions but the first if ($previousNodeWasExpression) { $transpiledView .= ' '; } $transpiledView .= $this->transpileExpressionNode($childNode); $previousNodeWasExpression = true; break; default: throw new RuntimeException(sprintf("Unknown node class %s", get_class($childNode))); } } return $transpiledView; }