| <?php |
| /** |
| * File containing the ezcTemplateParser class |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @package Template |
| * @version //autogen// |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| * @access private |
| */ |
| /** |
| * Parser for template files. |
| * |
| * @package Template |
| * @version //autogen// |
| * @access private |
| */ |
| class ezcTemplateParser |
| { |
| /** |
| * @var ezcTemplate |
| */ |
| public $template; |
| |
| /** |
| * @var ezcTemplateSourceCode |
| */ |
| public $source; |
| |
| /** |
| * Controls whether whitespace trimming is done on the parser tree or not. |
| * |
| * @var bool |
| */ |
| public $trimWhitespace; |
| |
| /** |
| * The object which is responsible for removing whitespace. |
| * |
| * @var ezcTemplateWhitespaceRemoval |
| */ |
| protected $whitespaceRemoval; |
| |
| /** |
| * Stores the symbol table. At the beginning of parsing (at ProgramSourceToTstParser) |
| * a new symbol table is created. The rest of the nodes can access the symbol |
| * table. |
| * |
| * @var ezcTemplateSymboLTable |
| */ |
| public $symbolTable; |
| |
| |
| /** |
| * Is set to true if the current template has cache blocks. |
| * |
| * @var bool |
| */ |
| public $hasCacheBlocks = false; |
| |
| /** |
| * Note: The source code in $code must be loaded/created before passing it to this parser. |
| * |
| * @param ezcTemplateSourceCode $source |
| * @param ezcTemplate $template |
| */ |
| function __construct( ezcTemplateSourceCode $source, ezcTemplate $template ) |
| { |
| $this->source = $source; |
| $this->template = $template; |
| $this->textElements = array(); |
| $this->trimWhitespace = $template->trimWhitespace; |
| |
| $this->symbolTable = ezcTemplateSymbolTable::getInstance(); |
| $this->symbolTable->reset(); |
| |
| $this->whitespaceRemoval = new ezcTemplateWhitespaceRemoval(); |
| } |
| |
| /** |
| * Creates a new cursor object with the text $sourceText and returns it. |
| * Note: This must be used instead of using new operator to instantiate |
| * cursors. This then allows the creation method to by testable. |
| * |
| * @param string $sourceText The source code. |
| * @return ezcTemplateCursor |
| */ |
| public function createCursor( $sourceText ) |
| { |
| return new ezcTemplateCursor( $sourceText ); |
| } |
| |
| /** |
| * Figures out the operator precedence for the new operator $newOperator |
| * by examining it with the current operator element. |
| * |
| * @param ezcTemplateTstNode $currentOperator Either the current operator |
| * element or general parameter |
| * element. |
| * @param ezcTemplateOperatorTstNode $newOperator The newly found operator. |
| * @return ezcTemplateOperatorTstNode |
| */ |
| public function handleOperatorPrecedence( /*ezcTemplateTstNode*/ $currentOperator, ezcTemplateOperatorTstNode $newOperator ) |
| { |
| if ( $currentOperator === null ) |
| { |
| throw new ezcTemplateInternalException( "No current operator/operand has been set" ); |
| } |
| |
| if ( !( $currentOperator instanceof ezcTemplateOperatorTstNode ) ) |
| { |
| // Note this operand should be prepended (not appended) in case |
| // the new operator already have some parameters set. |
| $newOperator->prependParameter( $currentOperator ); |
| return $newOperator; |
| } |
| |
| if ( $currentOperator->precedence > $newOperator->precedence ) |
| { |
| // Controls whether the $newOperator should be become the new root operator or not |
| // This happens if all operators have a higher precedence than the new operator. |
| $asRoot = false; |
| |
| // Find parent with less or equal precedence |
| while ( $currentOperator->precedence > $newOperator->precedence ) |
| { |
| if ( $currentOperator->parentOperator === null ) |
| { |
| $asRoot = true; |
| break; |
| } |
| $currentOperator = $currentOperator->parentOperator; |
| } |
| |
| if ( $asRoot ) |
| { |
| $newOperator->prependParameter( $currentOperator ); |
| $currentOperator->parentOperator = $newOperator; |
| |
| return $newOperator; |
| } |
| } |
| |
| |
| // Check if the operators can merge parameters, reasons for this can be: |
| // - The operators are of the same class |
| // - The : part of a conditional operator is found |
| if ( $currentOperator->canMergeParametersOf( $newOperator ) ) |
| { |
| $currentOperator->mergeParameters( $newOperator ); |
| return $currentOperator; |
| } |
| |
| if ( $currentOperator->precedence < $newOperator->precedence ) |
| { |
| $parameter = $currentOperator->getLastParameter(); |
| $currentOperator->setLastParameter( $newOperator ); |
| $newOperator->parentOperator = $currentOperator; |
| if ( $parameter !== null ) |
| $newOperator->prependParameter( $parameter ); |
| |
| return $newOperator; |
| } |
| |
| // Same precedence, order must be checked |
| if ( $currentOperator->precedence == $newOperator->precedence ) |
| { |
| $parentOperator = $currentOperator->parentOperator; |
| $parameter = $currentOperator->getLastParameter(); |
| $newOperator->prependParameter( $currentOperator ); |
| if ( $parentOperator !== null ) |
| $parentOperator->setLastParameter( $newOperator ); |
| $currentOperator->parentOperator = $newOperator; |
| $newOperator->parentOperator = $parentOperator; |
| |
| return $newOperator; |
| } |
| |
| throw new ezcTemplateInternalException( "Should not reach this place." ); |
| } |
| |
| /** |
| * 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( /*ezcTemplateTstNode*/ $currentOperator, ezcTemplateTstNode $operand ) |
| { |
| if ( $currentOperator !== null ) |
| { |
| $currentOperator->appendParameter( $operand ); |
| return $currentOperator; |
| } |
| else |
| { |
| return $operand; |
| } |
| } |
| |
| /** |
| * Creates the TST tree structure from the source code. |
| * |
| * @return void |
| */ |
| public function parseIntoNodeTree() |
| { |
| if ( !$this->source->hasCode() ) |
| { |
| throw new ezcTemplateException( ezcTemplateSourceToTstErrorMessages::MSG_NO_SOURCE_CODE ); |
| } |
| |
| $sourceText = $this->source->code; |
| $cursor = new ezcTemplateCursor( $sourceText ); |
| |
| $this->textElements = array(); |
| |
| $parser = new ezcTemplateProgramSourceToTstParser( $this, null, null ); |
| $parser->setAllCursors( $cursor ); |
| |
| if ( !$parser->parse() ) |
| { |
| $currentParser = $parser->getFailingParser(); |
| } |
| |
| // Trim starting/trailing whitespace |
| if ( $this->trimWhitespace ) |
| { |
| $this->whitespaceRemoval->trimProgram( $parser->program ); |
| } |
| |
| return $parser->program; |
| } |
| |
| /** |
| * Trims away indentation for one block level. |
| * |
| * The parser will call the ezcTemplateBlockTstNode::trimIndentation() method |
| * of the specified block object with the whitespace removal object passed |
| * as parameter. This allows the block element to choose how to apply the trimming |
| * process since it may have more than one child list. |
| * |
| * Note: This does nothing if self::$trimWhitespace is set to false. |
| * @param ezcTemplateBlockTstNode $block |
| * Block element which has its children trimmed of indentation whitespace. |
| * @return void |
| */ |
| public function trimBlockLevelIndentation( ezcTemplateBlockTstNode $block ) |
| { |
| if ( !$this->trimWhitespace ) |
| { |
| return; |
| } |
| |
| // Tell the block to trim its indentation by assign the object |
| // which has defined the rules for trimming whitespace |
| $block->trimIndentation( $this->whitespaceRemoval ); |
| } |
| |
| /** |
| * Trims away EOL whitespace for block lines for the specified block element. |
| * |
| * The parser will call the ezcTemplateBlockTstNode::trimLine() method |
| * of the specified block object with the whitespace removal object passed |
| * as parameter. This allows the block element to choose how to apply the trimming |
| * process since it may have more than one child list. |
| * |
| * Note: This does nothing if self::$trimWhitespace is set to false. |
| * @param ezcTemplateBlockTstNode $block |
| * Block element which has its child blocks trimmed of EOL whitespace. |
| * @return void |
| */ |
| public function trimBlockLine( ezcTemplateBlockTstNode $block ) |
| { |
| if ( !$this->trimWhitespace ) |
| { |
| return; |
| } |
| |
| // Tell the block to trim its block line for any whitespace and EOL characters |
| // by passign the object which has defined the rules for trimming whitespace |
| $block->trimLine( $this->whitespaceRemoval ); |
| } |
| } |
| |
| ?> |