[limb-svn] r6561 - in 3.x/trunk/limb/macro: src src/compiler src/filters src/tags/core src/tags/form src/tags/list src/tags/tree tests/cases tests/cases/compiler

svn at limb-project.com svn at limb-project.com
Mon Dec 3 14:56:55 MSK 2007


Author: serega
Date: 2007-12-03 14:56:54 +0300 (Mon, 03 Dec 2007)
New Revision: 6561
URL: http://fisheye.limb-project.com/changelog/limb/?cs=6561

Added:
   3.x/trunk/limb/macro/src/compiler/
   3.x/trunk/limb/macro/src/compiler/lmbMacroAnnotationParser.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroAnnotationParserListener.interface.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroBaseParsingState.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroBlockAnalizer.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroBlockAnalizerListener.interface.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroCodeWriter.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroCompiler.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroContentBlockAnalizerListener.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroExpression.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroExpressionInterface.interface.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroExpressionNode.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroFilter.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroFilterDictionary.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroFilterInfo.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroFilterParser.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroNode.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroOutputExpressionNode.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroParser.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroPassiveTag.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroPreprocessor.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTag.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTagAttribute.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTagAttributeBlockAnalizerListener.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTagDictionary.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTagInfo.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTagParsingState.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTemplateExecutor.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTextNode.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTokenizer.class.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTokenizerListener.interface.php
   3.x/trunk/limb/macro/src/compiler/lmbMacroTreeBuilder.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroAnnotationParserTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroCodeWriterTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroFilterDictionaryTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroFilterParserTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroFiltersAcceptanceTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroOutputExpressionTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroTagAcceptanceTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroTagDictionaryTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroTagTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroTemplateExecutorTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroTemplateTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroTokenizerMalformedTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroTokenizerTest.class.php
   3.x/trunk/limb/macro/tests/cases/compiler/lmbMacroTreeBuilderTest.class.php
Removed:
   3.x/trunk/limb/macro/src/lmbMacroAnnotationParser.class.php
   3.x/trunk/limb/macro/src/lmbMacroAnnotationParserListener.interface.php
   3.x/trunk/limb/macro/src/lmbMacroBaseParsingState.class.php
   3.x/trunk/limb/macro/src/lmbMacroBlockAnalizer.class.php
   3.x/trunk/limb/macro/src/lmbMacroBlockAnalizerListener.interface.php
   3.x/trunk/limb/macro/src/lmbMacroCodeWriter.class.php
   3.x/trunk/limb/macro/src/lmbMacroCompiler.class.php
   3.x/trunk/limb/macro/src/lmbMacroContentBlockAnalizerListener.class.php
   3.x/trunk/limb/macro/src/lmbMacroExpression.class.php
   3.x/trunk/limb/macro/src/lmbMacroExpressionInterface.interface.php
   3.x/trunk/limb/macro/src/lmbMacroExpressionNode.class.php
   3.x/trunk/limb/macro/src/lmbMacroFilter.class.php
   3.x/trunk/limb/macro/src/lmbMacroFilterDictionary.class.php
   3.x/trunk/limb/macro/src/lmbMacroFilterInfo.class.php
   3.x/trunk/limb/macro/src/lmbMacroFilterParser.class.php
   3.x/trunk/limb/macro/src/lmbMacroNode.class.php
   3.x/trunk/limb/macro/src/lmbMacroOutputExpressionNode.class.php
   3.x/trunk/limb/macro/src/lmbMacroParser.class.php
   3.x/trunk/limb/macro/src/lmbMacroPassiveTag.class.php
   3.x/trunk/limb/macro/src/lmbMacroPreprocessor.class.php
   3.x/trunk/limb/macro/src/lmbMacroTag.class.php
   3.x/trunk/limb/macro/src/lmbMacroTagAttribute.class.php
   3.x/trunk/limb/macro/src/lmbMacroTagAttributeBlockAnalizerListener.class.php
   3.x/trunk/limb/macro/src/lmbMacroTagDictionary.class.php
   3.x/trunk/limb/macro/src/lmbMacroTagInfo.class.php
   3.x/trunk/limb/macro/src/lmbMacroTagParsingState.class.php
   3.x/trunk/limb/macro/src/lmbMacroTemplateExecutor.class.php
   3.x/trunk/limb/macro/src/lmbMacroTextNode.class.php
   3.x/trunk/limb/macro/src/lmbMacroTokenizer.class.php
   3.x/trunk/limb/macro/src/lmbMacroTokenizerListener.interface.php
   3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroAnnotationParserTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroCodeWriterTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroFilterDictionaryTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroFilterParserTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroFiltersAcceptanceTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroOutputExpressionTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroTagAcceptanceTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroTagDictionaryTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroTagTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroTemplateExecutorTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroTemplateTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroTokenizerMalformedTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroTokenizerTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroTreeBuilderTest.class.php
Modified:
   3.x/trunk/limb/macro/src/filters/date.filter.php
   3.x/trunk/limb/macro/src/filters/lmbMacroPhpFunctionBasedFilter.class.php
   3.x/trunk/limb/macro/src/lmbMacroTemplate.class.php
   3.x/trunk/limb/macro/src/tags/core/apply.tag.php
   3.x/trunk/limb/macro/src/tags/core/include.tag.php
   3.x/trunk/limb/macro/src/tags/core/into.tag.php
   3.x/trunk/limb/macro/src/tags/core/slot.tag.php
   3.x/trunk/limb/macro/src/tags/core/template.tag.php
   3.x/trunk/limb/macro/src/tags/core/wrap.tag.php
   3.x/trunk/limb/macro/src/tags/form/lmbMacroRuntimeWidgetTag.class.php
   3.x/trunk/limb/macro/src/tags/list/list.tag.php
   3.x/trunk/limb/macro/src/tags/list/list_empty.tag.php
   3.x/trunk/limb/macro/src/tags/list/list_even.tag.php
   3.x/trunk/limb/macro/src/tags/list/list_fill.tag.php
   3.x/trunk/limb/macro/src/tags/list/list_glue.tag.php
   3.x/trunk/limb/macro/src/tags/list/list_item.tag.php
   3.x/trunk/limb/macro/src/tags/list/list_odd.tag.php
   3.x/trunk/limb/macro/src/tags/tree/tree.tag.php
   3.x/trunk/limb/macro/src/tags/tree/tree_branch.tag.php
   3.x/trunk/limb/macro/src/tags/tree/tree_item.tag.php
   3.x/trunk/limb/macro/tests/cases/.setup.php
   3.x/trunk/limb/macro/tests/cases/lmbBaseMacroTest.class.php
Log:
-- all compiler stuff moved to /compiler folder
-- tests restructurization


Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroAnnotationParser.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroAnnotationParser.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroAnnotationParser.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroAnnotationParser.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,62 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+lmb_require('limb/core/src/lmbPHPTokenizer.class.php');
+
+/**
+ * class lmbMacroAnnotationParser.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroAnnotationParser
+{
+  static function extractFromFile($file, $listener)
+  {
+    $infos = array();
+    $tokenizer = new lmbPHPTokenizer(file_get_contents($file));
+    while($token = $tokenizer->next())
+    {
+      if(!is_array($token))
+        continue;
+
+      //found class token
+      if($token[0] == T_CLASS)
+      {
+        //fetching class name
+        $token = $tokenizer->next();
+        $class = $token[1];
+
+        //now checking prev token for /**/
+        if(!is_array($prev_token) || $prev_token[0] != T_DOC_COMMENT)
+          throw new lmbMacroException('Invalid token, doc comment is expected');
+
+        //now parsing annotations
+        $annotations = self :: _extractAnnotations($prev_token[1]);
+        if(!$annotations)
+          throw new lmbMacroException("No annotations found in doc comment '{$prev_token[1]}' in file $file");
+
+        $infos[] = call_user_func_array (array($listener, 'createByAnnotations'), array($file, $class, $annotations));
+        //$infos[] = $listener->createByAnnotations($file, $class, $annotations);
+      }
+      $prev_token = $token;
+    }
+    return $infos;
+  }
+
+  static protected function _extractAnnotations($content)
+  {
+    if(!preg_match_all('~@(\S+)([^\n]+)?\n~', $content, $matches))
+      return false;
+    $annotations = array();
+    for($i=0;$i<count($matches[0]);$i++)
+      $annotations[trim($matches[1][$i])] = trim($matches[2][$i]);
+    return $annotations;
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroAnnotationParserListener.interface.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroAnnotationParserListener.interface.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroAnnotationParserListener.interface.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroAnnotationParserListener.interface.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,20 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * interface lmbMacroAnnotationParserListener.
+ *
+ * @package macro
+ * @version $Id$
+ */
+interface lmbMacroAnnotationParserListener
+{
+  function createByAnnotations($class, $file, $annotations);
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroBaseParsingState.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroBaseParsingState.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroBaseParsingState.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroBaseParsingState.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,61 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com 
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
+ */
+
+/**
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroBaseParsingState
+{
+  /**
+  * @var lmbMacroSourceFileParser
+  */
+  protected $parser;
+
+  /**
+  * @var lmbMacroTreeBuilder
+  */
+  protected $tree_builder;
+
+  protected $locator;
+
+  function __construct($parser, $tree_builder)
+  {
+    $this->parser = $parser;
+    $this->tree_builder = $tree_builder;
+  }
+
+  function setTemplateLocator($locator)
+  {
+    $this->locator = $locator;
+  }
+
+  function invalidAttributeSyntax($data)
+  {
+    throw new lmbException('Invalid attribute syntax starting from: ' . $data);
+  }
+
+  function getAttributeString($attrs)
+  {
+    $attrib_str = '';
+    foreach($attrs as $key => $value)
+    {
+      $attrib_str .= ' ' . $key;
+      if(!is_null($value))
+      {
+        if(strpos($value, '"') === FALSE)
+          $attrib_str .= '="' . $value . '"';
+        else
+          $attrib_str .= '=\'' . $value . '\'';
+      }
+    }
+    return $attrib_str;
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroBlockAnalizer.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroBlockAnalizer.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroBlockAnalizer.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroBlockAnalizer.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,52 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroContentAnalizer.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroBlockAnalizer
+{
+  const BEFORE_CONTENT = 1;
+  const EXPRESSION = 2;
+  const AFTER_CONTENT = 5;
+
+  protected function _getRegexp()
+  {
+    return '/^((?s).*?)'. preg_quote('{$', '/') . '((([^"\']+["\']?[^"\']?["\'])+)?[^}]+)' . preg_quote('}', '/') . '((?s).*)$/';
+  }
+
+  function parse($text, $observer)
+  {
+    // if there is no expression (common case), shortcut this process
+    if (strpos($text, '{$') === FALSE)
+    {
+      $observer->addLiteralFragment($text);
+      return;
+    }
+
+    $regexp = $this->_getRegexp();
+
+    while (preg_match($regexp, $text, $match))
+    {
+      if (strlen($match[self :: BEFORE_CONTENT]) > 0)
+        $observer->addLiteralFragment($match[self :: BEFORE_CONTENT]);
+
+      $observer->addExpressionFragment('$' . $match[self :: EXPRESSION]);
+
+      $text = $match[self :: AFTER_CONTENT];
+    }
+
+    if (strlen($text) > 0)
+      $observer->addLiteralFragment($text);
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroBlockAnalizerListener.interface.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroBlockAnalizerListener.interface.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroBlockAnalizerListener.interface.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroBlockAnalizerListener.interface.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,21 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * interface lmbMacroBlockAnalizerListener.
+ *
+ * @package macro
+ * @version $Id$
+ */
+interface lmbMacroBlockAnalizerListener
+{
+  function addLiteralFragment($text);
+  function addExpressionFragment($text);
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroCodeWriter.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroCodeWriter.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroCodeWriter.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroCodeWriter.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,242 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com 
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
+ */
+
+/**
+ * class lmbMacroCodeWriter.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroCodeWriter
+{
+  const MODE_PHP = 1;
+  const MODE_HTML = 2;
+
+  protected $class;
+
+  protected $parent;
+
+  protected $current_mode = self :: MODE_PHP;
+
+  protected $current_method;
+
+  protected $code = '';
+
+  protected $methods = array();
+
+  protected $init_code = '';
+
+  protected $methods_stack = array();
+
+  protected $include_list = array();
+
+  protected $temp_var_name = 1;
+
+  function __construct($class, $render_func = 'render')
+  {
+    $this->class = $class;
+    $this->render_func = $render_func;
+    $this->parent = 'lmbMacroTemplateExecutor';
+    $this->registerInclude('limb/macro/src/compiler/lmbMacroTemplateExecutor.class.php');
+
+    $this->beginMethod($render_func, array('$args = array()'));
+    $this->writePHP('if($args) extract($args);');
+    $this->writePHP('$this->_init();');
+  }
+
+  function getClass()
+  {
+    return $this->class;
+  }
+
+  function getRenderMethod()
+  {
+    return 'render';
+  }
+
+  protected function switchToPHP()
+  {
+    if($this->current_mode == self :: MODE_HTML)
+    {
+      $this->current_mode = self :: MODE_PHP;
+      $this->_append('<?php ');
+    }
+  }
+
+  protected function switchToHTML($context = null)
+  {
+    if($this->current_mode == self :: MODE_PHP)
+    {
+      $this->current_mode = self :: MODE_HTML;
+      if($context === "\n")
+        $this->_append(" ?>\n");
+      else
+        $this->_append(' ?>');
+    }
+  }
+
+  function writePHP($text)
+  {
+    $this->switchToPHP();
+    $this->_append($text);
+  }
+
+  function writePHPLiteral($text, $escape_text = true)
+  {
+    $this->switchToPHP();
+
+    if($escape_text)
+      $this->_append("'" . $this->escapeLiteral($text) . "'");
+    else
+      $this->_append("'" . $text . "'");
+  }
+
+  function escapeLiteral($text)
+  {
+    $text = str_replace('\'', "\\'", $text);
+    if(substr($text, -1) == '\\')
+      $text .= '\\';
+    return $text;
+  }
+
+  function writeHTML($text)
+  {
+    $this->switchToHTML(substr($text,0,1));
+    $this->_append($text);
+  }
+  
+  function writeRaw($text)
+  {
+    $this->_append($text);
+  }
+
+  function renderCode()
+  {
+    $this->endMethod();
+
+    $code = "<?php\n" .
+           //protection from self inclusion
+           "if(!class_exists('{$this->class}', false)){\n" .
+           $this->_renderIncludeList() . 
+           "class {$this->class} " . ($this->parent ? "extends {$this->parent} " : '') . "{\n" .
+           (!$this->init_code ? "" :
+           "\nfunction _init() {" .
+           "\n$this->init_code\n" .
+           "}\n" 
+           ) .
+           $this->_renderMethods() . 
+           "\n}" . 
+           "\n}";
+    return $code;
+  }
+
+  function getCode()
+  {
+    return $this->code;
+  }
+
+  function getMode()
+  {
+    return $this->current_mode;
+  }
+
+  function registerInclude($include_file)
+  {
+    if(!in_array($include_file, $this->include_list))
+      $this->include_list[] = $include_file;    
+  }
+
+  function getIncludeList()
+  {
+    return $this->include_list;
+  }
+
+  function beginFunction($name, $param_list = array())
+  {
+    $this->writePHP('function ' . $name . '(' . implode(',', $param_list) .") {\n");
+    return $name;
+  }
+
+  function endFunction()
+  {
+    $this->writePHP("\n}\n");
+  }
+
+  function beginMethod($name, $param_list = array())
+  {
+    $this->methods_stack[] = array($this->current_method, $this->current_mode);
+    $this->current_method = $name;
+
+    //we don't need to switch to PHP, since methods can be declared inside PHP only
+    $this->writeRaw('function ' . $name . '(' . implode(',', $param_list) .") {\n");
+    $this->current_mode = self :: MODE_PHP;
+    return $name;
+  }
+
+  function endMethod()
+  {
+    $this->writePHP("\n}\n");
+    list($this->current_method, $this->current_mode) = array_pop($this->methods_stack);
+  }
+
+  function writeToInit($code)
+  {
+    $this->init_code .= $code;
+  }
+
+  /**
+  * Utility method, which generates a unique variable name
+  */
+  function generateTempName()
+  {
+    $var = $this->temp_var_name++;
+    if($var > 675)
+      return chr(65 + ($var/26)/26) . chr(65 + ($var/26)%26) . chr(65 + $var%26);
+    elseif($var > 26)
+      return chr(65 + ($var/26)%26) . chr(65 + $var%26);
+    else
+      return chr(64 + $var);
+  }
+
+  /**
+  * Utility method, which generates a unique variable name, prefixed with a $
+  */
+  function generateVar()
+  {
+    return '$' . $this->generateTempName();
+  }
+
+  protected function _append($code)
+  {
+    if(!$this->current_method)
+    {
+      $this->code .= $code;
+      return;
+    }
+
+    if(!isset($this->methods[$this->current_method]))
+      $this->methods[$this->current_method] = '';
+
+    $this->methods[$this->current_method] .= $code;
+  }
+
+  protected function _renderMethods()
+  {
+    return implode("\n", $this->methods);
+  }
+
+  protected function _renderIncludeList()
+  {
+    $include_code = '';
+    foreach($this->include_list as $include_file)
+      $include_code .= "require_once('$include_file');\n";
+    return $include_code;
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroCompiler.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroCompiler.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroCompiler.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroCompiler.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,109 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroCompiler.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroCompiler
+{
+  /**
+  * @var lmbMacroTreeBuilder
+  */
+  protected $tree_builder;
+
+  /**
+  * @var lmbMacroTemplateLocator
+  */
+  protected $template_locator;
+
+  /**
+  * @var lmbMacroSourceParser
+  */
+  protected $parser;
+
+  /**
+  * @var lmbMacroTagDictionary
+  */
+  protected $tag_dictionary;
+
+  /**
+  * @var lmbMacroFilterDictionary
+  */
+  protected $filter_dictionary;
+
+  function __construct($tag_dictionary, $template_locator, $filter_dictionary)
+  {
+    $this->tree_builder = new lmbMacroTreeBuilder($this);
+
+    $this->template_locator = $template_locator;
+
+    $this->tag_dictionary = $tag_dictionary;
+    $this->filter_dictionary = $filter_dictionary;
+  }
+
+  function compile($source_file, $compiled_file, $class, $render_func)
+  {
+    $root_node = new lmbMacroNode(new lmbMacroSourceLocation($source_file, ''));
+    $this->parseTemplate($source_file, $root_node);
+
+    $generated_code = $this->_generateTemplateCode($class, $render_func, $root_node);
+    self :: writeFile($compiled_file, $generated_code);
+  }
+
+  function _generateTemplateCode($class, $render_func, $root_node)
+  {
+    $code_writer = new lmbMacroCodeWriter($class, $render_func);
+    $root_node->generate($code_writer);
+    return $code_writer->renderCode();
+  }
+
+  function parseTemplate($source_file_path, $root_node)
+  {
+    $parser = new lmbMacroParser($this->tree_builder, $this->template_locator, $this->tag_dictionary);
+    $parser->parse($source_file_path, $root_node);
+  }
+
+  /**
+  * @return lmbMacroTemplateLocator
+  */
+  function getTemplateLocator()
+  {
+    return $this->template_locator;
+  }
+
+  /**
+  * @return lmbMacroTreeBuilder
+  */
+  function getTreeBuilder()
+  {
+    return $this->tree_builder;
+  }
+
+  function getTagDictionary()
+  {
+    return $this->tag_dictionary;
+  }
+
+  function getFilterDictionary()
+  {
+    return $this->filter_dictionary;
+  }
+
+  static function writeFile($file, $data)
+  {
+    $dirname = dirname($file);
+    lmbFs :: mkdir($dirname);
+
+    file_put_contents($file, $data);
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroContentBlockAnalizerListener.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroContentBlockAnalizerListener.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroContentBlockAnalizerListener.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroContentBlockAnalizerListener.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,42 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroContentBlockAnalizerListener.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroContentBlockAnalizerListener implements lmbMacroBlockAnalizerListener
+{
+  protected $tree_builder;
+  protected $location;
+
+  function __construct($tree_builder, $location)
+  {
+    $this->tree_builder = $tree_builder;
+    $this->location = $location;
+  }
+
+  function addLiteralFragment($text)
+  {
+    $this->tree_builder->addTextNode($text);
+  }
+
+  function addExpressionFragment($text)
+  {
+    $expression = new lmbMacroExpressionNode($text,
+                                             $this->tree_builder->getCursor(),
+                                             $this->tree_builder->getFilterDictionary());
+
+    $output_expression = new lmbMacroOutputExpressionNode($this->location, $expression);
+    $this->tree_builder->addNode($output_expression);
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroExpression.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroExpression.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroExpression.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroExpression.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,103 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroExpression
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroExpression implements lmbMacroExpressionInterface
+{
+  protected $tmp;
+
+  function __construct($expression_str)
+  {
+    $this->expression_str = $expression_str;
+  }
+
+  function preGenerate($code)
+  {
+    $this->tmp = $code->generateVar();
+    $var = $code->generateVar();
+
+    // simple case if expression is just a variable
+    if(strpos($this->expression_str, '.') === false)
+    {
+      $this->tmp = $this->expression_str;
+      return;
+    }
+
+    $expr = '';
+    
+    $items = $this->_extractExpressionPathItems($this->expression_str);
+
+    //first item is variable itself
+    //$var = $items[0];
+    $expr .= $var . ' = ' . $items[0] . ';';
+    $code->writePHP($this->tmp . "='';");
+
+    for($i=1; $i<sizeof($items); $i++)
+    {
+      $item = $items[$i];
+      $expr .= 'if((is_array(' . $var . ') && isset(' . $var . '["' . $item . '"])) || ' .
+               '(is_object(' . $var . ') && ' . $this->tmp . '=' . $var . '->get("' . $item . '")))' .
+               '{if(is_array(' . $var . '))' . $this->tmp . ' = ' . $var . '["' . $item . '"];';
+      $var = $this->tmp;
+    }
+
+    //closing brackets
+    for($i=1; $i < sizeof($items); $i++)
+      $expr .= '}else{' . $this->tmp . '="";}';
+
+    $code->writePHP($expr);
+  }
+
+  protected function _extractExpressionPathItems($text)
+  {
+    $tokens = token_get_all('<?php ' . $text . '?>');
+    // removing first and last tokens since we just added them with the line above
+    array_shift($tokens);
+    array_pop($tokens);
+
+    $path_items = array();
+    $in_function = false;
+    
+    $item = '';
+    foreach($tokens as $token)
+    {
+      if(is_scalar($token))
+      {
+        if($token == '(')
+          $in_function = true;
+        if($token == ')')
+          $in_function = false;
+        if($token == '.' && !$in_function)
+        {
+          $path_items[] = $item;
+          $item = '';
+          continue;
+        }
+        
+        $item .= $token;
+      }
+      else
+        $item .= $token[1];
+    }
+    
+    $path_items[] = $item;
+    
+    return $path_items;
+  }
+
+  function getValue()
+  {
+    return $this->tmp;
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroExpressionInterface.interface.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroExpressionInterface.interface.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroExpressionInterface.interface.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroExpressionInterface.interface.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,20 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * interface lmbMacroExpressionInterface
+ * @package macro
+ * @version $Id$
+ */
+interface lmbMacroExpressionInterface
+{
+  function preGenerate($code);
+  function getValue();
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroExpressionNode.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroExpressionNode.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroExpressionNode.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroExpressionNode.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,104 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroExpressionNode.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroExpressionNode  implements lmbMacroExpressionInterface
+{
+  protected $context;
+
+  protected $original_expression;
+  protected $expression;
+
+  protected $parsed;
+
+  protected $filter_dictionary;
+
+  function __construct($expression, $context_node, $filter_dictionary)
+  {
+    $this->original_expression = $expression;
+    $this->expression = $expression;
+    $this->context = $context_node;
+    $this->filter_dictionary = $filter_dictionary;
+
+    $this->_createParsedExpression();
+  }
+
+  protected function _createParsedExpression()
+  {
+    $pos = strpos($this->expression, "|");
+
+    if ($pos === FALSE)
+      $this->parsed = new lmbMacroExpression($this->expression);
+    else
+    {
+      $base_expression = trim(substr($this->expression, 0, $pos));
+      $filters_expression = trim(substr($this->expression, $pos + 1));
+      $this->parsed = $this->createFilterChain($filters_expression, new lmbMacroExpression($base_expression));
+    }
+  }
+
+  /**
+  * Parses an expression, building a chain of filters for it
+  */
+  function createFilterChain($expression, $base)
+  {
+    $filter_parser = new lmbMacroFilterParser($this->context);
+    $filters_specs = $filter_parser->parse($expression);
+
+    foreach($filters_specs as $filter_spec)
+    {
+      $filter_name = $filter_spec['name'];
+      $filter_info = $this->filter_dictionary->findFilterInfo($filter_name);
+
+      if (!is_object($filter_info))
+        $this->context->raise('Unknown filter', array('filter' => $filter_name));
+
+      $base = $this->_createFilter($filter_name, $base, $filter_spec['params']);
+    }
+
+    return $base;
+  }
+
+  protected function _createFilter($name, $base, $params = "")
+  {
+    $filter_info = $this->filter_dictionary->findFilterInfo($name);
+
+    if (!is_object($filter_info))
+      $this->context->raise('Unknown filter', array('filter' => $name));
+
+    $filter_info->load();
+
+    $filter_class = $filter_info->getClass();
+    $filter = new $filter_class($base);
+    if(sizeof($params))
+      $filter->setParams($params);
+    return $filter;
+  }
+
+  function getValue()
+  {
+    return $this->parsed->getValue();
+  }
+
+  function preGenerate($code_writer)
+  {
+    $this->parsed->preGenerate($code_writer);
+  }
+
+  function getFilterDictionary()
+  {
+    return $this->filter_dictionary;
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroFilter.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroFilter.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroFilter.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroFilter.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,35 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroFilter
+ * @package macro
+ * @version $Id$
+ */
+abstract class lmbMacroFilter implements lmbMacroExpressionInterface
+{
+  protected $base;
+  protected $params = array();
+
+  function __construct($base)
+  {
+    $this->base = $base;
+  }
+
+  function preGenerate($code)
+  {
+    $this->base->preGenerate($code);
+  }
+  
+  function setParams($params)
+  {
+    $this->params = $params;
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroFilterDictionary.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroFilterDictionary.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroFilterDictionary.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroFilterDictionary.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,100 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroFilterDictionary.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroFilterDictionary
+{
+  protected $info = array();
+  static protected $instance;
+
+  static function instance()
+  {
+    if(self :: $instance)
+      return self :: $instance;
+
+    self :: $instance = new lmbMacroFilterDictionary();
+    return self :: $instance;
+  }
+
+  function load(lmbMacroConfig $config)
+  {
+    if(!$config->isForceScan() && $this->_loadCache($config))
+      return;
+
+    $dirs = $config->getFiltersScanDirectories();
+    foreach($dirs as $dir)
+    {
+      foreach(lmb_glob($dir . '/*.filter.php') as $file)
+        $this->registerFromFile($file);
+    }
+
+    $this->_saveCache($config);
+  }
+
+  protected function _loadCache(lmbMacroConfig $config)
+  {
+    $cache_file = $config->getCacheDir() . '/filters.cache';
+    if(!file_exists($cache_file))
+      return false;
+
+    $info = @unserialize(file_get_contents($cache_file));
+    if($info === false || !is_array($info))
+      return false;
+
+    $this->info = $info;
+
+    return true;
+  }
+
+  protected function _saveCache(lmbMacroConfig $config)
+  {
+    $cache_file = $config->getCacheDir() . '/filters.cache';
+    lmbFs :: safeWrite($cache_file, serialize($this->info));
+  }
+
+  function register($filter_info)
+  {
+    $names = array(strtolower($filter_info->getName()));
+    
+    $aliases = $filter_info->getAliases();
+    if(count($aliases))
+    {
+      $aliases = array_map('strtolower', $aliases);
+      $names = array_merge($names, $aliases);
+    }
+    
+    foreach($names as $filter_name)
+    {
+      if(isset($this->info[$filter_name]))
+        return;
+  
+      $this->info[$filter_name] = $filter_info;
+    }
+  }
+
+  function registerFromFile($file)
+  {
+    $infos = lmbMacroAnnotationParser :: extractFromFile($file, 'lmbMacroFilterInfo');
+    foreach($infos as $info)
+      $this->register($info, $file);
+  }
+
+  function findFilterInfo($name)
+  {
+    $name = strtolower($name);
+    if(isset($this->info[$name]))
+      return $this->info[$name];
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroFilterInfo.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroFilterInfo.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroFilterInfo.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroFilterInfo.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,85 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroFilterInfo.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroFilterInfo
+{
+  protected $name = '';
+  protected $class = '';
+  protected $file;
+  protected $aliases = array();
+
+  function __construct($name, $class)
+  {
+    $this->name = $name;
+    $this->class = $class;
+  }
+
+  static function createByAnnotations($file, $class, $annotations)
+  {
+    if(!isset($annotations['filter']))
+      throw new lmbMacroException("@filter annotation is missing for class '$class'");
+
+    $filter = $annotations['filter'];
+    $info = new lmbMacroFilterInfo($filter, $class);
+
+    if(isset($annotations['aliases']))
+    {
+      $filter_aliases = explode(',' , $annotations['aliases']);
+      $filter_aliases = array_map('trim', $filter_aliases);
+      $info->setAliases($filter_aliases);
+    }
+    
+    $info->setFile($file);
+
+    return $info;
+  }
+
+  function load()
+  {
+    if (!class_exists($this->class) && isset($this->file))
+        require_once $this->file;
+  }
+
+  function getName()
+  {
+    return $this->name;
+  }
+
+  function getClass()
+  {
+    return $this->class;
+  }
+
+  function setFile($file)
+  {
+    $this->file = $file;
+  }
+
+  function getFile()
+  {
+    return $this->file;
+  }
+  
+  function setAliases($aliases)
+  {
+    $this->aliases = $aliases;
+  }
+  
+  function getAliases()
+  {
+    return $this->aliases;
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroFilterParser.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroFilterParser.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroFilterParser.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroFilterParser.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,180 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroFilterParser.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroFilterParser
+{
+  protected $text;
+  protected $position;
+  protected $length;
+  protected $context;
+  protected $filters = array();
+
+  /**
+  * Construct this parser
+  */
+  function __construct($context)
+  {
+    $this->context = $context;
+  }
+
+  protected function getToken($pattern)
+  {
+    if (preg_match($pattern, $this->text, $match, PREG_OFFSET_CAPTURE, $this->position))
+    {
+      $this->position += strlen($match[0][0]);
+      return $match[1][0];
+    }
+    else
+      return FALSE;
+  }
+
+  function getFilters()
+  {
+    return $this->filters;
+  }
+
+  /**
+  * Parse text for expressions and emit a stream of events for expression fragments
+  */
+  function parse($text)
+  {
+    if (strlen($text) == 0)
+      return array();
+
+    $filters = array();
+
+    $this->text = $text;
+    $this->position = 0;
+
+    $filters_expressions = $this->_extractFiltersExpressions();
+    
+    foreach($filters_expressions as $filter_expression)
+    {
+      $filter_spec = array();
+      $result = preg_match('/\G\s*([A-Za-z][A-Za-z0-9_.]*)/u', $filter_expression, $match, PREG_OFFSET_CAPTURE);
+
+      if(!$result)
+         $this->context->raise('Filter name expected');
+
+      $position = $match[0][1];
+      $filter_name = $match[1][0];
+      $filter_spec = array('name' => $filter_name,
+                           'expression' => $filter_expression,
+                           'params' => array());
+
+      $params_expression = substr($filter_expression, strlen($filter_name));
+      if(!$params_expression)
+      {
+        $filters[] = $filter_spec;
+        continue;
+      }
+
+      if(strlen($params_expression))
+      {
+        $params_start_position = strpos($params_expression, ':');
+        if(($params_start_position === FALSE))
+          $this->context->raise('Unexpected symbol after filter name');
+      }
+
+      $params_str = substr($params_expression, $params_start_position + 1);
+      if (strlen($params_str) == 0)
+        $this->context->raise('Filter params expected after ":" symbol');
+
+      $filter_spec['params'] = $this->_parseParams($params_str);
+      $filters[] = $filter_spec; 
+    }
+
+    return $filters;
+  }
+  
+  protected function _parseParams($params_string)
+  {
+    $params = array();
+    
+    $length = strlen($params_string);
+    $this->position = 0;
+    $this->text = $params_string;
+
+    do
+    {
+      $token = $this->getToken('/\G("|\'|,|[^\'",]+)/u');
+      if ($token === FALSE)
+      {
+        $filters_expressions[] = $this->text;
+        break;
+      }
+
+      if ($token == '"' || $token == "'")
+      {
+        $string = $this->getToken('/\G([^' . $token . ']*' . $token . ')/u');
+
+        if ($string === FALSE)
+          $this->context->raise("Expecting a string literal in filter param");
+      }
+      elseif($token == ',')
+      {
+        $params[] = substr($this->text, 0, $this->position - 1);
+        $this->text = substr($this->text, $this->position);
+        $length = strlen($this->text);
+        $this->position = 0;
+      }
+    }
+    while($this->position < $length);
+
+    //ensures the last param added
+    $params[] = substr($this->text, 0, $this->position );
+    
+    return $params;
+  }
+
+  protected function _extractFiltersExpressions()
+  {
+    $length = strlen($this->text);
+    $this->position = 0;
+
+    $filters_expressions = array();
+    do
+    {
+      $token = $this->getToken('/\G("|\'|\||[^\'"\|]+)/u');
+      if ($token === FALSE)
+      {
+        $filters_expressions[] = $this->text;
+        break;
+      }
+
+      if ($token == '"' || $token == "'")
+      {
+        $string = $this->getToken('/\G([^' . $token . ']*)' . $token . ',?/u');
+
+        if ($string === FALSE)
+          $this->context->raise("Expecting a string literal in filter param");
+      }
+      elseif($token == '|')
+      {
+        $filters_expressions[] = substr($this->text, 0, $this->position - 1);
+        $this->text = substr($this->text, $this->position);
+        $length = strlen($this->text);
+        $this->position = 0;
+      }
+    }
+    while($this->position < $length);
+
+    //ensures the last filter expression added
+    $filters_expressions[] = substr($this->text, 0, $this->position );
+
+    return $filters_expressions;
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroNode.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroNode.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroNode.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroNode.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,254 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroNode.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroNode
+{
+  protected $id;
+  protected $children = array();
+  protected $parent;
+  /**
+  * @var lmbMacroSourceLocation
+  */
+  protected $location;
+
+  function __construct($location = null)
+  {
+    if($location)
+      $this->location = $location;
+    else
+      $this->location = new lmbMacroSourceLocation();
+  }
+
+  function setParent($parent)
+  {
+    $this->parent = $parent;
+  }
+
+  function getParent()
+  {
+    return $this->parent;
+  }
+
+  function getLocationInTemplate()
+  {
+    return $this->location;
+  }
+
+  function getTemplateFile()
+  {
+    return $this->location->getFile();
+  }
+
+  function getTemplateLine()
+  {
+    return $this->location->getLine();
+  }
+
+  function getId()
+  {
+    if($this->id)
+      return $this->id;
+
+    $this->id = self :: generateNewId();
+    return $this->id;
+  }
+
+  function setId($id)
+  {
+    $this->id = $id;
+  }
+
+  static function generateNewId()
+  {
+    static $counter = 1;
+    return 'id00' . $counter++;
+  }
+
+  function raise($error, $vars = array())
+  {
+    $vars['file'] = $this->location->getFile();
+    $vars['line'] = $this->location->getLine();
+    throw new lmbMacroException($error, $vars);
+  }
+
+  function addChild($child)
+  {
+    $child->parent = $this;
+    $this->children[] = $child;
+  }
+
+  function removeChild($id)
+  {
+    foreach($this->children as $key => $child)
+    {
+      if($child->getId() == $id)
+      {
+        unset($this->children[$key]);
+        return $child;
+      }
+    }
+  }
+
+  function getChildren()
+  {
+    return $this->children;
+  }
+
+  function removeChildren()
+  {
+    foreach($this->children as $child)
+    {
+      $child->removeChildren();
+      unset($child);
+    }
+  }
+
+  function getChild($id)
+  {
+    if($child = $this->findChild($id))
+      return $child;
+    else
+      $this->raise('Could not find component', array('id' => $id));
+  }
+
+  function findChild($id)
+  {
+    foreach($this->children as $child)
+    {
+      if($child->getId() == $id)
+        return $child;
+      else
+      {
+        if($result = $child->findChild($id))
+          return $result;
+      }
+    }
+  }
+  
+  /**
+   * Sometimes it is useful to find node located in another tree branch, eg:
+   *  <code>
+   *  {{block}}{{some_tag id='tag1'}}{{/block}}
+   *  {{block}}{{some_tag id='tag2'}}{{/block}}
+   *  </code>
+   * in this case we can find tag1 tag from tag2 tag using findUpChild.
+   */
+  function findUpChild($id)
+  {
+    if($child = $this->findChild($id))
+      return $child;
+
+    if($this->parent)
+      return $this->parent->findUpChild($id);
+  }
+
+  
+  function findChildByClass($class)
+  {
+    foreach($this->children as $child)
+    {
+      if(is_a($child, $class))
+        return $child;
+      else
+      {
+        if($result = $child->findChildByClass($class))
+          return $result;
+      }
+    }
+  }
+
+  function findChildrenByClass($class)
+  {
+    $ret = array();
+    foreach($this->children as $child)
+    {
+      if(is_a($child, $class))
+        $ret[] = $child;
+      else
+      {
+        $more_children = $child->findChildrenByClass($class);
+        if(count($more_children))
+          $ret = array_merge($ret, $more_children);
+      }
+    }
+    return $ret;
+  }
+
+  function findImmediateChildByClass($class)
+  {
+    foreach($this->children as $child)
+    {
+      if(is_a($child, $class))
+        return $child;
+    }
+  }
+
+  function findImmediateChildrenByClass($class)
+  {
+    $result = array();
+    foreach($this->children as $child)
+    {
+      if(is_a($child, $class))
+        $result[] = $child;
+    }
+    return $result;
+  }
+
+  function findParentByClass($class)
+  {
+    $parent = $this->parent;
+
+    while($parent && !is_a($parent, $class))
+      $parent = $parent->parent;
+
+    return $parent;
+  }
+
+  function preParse(){}
+
+  function generate($code_writer)
+  {
+    foreach($this->children as $child)
+      $child->generate($code_writer);
+  }
+
+  /**
+  * Checks that each immediate child of the current component has a unique ID
+  * amongst its siblings.
+  */
+  function checkChildrenIds()
+  {
+    $child_ids = array();
+    $checked_children = array();
+    foreach($this->getChildren() as $key => $child)
+    {
+      $id = $child->getId();
+      if(in_array($id, $child_ids))
+      {
+        $duplicate_child = $checked_children[$id];
+        $child->raise('Duplicate "id" attribute',
+                                   array('id' => $id,
+                                         'duplicate_node_file' => $duplicate_child->getTemplateFile(),
+                                         'duplicate_node_line' => $duplicate_child->getTemplateLine()));
+      }
+      else
+      {
+        $child_ids[] = $id;
+        $checked_children[$id] = $child;
+      }
+    }
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroOutputExpressionNode.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroOutputExpressionNode.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroOutputExpressionNode.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroOutputExpressionNode.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,33 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroTag.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroOutputExpressionNode extends lmbMacroNode
+{
+  protected $expression;
+
+  function __construct($location, $expression)
+  {
+    $this->expression = $expression;
+
+    parent :: __construct($location);
+  }
+
+  function generate($code)
+  {
+    $this->expression->preGenerate($code);
+    $code->writePHP('echo ' . $this->expression->getValue() .  ";\n");
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroParser.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroParser.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroParser.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroParser.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,147 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com 
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
+ */
+
+/**
+ * class lmbMacroParser.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroParser implements lmbMacroTokenizerListener
+{
+  protected $active_parsing_state;
+
+  protected $tag_parsing_state;
+
+  /**
+   * @var lmbMacroTreebuilder
+   */
+  protected $tree_builder;
+
+  /**
+   * @var lmbMacroTemplateLocator
+   */
+  protected $template_locator;
+
+  protected $tokenizer;
+
+  function __construct($tree_builder, $template_locator, $tag_dictionary)
+  {
+    $this->tokenizer = new lmbMacroTokenizer($this);
+    $this->preprocessor = new lmbMacroPreprocessor();
+
+    $this->tree_builder = $tree_builder;
+    $this->template_locator = $template_locator;
+
+    $this->tag_parsing_state = $this->_createTagParsingState($tag_dictionary);
+  }
+
+  function getCurrentLocation()
+  {
+    return $this->tokenizer->getCurrentLocation();
+  }
+
+  protected function _createTagParsingState($tag_dictionary)
+  {
+    return new lmbMacroTagParsingState($this, $this->tree_builder, $tag_dictionary);
+  }
+
+  /**
+  * 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->changeToTagParsingState();
+
+    $this->setTemplateLocator($this->template_locator);
+
+    $content = file_get_contents($source_file_path);
+
+    $this->preprocessor->process($content);
+
+    $this->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 changeToTagParsingState()
+  {
+    $this->active_parsing_state = $this->tag_parsing_state;
+  }  
+
+  function setTemplateLocator($template_locator)
+  {
+    $this->tag_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 php($text)
+  {
+    $this->active_parsing_state->php($text);
+  }
+
+  function unexpectedEOF($text)
+  {
+    $this->active_parsing_state->unexpectedEOF($text);
+  }
+
+  function invalidEntitySyntax($text)
+  {
+    $this->active_parsing_state->invalidEntitySyntax($text);
+  }
+
+  function invalidAttributeSyntax($text)
+  {
+    $this->active_parsing_state->invalidAttributeSyntax($text);
+  }
+}
+
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroPassiveTag.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroPassiveTag.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroPassiveTag.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroPassiveTag.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,28 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroPassiveTag.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroPassiveTag extends lmbMacroTag 
+{
+
+  function generate($code_writer)
+  {
+  }
+  
+  function generateNow($code_writer)
+  {
+    parent :: generate($code_writer);
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroPreprocessor.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroPreprocessor.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroPreprocessor.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroPreprocessor.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,25 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com 
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
+ */
+
+/**
+ * class lmbMacroPreprocessor
+ *
+ * @package macro
+ * @version $Id$
+ */
+
+class lmbMacroPreprocessor
+{
+  function process(&$contents)
+  {
+    $contents = str_replace('<?=', '<?php echo ', $contents);
+    $contents = preg_replace('~<\?(?!php|=)~', '<?php ', $contents);    
+    $contents = str_replace('$#', '$this->', $contents);
+  }
+}

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTag.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTag.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTag.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTag.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,228 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroTag.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroTag extends lmbMacroNode
+{
+  protected $tag;
+  protected $tag_info;
+  protected $has_closing_tag = true;
+  protected $attributes = array();
+
+  function __construct($location, $tag, $tag_info)
+  {
+    $this->tag = $tag;
+    $this->tag_info = $tag_info;
+
+    parent :: __construct($location);
+  }
+
+  function getTag()
+  {
+    return $this->tag;
+  }
+
+  function getHasClosingTag()
+  {
+    return $this->has_closing_tag;
+  }
+
+  function setHasClosingTag($flag)
+  {
+    return $this->has_closing_tag = $flag;
+  }
+
+  function getId()
+  {
+    if($this->id)
+      return $this->id;
+
+    if($id = $this->get('id'))
+      $this->id = $id;
+    else
+      $this->id = self :: generateNewId();
+
+    return $this->id;
+  }
+  
+  function getEscapedId()
+  {
+    $id = $this->getId();
+    return "'" .  $id . "'";
+  }
+
+  function get($name)
+  {
+    if(!array_key_exists(strtolower($name), $this->attributes))
+      return;
+
+    return $this->attributes[strtolower($name)]->getValue();
+  }
+
+  function getEscaped($name)
+  {
+    if(!$this->has($name))
+      return;
+      
+    $value = $this->get($name);
+    if($this->isDynamic($name))
+      return $value;
+    else
+      return "'" .  $value . "'";
+  }
+
+  /**
+  * Should be used for testing purposes only since not parses $value for any output expressions
+  */
+  function set($name, $value)
+  {
+    $this->attributes[strtolower($name)] = new lmbMacroTagAttribute($name, $value);
+  }
+
+  function add($attribute)
+  {
+    $this->attributes[strtolower($attribute->getName())] = $attribute;
+  }
+
+  function has($name)
+  {
+    return array_key_exists(strtolower($name), $this->attributes);
+  }
+  
+  function hasConstant($name)
+  {
+    return $this->has($name) && !$this->attributes[strtolower($name)]->isDynamic();
+  }
+
+  function isDynamic($name)
+  {
+    return !$this->hasConstant($name);
+  }
+  
+  function getConstantAttributes()
+  {
+    $res = array();
+    foreach($this->attributes as $key => $attr)
+    {
+      if(!$attr->isDynamic())
+        $res[$attr->getName()] = $attr->getValue();
+    }
+    return $res;
+  }
+
+  /**
+  * Return the value of a boolean attribute as a boolean.
+  * ATTRIBUTE=ANYTHING  (true)
+  * ATTRIBUTE=(false|N|NA|NO|NONE|0) (false)
+  * ATTRIBUTE (true)
+  * (attribute unspecified) (default)
+  */
+  function getBool($name, $default = false)
+  {
+    if(!isset($this->attributes[strtolower($name)]))
+      return $default;
+
+    return self :: getBooleanValue($this->attributes[strtolower($name)]->getValue());
+  }
+
+  static function getBooleanValue($value)
+  {
+    if(!$value)
+      return $value;
+
+    switch(strtoupper($value))
+    {
+      case 'FALSE':
+      case 'N':
+      case 'NO':
+      case 'NONE':
+      case 'NA':
+      case '0':
+        return false;
+      default:
+        return true;
+    }
+  }
+
+  function generate($code_writer)
+  {
+    $this->_preGenerataAttributes($code_writer);
+    
+    $this->_generateBeforeContent($code_writer);
+    
+    $this->_generateContent($code_writer);
+    
+    $this->_generateAfterContent($code_writer);
+  }
+  
+  // children can override this method if they need to generate some code around content in simple cases
+  // but it's recommended to override generateBeforeContent() and generateAfterContent() instead.
+  protected function _generateContent($code_writer)
+  {
+    parent :: generate($code_writer);
+  }
+ 
+  protected function _generateBeforeContent($code_writer)
+  {
+  }
+
+  protected function _generateAfterContent($code_writer)
+  {
+  }
+  
+  protected function _preGenerataAttributes($code_writer)
+  {
+    foreach($this->attributes as $attribute)
+      $attribute->preGenerate($code_writer);
+  }
+
+  function remove($attrib)
+  {
+    unset($this->attributes[strtolower($attrib)]);
+  }
+
+  function raise($error, $vars = array())
+  {
+    $vars['tag'] = $this->tag;
+    parent :: raise($error, $vars);
+  }
+
+  function raiseRequiredAttribute($attribute_name)
+  {
+    $this->raise('Missing required attribute', array('attribute' => $attribute_name));
+  }
+
+  function preParse($compiler)
+  {
+    foreach($this->tag_info->getRequiredAttributes() as $attr_name)
+    {
+      if(!$this->has($attr_name))
+        $this->raiseRequiredAttribute($attr_name);
+    }
+
+    if($this->tag_info->isRestrictSelfNesting() && $parent = $this->findParentByClass(get_class($this)))
+      $this->raise('Tag cannot be nested within the same tag',
+                                array('same_tag_file' => $parent->getTemplateFile(),
+                                      'same_tag_line' => $parent->getTemplateLine()));
+
+    if(($parent_class = $this->tag_info->getParentClass()) &&
+       !$parent = $this->findParentByClass($parent_class))
+    {
+      $this->raise('Tag must be enclosed by a proper parent tag',
+                                array('required_parent_tag_class' => $parent_class));
+
+    }
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTagAttribute.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTagAttribute.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTagAttribute.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTagAttribute.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,76 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * Used to store complex expressions like "{$var1}_my_{$var2}" found inside tag attributes
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroTagAttribute implements lmbMacroExpressionInterface
+{
+  protected $name;
+  protected $expressions = array();
+  protected $raw_string = '';
+
+  function __construct($name, $value = null)
+  {
+    $this->name = $name;
+
+    if($value)
+      $this->raw_string .= $value;
+  }
+
+  function getName()
+  {
+    return $this->name;
+  }
+
+  function addTextFragment($text)
+  {
+    $this->raw_string .= $text;
+  }
+
+  function addExpressionFragment($expression)
+  {
+     $this->raw_string .= '%s';
+    $this->expressions[] = $expression;
+  }
+
+  function preGenerate($code_writer)
+  {
+    foreach($this->expressions as $fragment)
+      $fragment->preGenerate($code_writer);
+  }
+
+  function isDynamic()
+  {
+    return (bool)sizeof($this->expressions) || (strpos($this->raw_string, '$') === 0);
+  }
+
+  function getValue()
+  {
+    // simple case
+    if(!sizeof($this->expressions))
+       return $this->raw_string;
+
+    $res = 'sprintf(\'' . $this->raw_string. '\',';
+
+    $separator = '';
+    foreach($this->expressions as $expressions)
+    {
+      $res .= $separator . $expressions->getValue();
+      $separator = ',';
+    }
+
+    $res .= ')';
+    return $res;
+  }
+}
+
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTagAttributeBlockAnalizerListener.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTagAttributeBlockAnalizerListener.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTagAttributeBlockAnalizerListener.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTagAttributeBlockAnalizerListener.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,39 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroTagAttributeBlockAnalizerListener.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroTagAttributeBlockAnalizerListener implements lmbMacroBlockAnalizerListener
+{
+  protected $attribute;
+  protected $tag_node;
+  protected $filter_dictionary;
+
+  function __construct($attribute, $tag_node)
+  {
+    $this->attribute = $attribute;
+    $this->tag_node = $tag_node;
+  }
+
+  function addLiteralFragment($text)
+  {
+    $this->attribute->addTextFragment($text);
+  }
+
+  function addExpressionFragment($text)
+  {
+    $expression = new lmbMacroExpression($text);
+    $this->attribute->addExpressionFragment($expression);
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTagDictionary.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTagDictionary.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTagDictionary.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTagDictionary.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,121 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroTagDictionary.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroTagDictionary
+{
+  protected $info = array();
+  static protected $instance;
+
+  static function instance()
+  {
+    if(self :: $instance)
+      return self :: $instance;
+
+    self :: $instance = new lmbMacroTagDictionary();
+    return self :: $instance;
+  }
+
+  function load(lmbMacroConfig $config)
+  {
+    if(!$config->isForceScan() && $this->_loadCache($config))
+      return;
+
+    $config_scan_dirs = $config->getTagsScanDirectories();
+    $real_scan_dirs = array();
+    
+    foreach($config_scan_dirs as $dir)
+    {
+      foreach($this->_getThisAndImmediateDirectories($dir) as $item)
+        $real_scan_dirs[] = $item;
+    }
+    
+    foreach($real_scan_dirs as $scan_dir)
+    {
+      foreach(lmb_glob($scan_dir . '/*.tag.php') as $file)
+        $this->registerFromFile($file);
+    }
+
+    $this->_saveCache($config);
+  }
+
+  function _getThisAndImmediateDirectories($dir)
+  {
+    $dirs = array();
+    foreach(lmb_glob("$dir/*") as $item) {
+      if($item{0} != '.' && is_dir($item))
+        $dirs[] = $item;
+    }
+
+    $dirs[] = $dir;
+
+    return $dirs;
+  }  
+
+  protected function _loadCache(lmbMacroConfig $config)
+  {
+    $cache_file = $config->getCacheDir() . '/tags.cache';
+    if(!file_exists($cache_file))
+      return false;
+
+    $info = @unserialize(file_get_contents($cache_file));
+    if($info === false || !is_array($info))
+      return false;
+
+    $this->info = $info;
+
+    return true;
+  }
+
+  protected function _saveCache(lmbMacroConfig $config)
+  {
+    $cache_file = $config->getCacheDir() . '/tags.cache';
+    lmbFs :: safeWrite($cache_file, serialize($this->info));
+  }
+
+  function register($tag_info)
+  {
+    $names = array(strtolower($tag_info->getTag()));
+
+    $aliases = $tag_info->getAliases();
+    if(count($aliases))
+    {
+      $aliases = array_map('strtolower', $aliases);
+      $names = array_merge($names, $aliases);
+    }
+    
+    foreach($names as $tag_name)
+    {
+      if(isset($this->info[$tag_name]))
+        return;
+  
+      $this->info[$tag_name] = $tag_info;
+    }
+  }
+
+  function registerFromFile($file)
+  {
+    $infos = lmbMacroAnnotationParser :: extractFromFile($file, 'lmbMacroTagInfo');
+    foreach($infos as $info)
+      $this->register($info, $file);
+  }
+
+  function findTagInfo($tag)
+  {
+    $tag = strtolower($tag);
+    if(isset($this->info[$tag]))
+      return $this->info[$tag];
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTagInfo.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTagInfo.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTagInfo.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTagInfo.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,156 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroTagInfo.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroTagInfo
+{
+  protected $tag;
+  protected $class;
+  protected $file;
+  protected $aliases = array();
+  protected $req_attributes = array();
+  protected $parent_class;
+  protected $restrict_self_nesting = false;
+  protected $require_endtag = true;
+
+  function __construct($tag, $class, $require_endtag = true)
+  {
+    $this->tag = $tag;
+    $this->class = $class;
+    $this->require_endtag = $require_endtag;
+  }
+
+  static function createByAnnotations($file, $class, $annotations)
+  {
+    if(!isset($annotations['tag']))
+      throw new lmbMacroException("@tag annotation is missing for class '$class'");
+
+    $tag = $annotations['tag'];
+    $info = new lmbMacroTagInfo($tag, $class);
+
+    $info->setFile($file);
+
+    if(isset($annotations['forbid_end_tag']))
+      $info->setForbidEndtag(true);
+
+    if(isset($annotations['restrict_self_nesting']))
+      $info->setRestrictSelfNesting(true);
+
+    if(isset($annotations['parent_tag_class']))
+      $info->setParentClass(trim($annotations['parent_tag_class']));
+    
+    if(isset($annotations['req_attributes']))
+    {
+      $req_attributes = explode(',' , $annotations['req_attributes']);
+      $req_attributes = array_map('trim', $req_attributes);
+      $info->setRequiredAttributes($req_attributes);
+    }
+    
+    if(isset($annotations['aliases']))
+    {
+      $filter_aliases = explode(',' , $annotations['aliases']);
+      $filter_aliases = array_map('trim', $filter_aliases);
+      $info->setAliases($filter_aliases);
+    }
+
+    return $info;
+  }
+
+  function getTag()
+  {
+    return $this->tag;
+  }
+
+  function getClass()
+  {
+    return $this->class;
+  }
+
+  function setFile($file)
+  {
+    $this->file = $file;
+  }
+
+  function getFile()
+  {
+    return $this->file;
+  }
+
+  function setForbidEndtag($flag = true)
+  {
+    $this->require_endtag = !$flag;
+  }
+
+  function isEndtagForbidden()
+  {
+    return !$this->require_endtag;
+  }
+
+  function setRequiredAttributes($attributes)
+  {
+    $this->req_attributes = $attributes;
+  }
+
+  function getRequiredAttributes()
+  {
+    return $this->req_attributes;
+  }
+
+  function setParentClass($parent_tag_class)
+  {
+    $this->parent_class = $parent_tag_class;
+  }
+
+  function getParentClass()
+  {
+    return $this->parent_class;
+  }
+
+  function setAliases($aliases)
+  {
+    $this->aliases = $aliases;
+  }
+  
+  function getAliases()
+  {
+    return $this->aliases;
+  }
+  
+  function setRestrictSelfNesting($flag = true)
+  {
+    $this->restrict_self_nesting = $flag;
+  }
+
+  function isRestrictSelfNesting()
+  {
+    return $this->restrict_self_nesting;
+  }
+
+  function setForbidParsing($flag = true)
+  {
+    $this->forbid_parsing = $flag;
+  }
+
+  function isParsingForbidden()
+  {
+    return $this->forbid_parsing;
+  }
+
+  function load()
+  {
+    if(!class_exists($this->class) && isset($this->file))
+      require_once($this->file);
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTagParsingState.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTagParsingState.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTagParsingState.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTagParsingState.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,156 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * class lmbMacroTagParsingState.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroTagParsingState extends lmbMacroBaseParsingState implements lmbMacroTokenizerListener
+{
+  protected $tag_dictionary;
+
+  function __construct(lmbMacroParser $parser, lmbMacroTreeBuilder $tree_builder, lmbMacroTagDictionary $tag_dictionary)
+  {
+    parent :: __construct($parser, $tree_builder);
+    $this->tag_dictionary = $tag_dictionary;
+  }
+
+  function startElement($tag, $attrs)
+  {
+    $location = $this->parser->getCurrentLocation();
+
+    $lower_attributes = $this->normalizeAttributes($attrs, $location);
+
+    if(!$tag_info = $this->tag_dictionary->findTagInfo($tag))
+      throw new lmbMacroException("Tag '$tag' not found in dictionary");
+
+    if($tag_info->isEndTagForbidden())
+    {
+      $tag_node = $this->buildTagNode($tag_info, $tag, $attrs);
+      $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)
+  {
+    if(!$tag_info = $this->tag_dictionary->findTagInfo($tag))
+      throw new lmbMacroException("Tag '$tag' not found in dictionary");
+    
+    $location = $this->parser->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->parser->getCurrentLocation();
+    $lower_attributes = $this->normalizeAttributes($attrs, $location);
+
+    if(!$tag_info = $this->tag_dictionary->findTagInfo($tag))
+      throw new lmbMacroException("Tag '$tag' not found in dictionary");
+    $tag_info->load();
+
+    $tag_node = $this->buildTagNode($tag_info, $tag, $attrs);
+    $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)
+  {
+    $tag_node = $this->_createTagNode($tag_info, $tag);
+    $this->_addAttributesToTagNode($tag_node, $attrs);
+    return $tag_node;
+  }
+
+  protected function _addAttributesToTagNode($tag_node, $attrs)
+  {
+    foreach($attrs as $name => $value)
+    {
+      if($value === null)
+      {
+        $location = $this->parser->getCurrentLocation();
+        throw new lmbMacroException('Attribute should have a value',
+                              array('file' => $location->getFile(),
+                                    'line' => $location->getLine(),
+                                    'tag' => $tag_node->getTag(),
+                                    'attribute' => $name));
+      }
+
+      $attribute = new lmbMacroTagAttribute($name);
+      $listener = new lmbMacroTagAttributeBlockAnalizerListener($attribute, $tag_node);
+
+      $analizer = new lmbMacroBlockAnalizer();
+      $analizer->parse($value, $listener);
+
+      $tag_node->add($attribute);
+    }
+  }
+
+  protected function _createTagNode($tag_info, $tag)
+  {
+    $class = $tag_info->getClass();
+    require_once($tag_info->getFile());
+    $tag_node = new $class($this->parser->getCurrentLocation(), $tag, $tag_info);
+    return $tag_node;
+  }
+
+  function normalizeAttributes($attrs)
+  {
+    return array_change_key_case($attrs, CASE_LOWER);
+  }
+
+  function characters($text)
+  {
+    $this->tree_builder->addContent($text, $this->parser->getCurrentLocation());
+  }
+
+  function php($text)
+  {
+    $this->tree_builder->addTextNode($text);
+  }
+
+  function unexpectedEOF($text)
+  {
+    $this->tree_builder->addTextNode($text);
+  }
+
+  function invalidEntitySyntax($text)
+  {
+    $this->tree_builder->addTextNode($text);
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTemplateExecutor.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTemplateExecutor.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTemplateExecutor.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTemplateExecutor.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,81 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com 
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
+ */
+
+/**
+ * class lmbMacroTemplateExecutor.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroTemplateExecutor
+{
+  //we add prefixes in order to avoid possible conflict
+  //since variables are added directly
+  protected $__config;
+  protected $__context;
+
+  function __construct(lmbMacroConfig $config = null, $vars = array())
+  {
+    $this->__config = $config ? $config : new lmbMacroConfig();
+    $this->setVars($vars);
+  }
+
+  //overridden in children
+  protected function _init(){}
+
+  function setVars($vars)
+  {
+    foreach($vars as $name => $value)
+      $this->$name = $value;
+  }
+
+  function set($name, $value)
+  {
+    $this->$name = $value;
+  }
+
+  function setContext(lmbMacroTemplateExecutor $context)
+  {
+    $this->__context = $context;
+  }
+
+  function __get($name)
+  {
+    //we can have parent variable context which should be consulted for all missing variables
+    //actually, it's quite a dirty hack for a deeper problem which should be addressed later
+    if($this->__context)
+      return $this->__context->$name;
+
+    //we definitely want to supress warnings, make it some sort of a NullObject?
+    return '';
+  }
+
+  function render($args = array())
+  {
+    extract($args);
+  }
+
+  function includeTemplate($file, $vars = array())
+  {
+    $template = new lmbMacroTemplate($file, $this->__config);
+    $template->setVars(get_object_vars($this));//global template vars
+    echo $template->render($vars);//local template vars
+  }
+
+  function wrapTemplate($file, $slots_handlers)
+  {
+    $template = new lmbMacroTemplate($file, $this->__config);
+    $template->setVars(get_object_vars($this));//global template vars
+    foreach($slots_handlers as $name => $handler)
+      $template->set('__slot_handler_' . $name, $handler);
+    $template->setChildExecutor($this);//from now we consider the wrapper to be a master variable context
+    echo $template->render();
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTextNode.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTextNode.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTextNode.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTextNode.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,38 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com 
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
+ */
+
+/**
+ * class lmbMacroTextNode.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroTextNode extends lmbMacroNode
+{
+  protected $contents;
+
+  function __construct($location, $text)
+  {
+    parent :: __construct($location);
+    $this->contents = $text;
+  }
+
+  function generate($code_writer)
+  {
+    $code_writer->writeHtml($this->contents);
+    parent :: generate($code_writer);
+  }
+
+  function getText()
+  {
+    return $this->contents;
+  }
+}
+
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTokenizer.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTokenizer.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTokenizer.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTokenizer.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,312 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com 
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
+ */
+
+/**
+ * class lmbMacroTokenizer.
+ *
+ * @package macro
+ * @version $Id$
+ */
+class lmbMacroTokenizer
+{
+  protected $publicId;
+  protected $observer;
+  protected $rawtext;
+  protected $position;
+  protected $length;
+
+  function __construct($observer)
+  {
+    $this->observer = $observer;
+  }
+
+  function getLineNumber()
+  {
+    return 1 + substr_count(substr($this->rawtext, 0, $this->position), "\n");
+  }
+
+  function getCurrentLocation()
+  {
+    return new lmbMacroSourceLocation($this->getPublicId(), $this->getLineNumber());
+  }
+
+  function getPublicId()
+  {
+    return $this->publicId;
+  }
+
+  /**
+  * Moves the position forward past any whitespace characters
+  */
+  function ignoreWhitespace()
+  {
+    while($this->position < $this->length &&
+        strpos(" \n\r\t", $this->rawtext{$this->position}) !== false)
+      $this->position++;
+  }
+
+  protected function _parseUntilTagStart($start)
+  {
+    do
+    {
+      $php_start = strpos($this->rawtext, '<?php', $start);
+      $tag_start = strpos($this->rawtext, '{{', $start);
+
+      //no php found
+      if($php_start === false)
+      {
+        //tag candidate found
+        if($tag_start !== false)
+        {
+          //add preceding characters
+          if($tag_start > $start)
+            $this->observer->characters(substr($this->rawtext, $start, $tag_start - $start));
+          return $tag_start;
+        }
+        //no tags at all
+        else
+        {
+          //add preceding characters
+          if($start != $this->length)
+            $this->observer->characters(substr($this->rawtext, $start, $this->length - $start));
+          return null;
+        }
+      }
+      //php found
+      else
+      {
+        $php_end = strpos($this->rawtext, '?>', $start);
+        //php end found
+        if($php_end !== false)
+        {
+          //at the same time tag found and it's not inside php
+          if($tag_start !== false && $tag_start < $php_start)
+          {
+            //add preceding characters
+            if($start < $tag_start)
+              $this->observer->characters(substr($this->rawtext, $start, $tag_start - $start));
+            return $tag_start;
+          }
+          //extract php block
+          else
+          {
+            //add preceding characters
+            if($start < $php_start)
+              $this->observer->characters(substr($this->rawtext, $start, $php_start - $start));
+            $this->observer->php(substr($this->rawtext, $php_start, $php_end - $php_start + 2));
+            $start = $php_end + 2;
+          }
+        }
+        //no php end found, everything is php then
+        else
+        {
+          $this->observer->php(substr($this->rawtext, $php_start));
+          return null;
+        }
+                      
+      }
+    }while($start < $this->length);
+    return null;
+  }
+
+  /**
+  * Begins the parsing operation, setting up any decorators, depending on
+  * parse options invoking _parse() to execute parsing
+  */
+  function parse($data, $publicId = null)
+  {
+    $this->rawtext = $data;
+    $this->length = strlen($data);
+    $this->position = 0;
+    $this->publicId = $publicId;
+
+    do
+    {
+      $start = $this->position;
+
+      $this->position = $this->_parseUntilTagStart($start);
+      if($this->position === null)
+        return;
+
+      $this->position += 2;   // ignore '{{' string
+      if($this->position >= $this->length)
+      {
+        $this->observer->unexpectedEOF('{{');
+        return;
+      }
+
+      $element_pos = $this->position;
+      $this->position += 1;
+
+      switch($this->rawtext{$element_pos})
+      {
+        case '/':
+          $start = $this->position;
+          while($this->position < $this->length &&
+                !($this->rawtext{$this->position} == '}' &&
+                  $this->rawtext{$this->position+1} == '}'))
+            $this->position++;
+
+          if($this->position >= $this->length)
+          {
+            $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+            return;
+          }
+
+          $tag = substr($this->rawtext, $start, $this->position - $start);
+
+          $this->observer->endElement($tag);
+          $this->position += 2;   // ignore '}}' string
+          break;
+
+      default:
+          while($this->position < $this->length && strpos("}/ \n\r\t", $this->rawtext{$this->position}) === false)
+            $this->position++;
+
+          if($this->position >= $this->length)
+          {
+            $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+            return;
+          }
+
+          $tag = substr($this->rawtext, $element_pos, $this->position - $element_pos);
+          $attributes = array();
+
+          $this->ignoreWhitespace();
+
+          //tag attributes
+          while($this->position < $this->length &&
+                $this->rawtext{$this->position} != '}' &&
+                $this->rawtext{$this->position} != '/')
+          {
+            $start = $this->position;
+            while($this->position < $this->length && strpos("}= \n\r\t", $this->rawtext{$this->position}) === false)
+              $this->position++;
+
+            if($this->position >= $this->length)
+            {
+              $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+              return;
+            }
+
+            $attribute_name = substr($this->rawtext, $start, $this->position - $start);
+            $attribute_value = null;
+
+            $this->ignoreWhitespace();
+            if($this->position >= $this->length)
+            {
+              $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+              return;
+            }
+
+            if($this->rawtext{$this->position} == '=')
+            {
+              $attribute_value = "";
+
+              $this->position++;
+              $this->ignoreWhitespace();
+              if($this->position >= $this->length)
+              {
+                $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                return;
+              }
+
+              $quote = $this->rawtext{$this->position};
+              if($quote == '"' || $quote == "'")
+              {
+                $start = $this->position + 1;
+                $this->position = strpos($this->rawtext, $quote, $start);
+                if($this->position === false)
+                {
+                  $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                  return;
+                }
+
+                $attribute_value = substr($this->rawtext, $start, $this->position - $start);
+
+                $this->position++;
+                if($this->position >= $this->length)
+                {
+                  $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                  return;
+                }
+
+                if(strpos("/} \n\r\t", $this->rawtext{$this->position}) === false)
+                  $this->observer->invalidAttributeSyntax(substr($this->rawtext, $this->position));
+              }
+              else
+              {
+                $start = $this->position;
+                while($this->position < $this->length && strpos("} \n\r\t", $this->rawtext{$this->position}) === false)
+                  $this->position++;
+
+                if($this->position >= $this->length)
+                {
+                  $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                  return;
+                }
+                $attribute_value = substr($this->rawtext, $start, $this->position - $start);
+              }
+            }
+
+            $attributes[$attribute_name] = $attribute_value;
+
+            $this->ignoreWhitespace();
+          }
+
+          if($this->position >= $this->length)
+          {
+            $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+            return;
+          }
+
+          //self closing tag check
+          if($this->rawtext{$this->position} == '/' && $this->rawtext{$this->position + 1} == '}')
+          {
+            $this->position += 2;
+            if($this->position >= $this->length)
+            {
+              $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+              return;
+            }
+
+            if($this->rawtext{$this->position} != '}')
+            {
+              $start = $this->position;
+              while($this->position < $this->length && $this->rawtext{$this->position} != '}')
+                $this->position++;
+
+              if($this->position >= $this->length)
+              {
+                $this->observer->invalidEntitySyntax(substr($this->rawtext, $element_pos - 2));
+                break;
+              }
+
+              $this->observer->invalidEntitySyntax(substr($this->rawtext, $element_pos - 2,
+                                                          $this->position - $element_pos + 2));
+              $this->position += 1;
+              break;
+            }
+            $this->observer->emptyElement($tag, $attributes);
+          }
+          else
+          {
+            $this->observer->startElement($tag, $attributes);
+            //skipping }
+            $this->position += 1;
+          }
+
+          $this->position += 1;
+          break;
+        }
+    }
+    while($this->position < $this->length);
+  }
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTokenizerListener.interface.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTokenizerListener.interface.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTokenizerListener.interface.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTokenizerListener.interface.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,28 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com 
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
+ */
+
+/**
+ * interface lmbMacroTokenizerListener.
+ *
+ * @package macro
+ * @version $Id$
+ */
+interface lmbMacroTokenizerListener
+{
+  function startElement($tag_name, $attrs);
+  function endElement($tag_name);
+  function emptyElement($tag_name, $attrs);
+  function characters($data);
+  function php($data);
+  function unexpectedEOF($data);
+  function invalidEntitySyntax($data);
+  function invalidAttributeSyntax($data);
+  function setTemplateLocator($locator);
+}
+

Copied: 3.x/trunk/limb/macro/src/compiler/lmbMacroTreeBuilder.class.php (from rev 6555, 3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php)
===================================================================
--- 3.x/trunk/limb/macro/src/compiler/lmbMacroTreeBuilder.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/compiler/lmbMacroTreeBuilder.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -0,0 +1,188 @@
+<?php
+/*
+ * Limb PHP Framework
+ *
+ * @link http://limb-project.com
+ * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ */
+
+/**
+ * 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.
+ *
+ * These methods do not actually add nodes to the tree, as tags and nodes
+ * do not necessarily match up.
+ *
+ * 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 addTextNode().
+ * @package macro
+ * @version $Id$
+ */
+
+class lmbMacroTreeBuilder
+{
+  protected $compiler;
+  protected $node;
+
+  protected $filter_dictionary;
+  /**
+  * Stack of tags pushed onto the tree builder
+  */
+  var $expected_tags = array();
+
+  function __construct($compiler)
+  {
+    $this->compiler = $compiler;
+  }
+
+  /**
+  * Returns the current node
+  */
+  function getCursor()
+  {
+    return $this->node;
+  }
+
+  /**
+  * Sets the cursor (the current working node) of the tree builder
+  */
+  function setCursor($node)
+  {
+    $this->node = $node;
+  }
+
+  /**
+  * 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 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 addContent($text, $location)
+  {
+    $listener = new lmbMacroContentBlockAnalizerListener($this, $location, $this->filter_dictionary);
+    $analizer = new lmbMacroBlockAnalizer();
+    $analizer->parse($text, $listener);
+  }
+
+  /**
+  * 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());
+  }
+
+  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))
+    {
+      throw new lmbMacroException('Lonely closing tag', array('tag' => $tag,
+                                                          'file' => $location->getFile(),
+                                                          'line' => $location->getLine()));
+    }
+
+    // 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]))
+    {
+      $this->node = $expected_tag_item[0];
+      return $this->popExpectedTag($tag, $location);
+    }
+
+    $expected_tag = $expected_tag_item[0];
+    $expected_location = $expected_tag_item[1];
+
+    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()));
+  }
+
+  /**
+  * Return the size of the expected tags stack
+  */
+  function getExpectedTagCount()
+  {
+    return count($this->expected_tags);
+  }
+
+  /**
+  * Returns the current expected tag
+  */
+  function getExpectedTag()
+  {
+    // 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->expected_tags);
+    return $item ? $item[0] : false;
+  }
+
+  function getExpectedTagLocation()
+  {
+    $item = end($this->expected_tags);
+    return $item[1];
+  }
+
+  function getFilterDictionary()
+  {
+    return $this->compiler->getFilterDictionary();
+  }
+}
+

Modified: 3.x/trunk/limb/macro/src/filters/date.filter.php
===================================================================
--- 3.x/trunk/limb/macro/src/filters/date.filter.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/filters/date.filter.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -6,7 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */ 
-lmb_require('limb/macro/src/lmbMacroFilter.class.php');
  
 /**
  * class lmbMacroDateFilter.

Modified: 3.x/trunk/limb/macro/src/filters/lmbMacroPhpFunctionBasedFilter.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/filters/lmbMacroPhpFunctionBasedFilter.class.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/filters/lmbMacroPhpFunctionBasedFilter.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -6,7 +6,6 @@
  * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
  * @license    LGPL http://www.gnu.org/copyleft/lesser.html
  */ 
-lmb_require('limb/macro/src/lmbMacroFilter.class.php');
 
 /**
  * class lmbMacroStrToUpperFilter.

Deleted: 3.x/trunk/limb/macro/src/lmbMacroAnnotationParser.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroAnnotationParser.class.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/lmbMacroAnnotationParser.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -1,63 +0,0 @@
-<?php
-/*
- * Limb PHP Framework
- *
- * @link http://limb-project.com
- * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
- * @license    LGPL http://www.gnu.org/copyleft/lesser.html
- */
-lmb_require('limb/core/src/lmbPHPTokenizer.class.php');
-lmb_require('limb/macro/src/lmbMacroException.class.php');
-
-/**
- * class lmbMacroAnnotationParser.
- *
- * @package macro
- * @version $Id$
- */
-class lmbMacroAnnotationParser
-{
-  static function extractFromFile($file, $listener)
-  {
-    $infos = array();
-    $tokenizer = new lmbPHPTokenizer(file_get_contents($file));
-    while($token = $tokenizer->next())
-    {
-      if(!is_array($token))
-        continue;
-
-      //found class token
-      if($token[0] == T_CLASS)
-      {
-        //fetching class name
-        $token = $tokenizer->next();
-        $class = $token[1];
-
-        //now checking prev token for /**/
-        if(!is_array($prev_token) || $prev_token[0] != T_DOC_COMMENT)
-          throw new lmbMacroException('Invalid token, doc comment is expected');
-
-        //now parsing annotations
-        $annotations = self :: _extractAnnotations($prev_token[1]);
-        if(!$annotations)
-          throw new lmbMacroException("No annotations found in doc comment '{$prev_token[1]}' in file $file");
-
-        $infos[] = call_user_func_array (array($listener, 'createByAnnotations'), array($file, $class, $annotations));
-        //$infos[] = $listener->createByAnnotations($file, $class, $annotations);
-      }
-      $prev_token = $token;
-    }
-    return $infos;
-  }
-
-  static protected function _extractAnnotations($content)
-  {
-    if(!preg_match_all('~@(\S+)([^\n]+)?\n~', $content, $matches))
-      return false;
-    $annotations = array();
-    for($i=0;$i<count($matches[0]);$i++)
-      $annotations[trim($matches[1][$i])] = trim($matches[2][$i]);
-    return $annotations;
-  }
-}
-

Deleted: 3.x/trunk/limb/macro/src/lmbMacroAnnotationParserListener.interface.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroAnnotationParserListener.interface.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/lmbMacroAnnotationParserListener.interface.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -1,20 +0,0 @@
-<?php
-/*
- * Limb PHP Framework
- *
- * @link http://limb-project.com
- * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
- * @license    LGPL http://www.gnu.org/copyleft/lesser.html
- */
-
-/**
- * interface lmbMacroAnnotationParserListener.
- *
- * @package macro
- * @version $Id$
- */
-interface lmbMacroAnnotationParserListener
-{
-  function createByAnnotations($class, $file, $annotations);
-}
-

Deleted: 3.x/trunk/limb/macro/src/lmbMacroBaseParsingState.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroBaseParsingState.class.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/lmbMacroBaseParsingState.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -1,61 +0,0 @@
-<?php
-/*
- * Limb PHP Framework
- *
- * @link http://limb-project.com 
- * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
- * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
- */
-
-/**
- * @package macro
- * @version $Id$
- */
-class lmbMacroBaseParsingState
-{
-  /**
-  * @var lmbMacroSourceFileParser
-  */
-  protected $parser;
-
-  /**
-  * @var lmbMacroTreeBuilder
-  */
-  protected $tree_builder;
-
-  protected $locator;
-
-  function __construct($parser, $tree_builder)
-  {
-    $this->parser = $parser;
-    $this->tree_builder = $tree_builder;
-  }
-
-  function setTemplateLocator($locator)
-  {
-    $this->locator = $locator;
-  }
-
-  function invalidAttributeSyntax($data)
-  {
-    throw new lmbException('Invalid attribute syntax starting from: ' . $data);
-  }
-
-  function getAttributeString($attrs)
-  {
-    $attrib_str = '';
-    foreach($attrs as $key => $value)
-    {
-      $attrib_str .= ' ' . $key;
-      if(!is_null($value))
-      {
-        if(strpos($value, '"') === FALSE)
-          $attrib_str .= '="' . $value . '"';
-        else
-          $attrib_str .= '=\'' . $value . '\'';
-      }
-    }
-    return $attrib_str;
-  }
-}
-

Deleted: 3.x/trunk/limb/macro/src/lmbMacroBlockAnalizer.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroBlockAnalizer.class.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/lmbMacroBlockAnalizer.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -1,52 +0,0 @@
-<?php
-/*
- * Limb PHP Framework
- *
- * @link http://limb-project.com
- * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
- * @license    LGPL http://www.gnu.org/copyleft/lesser.html
- */
-
-/**
- * class lmbMacroContentAnalizer.
- *
- * @package macro
- * @version $Id$
- */
-class lmbMacroBlockAnalizer
-{
-  const BEFORE_CONTENT = 1;
-  const EXPRESSION = 2;
-  const AFTER_CONTENT = 5;
-
-  protected function _getRegexp()
-  {
-    return '/^((?s).*?)'. preg_quote('{$', '/') . '((([^"\']+["\']?[^"\']?["\'])+)?[^}]+)' . preg_quote('}', '/') . '((?s).*)$/';
-  }
-
-  function parse($text, $observer)
-  {
-    // if there is no expression (common case), shortcut this process
-    if (strpos($text, '{$') === FALSE)
-    {
-      $observer->addLiteralFragment($text);
-      return;
-    }
-
-    $regexp = $this->_getRegexp();
-
-    while (preg_match($regexp, $text, $match))
-    {
-      if (strlen($match[self :: BEFORE_CONTENT]) > 0)
-        $observer->addLiteralFragment($match[self :: BEFORE_CONTENT]);
-
-      $observer->addExpressionFragment('$' . $match[self :: EXPRESSION]);
-
-      $text = $match[self :: AFTER_CONTENT];
-    }
-
-    if (strlen($text) > 0)
-      $observer->addLiteralFragment($text);
-  }
-}
-

Deleted: 3.x/trunk/limb/macro/src/lmbMacroBlockAnalizerListener.interface.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroBlockAnalizerListener.interface.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/lmbMacroBlockAnalizerListener.interface.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -1,21 +0,0 @@
-<?php
-/*
- * Limb PHP Framework
- *
- * @link http://limb-project.com
- * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
- * @license    LGPL http://www.gnu.org/copyleft/lesser.html
- */
-
-/**
- * interface lmbMacroBlockAnalizerListener.
- *
- * @package macro
- * @version $Id$
- */
-interface lmbMacroBlockAnalizerListener
-{
-  function addLiteralFragment($text);
-  function addExpressionFragment($text);
-}
-

Deleted: 3.x/trunk/limb/macro/src/lmbMacroCodeWriter.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroCodeWriter.class.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/lmbMacroCodeWriter.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -1,242 +0,0 @@
-<?php
-/*
- * Limb PHP Framework
- *
- * @link http://limb-project.com 
- * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
- * @license    LGPL http://www.gnu.org/copyleft/lesser.html 
- */
-
-/**
- * class lmbMacroCodeWriter.
- *
- * @package macro
- * @version $Id$
- */
-class lmbMacroCodeWriter
-{
-  const MODE_PHP = 1;
-  const MODE_HTML = 2;
-
-  protected $class;
-
-  protected $parent;
-
-  protected $current_mode = self :: MODE_PHP;
-
-  protected $current_method;
-
-  protected $code = '';
-
-  protected $methods = array();
-
-  protected $init_code = '';
-
-  protected $methods_stack = array();
-
-  protected $include_list = array();
-
-  protected $temp_var_name = 1;
-
-  function __construct($class, $render_func = 'render')
-  {
-    $this->class = $class;
-    $this->render_func = $render_func;
-    $this->parent = 'lmbMacroTemplateExecutor';
-    $this->registerInclude('limb/macro/src/lmbMacroTemplateExecutor.class.php');
-
-    $this->beginMethod($render_func, array('$args = array()'));
-    $this->writePHP('if($args) extract($args);');
-    $this->writePHP('$this->_init();');
-  }
-
-  function getClass()
-  {
-    return $this->class;
-  }
-
-  function getRenderMethod()
-  {
-    return 'render';
-  }
-
-  protected function switchToPHP()
-  {
-    if($this->current_mode == self :: MODE_HTML)
-    {
-      $this->current_mode = self :: MODE_PHP;
-      $this->_append('<?php ');
-    }
-  }
-
-  protected function switchToHTML($context = null)
-  {
-    if($this->current_mode == self :: MODE_PHP)
-    {
-      $this->current_mode = self :: MODE_HTML;
-      if($context === "\n")
-        $this->_append(" ?>\n");
-      else
-        $this->_append(' ?>');
-    }
-  }
-
-  function writePHP($text)
-  {
-    $this->switchToPHP();
-    $this->_append($text);
-  }
-
-  function writePHPLiteral($text, $escape_text = true)
-  {
-    $this->switchToPHP();
-
-    if($escape_text)
-      $this->_append("'" . $this->escapeLiteral($text) . "'");
-    else
-      $this->_append("'" . $text . "'");
-  }
-
-  function escapeLiteral($text)
-  {
-    $text = str_replace('\'', "\\'", $text);
-    if(substr($text, -1) == '\\')
-      $text .= '\\';
-    return $text;
-  }
-
-  function writeHTML($text)
-  {
-    $this->switchToHTML(substr($text,0,1));
-    $this->_append($text);
-  }
-  
-  function writeRaw($text)
-  {
-    $this->_append($text);
-  }
-
-  function renderCode()
-  {
-    $this->endMethod();
-
-    $code = "<?php\n" .
-           //protection from self inclusion
-           "if(!class_exists('{$this->class}', false)){\n" .
-           $this->_renderIncludeList() . 
-           "class {$this->class} " . ($this->parent ? "extends {$this->parent} " : '') . "{\n" .
-           (!$this->init_code ? "" :
-           "\nfunction _init() {" .
-           "\n$this->init_code\n" .
-           "}\n" 
-           ) .
-           $this->_renderMethods() . 
-           "\n}" . 
-           "\n}";
-    return $code;
-  }
-
-  function getCode()
-  {
-    return $this->code;
-  }
-
-  function getMode()
-  {
-    return $this->current_mode;
-  }
-
-  function registerInclude($include_file)
-  {
-    if(!in_array($include_file, $this->include_list))
-      $this->include_list[] = $include_file;    
-  }
-
-  function getIncludeList()
-  {
-    return $this->include_list;
-  }
-
-  function beginFunction($name, $param_list = array())
-  {
-    $this->writePHP('function ' . $name . '(' . implode(',', $param_list) .") {\n");
-    return $name;
-  }
-
-  function endFunction()
-  {
-    $this->writePHP("\n}\n");
-  }
-
-  function beginMethod($name, $param_list = array())
-  {
-    $this->methods_stack[] = array($this->current_method, $this->current_mode);
-    $this->current_method = $name;
-
-    //we don't need to switch to PHP, since methods can be declared inside PHP only
-    $this->writeRaw('function ' . $name . '(' . implode(',', $param_list) .") {\n");
-    $this->current_mode = self :: MODE_PHP;
-    return $name;
-  }
-
-  function endMethod()
-  {
-    $this->writePHP("\n}\n");
-    list($this->current_method, $this->current_mode) = array_pop($this->methods_stack);
-  }
-
-  function writeToInit($code)
-  {
-    $this->init_code .= $code;
-  }
-
-  /**
-  * Utility method, which generates a unique variable name
-  */
-  function generateTempName()
-  {
-    $var = $this->temp_var_name++;
-    if($var > 675)
-      return chr(65 + ($var/26)/26) . chr(65 + ($var/26)%26) . chr(65 + $var%26);
-    elseif($var > 26)
-      return chr(65 + ($var/26)%26) . chr(65 + $var%26);
-    else
-      return chr(64 + $var);
-  }
-
-  /**
-  * Utility method, which generates a unique variable name, prefixed with a $
-  */
-  function generateVar()
-  {
-    return '$' . $this->generateTempName();
-  }
-
-  protected function _append($code)
-  {
-    if(!$this->current_method)
-    {
-      $this->code .= $code;
-      return;
-    }
-
-    if(!isset($this->methods[$this->current_method]))
-      $this->methods[$this->current_method] = '';
-
-    $this->methods[$this->current_method] .= $code;
-  }
-
-  protected function _renderMethods()
-  {
-    return implode("\n", $this->methods);
-  }
-
-  protected function _renderIncludeList()
-  {
-    $include_code = '';
-    foreach($this->include_list as $include_file)
-      $include_code .= "require_once('$include_file');\n";
-    return $include_code;
-  }
-}
-

Deleted: 3.x/trunk/limb/macro/src/lmbMacroCompiler.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroCompiler.class.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/lmbMacroCompiler.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -1,117 +0,0 @@
-<?php
-/*
- * Limb PHP Framework
- *
- * @link http://limb-project.com
- * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
- * @license    LGPL http://www.gnu.org/copyleft/lesser.html
- */
-
-lmb_require('limb/macro/src/lmbMacroTreeBuilder.class.php');
-lmb_require('limb/macro/src/lmbMacroNode.class.php');
-lmb_require('limb/macro/src/lmbMacroParser.class.php');
-lmb_require('limb/macro/src/lmbMacroCodeWriter.class.php');
-
-lmb_require('limb/macro/src/lmbMacroTag.class.php');
-lmb_require('limb/macro/src/lmbMacroPassiveTag.class.php');
-
-/**
- * class lmbMacroCompiler.
- *
- * @package macro
- * @version $Id$
- */
-class lmbMacroCompiler
-{
-  /**
-  * @var lmbMacroTreeBuilder
-  */
-  protected $tree_builder;
-
-  /**
-  * @var lmbMacroTemplateLocator
-  */
-  protected $template_locator;
-
-  /**
-  * @var lmbMacroSourceParser
-  */
-  protected $parser;
-
-  /**
-  * @var lmbMacroTagDictionary
-  */
-  protected $tag_dictionary;
-
-  /**
-  * @var lmbMacroFilterDictionary
-  */
-  protected $filter_dictionary;
-
-  function __construct($tag_dictionary, $template_locator, $filter_dictionary)
-  {
-    $this->tree_builder = new lmbMacroTreeBuilder($this);
-
-    $this->template_locator = $template_locator;
-
-    $this->tag_dictionary = $tag_dictionary;
-    $this->filter_dictionary = $filter_dictionary;
-  }
-
-  function compile($source_file, $compiled_file, $class, $render_func)
-  {
-    $root_node = new lmbMacroNode(new lmbMacroSourceLocation($source_file, ''));
-    $this->parseTemplate($source_file, $root_node);
-
-    $generated_code = $this->_generateTemplateCode($class, $render_func, $root_node);
-    self :: writeFile($compiled_file, $generated_code);
-  }
-
-  function _generateTemplateCode($class, $render_func, $root_node)
-  {
-    $code_writer = new lmbMacroCodeWriter($class, $render_func);
-    $root_node->generate($code_writer);
-    return $code_writer->renderCode();
-  }
-
-  function parseTemplate($source_file_path, $root_node)
-  {
-    $parser = new lmbMacroParser($this->tree_builder, $this->template_locator, $this->tag_dictionary);
-    $parser->parse($source_file_path, $root_node);
-  }
-
-  /**
-  * @return lmbMacroTemplateLocator
-  */
-  function getTemplateLocator()
-  {
-    return $this->template_locator;
-  }
-
-  /**
-  * @return lmbMacroTreeBuilder
-  */
-  function getTreeBuilder()
-  {
-    return $this->tree_builder;
-  }
-
-  function getTagDictionary()
-  {
-    return $this->tag_dictionary;
-  }
-
-  function getFilterDictionary()
-  {
-    return $this->filter_dictionary;
-  }
-
-  static function writeFile($file, $data)
-  {
-    $dirname = dirname($file);
-    lmbFs :: mkdir($dirname);
-
-    file_put_contents($file, $data);
-  }
-}
-

Deleted: 3.x/trunk/limb/macro/src/lmbMacroContentBlockAnalizerListener.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroContentBlockAnalizerListener.class.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/lmbMacroContentBlockAnalizerListener.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -1,45 +0,0 @@
-<?php
-/*
- * Limb PHP Framework
- *
- * @link http://limb-project.com
- * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
- * @license    LGPL http://www.gnu.org/copyleft/lesser.html
- */
-lmb_require('limb/macro/src/lmbMacroBlockAnalizerListener.interface.php');
-lmb_require('limb/macro/src/lmbMacroOutputExpressionNode.class.php');
-lmb_require('limb/macro/src/lmbMacroExpressionNode.class.php');
-
-/**
- * class lmbMacroContentBlockAnalizerListener.
- *
- * @package macro
- * @version $Id$
- */
-class lmbMacroContentBlockAnalizerListener implements lmbMacroBlockAnalizerListener
-{
-  protected $tree_builder;
-  protected $location;
-
-  function __construct($tree_builder, $location)
-  {
-    $this->tree_builder = $tree_builder;
-    $this->location = $location;
-  }
-
-  function addLiteralFragment($text)
-  {
-    $this->tree_builder->addTextNode($text);
-  }
-
-  function addExpressionFragment($text)
-  {
-    $expression = new lmbMacroExpressionNode($text,
-                                             $this->tree_builder->getCursor(),
-                                             $this->tree_builder->getFilterDictionary());
-
-    $output_expression = new lmbMacroOutputExpressionNode($this->location, $expression);
-    $this->tree_builder->addNode($output_expression);
-  }
-}
-

Deleted: 3.x/trunk/limb/macro/src/lmbMacroExpression.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroExpression.class.php	2007-12-03 11:44:06 UTC (rev 6560)
+++ 3.x/trunk/limb/macro/src/lmbMacroExpression.class.php	2007-12-03 11:56:54 UTC (rev 6561)
@@ -1,105 +0,0 @@
-<?php
-/*
- * Limb PHP Framework
- *
- * @link http://limb-project.com
- * @copyright  Copyright &copy; 2004-2007 BIT(http://bit-creative.com)
- * @license    LGPL http://www.gnu.org/copyleft/lesser.html
- */
-
-lmb_require('limb/macro/src/lmbMacroExpressionInterface.interface.php');
-
-/**
- * class lmbMacroExpression
- * @package macro
- * @version $Id$
- */
-class lmbMacroExpression implements lmbMacroExpressionInterface
-{
-  protected $tmp;
-
-  function __construct($expression_str)
-  {
-    $this->expression_str = $expression_str;
-  }
-
-  function preGenerate($code)
-  {
-    $this->tmp = $code->generateVar();
-    $var = $code->generateVar();
-
-    // simple case if expression is just a variable
-    if(strpos($this->expression_str, '.') === false)
-    {
-      $this->tmp = $this->expression_str;
-      return;
-    }
-
-    $expr = '';
-    
-    $items = $this->_extractExpressionPathItems($this->expression_str);
-
-    //first item is variable itself
-    //$var = $items[0];
-    $expr .= $var . ' = ' . $items[0] . ';';
-    $code->writePHP($this->tmp . "='';");
-
-    for($i=1; $i<sizeof($items); $i++)
-    {
-      $item = $items[$i];
-      $expr .= 'if((is_array(' . $var . ') && isset(' . $var . '["' . $item . '"])) || ' .
-               '(is_object(' . $var . ') && ' . $this->tmp . '=' . $var . '->get("' . $item . '")))' .
-               '{if(is_array(' . $var . '))' . $this->tmp . ' = ' . $var . '["' . $item . '"];';
-      $var = $this->tmp;
-    }
-
-    //closing brackets
-    for($i=1; $i < sizeof($items); $i++)
-      $expr .= '}else{' . $this->tmp . '="";}';
-
-    $code->writePHP($expr);
-  }
-
-  protected function