[limb-svn] r5843 - in 3.x/trunk/limb/macro: src tests/cases
svn at limb-project.com
svn at limb-project.com
Wed May 9 02:59:01 MSD 2007
Author: pachanga
Date: 2007-05-09 02:59:01 +0400 (Wed, 09 May 2007)
New Revision: 5843
URL: http://fisheye.limb-project.com/changelog/limb/?cs=5843
Added:
3.x/trunk/limb/macro/src/lmbMacroCompiler.class.php
3.x/trunk/limb/macro/src/lmbMacroParser.class.php
3.x/trunk/limb/macro/src/lmbMacroTagDictionary.class.php
3.x/trunk/limb/macro/src/lmbMacroTagParsingState.class.php
3.x/trunk/limb/macro/src/lmbMacroTemplateLocator.class.php
3.x/trunk/limb/macro/src/lmbMacroTextNode.class.php
3.x/trunk/limb/macro/tests/cases/lmbMacroCodeWriterTest.class.php
3.x/trunk/limb/macro/tests/cases/lmbMacroTreeBuilderTest.class.php
Modified:
3.x/trunk/limb/macro/src/lmbMacroCodeWriter.class.php
3.x/trunk/limb/macro/src/lmbMacroNode.class.php
3.x/trunk/limb/macro/src/lmbMacroTag.class.php
3.x/trunk/limb/macro/src/lmbMacroTokenizerListener.interface.php
3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php
Log:
-- initial versions of lmbMacroCompiler, lmbMacroParser, lmbMacroTagDictionary, lmbMacroCodeWriter, lmbMacroTextNode, lmbMacroTemplateLocator added
-- code cleanup
-- tests for lmbMacroCodeWriter, lmbMacroTreeBuilder added
Modified: 3.x/trunk/limb/macro/src/lmbMacroCodeWriter.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroCodeWriter.class.php 2007-05-08 21:36:03 UTC (rev 5842)
+++ 3.x/trunk/limb/macro/src/lmbMacroCodeWriter.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -83,6 +83,11 @@
$this->switchToHTML(substr($text,0,1));
$this->code .= $text;
}
+
+ function writeRaw($text)
+ {
+ $this->code .= $text;
+ }
function renderCode()
{
Added: 3.x/trunk/limb/macro/src/lmbMacroCompiler.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroCompiler.class.php (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroCompiler.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -0,0 +1,133 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright Copyright © 2004-2007 BIT
+ * @license LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version $Id: lmbMacroCompiler.class.php 5594 2007-04-10 09:00:02Z pachanga $
+ * @package macro
+ */
+
+class lmbMacroCompiler
+{
+ /**
+ * @var lmbMacroTreeBuilder
+ */
+ protected $tree_builder;
+
+ /**
+ * @var lmbMacroConfig
+ */
+ protected $config;
+
+ /**
+ * @var lmbMacroTemplateLocator
+ */
+ protected $template_locator;
+
+ /**
+ * @var lmbMacroSourceParser
+ */
+ protected $parser;
+
+ /**
+ * @var lmbMacroTagDictionary
+ */
+ protected $tag_dictionary;
+
+
+ function __construct($config, $tag_dictionary, $template_locator)
+ {
+ $this->config = $config;
+ $this->template_locator = $template_locator;
+
+ $this->tag_dictionary = $tag_dictionary;
+ $this->tree_builder = new lmbMacroTreeBuilder($this);
+ }
+
+ function compile($file_name)
+ {
+ if(!$source_file_path = $this->template_locator->locateSourceTemplate($file_name))
+ throw new lmbMacroException('Template source file not found', array('file_name' => $file_name));
+
+ $root_node = new lmbMacroRootNode(new lmbMacroSourceLocation($source_file_path, ''));
+
+ $this->parseTemplate($file_name, $root_node);
+
+ $root_node->prepare();
+
+ $compiled_file_path = $this->template_locator->locateCompiledTemplate($file_name);
+ $generated_code = $this->_generateTemplateCode(md5($compiled_file_path), $root_node);
+ self :: writeFile($compiled_file_path, $generated_code);
+ }
+
+ function _generateTemplateCode($prefix, $root_node)
+ {
+ $code_writer = new lmbMacroCodeWriter();
+ $code_writer->setFunctionPrefix($prefix);
+
+ $constructor_func = $code_writer->beginFunction('($root, $components)');
+ $root_node->generateConstructor($code_writer);
+ $code_writer->endFunction();
+
+ $render_func = $code_writer->beginFunction('($root, $components)');
+ $code_writer->writePHP('$template = $root;' . "\n");
+ $root_node->generate($code_writer);
+ $code_writer->endFunction();
+
+ $code_writer->writePHP('$GLOBALS[\'TemplateRender\'][$compiled_template_path] = \'' . $render_func . '\';');
+ $code_writer->writePHP('$GLOBALS[\'TemplateConstruct\'][$compiled_template_path] = \'' . $constructor_func . '\';');
+
+ return $code_writer->renderCode();
+ }
+
+ function parseTemplate($source_file_path, $root_node)
+ {
+ $parser = new lmbMacroParser($this->tree_builder,
+ $this->config,
+ $this->template_locator,
+ $this->tag_dictionary);
+
+ $parser->parse($source_file_path, $root_node);
+ }
+
+ /**
+ * @return lmbMacroConfig
+ **/
+ function getConfig()
+ {
+ return $this->config;
+ }
+
+ /**
+ * @return lmbMacroTemplateLocator
+ **/
+ function getTemplateLocator()
+ {
+ return $this->template_locator;
+ }
+
+ /**
+ * @return lmbMacroTreeBuilder
+ **/
+ function getTreeBuilder()
+ {
+ return $this->tree_builder;
+ }
+
+ function getTagDictionary()
+ {
+ return $this->tag_dictionary;
+ }
+
+ static function writeFile($file, $data)
+ {
+ $dirname = dirname($file);
+ lmbFs :: mkdir($dirname);
+
+ file_put_contents($file, $data);
+ }
+}
+?>
Modified: 3.x/trunk/limb/macro/src/lmbMacroNode.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroNode.class.php 2007-05-08 21:36:03 UTC (rev 5842)
+++ 3.x/trunk/limb/macro/src/lmbMacroNode.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -35,6 +35,11 @@
$this->parent = $parent;
}
+ function getParent()
+ {
+ return $this->parent;
+ }
+
function getLocationInTemplate()
{
return $this->location;
Added: 3.x/trunk/limb/macro/src/lmbMacroParser.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroParser.class.php (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroParser.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -0,0 +1,153 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright Copyright © 2004-2007 BIT
+ * @license LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version $Id: lmbMacroSourceFileParser.class.php 5553 2007-04-06 09:05:17Z serega $
+ * @package macro
+ */
+
+class lmbMacroParser implements lmbMacroTokenizerListener
+{
+ protected $active_parsing_state;
+ protected $component_parsing_state;
+ protected $literal_parsing_state;
+
+ /**
+ * @var lmbMacroConfig
+ */
+ protected $config;
+
+ /**
+ * @var lmbMacrotree_builder
+ */
+ protected $tree_builder;
+
+ /**
+ * @var lmbMacroTemplateLocator
+ */
+ protected $template_locator;
+
+ function __construct($tree_builder, $config, $template_locator, $tag_dictionary)
+ {
+ $this->tree_builder = $tree_builder;
+
+ $this->config = $config;
+ $this->template_locator = $template_locator;
+
+ $this->component_parsing_state = $this->_createComponentParsingState($tag_dictionary);
+ $this->literal_parsing_state = $this->_createLiteralParsingState();
+
+ $this->changeToComponentParsingState();
+ }
+
+ // for testing purposes
+ protected function _createComponentParsingState($tag_dictionary)
+ {
+ return new lmbMacroTagParsingState($this, $this->tree_builder, $tag_dictionary);
+ }
+
+ // for testing purposes
+ protected function _createLiteralParsingState()
+ {
+ return new lmbMacroLiteralParsingState($this, $this->tree_builder);
+ }
+
+ /**
+ * Used to parse the source template.
+ * Initially invoked by the CompileTemplate function,
+ * the first component argument being a root node.
+ */
+ function parse($file_name, $root_node)
+ {
+ $source_file_path = $this->template_locator->locateSourceTemplate($file_name);
+
+ if(empty($source_file_path))
+ throw new lmbMacroException('Template source file not found', array('file_name' => $file_name));
+
+ $tags_before_parse = $this->tree_builder->getExpectedTagCount();
+
+ $this->tree_builder->setCursor($root_node);
+
+ $this->changeToComponentParsingState();
+
+ $tokenizer = new lmbMacroTokenizer($this);
+
+ $this->setTemplateLocator($parser);
+
+ $content = $this->template_locator->readTemplateFile($source_file_path);
+
+ $tokenizer->parse($content, $source_file_path);
+
+ if($tags_before_parse != $this->tree_builder->getExpectedTagCount())
+ {
+ $location = $this->tree_builder->getExpectedTagLocation();
+ throw new lmbMacroException('Missing close tag',
+ array('tag' => $this->tree_builder->getExpectedTag(),
+ 'file' => $location->getFile(),
+ 'line' => $location->getLine()));
+ }
+ }
+
+ function getActiveParsingState()
+ {
+ return $this->active_parsing_state;
+ }
+
+ function changeToComponentParsingState()
+ {
+ $this->active_parsing_state = $this->component_parsing_state;
+ }
+
+ function changeToLiteralParsingState($tag)
+ {
+ $this->active_parsing_state = $this->literal_parsing_state;
+ $this->active_parsing_state->setLiteralTag($tag);
+ }
+
+ function setTemplateLocator($template_locator)
+ {
+ $this->literal_parsing_state->setTemplateLocator($template_locator);
+ $this->component_parsing_state->setTemplateLocator($template_locator);
+ }
+
+ function startElement($tag, $attrs)
+ {
+ $this->active_parsing_state->startElement($tag, $attrs);
+ }
+
+ function endElement($tag)
+ {
+ $this->active_parsing_state->endElement($tag);
+ }
+
+ function emptyElement($tag, $attrs)
+ {
+ $this->active_parsing_state->emptyElement($tag, $attrs);
+ }
+
+ function characters($text)
+ {
+ $this->active_parsing_state->characters($text);
+ }
+
+ function unexpectedEOF($text)
+ {
+ $this->active_parsing_state->unexpectedEOF($text);
+ }
+
+ function invalidEntitySyntax($text)
+ {
+ $this->active_parsing_state->invalidEntitySyntax($text);
+ }
+
+ function invalidAttributeSyntax()
+ {
+ $this->active_parsing_state->invalidAttributeSyntax();
+ }
+}
+
+?>
Modified: 3.x/trunk/limb/macro/src/lmbMacroTag.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroTag.class.php 2007-05-08 21:36:03 UTC (rev 5842)
+++ 3.x/trunk/limb/macro/src/lmbMacroTag.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -33,6 +33,16 @@
return $this->tag;
}
+ function getHasClosingTag()
+ {
+ return $this->has_closing_tag;
+ }
+
+ function setHasClosingTag($flag)
+ {
+ return $this->has_closing_tag = $flag;
+ }
+
function getId()
{
if($this->id)
Added: 3.x/trunk/limb/macro/src/lmbMacroTagDictionary.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroTagDictionary.class.php (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroTagDictionary.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright Copyright © 2004-2007 BIT
+ * @license LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version $Id$
+ * @package macro
+ */
+
+lmb_require('limb/macro/src/lmbMacroTagInfo.class.php');
+
+class lmbMacroTagDictionary
+{
+ protected $info = array();
+
+ function register($taginfo, $file)
+ {
+ $tag_to_lower = strtolower($taginfo->getTag());
+
+ if(isset($this->info[$tag_to_lower]))
+ return;
+
+ $taginfo->setFile($file);
+ $this->info[$tag_to_lower] = $taginfo;
+ }
+
+ function findTagInfo($tag)
+ {
+ $tag = strtolower($tag);
+ if(isset($this->info[$tag]))
+ return $this->info[$tag];
+ }
+}
+?>
\ No newline at end of file
Added: 3.x/trunk/limb/macro/src/lmbMacroTagParsingState.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroTagParsingState.class.php (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroTagParsingState.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -0,0 +1,140 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright Copyright © 2004-2007 BIT
+ * @license LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version $Id: lmbMacroTagParsingState.class.php 5780 2007-04-28 13:03:26Z serega $
+ * @package macro
+ */
+
+lmb_require('limb/macro/src/lmbMacroTokenizerListener.interface.php');
+lmb_require('limb/macro/src/lmbMacroBaseParsingState.class.php');
+
+class lmbMacroTagParsingState extends lmbMacroBaseParsingState implements lmbMacroTokenizerListener
+{
+ protected $tag_dictionary;
+
+ function __construct($parser, $tree_builder, $tag_dictionary)
+ {
+ parent :: __construct($parser, $tree_builder);
+ $this->tag_dictionary = $tag_dictionary;
+ }
+
+ function startElement($tag, $attrs)
+ {
+ $location = $this->locator->getCurrentLocation();
+
+ $lower_attributes = $this->normalizeAttributes($attrs, $location);
+
+ $tag_info = $this->tag_dictionary->findTagInfo($tag);
+
+ if($tag_info->isEndTagForbidden())
+ {
+ $tag_node = $this->buildTagNode($tag_info, $tag, $attrs, $self_closed_tag = true);
+ $tag_node->setHasClosingTag(false);
+ $this->tree_builder->pushNode($tag_node); // for cases like <%include%> we do pushNode() and popNode() here.
+ $this->tree_builder->popNode();
+ }
+ else
+ {
+ $this->tree_builder->pushExpectedTag($tag, $location);
+ $tag_node = $this->buildTagNode($tag_info, $tag, $attrs, $self_closed_tag = false);
+ $result = $this->tree_builder->pushNode($tag_node);
+ }
+ }
+
+ function endElement($tag)
+ {
+ $tag_info = $this->tag_dictionary->getTagInfo($tag);
+ $location = $this->locator->getCurrentLocation();
+
+ if($tag_info->isEndTagForbidden())
+ {
+ throw new lmbMacroException('Closing tag forbidden',
+ array('tag' => $tag_info->getTag(),
+ 'file' => $location->getFile(),
+ 'line' => $location->getLine()));
+ }
+
+ $this->tree_builder->popExpectedTag($tag, $location);
+ $this->tree_builder->popNode();
+ }
+
+ function emptyElement($tag, $attrs)
+ {
+ $location = $this->locator->getCurrentLocation();
+ $lower_attributes = $this->normalizeAttributes($attrs, $location);
+
+ $tag_info = $this->tag_dictionary->findTagInfo($tag);
+ $tag_info->load();
+
+ $tag_node = $this->buildTagNode($tag_info, $tag, $attrs, $self_closed_tag = true);
+ $tag_node->setHasClosingTag(false);
+ $this->tree_builder->pushNode($tag_node); // for cases like <%include%> we do pushNode() and popNode() here.
+ $this->tree_builder->popNode();
+ }
+
+ /**
+ * Builds a component, adding attributes
+ * @param lmbMacroTagInfo
+ * @param string XML tag name of component
+ * @param array attributes for tag
+ * @param boolean whether the tag has contents
+ * @return lmbMacroNode
+ */
+ function buildTagNode($tag_info, $tag, $attrs, $isEmpty)
+ {
+ $tag_node = $this->_createTagNode($tag_info, $tag);
+ $tag_node->emptyClosedTag = $isEmpty;
+ $this->_addAttributesToTagNode($tag_node, $attrs);
+ return $tag_node;
+ }
+
+ protected function _addAttributesToTagNode($tag_node, $attrs)
+ {
+ foreach($attrs as $name => $value)
+ {
+ if($value === null)
+ {
+ $location = $this->locator->getCurrentLocation();
+ throw new lmbMacroException('Attribute should have a value',
+ array('file' => $location->getFile(),
+ 'line' => $location->getLine(),
+ 'tag' => $tag_node->getTag(),
+ 'attribute' => $name));
+ }
+ $tag_node->set($name, $value);
+ }
+ }
+
+ protected function _createTagNode($tag_info, $tag)
+ {
+ $class = $tag_info->getClass();
+ $tag_node = new $class($this->locator->getCurrentLocation(), $tag, $tag_info);
+ return $tag_node;
+ }
+
+ function normalizeAttributes($attrs)
+ {
+ return array_change_key_case($attrs, CASE_LOWER);
+ }
+
+ function characters($text)
+ {
+ $this->tree_builder->addTextNode($text);
+ }
+
+ function unexpectedEOF($text)
+ {
+ $this->tree_builder->addTextNode($text);
+ }
+
+ function invalidEntitySyntax($text)
+ {
+ $this->tree_builder->addTextNode($text);
+ }
+}
+?>
\ No newline at end of file
Added: 3.x/trunk/limb/macro/src/lmbMacroTemplateLocator.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroTemplateLocator.class.php (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroTemplateLocator.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright Copyright © 2004-2007 BIT
+ * @license LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version $Id: WactDefaultTemplateLocator.class.php 5420 2007-03-29 12:45:34Z serega $
+ * @package macro
+ */
+
+class lmbMacroTemplateLocator
+{
+ protected $config;
+ protected $templates_dir;
+
+ public function __construct($config)
+ {
+ $this->config = $config;
+ $this->templates_dir = 'templates/';//fix it
+ }
+
+ public function locateCompiledTemplate($file_name)
+ {
+ return $this->config->getCacheDir() . '/' . md5($file_name) . '.php';
+ }
+
+ public function locateSourceTemplate($file_name)
+ {
+ return $this->templates_dir . '/' . $file_name;
+ }
+
+ public function readTemplateFile($file_name)
+ {
+ return file_get_contents($file_name, 1);
+ }
+}
+?>
\ No newline at end of file
Added: 3.x/trunk/limb/macro/src/lmbMacroTextNode.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroTextNode.class.php (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroTextNode.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright Copyright © 2004-2007 BIT
+ * @license LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version $Id: lmbMacroTextNode.class.php 5780 2007-04-28 13:03:26Z serega $
+ * @package macro
+ */
+
+class lmbMacroTextNode extends lmbMacroNode
+{
+ protected $contents;
+
+ function __construct($location, $text)
+ {
+ parent :: __construct($location);
+ $this->contents = $text;
+ }
+
+ function generateContents($code_writer)
+ {
+ $code_writer->writeRaw($this->contents);
+ parent :: generateContents($code_writer);
+ }
+
+ function getText()
+ {
+ return $this->contents;
+ }
+}
+
+?>
\ No newline at end of file
Modified: 3.x/trunk/limb/macro/src/lmbMacroTokenizerListener.interface.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroTokenizerListener.interface.php 2007-05-08 21:36:03 UTC (rev 5842)
+++ 3.x/trunk/limb/macro/src/lmbMacroTokenizerListener.interface.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -19,6 +19,6 @@
function unexpectedEOF($data);
function invalidEntitySyntax($data);
function invalidAttributeSyntax();
- function setDocumentLocator($locator);
+ function setTemplateLocator($locator);
}
?>
\ No newline at end of file
Modified: 3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php 2007-05-08 21:36:03 UTC (rev 5842)
+++ 3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -11,7 +11,7 @@
*/
/**
-* Acts on the lmbMacroTreeRootNode in response to events within the lmbMacroSourceFileParser
+* Acts on the root lmbMacroNode in response to events within the lmbMacroParser
*
* When adding an open tag to the tree, call pushExpectedTag(). When closing
* a tag, call popExpectedTag(), which ensures the tree is balanced.
@@ -21,150 +21,140 @@
*
* To add a node to the tree, you have the following choices. To add a node
* which can have children, use pushNode(). To add a terminal node use addNode(),
-* or addWactTextNode().
-*
+* or addTextNode().
*/
+
+lmb_require('limb/macro/src/lmbMacroTextNode.class.php');
+
class lmbMacroTreeBuilder
{
- protected $compiler;
- protected $component;
+ protected $compiler;
+ protected $node;
- /**
- * Stack of tags pushed onto the tree builder, may also contain components
- * @see pushExpectedTag
- * @see popExpectedTag
- * @see pushCursor
- * @var array of array($tagname, $info) or array($Component)
- * @access private
- */
- var $expectedTags = array();
+ /**
+ * Stack of tags pushed onto the tree builder
+ */
+ var $expected_tags = array();
- function __construct($compiler)
- {
- $this->compiler = $compiler;
- }
+ function __construct($compiler)
+ {
+ $this->compiler = $compiler;
+ }
- /**
- * Returns the current component
- */
- function getCursor()
- {
- return $this->component;
- }
+ /**
+ * Returns the current node
+ */
+ function getCursor()
+ {
+ return $this->node;
+ }
- /**
- * Sets the cursor (the current working component) of the tree builder
- */
- function setCursor($component)
- {
- $this->component = $component;
- }
+ /**
+ * Sets the cursor (the current working node) of the tree builder
+ */
+ function setCursor($node)
+ {
+ $this->node = $node;
+ }
- /**
- * Begins a component's build phase in relation to the component tree.
- * Adds a component to the tree, then makes that component the 'cursor'.
- */
- function pushNode($newComponent)
- {
- $this->component->addChild($newComponent);
- $this->setCursor($newComponent);
- return $this->component->preParse($this->compiler);
- }
+ /**
+ * Begins a node's build phase in relation to the node tree.
+ * Adds a node to the tree, then makes that node the 'cursor'.
+ */
+ function pushNode($node)
+ {
+ $this->node->addChild($node);
+ $this->setCursor($node);
+ return $this->node->preParse($this->compiler);
+ }
- /**
- * Adds a component to the tree, without descending into it.
- * This begins and finishes the component's composition
- */
- function addNode($childComponent)
- {
- $childComponent->preParse($this->compiler);
- $this->component->addChild($childComponent);
- }
+ /**
+ * Adds a node to the tree, without descending into it.
+ * This begins and finishes the node's composition
+ */
+ function addNode($node)
+ {
+ $node->preParse($this->compiler);
+ $this->node->addChild($node);
+ }
- function addTextNode($text)
- {
- $this->addNode(new lmbMacroTextNode(null, $text));
- }
+ function addTextNode($text)
+ {
+ $this->addNode(new lmbMacroTextNode(null, $text));
+ }
- /**
- * Ends a component's build phase in relation to the tree.
- * Checks child server ids and moves the 'cursor' up the tree to the parent
- * component.
- */
- function popNode($hasClosingTag)
- {
- $this->component->hasClosingTag = $hasClosingTag;
- $this->component->checkChildrenServerIds();
- $this->setCursor($this->component->parent);
- }
+ /**
+ * Ends a node's build phase in relation to the tree.
+ * Checks child server ids and moves the 'cursor' up the tree to the parent
+ * node.
+ */
+ function popNode()
+ {
+ $this->node->checkChildrenIds();
+ $this->setCursor($this->node->getParent());
+ }
- /**
- * Expects the passed tag. Optionally $info may be passed which is info
- * about that tag. The parser state that calls TreeBuilder may use this info
- * to differentiate, say, plain vs. component tags.
- */
- function pushExpectedTag($tag, $info = null, $location = null)
+ function pushExpectedTag($tag, $location = null)
+ {
+ array_push($this->expected_tags, array($tag, $location));
+ }
+
+ /**
+ * Sets the cursor to a new position, and pushes the old cursor onto the
+ * expected tags stack.
+ * @see popExpectedTag
+ */
+ function pushCursor($newPosition, $location)
+ {
+ // use of array() is to preserve reference from array_pop()
+ array_push($this->expected_tags, array($this->node, $location));
+ $this->setCursor($newPosition);
+ }
+
+ /**
+ * Tests the passed tag against what is expected. Returns any info that
+ * was kept about the expected tag.
+ * If the item in the tag stack is a node, then the cursor is
+ * restored to that, and popExpectedTag is called again.
+ */
+ function popExpectedTag($tag, $location)
+ {
+ if(!$expected_tag_item = array_pop($this->expected_tags))
{
- array_push($this->expectedTags, array($tag, $info, $location));
+ throw new lmbMacroException('Lonely closing tag', array('tag' => $tag,
+ 'file' => $location->getFile(),
+ 'line' => $location->getLine()));
}
- /**
- * Sets the cursor to a new position, and pushes the old cursor onto the
- * expected tags stack.
- * @see popExpectedTag
- */
- function pushCursor($newPosition, $location)
+ // if we have a node on the stack, restore the cursor to that, and
+ // pop the stack again
+ if(is_object($expected_tag_item[0]))
{
- // use of array() is to preserve reference from array_pop()
- array_push($this->expectedTags, array($this->component, PARSER_TAG_IS_COMPONENT, $location));
- $this->setCursor($newPosition);
+ $this->node = $expected_tag_item[0];
+ return $this->popExpectedTag($tag, $location);
}
- /**
- * Tests the passed tag against what is expected. Returns any info that
- * was kept about the expected tag.
- * If the item in the tag stack is a component, then the cursor is
- * restored to that, and popExpectedTag is called again.
- */
- function popExpectedTag($tag, $location)
- {
- if(!$expectedTagItem = array_pop($this->expectedTags))
- {
- throw new lmbMacroException('Lonely closing tag', array('tag' => $tag,
- 'file' => $location->getFile(),
- 'line' => $location->getLine()));
- }
+ $expected_tag = $expected_tag_item[0];
+ $expected_location = $expected_tag_item[1];
- // if we have a component on the stack, restore the cursor to that, and
- // pop the stack again
- if(is_object($expectedTagItem[0]))
- {
- $this->component =& $expectedTagItem[0];
- return $this->popExpectedTag($tag, $location);
- }
+ if(strcasecmp($expected_tag, $tag) === 0)
+ return $tag;
+
+ throw new lmbMacroException('Unexpected closing tag',
+ array('file' => $location->getFile(),
+ 'tag' => $tag,
+ 'line' => $location->getLine(),
+ 'expected_tag' => $expected_tag,
+ 'expected_file' => $expected_location->getFile(),
+ 'expected_line' => $expected_location->getLine()));
+ }
- $expectedTag = $expectedTagItem[0];
- $info = $expectedTagItem[1];
-
- if(strcasecmp($expectedTag, $tag) !== 0)
- {
- throw new lmbMacroException('Unexpected closing tag',
- array('file' => $location->getFile(),
- 'tag' => $tag,
- 'line' => $location->getLine(),
- 'ExpectTag' => $expectedTag,
- 'ExpectTagFile' => $expectedTagItem[2]->getFile(),
- 'ExpectedTagLine' => $expectedTagItem[2]->getLine()));
- }
- return $info;
- }
-
/**
* Return the size of the expected tags stack
*/
function getExpectedTagCount()
{
- return count($this->expectedTags);
+ return count($this->expected_tags);
}
/**
@@ -172,16 +162,16 @@
*/
function getExpectedTag()
{
- // Returns the tagname of the first non-component item on the stack
- $item = end($this->expectedTags);
+ // Returns the tagname of the first non-node item on the stack
+ $item = end($this->expected_tags);
while($item && !is_string($item[0]))
- $item = prev($this->expectedTags);
+ $item = prev($this->expected_tags);
return $item ? $item[0] : false;
}
function getExpectedTagLocation()
{
- $item = end($this->expectedTags);
+ $item = end($this->expected_tags);
return $item[2];
}
}
Added: 3.x/trunk/limb/macro/tests/cases/lmbMacroCodeWriterTest.class.php
===================================================================
--- 3.x/trunk/limb/macro/tests/cases/lmbMacroCodeWriterTest.class.php (rev 0)
+++ 3.x/trunk/limb/macro/tests/cases/lmbMacroCodeWriterTest.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright Copyright © 2004-2007 BIT
+ * @license LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version $Id$
+ * @package macro
+ */
+
+lmb_require('limb/macro/src/lmbMacroCodeWriter.class.php');
+
+class lmbMacroCodeWriterTest extends UnitTestCase
+{
+ protected $writer;
+
+ function setUp()
+ {
+ $this->writer = new lmbMacroCodeWriter();
+ }
+
+ function testGetCode()
+ {
+ $this->assertEqual($this->writer->renderCode(),'');
+ }
+
+ function testGetSetCode()
+ {
+ $this->writer->setCode($code = 'code');
+ $this->assertEqual($code, $this->writer->getCode());
+ }
+
+ function testWritePHP()
+ {
+ $this->writer->writePHP('echo ("Hello World!");');
+ $this->assertEqual($this->writer->renderCode(),'<?php echo ("Hello World!"); ?>');
+ }
+
+ function testWriteHTML()
+ {
+ $this->writer->writeHTML('<p>Hello World!</p>');
+ $this->assertEqual($this->writer->renderCode(),'<p>Hello World!</p>');
+ }
+
+ function testSwithBetweenPHPAndHTML()
+ {
+ $this->writer->writePHP('echo ("Hello World!");');
+ $this->writer->writeHTML('<p>Hello World!</p>');
+ $this->writer->writePHP('echo ("Hello World!");');
+ $this->assertEqual($this->writer->renderCode(),
+ '<?php echo ("Hello World!"); ?><p>Hello World!</p><?php echo ("Hello World!"); ?>');
+ }
+
+ function testRegisterInclude()
+ {
+ $this->writer->registerInclude('test.php');
+ $this->assertEqual($this->writer->renderCode(),'<?php '."require_once('test.php');\n".'?>');
+ }
+
+ function testReset()
+ {
+ $this->writer->writePHP('echo ("Hello World!");');
+ $this->writer->registerInclude('test.php');
+ $this->writer->reset();
+ $this->assertEqual($this->writer->renderCode(), '');
+ }
+
+ function testBeginFunction()
+ {
+ $params = '($a,$b,$c)';
+ $this->writer->beginFunction($params);
+ $this->assertEqual($this->writer->renderCode(),'<?php function tpl1'.$params ." {\n ?>");
+ }
+
+ function testEndFunction()
+ {
+ $this->writer->endFunction();
+ $this->assertEqual($this->writer->renderCode(),'<?php '." }\n".' ?>');
+ }
+
+ function testSetFunctionPrefix()
+ {
+ $this->writer->setFunctionPrefix('Test');
+ $params = '($a,$b,$c)';
+ $this->writer->beginFunction($params);
+ $this->assertEqual($this->writer->renderCode(),'<?php function tplTest1'.$params ." {\n ?>");
+ }
+
+ function testGetTempVariable()
+ {
+ $var = $this->writer->getTempVariable();
+ $this->assertWantedPattern('/[a-z][a-z0-9]*/i', $var);
+ }
+
+ function testGetSecondTempVariable()
+ {
+ $A = $this->writer->getTempVariable();
+ $B = $this->writer->getTempVariable();
+ $this->assertNotEqual($A, $B);
+ }
+
+ function testGetTempVariablesMany()
+ {
+ for ($i = 1; $i <= 30; $i++)
+ {
+ $var = $this->writer->getTempVariable();
+ $this->assertWantedPattern('/[a-z][a-z0-9]*/i', $var);
+ }
+ }
+}
+?>
\ No newline at end of file
Added: 3.x/trunk/limb/macro/tests/cases/lmbMacroTreeBuilderTest.class.php
===================================================================
--- 3.x/trunk/limb/macro/tests/cases/lmbMacroTreeBuilderTest.class.php (rev 0)
+++ 3.x/trunk/limb/macro/tests/cases/lmbMacroTreeBuilderTest.class.php 2007-05-08 22:59:01 UTC (rev 5843)
@@ -0,0 +1,237 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright Copyright © 2004-2007 BIT
+ * @license LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version $Id: lmbMacroTreeBuilderTest.class.php 5783 2007-04-29 07:04:40Z serega $
+ * @package core
+ */
+
+lmb_require('limb/macro/src/lmbMacroCompiler.class.php');
+lmb_require('limb/macro/src/lmbMacroTagDictionary.class.php');
+lmb_require('limb/macro/src/lmbMacroSourceLocation.class.php');
+lmb_require('limb/macro/src/lmbMacroTagInfo.class.php');
+lmb_require('limb/macro/src/lmbMacroNode.class.php');
+lmb_require('limb/macro/src/lmbMacroTag.class.php');
+lmb_require('limb/macro/src/lmbMacroTreeBuilder.class.php');
+
+Mock::generate('lmbMacroNode', 'MockMacroNode');
+Mock::generate('lmbMacroCompiler', 'MockMacroCompiler');
+
+class lmbMacroTreeBuilderTest extends UnitTestCase
+{
+ protected $compiler;
+ protected $tree_builder;
+ protected $component;
+ protected $tag_dictionary;
+
+ function setUp()
+ {
+ $this->compiler = new MockMacroCompiler();
+ $this->tag_dictionary = new lmbMacroTagDictionary();
+ $this->component = new lmbMacroTag(new lmbMacroSourceLocation('my_file', 1),
+ $tag_name = 'my_tag',
+ new lmbMacroTagInfo($tag_name, 'MyTagClass'));
+ $this->tree_builder = new lmbMacroTreeBuilder($this->compiler);
+ $this->tree_builder->setCursor($this->component);
+ }
+
+ function testPushNodeMakedPushedNodeCurrentCursor()
+ {
+ $this->assertEqual($this->component->getChildren(), array());
+ $this->assertReference($this->component, $this->tree_builder->getCursor());
+
+ $child_component = new MockMacroNode();
+ $child_component->expectOnce('preParse', array($this->compiler));
+
+ $this->tree_builder->pushNode($child_component);
+
+ $this->assertReference($child_component, $this->tree_builder->getCursor());
+ $children = $this->component->getChildren();
+ $this->assertReference($children[0], $child_component);
+ }
+
+ function testAddNodeDontChangeCursor()
+ {
+ $this->assertEqual($this->component->getChildren(), array());
+ $this->assertReference($this->component, $this->tree_builder->getCursor());
+
+ $child_component = new MockMacroNode();
+ $child_component->expectOnce('preParse');
+
+ $this->tree_builder->addNode($child_component);
+
+ $this->assertReference($this->component, $this->tree_builder->getCursor());
+ $children = $this->component->getChildren();
+ $this->assertReference($children[0], $child_component);
+ }
+
+ function testAddlmbMacroTextNode()
+ {
+ $this->assertReference($this->component, $this->tree_builder->getCursor());
+
+ $this->tree_builder->addTextNode('text');
+
+ $this->assertReference($this->component, $this->tree_builder->getCursor());
+ $children = $this->component->getChildren();
+ $this->assertEqual(sizeof($children), 1);
+ $this->assertIsA($children[0], 'lmbMacroTextNode');
+ $this->assertEqual($children[0]->getText(), 'text');
+ }
+
+ function testPopNodeChangeCursorToParent()
+ {
+ $this->assertReference($this->component, $this->tree_builder->getCursor());
+
+ $parent_component = new lmbMacroNode();
+ $this->component->setParent($parent_component);
+
+ $this->tree_builder->popNode();
+
+ $this->assertTrue($this->component->getHasClosingTag());
+ $this->assertReference($parent_component, $this->tree_builder->getCursor());
+ }
+
+ function testPopExpectedTagWithoutAnyExpected()
+ {
+ $location = new lmbMacroSourceLocation('my_file', 10);
+
+ try
+ {
+ $this->tree_builder->popExpectedTag('tag2', $location);
+ }
+ catch(lmbMacroException $e)
+ {
+ $this->assertWantedPattern('/Lonely closing tag/', $e->getMessage());
+ $params = $e->getParams();
+ $this->assertEqual($params['file'], 'my_file');
+ $this->assertEqual($params['line'], 10);
+ $this->assertEqual($params['tag'], 'tag2');
+ }
+ }
+
+ function testPairPushAndPopTheSameTagWorksOk()
+ {
+ $whatever_location = new lmbMacroSourceLocation('my_file', 1);
+
+ $open_location = new lmbMacroSourceLocation('my_file', 10);
+ $close_location = new lmbMacroSourceLocation('my_file', 12);
+
+ $this->tree_builder->pushExpectedTag('other_tag', $whatever_location);
+ $this->tree_builder->pushExpectedTag('tag', $open_location);
+ $this->tree_builder->popExpectedTag('tag', $close_location);
+
+ $this->assertEqual($this->tree_builder->getExpectedTagCount(), 1);
+ $this->assertEqual($this->tree_builder->getExpectedTag(), 'other_tag');
+ }
+
+ function testPopTagThrowsExceptionForNonClosedTags()
+ {
+ $first_location = new lmbMacroSourceLocation('my_file', 1);
+ $open_location = new lmbMacroSourceLocation('my_file', 10);
+ $second_location = new lmbMacroSourceLocation('my_file', 11);
+ $close_location = new lmbMacroSourceLocation('my_file', 12);
+
+ $this->tree_builder->pushExpectedTag('first_tag', $first_location);
+ $this->tree_builder->pushExpectedTag('our_tag', $open_location);
+ $this->tree_builder->pushExpectedTag('plain_tag', $second_location);
+
+ try
+ {
+ $this->tree_builder->popExpectedTag('our_tag', $close_location);
+ }
+ catch(lmbMacroException $e)
+ {
+ $this->assertWantedPattern('/Unexpected closing tag/', $e->getMessage());
+ $params = $e->getParams();
+ $this->assertEqual($params['file'], 'my_file');
+ $this->assertEqual($params['line'], 12);
+ $this->assertEqual($params['tag'], 'our_tag');
+ $this->assertEqual($params['expected_tag'], 'plain_tag');
+ $this->assertEqual($params['expected_file'], 'my_file');
+ $this->assertEqual($params['expected_line'], 11);
+ }
+ }
+
+ function testPushCursor()
+ {
+ // This test is essentially a test of the functionality that enables the
+ // <%wrap%> implementation.
+ // Briefly:
+ // (1) A tree is set up
+ // (2) A new cursor is pushed
+ // (3) New components added should appear under the tree
+ // (4) When the parser pops the tag at which the cursor was pushed
+ // the cursor returns where it was before step (2)
+ // (5) New components added should appear under this orig. point
+
+ $root = new lmbMacroNode();
+ $InsertionPoint = new lmbMacroNode();
+ $child1 = new lmbMacroNode();
+ $child2 = new lmbMacroNode();
+
+ // set up an open tag at root
+ $this->tree_builder->setCursor($root);
+ $this->tree_builder->pushExpectedTag('tag', new lmbMacroSourceLocation('my_file', 10));
+
+ // add some content to the tree
+ $this->tree_builder->pushNode($InsertionPoint);
+ $this->tree_builder->popNode();
+
+ // make sure the tree is: Root --child--> InsertionPoint with cursor
+ // at Root and open 'tag'
+ $this->assertReference($this->tree_builder->getCursor(), $root);
+ $this->assertReference($InsertionPoint->getParent(), $root);
+ $this->assertEqual($this->tree_builder->getExpectedTag(), 'tag');
+
+ // push InsertionPoint as cursor, and add another node to the tree
+ $this->tree_builder->pushCursor($InsertionPoint, new lmbMacroSourceLocation('my_file', 15));
+ $this->tree_builder->pushNode($child1);
+ $this->tree_builder->popNode();
+
+ // make sure cursor is at InsertionPoint, and new node is child of InsertionPoint
+ $this->assertReference($this->tree_builder->getCursor(), $InsertionPoint);
+ $this->assertReference($child1->getParent(), $InsertionPoint);
+
+ // now the parser gets '</tag>', and then more content
+ // so we pop 'tag' (should restore orig cursor), and add a new node
+ $this->tree_builder->popExpectedTag('tag', new lmbMacroSourceLocation('my_file', 16));
+ $this->tree_builder->pushNode($child2);
+ $this->tree_builder->popNode();
+
+ // the new node should be a child of Root, not InsertionPoint
+ $this->assertReference($this->tree_builder->getCursor(), $root);
+ $this->assertReference($child2->getParent(), $root);
+ }
+
+ function testPushAndPopExpectedTagsWithPushCursor()
+ {
+ $new_cursor = new lmbMacroNode();
+
+ $this->tree_builder->pushExpectedTag('tag1', new lmbMacroSourceLocation('my_file', 10));
+
+ // push a new cursor
+ $this->tree_builder->pushCursor($new_cursor, new lmbMacroSourceLocation('my_file', 12));
+ $this->assertReference($this->tree_builder->getCursor(), $new_cursor);
+
+ $this->tree_builder->pushExpectedTag('tag2', new lmbMacroSourceLocation('my_file', 13));
+
+ $this->assertEqual($this->tree_builder->getExpectedTagCount(), 3);
+
+ $this->assertEqual($this->tree_builder->getExpectedTag(), 'tag2');
+ $this->assertEqual($this->tree_builder->popExpectedTag('tag2', new lmbMacroSourceLocation('my_file', 15)), 'tag2');
+ $this->assertEqual($this->tree_builder->getExpectedTagCount(), 2);
+
+ // getting expected tag should skip the cursor
+ $this->assertEqual($this->tree_builder->getExpectedTag(), 'tag1');
+
+ // popping the next tag should restore the cursor to the original
+ $this->assertEqual($this->tree_builder->popExpectedTag('tag1', new lmbMacroSourceLocation('my_file', 17)), 'tag1');
+ $this->assertReference($this->tree_builder->getCursor(), $this->component);
+ $this->assertEqual($this->tree_builder->getExpectedTagCount(), 0);
+ }
+}
+?>
\ No newline at end of file
More information about the limb-svn
mailing list