/** * Trim away the minimum indentation level for all elements in $elements. * * @param ezcTemplateBlockTstNode $parentBlock The block which owns elements in $elements. * @param array(ezcTemplateTstNode) $elements List of elements to trim. */ public function trimBlockLevelIndentation(ezcTemplateTstNode $parentBlock, array $elements) { // First figure out the smallest amount of indentation that can be removed $indentation = $parentBlock->minimumWhitespaceColumn(); $nrOfElements = sizeof($elements); for ($el = 0; $el < $nrOfElements; $el++) { $element = $elements[$el]; if ($element instanceof ezcTemplateTextBlockTstNode) { $lines = $element->lines; $count = count($lines); for ($i = 0; $i < $count; ++$i) { // Skip the first line if it is placed after another element (column > 0 ). // We can only modify lines with leading point at column 0. if ($i == 0 && $element->firstLineColumn() > 0) { // It prevents some text nodes from removal. continue; } // Trim the line and leave EOL alone $lines[$i][0] = $this->trimIndentationLine($lines[$i][0], $indentation); if ($i == $count - 1) { if ($el < $nrOfElements - 1) { if ($elements[$el + 1] instanceof ezcTemplateBlockTstNode && !$elements[$el + 1] instanceof ezcTemplateOutputBlockTstNode) { $trimmed = trim($lines[$i][0], " \t"); if (strlen($trimmed) == 0) { $lines[$i][0] = ""; } } } else { if ($parentBlock instanceof ezcTemplateBlockTstNode && !$parentBlock instanceof ezcTemplateOutputBlockTstNode) { $last = sizeof($lines) - 1; $trimmed = trim($lines[$last][0], " \t"); if (strlen(trim($lines[$last][0], " \t")) == 0) { $lines[$last][0] = ""; } } } } } $element->setTextLines($lines); } elseif ($element instanceof ezcTemplateConditionBodyTstNode) { $this->trimBlockLevelIndentation($element, $element->children); } } }
/** * Constructs a new ezcTemplateExpressionTstNode * * @param ezcTemplateSourceCode $source * @param ezcTemplateCursor $start * @param ezcTemplateCursor $end */ public function __construct(ezcTemplateSourceCode $source, $start, $end) { parent::__construct($source, $start, $end); }
/** * Matches an open with an closing block. * * @param ezcTemplateTstNode $element * @throws ezcTemplateParserException for non matching open and close blocks. * @return void */ protected function closeOpenBlock($element) { // The previous element must be a block element, // if not throw an exception if (!$this->lastBlock instanceof ezcTemplateBlockTstNode) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, "Found closing block {" . $element->name . "} without a previous block element <" . get_class($this->lastBlock) . ">"); } // Check for closing blocks that do not belong to an opening block. if ($this->lastBlock->parentBlock === null && $element->isClosingBlock) { if ($element instanceof ezcTemplateCustomBlockTstNode) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->startCursor, "The custom block: {" . $element->name . "} should not have a closing block. Check the custom block definition. "); } else { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->startCursor, "Found closing block {/" . $element->name . "} without an opening block."); } } // The name of the previous element must match the closing block, // if not throw an exception if ($this->lastBlock->name != $element->name) { throw new ezcTemplateParserException($this->parser->source, $this->startCursor, $this->currentCursor, "Open and close block do not match: {" . $this->lastBlock->name . "} and {/" . $element->name . "}"); } // Sanity check if ($this->lastBlock->parentBlock === null) { throw new ezcTemplateInternalException("Parent block of last block <" . get_class($this->lastBlock) . "> is null, should not happen."); } // Call the closing element with the block element it closes, // this allows it to update the open block if required. $element->closeOpenBlock($this->lastBlock); // Tell the main parser to trim indentation for the block, // the whitespace trimming rules are defined within the main parser. $this->parser->trimBlockLevelIndentation($this->lastBlock); // Get rid of whitespace for the block line $this->parser->trimBlockLine($this->lastBlock); // Go up (closer to program) one level in the nested tree structure $this->lastBlock = $this->lastBlock->parentBlock; }
/** * Handles a newly parsed operand, the operand will be appended to the * current operator if there is one, if not it becomes the current item. * The element which should be the current item is returned by this function. * * @param ezcTemplateTstNode $currentOperator The current operator/operand element, can be null. * @param ezcTemplateTstNode $operand The parsed operator/operand which should be added as parameter. * @return ezcTemplateTstNode */ public function handleOperand($currentOperator, ezcTemplateTstNode $operand) { if ($currentOperator !== null) { $currentOperator->appendParameter($operand); return $currentOperator; } else { return $operand; } }
/** * Calls the accept method on the given tst node. The return value * replaces the $node. * * @param ezcTemplateTstNode $node * @return void */ protected function acceptAndUpdate(ezcTemplateTstNode &$node) { $ret = $node->accept($this); if ($ret !== null) { $node = $ret; } }
/** * Convenience function for outputting a node. * Instantiates the ezcTemplateTstTreeOutput class and calls accept() on * $node, the resulting text is returned. * * @param ezcTemplateAstNode $node * @return string */ public static function output(ezcTemplateTstNode $node) { $treeOutput = new ezcTemplateTstTreeOutput(); $node->accept($treeOutput); return $treeOutput->text . "\n"; }