Skip to content
This repository has been archived by the owner on May 8, 2021. It is now read-only.

Commit

Permalink
Moving the opt:tree code generation to data formats.
Browse files Browse the repository at this point in the history
  • Loading branch information
zyxist committed Aug 28, 2010
1 parent ff876e9 commit f312cc6
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 93 deletions.
5 changes: 4 additions & 1 deletion lib/Opt/Class.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ class Opt_Class extends Opl_Class
public $escape = true;
public $defaultFormat = 'Array';
public $containerFormat = 'Container';
public $treeFormat = 'DepthTree';

/**
* The compiler object
Expand Down Expand Up @@ -294,7 +295,9 @@ class Opt_Class extends Opl_Class
'System' => 'Opt_Format_System',
'SwitchEquals' => 'Opt_Format_SwitchEquals',
'SwitchContains' => 'Opt_Format_SwitchContains',
'Container' => 'Opt_Format_Container'
'Container' => 'Opt_Format_Container',
'DepthTree' => 'Opt_Format_DepthTree',
'NestedTree' => 'Opt_Format_NestedTree'
);
/**
* The extra entities replaced by OPT
Expand Down
172 changes: 172 additions & 0 deletions lib/Opt/Format/DepthTree.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php
/*
* OPEN POWER LIBS <http://www.invenzzia.org>
*
* This file is subject to the new BSD license that is bundled
* with this package in the file LICENSE. It is also available through
* WWW at this URL: <http://www.invenzzia.org/license/new-bsd>
*
* Copyright (c) Invenzzia Group <http://www.invenzzia.org>
* and other contributors. See website for details.
*/

/**
* The default data format implementation for opt:tree. It generates the
* nesting level, using the information about the depth of each element.
*
* @author Tomasz Jędrzejewski
* @copyright Invenzzia Group <http://www.invenzzia.org/> and contributors.
* @license http://www.invenzzia.org/license/new-bsd New BSD License
* @package Formats
*/
class Opt_Format_DepthTree extends Opt_Format_Abstract
{
/**
* The list of supported hook types.
* @var array
*/
protected $_supports = array(
'tree'
);

/**
* Build a PHP code for the specified hook name.
*
* @internal
* @param string $hookName The hook name
* @return string The output PHP code
*/
protected function _build($hookName)
{
switch($hookName)
{
// The code generated before the tree content.
case 'tree:before':
$section = $this->_getVar('section');
$section['format']->action('section:forceItemVariables');
$section['format']->assign('item', 'depth');

/*
* Recursion is a native mechanism for tree processing, and in fact - the template syntax suggest we use it here. However, it
* would be too expensive (functions, other stupidities). The trees are rendered in the imperative way. Both opt:list and opt:node
* are split with the opt:content tag and this way we have four rendering commands: beginning of the list, its end, beginning of the node
* and its end. The loop does not iterate through list items, but is a simple automata: if the rendering command queue is empty,
* we move to the next list item, decide what it is (leaf, subnode, etc.) and create a command chain that is necessary to render it.
*
* If the chain is empty, we have to close the nodes that are still open and the main list itself. After processing this chain, we finish the job.
*/
return $section['format']->get('section:loopBefore').'
'.$section['format']->get('section:reset').'
$_'.$section['name'].'_depth = -1;
$_'.$section['name'].'_initDepth = null;
$_'.$section['name'].'_over = 0;
$_'.$section['name'].'_cmd = new SplQueue;
$_'.$section['name'].'_stack = new SplStack;
while(1)
{
if($_'.$section['name'].'_cmd->count() == 0)
{
switch($_'.$section['name'].'_over)
{
case 0:
$_'.$section['name'].'_over = 1;
break;
case 1:
'.$section['format']->get('section:next').'
break;
case 2:
break 2;
}
if(!'.$section['format']->get('section:valid').')
{
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
for($k = $_'.$section['name'].'_initDepth; $k < $_'.$section['name'].'_depth; $k++)
{
$_'.$section['name'].'_cmd->enqueue(array(4, null));
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
}
$_'.$section['name'].'_cmd->enqueue(array(4, null));
$_'.$section['name'].'_over = 2;
}
else
{
'.$section['format']->get('section:populate').'
if(is_null($_'.$section['name'].'_initDepth))
{
$_'.$section['name'].'_initDepth = '.$section['format']->get('section:variable').';
}
if($_'.$section['name'].'_initDepth > '.$section['format']->get('section:variable').')
{
throw new Opt_Runtime_Exception(\'The tree element depth is too low: \'.'.$section['format']->get('section:variable').'.\'. It must be greater or equal to the initial depth \'.$_'.$section['name'].'_initDepth.\'.\');
}
if($_'.$section['name'].'_depth < '.$section['format']->get('section:variable').')
{
$_'.$section['name'].'_cmd->enqueue(array(1, null));
$_'.$section['name'].'_cmd->enqueue(array(2, $_sect'.$section['name'].'_v));
$_'.$section['name'].'_stack->push($_sect'.$section['name'].'_v);
}
elseif($_'.$section['name'].'_depth > '.$section['format']->get('section:variable').')
{
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
for($k = '.$section['format']->get('section:variable').'; $k < $_'.$section['name'].'_depth; $k++)
{
$_'.$section['name'].'_cmd->enqueue(array(4, null));
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
}
$_'.$section['name'].'_cmd->enqueue(array(2, $_sect'.$section['name'].'_v));
$_'.$section['name'].'_stack->push($_sect'.$section['name'].'_v);
}
else
{
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
$_'.$section['name'].'_cmd->enqueue(array(2, $_sect'.$section['name'].'_v));
$_'.$section['name'].'_stack->push($_sect'.$section['name'].'_v);
}
$_'.$section['name'].'_depth = '.$section['format']->get('section:variable').';
}
}
list($cmd, $_sect'.$section['name'].'_v) = $_'.$section['name'].'_cmd->dequeue();
switch($cmd)
{';

// Before case 1...
case 'tree:case1:before':
return ' case 1: ';

// After case 1...
case 'tree:case1:after':
return ' break; ';

// Before case 2...
case 'tree:case2:before':
return ' case 2: ';

// After case 2...
case 'tree:case2:after':
return ' break; ';

// Before case 3...
case 'tree:case3:before':
return ' case 3: ';

// After case 3...
case 'tree:case3:after':
return ' break; ';

// Before case 4...
case 'tree:case4:before':
return ' case 4: ';

// After case 4...
case 'tree:case4:after':
return ' break; ';

// End of the tree rendering.
case 'tree:after':
$section = $this->_getVar('section');

return '} } unset($_'.$section['name'].'_stack); unset($_'.$section['name'].'_cmd);';
}
} // end _build();
} // end Opt_Format_DepthTree;
32 changes: 32 additions & 0 deletions lib/Opt/Format/NestedTree.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/*
* OPEN POWER LIBS <http://www.invenzzia.org>
*
* This file is subject to the new BSD license that is bundled
* with this package in the file LICENSE. It is also available through
* WWW at this URL: <http://www.invenzzia.org/license/new-bsd>
*
* Copyright (c) Invenzzia Group <http://www.invenzzia.org>
* and other contributors. See website for details.
*/

/**
* The alternative tree implementation for OPT that is able to parse
* nested lists and generate a tree from them.
*
* @author Tomasz Jędrzejewski
* @copyright Invenzzia Group <http://www.invenzzia.org/> and contributors.
* @license http://www.invenzzia.org/license/new-bsd New BSD License
* @package Formats
*/
class Opt_Format_NestedTree extends Opt_Format_Abstract
{
/**
* The list of supported hook types.
* @var array
*/
protected $_supports = array(
'tree'
);

} // end Opt_Format_NestedTree;
109 changes: 17 additions & 92 deletions lib/Opt/Instruction/Tree.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,102 +171,27 @@ protected function _processBody(Opt_Xml_Element $node)
$tag = $tag->getParent();
}
}

// Now, generate the source code.
/*
* Recursion is a native mechanism to process trees, and in fact - the template syntax suggest we use it here. However, it
* would be too expensive (functions, other stupidities). The trees are rendered in the imperative way. Both opt:list and opt:node
* are split with the opt:content tag and this way we have four rendering commands: beginning of the list, its end, beginning of the node
* and its end. The loop does not iterate through list items, but is a simple automata: if the rendering command queue is empty,
* we move to the next list item, decide what it is (leaf, subnode, etc.) and create a command chain that is necessary to render it.
*
* If the chain is empty, we have to close the nodes that are still open and the main list itself. After processing this chain, we finish the job.
*/
$section['format']->action('section:forceItemVariables');
$section['format']->assign('item', 'depth');
$node->addAfter(Opt_Xml_Buffer::TAG_BEFORE, $section['format']->get('section:loopBefore').'
'.$section['format']->get('section:reset').'
$_'.$section['name'].'_depth = -1;
$_'.$section['name'].'_initDepth = null;
$_'.$section['name'].'_over = 0;
$_'.$section['name'].'_cmd = new SplQueue;
$_'.$section['name'].'_stack = new SplStack;
while(1)
{
if($_'.$section['name'].'_cmd->count() == 0)
{
switch($_'.$section['name'].'_over)
{
case 0:
$_'.$section['name'].'_over = 1;
break;
case 1:
'.$section['format']->get('section:next').'
break;
case 2:
break 2;
}
if(!'.$section['format']->get('section:valid').')
{
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
for($k = $_'.$section['name'].'_initDepth; $k < $_'.$section['name'].'_depth; $k++)
{
$_'.$section['name'].'_cmd->enqueue(array(4, null));
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
}
$_'.$section['name'].'_cmd->enqueue(array(4, null));
$_'.$section['name'].'_over = 2;
}
else
{
'.$section['format']->get('section:populate').'
if(is_null($_'.$section['name'].'_initDepth))
{
$_'.$section['name'].'_initDepth = '.$section['format']->get('section:variable').';
}
if($_'.$section['name'].'_initDepth > '.$section['format']->get('section:variable').')
// Select the data format
$format = $this->_compiler->getFormat('tree#'.$section['name'], false, $this->_tpl->treeFormat);

if(!$format->supports('tree'))
{
throw new Opt_Runtime_Exception(\'The tree element depth is too low: \'.'.$section['format']->get('section:variable').'.\'. It must be greater or equal to the initial depth \'.$_'.$section['name'].'_initDepth.\'.\');
throw new Opt_Format_Exception('The format '.$format->getName().' does not support "tree" type.');
}
if($_'.$section['name'].'_depth < '.$section['format']->get('section:variable').')
{
$_'.$section['name'].'_cmd->enqueue(array(1, null));
$_'.$section['name'].'_cmd->enqueue(array(2, $_sect'.$section['name'].'_v));
$_'.$section['name'].'_stack->push($_sect'.$section['name'].'_v);
}
elseif($_'.$section['name'].'_depth > '.$section['format']->get('section:variable').')
{
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
for($k = '.$section['format']->get('section:variable').'; $k < $_'.$section['name'].'_depth; $k++)
{
$_'.$section['name'].'_cmd->enqueue(array(4, null));
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
}
$_'.$section['name'].'_cmd->enqueue(array(2, $_sect'.$section['name'].'_v));
$_'.$section['name'].'_stack->push($_sect'.$section['name'].'_v);
}
else
{
$_'.$section['name'].'_cmd->enqueue(array(3, $_'.$section['name'].'_stack->pop()));
$_'.$section['name'].'_cmd->enqueue(array(2, $_sect'.$section['name'].'_v));
$_'.$section['name'].'_stack->push($_sect'.$section['name'].'_v);
}
$_'.$section['name'].'_depth = '.$section['format']->get('section:variable').';
}

}
list($cmd, $_sect'.$section['name'].'_v) = $_'.$section['name'].'_cmd->dequeue();
switch($cmd)
{');
$format->assign('section', $section);

// Now, generate the source code.
$node->addAfter(Opt_Xml_Buffer::TAG_BEFORE, $format->get('tree:before'));
// Add the four case code.
$stList->addBefore(Opt_Xml_Buffer::TAG_BEFORE, 'case 1: ');
$content['list']->addAfter(Opt_Xml_Buffer::TAG_BEFORE, 'break; ');
$content['list']->addBefore(Opt_Xml_Buffer::TAG_AFTER, 'case 4: ');
$stList->addAfter(Opt_Xml_Buffer::TAG_AFTER, ' break; ');
$stNode->addBefore(Opt_Xml_Buffer::TAG_BEFORE, 'case 2: ');
$content['node']->addAfter(Opt_Xml_Buffer::TAG_BEFORE, 'break; ');
$content['node']->addBefore(Opt_Xml_Buffer::TAG_AFTER, 'case 3: ');
$stNode->addAfter(Opt_Xml_Buffer::TAG_AFTER, 'break; } } unset($_'.$section['name'].'_stack); unset($_'.$section['name'].'_cmd); ');
$stList->addBefore(Opt_Xml_Buffer::TAG_BEFORE, $format->get('tree:case1:before'));
$content['list']->addAfter(Opt_Xml_Buffer::TAG_BEFORE, $format->get('tree:case1:after'));
$content['list']->addBefore(Opt_Xml_Buffer::TAG_AFTER, $format->get('tree:case4:before'));
$stList->addAfter(Opt_Xml_Buffer::TAG_AFTER, $format->get('tree:case4:after'));
$stNode->addBefore(Opt_Xml_Buffer::TAG_BEFORE, $format->get('tree:case2:before'));
$content['node']->addAfter(Opt_Xml_Buffer::TAG_BEFORE, $format->get('tree:case2:after'));
$content['node']->addBefore(Opt_Xml_Buffer::TAG_AFTER, $format->get('tree:case3:before'));
$stNode->addAfter(Opt_Xml_Buffer::TAG_AFTER, $format->get('tree:case3:after').' '.$format->get('tree:after'));

$this->processSeparator('$__sect_'.$section['name'], $section['separator'], $node);

Expand Down

0 comments on commit f312cc6

Please sign in to comment.