PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/ezc/Template/src/parsers/source_to_tst/implementations/program.php

https://bitbucket.org/crevillo/enetcall
PHP | 298 lines | 162 code | 37 blank | 99 comment | 27 complexity | 939158314d2ce25ef4e2cbac020a9d14 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * File containing the ezcTemplateProgramSourceToTstParser class
  4. *
  5. * @package Template
  6. * @version //autogen//
  7. * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
  8. * @license http://ez.no/licenses/new_bsd New BSD License
  9. * @access private
  10. */
  11. /**
  12. * Element parser for the program part of the template code.
  13. *
  14. * @package Template
  15. * @version //autogen//
  16. * @access private
  17. */
  18. class ezcTemplateProgramSourceToTstParser extends ezcTemplateSourceToTstParser
  19. {
  20. /**
  21. * The program element of the parse operation if the parsing was successful.
  22. *
  23. * @var ezcTemplateProgramTstNode
  24. */
  25. public $program;
  26. /**
  27. * The last block which was processed by the parser. This is used to
  28. * figure out the correct nesting of block elements.
  29. *
  30. * @var ezcTemplateTstNode
  31. */
  32. private $lastBlock;
  33. /**
  34. * Passes control to parent.
  35. *
  36. * @param ezcTemplateParser $parser
  37. * @param ezcTemplateSourceToTstParser $parentParser
  38. * @param ezcTemplateCursor $startCursor
  39. */
  40. function __construct( ezcTemplateParser $parser, /*ezcTemplateSourceToTstParser*/ $parentParser, /*ezcTemplateCursor*/ $startCursor )
  41. {
  42. parent::__construct( $parser, $parentParser, $startCursor );
  43. $this->program = null;
  44. $this->lastBlock = null;
  45. }
  46. /**
  47. * Parses the code by looking for start of expression blocks and then
  48. * passing control to the block parser (ezcTemplateBlockSourceToTstParser). The
  49. * text which is not covered by the block parser will be added as
  50. * text elements.
  51. *
  52. * @param ezcTemplateCursor $cursor
  53. * @return bool
  54. */
  55. protected function parseCurrent( ezcTemplateCursor $cursor )
  56. {
  57. $this->program = new ezcTemplateProgramTstNode( $this->parser->source, $this->startCursor, $cursor );
  58. $this->lastBlock = $this->program;
  59. while ( !$cursor->atEnd() )
  60. {
  61. // Find the first block
  62. $bracePosition = $cursor->findPosition( "{", true );
  63. if ( $bracePosition === false )
  64. {
  65. $cursor->gotoEnd();
  66. // This will cause handleSuccessfulResult() to be called
  67. return true;
  68. }
  69. // Reached a block {...}
  70. $cursor->gotoPosition( $bracePosition );
  71. $blockCursor = clone $cursor;
  72. $cursor->advance( 1 );
  73. if ( $this->lastCursor->length( $blockCursor ) > 0 )
  74. {
  75. $textElement = new ezcTemplateTextBlockTstNode( $this->parser->source, clone $this->lastCursor, clone $blockCursor );
  76. $this->handleElements( array( $textElement ) );
  77. unset( $textElement );
  78. }
  79. $this->startCursor->copy( $blockCursor );
  80. $this->lastCursor->copy( $cursor );
  81. if ( !$this->parseRequiredType( 'Block', $this->startCursor, false ) )
  82. {
  83. return false;
  84. }
  85. $this->startCursor->copy( $cursor );
  86. $elements = $this->lastParser->elements;
  87. // Sanity checking to make sure element list does not contain duplicates,
  88. // this avoids having infinite recursions
  89. $count = count( $elements );
  90. if ( $count > 0 )
  91. {
  92. $offset = 0;
  93. while ( $offset < $count )
  94. {
  95. $element = $elements[$offset];
  96. for ( $i = $offset + 1; $i < $count; ++$i )
  97. {
  98. if ( $element === $elements[$i] )
  99. throw new ezcTemplateInternalException( "Received element list with duplicate objects from parser " . get_class( $this->lastParser ) );
  100. }
  101. ++$offset;
  102. }
  103. }
  104. $this->handleElements( $elements );
  105. }
  106. // This will cause handleSuccessfulResult() to be called
  107. return true;
  108. }
  109. /**
  110. * Performs checking on the parse result.
  111. *
  112. * The method will check if there are more text after the current cursor
  113. * location and if so appends a new ezcTextElement object containing the
  114. * text.
  115. *
  116. * It also checks if the $lastBlock contains the current program parser, if it
  117. * does not it means the nesting in the current source code is incorrect.
  118. *
  119. * @param ezcTemplateCursor $lastCursor
  120. * @param ezcTemplateCursor $cursor
  121. *
  122. * @throws ezcTemplateParserException if blocks are incorrectly nested.
  123. *
  124. * @return void
  125. */
  126. protected function handleSuccessfulResult( ezcTemplateCursor $lastCursor, ezcTemplateCursor $cursor )
  127. {
  128. if ( $lastCursor->length( $cursor ) > 0 )
  129. {
  130. $textElement = new ezcTemplateTextBlockTstNode( $this->parser->source, clone $lastCursor, clone $cursor );
  131. $this->handleElements( array( $textElement ) );
  132. }
  133. if ( $this->lastBlock === null )
  134. {
  135. throw new ezcTemplateInternalException( "lastBlock is null, should have been a parser element object." );
  136. }
  137. if ( !$this->lastBlock instanceof ezcTemplateProgramTstNode )
  138. {
  139. $parents = array();
  140. // Calculate level of the last block, this used to indent the last block
  141. $level = 0;
  142. $block = $this->lastBlock;
  143. while ( $block->parentBlock !== null &&
  144. !( $block->parentBlock instanceof ezcTemplateProgramTstNode ) )
  145. {
  146. if ( $block === $block->parentBlock )
  147. {
  148. throw new ezcTemplateInternalException( "Infinite recursion found in parser element " . get_class( $block ) );
  149. }
  150. ++$level;
  151. $block = $block->parentBlock;
  152. }
  153. $block = $this->lastBlock;
  154. // Go trough all parents until the root is reached
  155. while ( $block->parentBlock !== null &&
  156. !( $block->parentBlock instanceof ezcTemplateProgramTstNode ) )
  157. {
  158. if ( $block === $block->parentBlock )
  159. {
  160. throw new ezcTemplateInternalException( "Infinite recursion found in parser element " . get_class( $block ) );
  161. }
  162. $block = $block->parentBlock;
  163. --$level;
  164. $parents[] = str_repeat( " ", $level ) . "{" . $block->name . "} @ {$block->startCursor->line}:{$block->startCursor->column}:";
  165. }
  166. $parents = array_reverse( $parents );
  167. $treeText = "The current nesting structure:\n" . join( "\n", $parents );
  168. throw new ezcTemplateParserException( $this->parser->source, $this->startCursor, $this->currentCursor,
  169. "Incorrect nesting in code, close block {/" . $this->lastBlock->name . "} expected." );
  170. }
  171. // Get rid of whitespace for the block line of the program element
  172. $this->parser->trimBlockLine( $this->program );
  173. }
  174. /**
  175. * Handles elements
  176. *
  177. * @param array(ezcTemplateTstNode) $elements
  178. * @return void
  179. */
  180. public function handleElements( $elements )
  181. {
  182. foreach ( $elements as $element )
  183. {
  184. if ( $element instanceof ezcTemplateBlockTstNode && $element->isClosingBlock )
  185. {
  186. // Check for closing of current block
  187. // echo ("Closing block: ". get_class( $element ) ."\n" );
  188. $this->closeOpenBlock( $element );
  189. $this->parser->symbolTable->decreaseScope();
  190. }
  191. else
  192. {
  193. // This method throws an exception if the node cannot be attached.
  194. $element->canAttachToParent( $this->lastBlock );
  195. $this->lastBlock->handleElement( $element );
  196. if ( $element instanceof ezcTemplateBlockTstNode && $element->isNestingBlock)
  197. {
  198. // No special handling required so we check if the element
  199. // is a nesting block and should start a new nesting level
  200. $element->parentBlock = $this->lastBlock;
  201. $this->lastBlock = $element;
  202. $this->parser->symbolTable->increaseScope();
  203. }
  204. }
  205. }
  206. }
  207. /**
  208. * Matches an open with an closing block.
  209. *
  210. * @param ezcTemplateTstNode $element
  211. * @throws ezcTemplateParserException for non matching open and close blocks.
  212. * @return void
  213. */
  214. protected function closeOpenBlock( $element )
  215. {
  216. // The previous element must be a block element,
  217. // if not throw an exception
  218. if ( !$this->lastBlock instanceof ezcTemplateBlockTstNode )
  219. {
  220. throw new ezcTemplateParserException( $this->parser->source, $this->startCursor, $this->currentCursor,
  221. "Found closing block {" . $element->name . "} without a previous block element <" . get_class( $this->lastBlock ) . ">" );
  222. }
  223. // Check for closing blocks that do not belong to an opening block.
  224. if ( $this->lastBlock->parentBlock === null && $element->isClosingBlock )
  225. {
  226. if ( $element instanceof ezcTemplateCustomBlockTstNode )
  227. {
  228. throw new ezcTemplateParserException( $this->parser->source, $this->startCursor, $this->startCursor,
  229. "The custom block: {".$element->name."} should not have a closing block. Check the custom block definition. " );
  230. }
  231. else
  232. {
  233. throw new ezcTemplateParserException( $this->parser->source, $this->startCursor, $this->startCursor,
  234. "Found closing block {/". $element->name."} without an opening block." );
  235. }
  236. }
  237. // The name of the previous element must match the closing block,
  238. // if not throw an exception
  239. if ( $this->lastBlock->name != $element->name )
  240. {
  241. throw new ezcTemplateParserException( $this->parser->source, $this->startCursor, $this->currentCursor,
  242. "Open and close block do not match: {". $this->lastBlock->name ."} and {/".$element->name. "}" );
  243. }
  244. // Sanity check
  245. if ( $this->lastBlock->parentBlock === null )
  246. {
  247. throw new ezcTemplateInternalException( "Parent block of last block <" . get_class( $this->lastBlock ) . "> is null, should not happen." );
  248. }
  249. // Call the closing element with the block element it closes,
  250. // this allows it to update the open block if required.
  251. $element->closeOpenBlock( $this->lastBlock );
  252. // Tell the main parser to trim indentation for the block,
  253. // the whitespace trimming rules are defined within the main parser.
  254. $this->parser->trimBlockLevelIndentation( $this->lastBlock );
  255. // Get rid of whitespace for the block line
  256. $this->parser->trimBlockLine( $this->lastBlock );
  257. // Go up (closer to program) one level in the nested tree structure
  258. $this->lastBlock = $this->lastBlock->parentBlock;
  259. }
  260. }
  261. ?>