<?php

/*

 * Project:	template_lite, a smarter template engine

 * File:	class.compiler.php

 * Author:	Paul Lockaby <paul@paullockaby.com>, Mark Dickenson <akapanamajack@sourceforge.net>

 * Copyright:	2003,2004,2005 by Paul Lockaby, 2005,2006 Mark Dickenson

 *

 * This library is free software; you can redistribute it and/or

 * modify it under the terms of the GNU Lesser General Public

 * License as published by the Free Software Foundation; either

 * version 2.1 of the License, or (at your option) any later version.

 *

 * This library is distributed in the hope that it will be useful,

 * but WITHOUT ANY WARRANTY; without even the implied warranty of

 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

 * Lesser General Public License for more details.

 *

 * You should have received a copy of the GNU Lesser General Public

 * License along with this library; if not, write to the Free Software

 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 *

 * The latest version of template_lite can be obtained from:

 * http://templatelite.sourceforge.net

 *

 */



class Template_Lite_Compiler extends Template_Lite {

	// public configuration variables

	var $left_delimiter			= "";

	var $right_delimiter			= "";

	var $plugins_dir			= "";

	var $template_dir		= "";

	var $reserved_template_varname = "";

	var $default_modifiers		= array();



	var $php_extract_vars		=	true;	// Set this to false if you do not want the $this->_tpl variables to be extracted for use by PHP code inside the template.



	// private internal variables

	var $_vars			=	array();	// stores all internal assigned variables

	var $_confs			=	array();	// stores all internal config variables

	var $_plugins			=	array();	// stores all internal plugins

	var $_linenum			=	0;		// the current line number in the file we are processing

	var $_file			=	"";		// the current file we are processing

	var $_literal			=	array();	// stores all literal blocks

	var $_foreachelse_stack		=	array();

	var $_for_stack			=	0;

	var $_sectionelse_stack	 =   array();	// keeps track of whether section had 'else' part

	var $_switch_stack		=	array();

	var $_tag_stack			=	array();

	var $_require_stack		=	array();	// stores all files that are "required" inside of the template

	var $_php_blocks		=	array();	// stores all of the php blocks

	var $_error_level		=	null;

	var $_sl_md5			=	'39fc70570b8b60cbc1b85839bf242aff';



	var $_db_qstr_regexp		=	null;		// regexps are setup in the constructor

	var $_si_qstr_regexp		=	null;

	var $_qstr_regexp		=	null;

	var $_func_regexp		=	null;

	var $_var_bracket_regexp	=	null;

	var $_dvar_regexp		=	null;

	var $_cvar_regexp		=	null;

	var $_svar_regexp		=	null;

	var $_mod_regexp		=	null;

	var $_var_regexp		=	null;

    var $_obj_params_regexp     =   null;

	var $_templatelite_vars		=	array();



	function Template_Lite_compiler()

	{

		// matches double quoted strings:

		// "foobar"

		// "foo\"bar"

		// "foobar" . "foo\"bar"

		$this->_db_qstr_regexp = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';



		// matches single quoted strings:

		// 'foobar'

		// 'foo\'bar'

		$this->_si_qstr_regexp = '\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'';



		// matches single or double quoted strings

		$this->_qstr_regexp = '(?:' . $this->_db_qstr_regexp . '|' . $this->_si_qstr_regexp . ')';



		// matches bracket portion of vars

		// [0]

		// [foo]

		// [$bar]

		// [#bar#]

		$this->_var_bracket_regexp = '\[[\$|\#]?\w+\#?\]';

//		$this->_var_bracket_regexp = '\[\$?[\w\.]+\]';



		// matches section vars:

		// %foo.bar%

		$this->_svar_regexp = '\%\w+\.\w+\%';



		// matches $ vars (not objects):

		// $foo

		// $foo[0]

		// $foo[$bar]

		// $foo[5][blah]

//		$this->_dvar_regexp = '\$[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*';

		$this->_dvar_regexp = '\$[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:\.\$?\w+(?:' . $this->_var_bracket_regexp . ')*)*';



		// matches config vars:

		// #foo#

		// #foobar123_foo#

		$this->_cvar_regexp = '\#[a-zA-Z0-9_]{1,}(?:' . $this->_var_bracket_regexp . ')*(?:' . $this->_var_bracket_regexp . ')*\#';



		// matches valid variable syntax:

		// $foo

		// 'text'

		// "text"

		$this->_var_regexp = '(?:(?:' . $this->_dvar_regexp . '|' . $this->_cvar_regexp . ')|' . $this->_qstr_regexp . ')';



		// matches valid modifier syntax:

		// |foo

		// |@foo

		// |foo:"bar"

		// |foo:$bar

		// |foo:"bar":$foobar

		// |foo|bar

		$this->_mod_regexp = '(?:\|@?[0-9a-zA-Z_]+(?::(?>-?\w+|' . $this->_dvar_regexp . '|' . $this->_qstr_regexp .'))*)';		



		// matches valid function name:

		// foo123

		// _foo_bar

		$this->_func_regexp = '[a-zA-Z_]+';

//		$this->_func_regexp = '[a-zA-Z_]\w*';



	}



	function _compile_file($file_contents)

	{

		$ldq = preg_quote($this->left_delimiter);

		$rdq = preg_quote($this->right_delimiter);

		$_match		= array();		// a temp variable for the current regex match

		$tags		= array();		// all original tags

		$text		= array();		// all original text

		$compiled_text	= '<?php /* '.$this->_version.' '.strftime("%Y-%m-%d %H:%M:%S %Z").' */ ?>'."\n\n"; // stores the compiled result

		$compiled_tags	= array();		// all tags and stuff



		$this->_require_stack = array();



		$this->_load_filters();



		if (count($this->_plugins['prefilter']) > 0)

		{

			foreach ($this->_plugins['prefilter'] as $function)

			{

				if ($function === false)

				{

					continue;

				}

				$file_contents = $function($file_contents, $this);

			}

		}



		// remove all comments

		$file_contents = preg_replace("!{$ldq}\*.*?\*{$rdq}!se","",$file_contents);



		// replace all php start and end tags

		$file_contents = preg_replace('%(<\?(?!php|=|$))%i', '<?php echo \'\\1\'?>'."\n", $file_contents);



		// remove literal blocks

		preg_match_all("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", $file_contents, $_match);

		$this->_literal = $_match[1];

		$file_contents = preg_replace("!{$ldq}\s*literal\s*{$rdq}(.*?){$ldq}\s*/literal\s*{$rdq}!s", stripslashes($ldq . "literal" . $rdq), $file_contents);



		// remove php blocks

		preg_match_all("!{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}!s", $file_contents, $_match);

		$this->_php_blocks = $_match[1];

		$file_contents = preg_replace("!{$ldq}\s*php\s*{$rdq}(.*?){$ldq}\s*/php\s*{$rdq}!s", stripslashes($ldq . "php" . $rdq), $file_contents);



		// gather all template tags

		preg_match_all("!{$ldq}\s*(.*?)\s*{$rdq}!s", $file_contents, $_match);

		$tags = $_match[1];



		// put all of the non-template tag text blocks into an array, using the template tags as delimiters

		$text = preg_split("!{$ldq}.*?{$rdq}!s", $file_contents);



		// compile template tags

		$count_tags = count($tags);

		for ($i = 0, $for_max = $count_tags; $i < $for_max; $i++)

		{

			$this->_linenum += substr_count($text[$i], "\n");

			$compiled_tags[] = $this->_compile_tag($tags[$i]);

			$this->_linenum += substr_count($tags[$i], "\n");

		}



		// build the compiled template by replacing and interleaving text blocks and compiled tags

		$count_compiled_tags = count($compiled_tags);

		for ($i = 0, $for_max = $count_compiled_tags; $i < $for_max; $i++)

		{

			if ($compiled_tags[$i] == '') {

				$text[$i+1] = preg_replace('~^(\r\n|\r|\n)~', '', $text[$i+1]);

			}

			$compiled_text .= $text[$i].$compiled_tags[$i];

		}

		$compiled_text .= $text[$i];



		foreach ($this->_require_stack as $key => $value)

		{

			$compiled_text = '<?php require_once(\''. $this->_get_plugin_dir($key) . $key . '\'); $this->register_' . $value[0] . '("' . $value[1] . '", "' . $value[2] . '"); ?>' . $compiled_text;

		}



		// remove unnecessary close/open tags

		$compiled_text = preg_replace('!\?>\n?<\?php!', '', $compiled_text);



		if (count($this->_plugins['postfilter']) > 0)

		{

			foreach ($this->_plugins['postfilter'] as $function)

			{

				if ($function === false)

				{

					continue;

				}

				$compiled_text = $function($compiled_text, $this);

			}

		}



		return $compiled_text;

	}



	function _compile_tag($tag)

	{

		$_match		= array();		// stores the tags

		$_result	= "";			// the compiled tag

		$_variable	= "";			// the compiled variable



		// extract the tag command, modifier and arguments

		preg_match_all('/(?:(' . $this->_var_regexp . '|' . $this->_svar_regexp . '|\/?' . $this->_func_regexp . ')(' . $this->_mod_regexp . '*)(?:\s*[,\.]\s*)?)(?:\s+(.*))?/xs', $tag, $_match);



		if ($_match[1][0]{0} == '$' || ($_match[1][0]{0} == '#' && $_match[1][0]{strlen($_match[1][0]) - 1} == '#') || $_match[1][0]{0} == "'" || $_match[1][0]{0} == '"' || $_match[1][0]{0} == '%')

		{

			$_result = $this->_parse_variables($_match[1], $_match[2]);

			return "<?php echo $_result; ?>\n";

		}

		// process a function

		$tag_command = $_match[1][0];

		$tag_modifiers = !empty($_match[2][0]) ? $_match[2][0] : null;

		$tag_arguments = !empty($_match[3][0]) ? $_match[3][0] : null;

		$_result = $this->_parse_function($tag_command, $tag_modifiers, $tag_arguments);

		return $_result;

	}



	function _parse_function($function, $modifiers, $arguments)

	{

		switch ($function) {

			case 'include':

				if (!function_exists('compile_include'))

				{

					require_once(TEMPLATE_LITE_DIR . "internal/compile.include.php");

				}

				return compile_include($arguments, $this);

				break;

			case 'insert':

				$_args = $this->_parse_arguments($arguments);

				if (!isset($_args['name']))

				{

					$this->trigger_error("missing 'name' attribute in 'insert'", E_USER_ERROR, __FILE__, __LINE__);

				}

				foreach ($_args as $key => $value)

				{

					if (is_bool($value))

					{

						$value = $value ? 'true' : 'false';

					}

					$arg_list[] = "'$key' => $value";

				}

				return '<?php echo $this->_run_insert(array(' . implode(', ', (array)$arg_list) . ')); ?>';

				break;

			case 'ldelim':

				return $this->left_delimiter;

				break;

			case 'rdelim':

				return $this->right_delimiter;

				break;

			case 'literal':

				list (,$literal) = each($this->_literal);

				$this->_linenum += substr_count($literal, "\n");

				return "<?php echo '" . str_replace("'", "\'", str_replace("\\", "\\\\", $literal)) . "'; ?>\n";

				break;

			case 'php':

				list (,$php_block) = each($this->_php_blocks);

				$this->_linenum += substr_count($php_block, "\n");

				$php_extract = '';

				if($this->php_extract_vars)

				{

					if (strnatcmp(PHP_VERSION, '4.3.0') >= 0)

					{

						$php_extract = '<?php extract($this->_vars, EXTR_REFS); ?>' . "\n";

					}

					else

					{

						$php_extract = '<?php extract($this->_vars); ?>' . "\n";

					}

				}

				return $php_extract . '<?php '.$php_block.' ?>';

				break;

			case 'foreach':

				array_push($this->_foreachelse_stack, false);

				$_args = $this->_parse_arguments($arguments);

				if (!isset($_args['from']))

				{

					$this->trigger_error("missing 'from' attribute in 'foreach'", E_USER_ERROR, __FILE__, __LINE__);

				}

				if (!isset($_args['value']) && !isset($_args['item']))

				{

					$this->trigger_error("missing 'value' attribute in 'foreach'", E_USER_ERROR, __FILE__, __LINE__);

				}

				if (isset($_args['value']))

				{

					$_args['value'] = $this->_dequote($_args['value']);

				}

				elseif (isset($_args['item']))

				{

					$_args['value'] = $this->_dequote($_args['item']);

				}

				isset($_args['key']) ? $_args['key'] = "\$this->_vars['".$this->_dequote($_args['key'])."'] => " : $_args['key'] = '';

				$_result = '<?php if (count((array)' . $_args['from'] . ')): foreach ((array)' . $_args['from'] . ' as ' . $_args['key'] . '$this->_vars[\'' . $_args['value'] . '\']): ?>';

				return $_result;

				break;

			case 'foreachelse':

				$this->_foreachelse_stack[count($this->_foreachelse_stack)-1] = true;

				return "<?php endforeach; else: ?>";

				break;

			case '/foreach':

				if (array_pop($this->_foreachelse_stack))

				{

					return "<?php endif; ?>";

				}

				else

				{

					return "<?php endforeach; endif; ?>";

				}

				break;

			case 'for':

				$this->_for_stack++;

				$_args = $this->_parse_arguments($arguments);

				if (!isset($_args['start']))

				{

					$this->trigger_error("missing 'start' attribute in 'for'", E_USER_ERROR, __FILE__, __LINE__);

				}

				if (!isset($_args['stop']))

				{

					$this->trigger_error("missing 'stop' attribute in 'for'", E_USER_ERROR, __FILE__, __LINE__);

				}

				if (!isset($_args['step']))

				{

					$_args['step'] = 1;

				}

				$_result = '<?php for($for' . $this->_for_stack . ' = ' . $_args['start'] . '; ((' . $_args['start'] . ' < ' . $_args['stop'] . ') ? ($for' . $this->_for_stack . ' < ' . $_args['stop'] . ') : ($for' . $this->_for_stack . ' > ' . $_args['stop'] . ')); $for' . $this->_for_stack . ' += ((' . $_args['start'] . ' < ' . $_args['stop'] . ') ? ' . $_args['step'] . ' : -' . $_args['step'] . ')): ?>';

				if (isset($_args['value']))

				{

					$_result .= '<?php $this->assign(\'' . $this->_dequote($_args['value']) . '\', $for' . $this->_for_stack . '); ?>';

				}

				return $_result;

				break;

			case '/for':

				$this->_for_stack--;

				return "<?php endfor; ?>";

				break;

			case 'section':

				array_push($this->_sectionelse_stack, false);

				if (!function_exists('compile_section_start'))

				{

					require_once(TEMPLATE_LITE_DIR . "internal/compile.section_start.php");

				}

				return compile_section_start($arguments, $this);

				break;

			case 'sectionelse':

				$this->_sectionelse_stack[count($this->_sectionelse_stack)-1] = true;

				return "<?php endfor; else: ?>";

				break;

			case '/section':

				if (array_pop($this->_sectionelse_stack))

				{

					return "<?php endif; ?>";

				}

				else

				{

					return "<?php endfor; endif; ?>";

				}

				break;

			case 'while':

				$_args = $this->_compile_if($arguments, false, true);

				return '<?php while(' . $_args . '): ?>';

				break;

			case '/while':

				return "<?php endwhile; ?>";

				break;

			case 'if':

				return $this->_compile_if($arguments);

				break;

			case 'else':

				return "<?php else: ?>";

				break;

			case 'elseif':

				return $this->_compile_if($arguments, true);

				break;

			case '/if':

				return "<?php endif; ?>";

				break;

			case 'assign':

				$_args = $this->_parse_arguments($arguments);

				if (!isset($_args['var']))

				{

					$this->trigger_error("missing 'var' attribute in 'assign'", E_USER_ERROR, __FILE__, __LINE__);

				}

				if (!isset($_args['value']))

				{

					$this->trigger_error("missing 'value' attribute in 'assign'", E_USER_ERROR, __FILE__, __LINE__);

				}

				return '<?php $this->assign(\'' . $this->_dequote($_args['var']) . '\', ' . $_args['value'] . '); ?>';

				break;

			case 'switch':

				$_args = $this->_parse_arguments($arguments);

				if (!isset($_args['from']))

				{

					$this->trigger_error("missing 'from' attribute in 'switch'", E_USER_ERROR, __FILE__, __LINE__);

				}

				array_push($this->_switch_stack, array("matched" => false, "var" => $this->_dequote($_args['from'])));

				return;

				break;

			case '/switch':

				array_pop($this->_switch_stack);

				return '<?php break; endswitch; ?>';

				break;

			case 'case':

				if (count($this->_switch_stack) > 0)

				{

					$_result = "<?php ";

					$_args = $this->_parse_arguments($arguments);

					$_index = count($this->_switch_stack) - 1;

					if (!$this->_switch_stack[$_index]["matched"])

					{

						$_result .= 'switch(' . $this->_switch_stack[$_index]["var"] . '): ';

						$this->_switch_stack[$_index]["matched"] = true;

					}

					else

					{

						$_result .= 'break; ';

					}

					if (!empty($_args['value']))

					{

						$_result .= 'case '.$_args['value'].': ';

					}

					else

					{

						$_result .= 'default: ';

					}

					return $_result . ' ?>';

				}

				else

				{

					$this->trigger_error("unexpected 'case', 'case' can only be in a 'switch'", E_USER_ERROR, __FILE__, __LINE__);

				}

				break;

			case 'config_load':

				$_args = $this->_parse_arguments($arguments);

				if (empty($_args['file']))

				{

					$this->trigger_error("missing 'file' attribute in 'config_load' tag", E_USER_ERROR, __FILE__, __LINE__);

				}

				isset($_args['section']) ? null : $_args['section'] = 'null';

				isset($_args['var']) ? null : $_args['var'] = 'null';

				return '<?php $this->config_load(' . $_args['file'] . ', ' . $_args['section'] . ', ' . $_args['var'] . '); ?>';

				break;

			default:

				$_result = "";

				if ($this->_compile_compiler_function($function, $arguments, $_result))

				{

					return $_result;

				}

				else if ($this->_compile_custom_block($function, $modifiers, $arguments, $_result))

				{

					return $_result;

				}

				elseif ($this->_compile_custom_function($function, $modifiers, $arguments, $_result))

				{

					return $_result;

				}

				else

				{

					$this->trigger_error($function." function does not exist", E_USER_ERROR, __FILE__, __LINE__);

				}

				break;

		}

	}



	function _compile_compiler_function($function, $arguments, &$_result)

	{

		if ($function = $this->_plugin_exists($function, "compiler"))

		{

			$_args = $this->_parse_arguments($arguments);

			$_result = '<?php ' . $function($_args, $this) . ' ?>';

			return true;

		}

		else

		{

			return false;

		}

	}



	function _compile_custom_function($function, $modifiers, $arguments, &$_result)

	{

		if (!function_exists('compile_compile_custom_function'))

		{

			require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_custom_function.php");

		}

		return compile_compile_custom_function($function, $modifiers, $arguments, $_result, $this);

	}



	function _compile_custom_block($function, $modifiers, $arguments, &$_result)

	{

		if (!function_exists('compile_compile_custom_block'))

		{

			require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_custom_block.php");

		}

		return compile_compile_custom_block($function, $modifiers, $arguments, $_result, $this);

	}



	function _compile_if($arguments, $elseif = false, $while = false)

	{

		if (!function_exists('compile_compile_if'))

		{

			require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_if.php");

		}

		return compile_compile_if($arguments, $elseif, $while, $this);

	}



	function _parse_is_expr($is_arg, $_arg)

	{

		if (!function_exists('compile_parse_is_expr'))

		{

			require_once(TEMPLATE_LITE_DIR . "internal/compile.parse_is_expr.php");

		}

		return compile_parse_is_expr($is_arg, $_arg, $this);

	}



	function _compile_config($variable)

	{

		if (!function_exists('compile_compile_config'))

		{

			require_once(TEMPLATE_LITE_DIR . "internal/compile.compile_config.php");

		}

		return compile_compile_config($variable, $this);

	}



	function _dequote($string)

	{

		if (($string{0} == "'" || $string{0} == '"') && $string{strlen($string)-1} == $string{0})

		{

			return substr($string, 1, -1);

		}

		else

		{

			return $string;

		}

	}



	function _parse_arguments($arguments)

	{

		$_match		= array();

		$_result	= array();

		$_variables	= array();

		preg_match_all('/(?:' . $this->_qstr_regexp . ' | (?>[^"\'=\s]+))+|[=]/x', $arguments, $_match);

		/*

		   Parse state:

			 0 - expecting attribute name

			 1 - expecting '='

			 2 - expecting attribute value (not '=')

		*/

		$state = 0;

		foreach($_match[0] as $value)

		{

			switch($state) {

				case 0:

					// valid attribute name

					if (is_string($value))

					{

						$a_name = $value;

						$state = 1;

					}

					else

					{

						$this->trigger_error("invalid attribute name: '$token'", E_USER_ERROR, __FILE__, __LINE__);

					}

					break;

				case 1:

					if ($value == '=')

					{

						$state = 2;

					}

					else

					{

						$this->trigger_error("expecting '=' after '$last_value'", E_USER_ERROR, __FILE__, __LINE__);

					}

					break;

				case 2:

					if ($value != '=')

					{

						if ($value == 'yes' || $value == 'on' || $value == 'true')

						{

							$value = true;

						}

						elseif ($value == 'no' || $value == 'off' || $value == 'false')

						{

							$value = false;

						}

						elseif ($value == 'null')

						{

							$value = null;

						}



						if(!preg_match_all('/(?:(' . $this->_var_regexp . '|' . $this->_svar_regexp . ')(' . $this->_mod_regexp . '*))(?:\s+(.*))?/xs', $value, $_variables))

						{

							$_result[$a_name] = $value;

						}

						else

						{

							$_result[$a_name] = $this->_parse_variables($_variables[1], $_variables[2]);

						}

						$state = 0;

					}

					else

					{

						$this->trigger_error("'=' cannot be an attribute value", E_USER_ERROR, __FILE__, __LINE__);

					}

					break;

			}

			$last_value = $value;

		}

		if($state != 0)

		{

			if($state == 1)

			{

				$this->trigger_error("expecting '=' after attribute name '$last_value'", E_USER_ERROR, __FILE__, __LINE__);

			}

			else

			{

				$this->trigger_error("missing attribute value", E_USER_ERROR, __FILE__, __LINE__);

			}

		}

		return $_result;

	}



	function _parse_variables($variables, $modifiers)

	{

		$_result = "";

		foreach($variables as $key => $value)

		{

			$tag_variable = trim($variables[$key]);

			if(!empty($this->default_modifiers) && !preg_match('!(^|\|)templatelite:nodefaults($|\|)!',$modifiers[$key]))

			{

				$_default_mod_string = implode('|',(array)$this->default_modifiers);

				$modifiers[$key] = empty($modifiers[$key]) ? $_default_mod_string : $_default_mod_string . '|' . $modifiers[$key];

			}

			if (empty($modifiers[$key]))

			{

				$_result .= $this->_parse_variable($tag_variable).'.';

			}

			else

			{

				$_result .= $this->_parse_modifier($this->_parse_variable($tag_variable), $modifiers[$key]).'.';

			}

		}

		return substr($_result, 0, -1);

	}



	function _parse_variable($variable)

	{

		// replace variable with value

		if ($variable{0} == "\$")

		{

			// replace the variable

			return $this->_compile_variable($variable);

		}

		elseif ($variable{0} == '#')

		{

			// replace the config variable

			return $this->_compile_config($variable);

		}

		elseif ($variable{0} == '"')

		{

			// expand the quotes to pull any variables out of it

			// fortunately variables inside of a quote aren't fancy, no modifiers, no quotes

			//   just get everything from the $ to the ending space and parse it

			// if the $ is escaped, then we won't expand it

			$_result = "";

			preg_match_all('/(?:[^\\\]' . $this->_dvar_regexp . ')/', substr($variable, 1, -1), $_expand);  // old match 

//			preg_match_all('/(?:[^\\\]' . $this->_dvar_regexp . '[^\\\])/', $variable, $_expand);

			$_expand = array_unique($_expand[0]);

			foreach($_expand as $key => $value)

			{

				$_expand[$key] = trim($value);

				if (strpos($_expand[$key], '$') > 0)

				{

					$_expand[$key] = substr($_expand[$key], strpos($_expand[$key], '$'));

				}

			}

			$_result = $variable;

			foreach($_expand as $value)

			{

				$value = trim($value);

				$_result = str_replace($value, '" . ' . $this->_parse_variable($value) . ' . "', $_result);

			}

			$_result = str_replace("`", "", $_result);

			return $_result;

		}

		elseif ($variable{0} == "'")

		{

			// return the value just as it is

			return $variable;

		}

		elseif ($variable{0} == "%")

		{

			return $this->_parse_section_prop($variable);

		}

		else

		{

			// return it as is; i believe that there was a reason before that i did not just return it as is,

			// but i forgot what that reason is ...

			// the reason i return the variable 'as is' right now is so that unquoted literals are allowed

			return $variable;

		}

	}



	function _parse_section_prop($section_prop_expr)

	{

		$parts = explode('|', $section_prop_expr, 2);

		$var_ref = $parts[0];

		$modifiers = isset($parts[1]) ? $parts[1] : '';



		preg_match('!%(\w+)\.(\w+)%!', $var_ref, $match);

		$section_name = $match[1];

		$prop_name = $match[2];



		$output = "\$this->_sections['$section_name']['$prop_name']";



		$this->_parse_modifier($output, $modifiers);



		return $output;

	}



	function _compile_variable($variable)

	{

		$_result	= "";



		// remove the $

		$variable = substr($variable, 1);



		// get [foo] and .foo and (...) pieces

		preg_match_all('!(?:^\w+)|(?:' . $this->_var_bracket_regexp . ')|\.\$?\w+|\S+!', $variable, $_match);

		$variable = $_match[0];

		$var_name = array_shift($variable);



		if ($var_name == $this->reserved_template_varname)

		{

			if ($variable[0]{0} == '[' || $variable[0]{0} == '.')

			{

				$find = array("[", "]", ".");

				switch(strtoupper(str_replace($find, "", $variable[0])))

				{

					case 'GET':

						$_result = "\$_GET";

						break;

					case 'POST':

						$_result = "\$_POST";

						break;

					case 'COOKIE':

						$_result = "\$_COOKIE";

						break;

					case 'ENV':

						$_result = "\$_ENV";

						break;

					case 'SERVER':

						$_result = "\$_SERVER";

						break;

					case 'SESSION':

						$_result = "\$_SESSION";

						break;

					case 'NOW':

						$_result = "time()";

						break;

					case 'SECTION':

						$_result = "\$this->_sections";

						break;

					case 'LDELIM':

						$_result = "\$this->left_delimiter";

						break;

					case 'RDELIM':

						$_result = "\$this->right_delimiter";

						break;

					case 'VERSION':

						$_result = "\$this->_version";

						break;

					case 'CONFIG':

						$_result = "\$this->_confs";

						break;

					case 'TEMPLATE':

						$_result = "\$this->_file";

						break;

					case 'CONST':

						$constant = str_replace($find, "", $_match[0][2]);

						$_result = "constant('$constant')";

						$variable = array();

						break;

					default:

						$_var_name = str_replace($find, "", $variable[0]);

						$_result = "\$this->_templatelite_vars['$_var_name']";

						break;

				}

				array_shift($variable);

			}

			else

			{

				$this->trigger_error('$' . $var_name.implode('', $variable) . ' is an invalid $templatelite reference', E_USER_ERROR, __FILE__, __LINE__);

			}

		}

		else

		{

			$_result = "\$this->_vars['$var_name']";

		}



		foreach ($variable as $var)

		{

			if ($var{0} == '[')

			{

				$var = substr($var, 1, -1);

				if (is_numeric($var))

				{

					$_result .= "[$var]";

				}

				elseif ($var{0} == '$')

				{

					$_result .= "[" . $this->_compile_variable($var) . "]";

				}

				elseif ($var{0} == '#')

				{

					$_result .= "[" . $this->_compile_config($var) . "]";

				}

				else

				{

//					$_result .= "['$var']";

					$parts = explode('.', $var);

					$section = $parts[0];

					$section_prop = isset($parts[1]) ? $parts[1] : 'index';

					$_result .= "[\$this->_sections['$section']['$section_prop']]";

				}

			}

			else if ($var{0} == '.')

			{

   				if ($var{1} == '$')

				{

	   				$_result .= "[\$this->_TPL['" . substr($var, 2) . "']]";

				}

		   		else

				{

			   		$_result .= "['" . substr($var, 1) . "']";

				}

			}

			else if (substr($var,0,2) == '->')

			{

				if(substr($var,2,2) == '__')

				{

					$this->trigger_error('call to internal object members is not allowed', E_USER_ERROR, __FILE__, __LINE__);

				}

				else if (substr($var, 2, 1) == '$')

				{

					$_output .= '->{(($var=$this->_TPL[\''.substr($var,3).'\']) && substr($var,0,2)!=\'__\') ? $_var : $this->trigger_error("cannot access property \\"$var\\"")}';

				}

			}

			else

			{

				$this->trigger_error('$' . $var_name.implode('', $variable) . ' is an invalid reference', E_USER_ERROR, __FILE__, __LINE__);

			}

		}

		return $_result;

	}



	function _parse_modifier($variable, $modifiers)

	{

		$_match		= array();

		$_mods		= array();		// stores all modifiers

		$_args		= array();		// modifier arguments



		preg_match_all('!\|(@?\w+)((?>:(?:'. $this->_qstr_regexp . '|[^|]+))*)!', '|' . $modifiers, $_match);

		list(, $_mods, $_args) = $_match;



		$count_mods = count($_mods);

		for ($i = 0, $for_max = $count_mods; $i < $for_max; $i++)

		{

			preg_match_all('!:(' . $this->_qstr_regexp . '|[^:]+)!', $_args[$i], $_match);

			$_arg = $_match[1];



			if ($_mods[$i]{0} == '@')

			{

				$_mods[$i] = substr($_mods[$i], 1);

				$_map_array = 0;

			} else {

				$_map_array = 1;

			}



			foreach($_arg as $key => $value)

			{

				$_arg[$key] = $this->_parse_variable($value);

			}



			if ($this->_plugin_exists($_mods[$i], "modifier") || function_exists($_mods[$i]))

			{

				if (count($_arg) > 0)

				{

					$_arg = ', '.implode(', ', $_arg);

				}

				else

				{

					$_arg = '';

				}



				$php_function = "PHP";

				if ($this->_plugin_exists($_mods[$i], "modifier"))

				{

					$php_function = "plugin";

				}

				$variable = "\$this->_run_modifier($variable, '$_mods[$i]', '$php_function', $_map_array$_arg)";

			}

			else

			{

				$variable = "\$this->trigger_error(\"'" . $_mods[$i] . "' modifier does not exist\", E_USER_NOTICE, __FILE__, __LINE__);";

			}

		}

		return $variable;

	}



	function _plugin_exists($function, $type)

	{

		// check for object functions

		if (isset($this->_plugins[$type][$function]) && is_array($this->_plugins[$type][$function]) && is_object($this->_plugins[$type][$function][0]) && method_exists($this->_plugins[$type][$function][0], $this->_plugins[$type][$function][1]))

		{

			return '$this->_plugins[\'' . $type . '\'][\'' . $function . '\'][0]->' . $this->_plugins[$type][$function][1];

		}

		// check for standard functions

		if (isset($this->_plugins[$type][$function]) && function_exists($this->_plugins[$type][$function]))

		{

			return $this->_plugins[$type][$function];

		}

		// check for a plugin in the plugin directory

		if (file_exists($this->_get_plugin_dir($type . '.' . $function . '.php') . $type . '.' . $function . '.php'))

		{

			require_once($this->_get_plugin_dir($type . '.' . $function . '.php') . $type . '.' . $function . '.php');

			if (function_exists('tpl_' . $type . '_' . $function))

			{

				$this->_require_stack[$type . '.' . $function . '.php'] = array($type, $function, 'tpl_' . $type . '_' . $function);

				return ('tpl_' . $type . '_' . $function);

			}

		}

		return false;

	}



	function _load_filters()

	{

		if (count($this->_plugins['prefilter']) > 0)

		{

			foreach ($this->_plugins['prefilter'] as $filter_name => $prefilter)

			{

				if (!function_exists($prefilter))

				{

					@include_once( $this->_get_plugin_dir("prefilter." . $filter_name . ".php") . "prefilter." . $filter_name . ".php");

				}

			}

		}

		if (count($this->_plugins['postfilter']) > 0)

		{

			foreach ($this->_plugins['postfilter'] as $filter_name => $postfilter)

			{

				if (!function_exists($postfilter))

				{

					@include_once( $this->_get_plugin_dir("postfilter." . $filter_name . ".php") . "postfilter." . $filter_name . ".php");

				}

			}

		}

	}

}



?>

