src/ets.php
function parse($ptype, &$i, &$line, $ncontent, $content)
	{
		$elts = array();
		$mode = _ETS_DATA;
		$ntext = $nname = $nvalue = $nspace = 0;
		$ctext = $cname = $cvalue = '';
		$nametype = NULL;
		$nspecial = 0;
		$saveline = $line;
		for ( ; $i < $ncontent; ++$i) {
			// skip parsing when error
			if ($this->skip) {
				return array();
			}
			// current character and following
			$c0 = $content{$i};
			$c1 = $content{$i + 1};
			$is_space0 = $this->is_space($c0);
			$a0 = ord($c0);
			// line count
			if ($a0 == 10 || ($a0 == 13 && ord($c1) != 10)) {
				++$line;
			}
			// data acquisition
			if ($mode == _ETS_DATA) {
				// tag?
				if ($c0 == '{') {
					$c2 = $content{$i + 2};
					$c3 = $content{$i + 3};
					$c4 = $content{$i + 4};
					$c5 = $content{$i + 5};
					$c6 = $content{$i + 6};
					$c7 = $content{$i + 7};
					// {* (comment)
					if ($c1 == '*') {
						if ($ptype & _ETS_CODEs) {
							$this->error(0, 1, 'comment', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_COMMENT;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						++$i;
						++$nspecial;
						$saveline = $line;
					// {# (cdata)
					} elseif ($c1 == '#') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 2, 'cdata', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_CDATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						++$i;
						++$nspecial;
						$saveline = $line;
					// {loop:   (formerly "{mask:")
					} elseif ($c1 == 'l' && $c2 == 'o' && $c3 == 'o' && $c4 == 'p' && $c5 == ':') {
						if ($ptype & _ETS_GROUP2) {
							$this->error(0, 3, 'template element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_TEMPLATE;
						$i += 5;
					// {mask:
					} elseif ($c1 == 'm' && $c2 == 'a' && $c3 == 's' && $c4 == 'k' && $c5 == ':') {
						if ($ptype & _ETS_GROUP2) {
							$this->error(0, 3, 'template element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_TEMPLATE;
						$i += 5;
					// {call:
					} elseif ($c1 == 'c' && $c2 == 'a' && $c3 == 'l' && $c4 == 'l' && $c5 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 4, 'call element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 6;
						$index = _ETS_CALL . ':' . $i;
						$elts[$index]['template'] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						$elts[$index]['args'] = $this->parse(_ETS_CALL, $i, $line, $ncontent, $content);
					// {const:
					} elseif ($c1 == 'c' && $c2 == 'o' && $c3 == 'n' && $c4 == 's' && $c5 == 't' && $c6 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 5, 'constant element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 7;
						$elts[_ETS_CONST . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {set:
					} elseif ($c1 == 's' && $c2 == 'e' && $c3 == 't' && $c4 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 6, 'set element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_SET;
						$i += 4;
					// {mis:
					} elseif ($c1 == 'm' && $c2 == 'i' && $c3 == 's' && $c4 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 7, 'missing element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_MIS;
						$i += 4;
					// {choose:
					} elseif ($c1 == 'c' && $c2 == 'h' && $c3 == 'o' && $c4 == 'o' && $c5 == 's' && $c6 == 'e' && $c7 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 8, 'choose element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_CHOOSEVAR;
						$i += 7;
					// {arg:
					} elseif ($c1 == 'a' && $c2 == 'r' && $c3 == 'g' && $c4 == ':') {
						if ($ptype == _ETS_CALL) {
							$mode = _ETS_NAME;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = _ETS_ARG;
							$i += 4;
						} else {
							$this->error(0, 9, 'argument element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {reduce:
					} elseif ($c1 == 'r' && $c2 == 'e' && $c3 == 'd' && $c4 == 'u' && $c5 == 'c' && $c6 == 'e' && $c7 == ':') {
						if ($ptype == _ETS_ROOT) {
							if (!isset($elts['0reduce']) || $elts['0reduce'] == _ETS_REDUCE_NULL) {
								$mode = _ETS_NAME;
								$ntext = $nname = $nvalue = $nspace = 0;
								$ctext = $cname = $cvalue = '';
								$nametype = _ETS_REDUCE;
								$i += 7;
							} else {
								$this->error(7, 10, 'reduce element already used', $line);
							}
						} else {
							$this->error(0, 11, 'reduce element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {include:
					} elseif ($c1 == 'i' && $c2 == 'n' && $c3 == 'c' && $c4 == 'l' && $c5 == 'u' && $c6 == 'd' && $c7 == 'e' && $content{$i + 8} == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 15, 'include element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 9;
						$elts[_ETS_INCLUDE . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {insert:
					} elseif ($c1 == 'i' && $c2 == 'n' && $c3 == 's' && $c4 == 'e' && $c5 == 'r' && $c6 == 't' && $c7 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 14, 'insert element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 8;
						$elts[_ETS_INSERT . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {eval:
					} elseif ($c1 == 'e' && $c2 == 'v' && $c3 == 'a' && $c4 == 'l' && $c5 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 15, 'eval element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 6;
						$elts[_ETS_EVAL . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {safe:
					} elseif ($c1 == 's' && $c2 == 'a' && $c3 == 'f' && $c4 == 'e' && $c5 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 16, 'safe eval element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 6;
						$elts[_ETS_SAFE . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {when:
					} elseif ($c1 == 'w' && $c2 == 'h' && $c3 == 'e' && $c4 == 'n' && $c5 == ':') {
						// of of whentest
						if ($ptype == _ETS_CHOOSE) {
							$mode = _ETS_DATA;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 6;
							$index = _ETS_WHENTEST . ':' . $i;
							$elts['when'][$index]['test'] = $this->parse(_ETS_CODE,     $i, $line, $ncontent, $content);
							$elts['when'][$index]['true'] = $this->parse(_ETS_WHENTEST, $i, $line, $ncontent, $content);
						}
						// of whenval
						elseif ($ptype == _ETS_CHOOSEVAR) {
							$mode = _ETS_VALUE;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = _ETS_WHENVAL;
							switch ($c6) {
								case '\'':	$quotetype = 1; $i += 6; break;
								case '"':	$quotetype = 2; $i += 6; break;
								default:	$quotetype = 0; $i += 5; break;
							}
						} else {
							$this->error(0, 17, 'when element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {if:
					} elseif ($c1 == 'i' && $c2 == 'f' && $c3 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 18, 'if element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 4;
						$index = _ETS_IF . ':' . $i;
						$elts[$index]['test'] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						$elts[$index]['true'] = $this->parse(_ETS_IF, $i, $line, $ncontent, $content);
					// {repeat:
					} elseif ($c1 == 'r' && $c2 == 'e' && $c3 == 'p' && $c4 == 'e' && $c5 == 'a' && $c6 == 't' && $c7 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 19, 'repeat element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 8;
						$index = _ETS_REPEAT . ':' . $i;
						$elts[$index]['loops'] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						$elts[$index]['repeated'] = $this->parse(_ETS_REPEAT, $i, $line, $ncontent, $content);
					// {rss:
					} elseif ($c1 =='r' && $c2 =='s' && $c3=='s' && $c4==':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 19, 'rss element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_RSS;
						$i += 4;
					// simple tag with absolute path
					} elseif ($c1 == '/' && $c2 == '/') {
						if ($ptype & _ETS_GROUP3) {
							$this->error(0, 20, 'simple tag element with absolute path', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cvalue = '';
						$cname = '//';
						$nametype = _ETS_TAG;
						$i += 2;
					// other simple tag
					} elseif ($c1 != '/' && !$this->is_space($c1)) {
						// {else
						if ($c1 == 'e' && $c2 == 'l' && $c3 == 's' && $c4 == 'e' && ($this->is_space($c5) || $c5 == '}' )) {
							if ($ptype & _ETS_CHOOSEs) {
								$mode = _ETS_NAME;
								$ntext = $nvalue = $nspace = 0;
								$nname = 1;
								$ctext = $cvalue = '';
								$cname = 'else';
								$nametype = _ETS_TAG;
								$i += 4;
							} else {
								$this->error(0, 21, 'else element', $line, $ptype);
								$this->skip = TRUE;
								return array();
							}
						} elseif ($ptype & _ETS_GROUP3) {
							$this->error(0, 22, 'simple tag element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						// other
						} else {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_NAME;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = _ETS_TAG;
						}
					// {/loop     (formerly "{/mask")
					} elseif ($c1 == '/' && $c2 == 'l' && $c3 == 'o' && $c4 == 'o' && $c5 == 'p') {
						if ($ptype == _ETS_TEMPLATE) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 23, 'loop', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/mask
					} elseif ($c1 == '/' && $c2 == 'm' && $c3 == 'a' && $c4 == 's' && $c5 == 'k') {
						if ($ptype == _ETS_TEMPLATE) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 23, 'mask', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/set
					} elseif ($c1 == '/' && $c2 == 's' && $c3 == 'e' && $c4 == 't') {
						if ($ptype & _ETS_SETs) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 24, 'set', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/mis
					} elseif ($c1 == '/' && $c2 == 'm' && $c3 == 'i' && $c4 == 's') {
						if ($ptype & _ETS_MISs) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 25, 'mis', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/php
					} elseif ($c1 == '/' && $c2 == 'p' && $c3 == 'h' && $c4 == 'p') {
						if ($ptype == _ETS_PHP) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 26, 'PHP', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/if
					} elseif ($c1 == '/' && $c2 == 'i' && $c3 == 'f') {
						if ($ptype == _ETS_IF) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 3;
						} else {
							$this->error(1, 27, 'if', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/choose
					} elseif ($c1 == '/' && $c2 == 'c' && $c3 == 'h' && $c4 == 'o' && $c5 == 'o' && $c6 == 's' && $c7 == 'e') {
						if ($ptype & _ETS_CHOOSEs) {
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 7;
						} else {
							$this->error(1, 28, 'choose', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/call
					} elseif ($c1 == '/' && $c2 == 'c' && $c3 == 'a' && $c4 == 'l' && $c5 == 'l') {
						if ($ptype == _ETS_CALL) {
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 29, 'call', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/arg
					} elseif ($c1 == '/' && $c2 == 'a' && $c3 == 'r' && $c4 == 'g') {
						if ($ptype == _ETS_ARG) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 30, 'arg', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/when
					} elseif ($c1 == '/' && $c2 == 'w' && $c3 == 'h' && $c4 == 'e' && $c5 == 'n') {
						// of when val
						if ($ptype == _ETS_WHENVAL) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						// of when test
						} elseif ($ptype == _ETS_WHENTEST) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 31, 'when', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/else
					} elseif ($c1 == '/' && $c2 == 'e' && $c3 == 'l' && $c4 == 's' && $c5 == 'e') {
						if ($ptype == _ETS_ELSE) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 32, 'else', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/repeat
					} elseif ($c1 == '/' && $c2 == 'r' && $c3 == 'e' && $c4 == 'p' && $c5 == 'e' && $c6 == 'a' && $c7 == 't') {
						if ($ptype == _ETS_REPEAT) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 7;
						} else {
							$this->error(1, 33, 'repeat', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/rss
					} elseif ($c1 == '/' && $c2 =='r' && $c3 == 's' && $c4 == 's') {
						if ($ptype == _ETS_RSS) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 35, 'rss', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/ (simplified closing tag)
					} elseif ($c1 == '/' && ($c2 == '}' || $this->is_space($c2))) {
						if ($ptype != _ETS_ROOT) {
							if (!($ptype & _ETS_GROUP4)) {
								$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							}
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							++$i;
						} else {
							$this->error(1, 34, '', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// text
					} elseif (!($ptype & _ETS_GROUP3)) {
						$ctext .= $c0;
						$ntext = 1;
					}
				// end of code element
				} elseif ($c0 == '}' && $ptype == _ETS_CODE) {
					$this->store_text($elts, $i, $ptype, $ntext, $ctext);
					++$i;
					return $elts;
				// escape } with \} in code acquisition
				} elseif ($c0 == '\\' && $c1 == '}' && $ptype == _ETS_CODE) {
					$ctext .= '}';
					$ntext = 1;
					++$i;
				// no text in choosevar element
				} elseif ($ptype == _ETS_CHOOSE && !$is_space0) {
					$this->error(2, 35, "unexpected character '$c0' in choose element", $line);
					$this->skip = TRUE;
					return array();
				// no text in choose element
				} elseif ($ptype == _ETS_CHOOSEVAR && !$is_space0) {
					$this->error(2, 36, "unexpected character '$c0' in choose-variable element", $line);
					$this->skip = TRUE;
					return array();
				// no text in call element
				} elseif ($ptype == _ETS_CALL && !$is_space0) {
					$this->error(2, 37, "unexpected character '$c0' in call element", $line);
					$this->skip = TRUE;
					return array();
				// text
				} elseif ($ptype != _ETS_ROOT) {
					$ctext .= $c0;
					$ntext = 1;
				}
			// name acquisition
			} elseif ($mode == _ETS_NAME) {
				// end of name acquisition
				if ($c0 == '}' && $nname == 1) {
					// reduce
					if ($nametype == _ETS_REDUCE) {
						if (!isset($elts['0reduce']) || $elts['0reduce'] == _ETS_REDUCE_NULL) {
							if (!$this->store_reduce($elts, $cname)) {
								$this->error(7, 38, "invalid value $cname for reduce element", $line);
							}
						}
					// template
					} elseif ($nametype == _ETS_TEMPLATE) {
						++$i;
						if ($ptype != _ETS_ROOT) {
							$this->store_node($elts, _ETS_TEMPLATE, $i, $line, $cname, NULL, $ncontent, $content);
						} elseif (isset($elts[$cname])) {
							$this->error(2, 39, "template $cname already defined", $line);
							$this->skip = TRUE;
							return $elts; //array();
						} else {
							$elts[$cname] = $this->parse(_ETS_TEMPLATE, $i, $line, $ncontent, $content);
						}
					// set
					} elseif ($nametype == _ETS_SET) {
						++$i;
						$this->store_node($elts, _ETS_SET, $i, $line, $cname, NULL, $ncontent, $content);
					// mis
					} elseif ($nametype == _ETS_MIS) {
						++$i;
						$this->store_node($elts, _ETS_MIS, $i, $line, $cname, NULL, $ncontent, $content);
					// tag?
					} elseif ($nametype == _ETS_TAG) {
						// php
						if ($cname == 'php') {
							++$i;
							$elts[_ETS_PHP . ':' . $i] = $this->parse(_ETS_PHP, $i, $line, $ncontent, $content);
						// choose
						} elseif ($cname == 'choose') {
							++$i;
							$elts[_ETS_CHOOSE . ':' . $i] = $this->parse(_ETS_CHOOSE, $i, $line, $ncontent, $content);
						// else
						} elseif (($ptype == _ETS_CHOOSE || $ptype == _ETS_CHOOSEVAR) && $cname == 'else') {
							if (isset($elts['else'])) {
								$this->error(2, 40, 'else element already exists in ' . $this->elt_label($ptype), $line);
								$this->skip = TRUE;
								return array();
							} else {
								++$i;
								$elts['else'] = $this->parse(_ETS_ELSE, $i, $line, $ncontent, $content);
							}
						// tag!
						} else {
							$this->store_leaf($elts, _ETS_TAG, $i, $cname);
						}
					// choose var
					} elseif ($nametype == _ETS_CHOOSEVAR) {
						++$i;
						$this->store_node($elts, _ETS_CHOOSEVAR, $i, $line, $cname, NULL, $ncontent, $content);
					// arg
					} elseif ($nametype == _ETS_ARG) {
						++$i;
						$this->store_node($elts, _ETS_ARG, $i, $line, $cname, NULL, $ncontent, $content);
					}
					$mode = _ETS_DATA;
				// space in name acquisition
				} elseif ($is_space0) {
					if ($nname == 1) {
						$nspace = 1;
					} else {
						$this->error(2, 41, "unexpected space before name", $line);
						$this->skip = TRUE;
						return array();
					}
				// start of value acquisition
				} elseif ($c0 == ':' && $nname == 1 && ($nametype == _ETS_SET || $nametype == _ETS_MIS)) {
					$cvalue = '';
					$nvalue = 0;
					$nspace = 0;
					$mode = _ETS_VALUE;
					switch ($c1) {
						case '\'':	$quotetype = 1; ++$i; break;
						case '"':	$quotetype = 2; ++$i; break;
						default:	$quotetype = 0;       break;
					}
				// start of second name acquisition
				} elseif ($c0 == ':' && $nametype == _ETS_TAG && $nname == 1 && $nvalue == 0) {
					++$i;
					$this->store_node($elts, _ETS_ALT_TAG, $i, $line, $cname, $cvalue, $ncontent, $content, TRUE);
					--$i;
					$mode = _ETS_DATA;
					$ntext = $nname = $nvalue = $nspace = 0;
					$ctext = $cname = $cvalue = '';
					$nametype = NULL;
				// data after trailing spaces
				} elseif ($nspace == 1) {
					$this->error(2, 42, "unexpected character '$c0' after spaces in name", $line);
					$this->skip = TRUE;
					return array();
				// name acquisition
				} elseif (($nname == 0 && preg_match('/[a-zA-Z_\.\x7f-\xff]/', $c0)) || ($nname == 1 && preg_match('/[\[\]\'\/a-zA-Z0-9_\.\x7f-\xff]/', $c0))) {
					$cname .= $c0;
					$nname = 1;
				// absolute path at the beginning of name acquisition
				} elseif ($c0 == '/' && $c1 == '/' && $nname == 0) {
					$cname = '//';
					++$i;
				// error in name acquisition
				} else {
					$this->error(2, 43, "unexpected character '$c0' in name", $line);
					$this->skip = TRUE;
					return array();
				}
			// end of closing tag
			} elseif ($mode == _ETS_CLOSING_TAG) {
				if ($c0 == '}') {
					$this->store_text($elts, $i, $ptype, $ntext, $ctext);
					return $elts;
				} elseif (!$is_space0) {
					$this->error(2, 44, "unexpected character '$c0' in closing tag", $line);
					$this->skip = TRUE;
					return array();
				}
			// value acquisition
			} elseif ($mode == _ETS_VALUE) {
				// end of value acquisition
				if ($c0 == '}' && $nvalue == 1 && ($quotetype == 0 || $nspace == 1 || $nspace == 2)) {
					if ($nametype == _ETS_SET) {
						++$i;
						$this->store_node($elts, _ETS_SETVAL, $i, $line, $cname, $cvalue, $ncontent, $content);
					} elseif ($nametype == _ETS_MIS) {
						++$i;
						$this->store_node($elts, _ETS_MISVAL, $i, $line, $cname, $cvalue, $ncontent, $content);
					} elseif ($nametype == _ETS_WHENVAL) {
						++$i;
						$elts['when'][_ETS_WHENVAL . ':' . $i . '::' . $cvalue] = $this->parse(_ETS_WHENVAL, $i, $line, $ncontent, $content);
					}
					$mode = _ETS_DATA;
				// no more character after space for single quoted string
				} elseif ($c0 == '\'' && $quotetype == 1 && $nspace == 0) {
					$nspace = 2;
				// no more character after space for double quoted string
				} elseif ($c0 == '"' && $quotetype == 2 && $nspace == 0) {
					$nspace = 2;
				// space in value acquisition
				} elseif ($is_space0) {
					if ($nvalue == 0 && $quotetype == 0) { // no value without quote
						$this->error(2, 45, "unexpected space at the beginning of a value", $line);
						$this->skip = TRUE;
						return array();
					} else { // value found or with quotes
						if ($quotetype == 0) { // no quote
							$nspace = 1;
						} else { // with quotes
							if ($nspace == 2) { // after quotes
								$nspace = 1;
							} else {			// in quotes
								$cvalue .= $c0;
								$nvalue = 1;
							}
						}
					}
				// escape } with \} in value acquisition without quote
				} elseif ($c0 == '\\' && $c1 == '}' && $nspace == 0) {
					$cvalue .= '}';
					$nvalue = 1;
					++$i;
				// espace ' with \' in value acquisition for single quoted string
				} elseif ($c0 == '\\' && $c1 == '\'' && $quotetype == 1 && $nspace == 0) {
					$cvalue .= '\'';
					$nvalue = 1;
					++$i;
				// espace " with \" in value acquisition for single quoted string
				} elseif ($c0 == '\\' && $c1 == '"' && $quotetype == 2 && $nspace == 0) {
					$cvalue .= '"';
					$nvalue = 1;
					++$i;
				// value acquisition
				} elseif ($nspace == 0) {
					$cvalue .= $c0;
					$nvalue = 1;
				// error in value acquisition
				} else {
					$this->error(2, 46, "unexpected character '$c0' in value", $line);
					$this->skip = TRUE;
					return array();
				}
			// comment
			} elseif ($mode == _ETS_COMMENT) {
				// nested
				if ($c0 == '{' && $c1 == '*') {
					++$i;
					++$nspecial;
				// end
				} elseif ($c0 == '*' && $c1 == '}') {
					++$i;
					--$nspecial;
					// last end
					if ($nspecial == 0) {
						$mode = _ETS_DATA;
					}
				}
			// cdata
			} elseif ($mode == _ETS_CDATA) {
				// nested
				if ($c0 == '{' && $c1 == '#') {
					++$i;
					++$nspecial;
				// end
				} elseif ($c0 == '#' && $c1 == '}') {
					++$i;
					--$nspecial;
					// last end
					if ($nspecial == 0) {
						$mode = _ETS_DATA;
					}
				// text acquisition
				} else {
					switch ($c0) {
						case "\n": $ctext .= "\1n\1"; break;
						case "\r": $ctext .= "\1r\1"; break;
						case "\t": $ctext .= "\1t\1"; break;
						case " " : $ctext .= "\1s\1"; break;
						default  : $ctext .= $c0; break;
					}
					$ntext = 1;
				}
			}
		}
		// end
		if ($mode & _ETS_GROUP0) {
			$this->error(3, 47, '', $saveline, $mode);
			$this->skip = TRUE;
			return array();
		}
		if ($ptype == _ETS_ROOT_EVAL) {
			$this->store_text($elts, $i, $ptype, $ntext, $ctext);
		} elseif ($ptype != _ETS_ROOT) {
			$this->error(4, 48, '', $saveline, $ptype);
			$this->skip = TRUE;
			return array();
		}
		return $elts;
	}
function parse($ptype, &$i, &$line, $ncontent, $content)
	{
		$elts = array();
		$mode = _ETS_DATA;
		$ntext = $nname = $nvalue = $nspace = 0;
		$ctext = $cname = $cvalue = '';
		$nametype = NULL;
		$nspecial = 0;
		$saveline = $line;
		for ( ; $i < $ncontent; ++$i) {
			// skip parsing when error
			if ($this->skip) {
				return array();
			}
			// current character and following
			$c0 = $content{$i};
			$c1 = $content{$i + 1};
			$is_space0 = $this->is_space($c0);
			$a0 = ord($c0);
			// line count
			if ($a0 == 10 || ($a0 == 13 && ord($c1) != 10)) {
				++$line;
			}
			// data acquisition
			if ($mode == _ETS_DATA) {
				// tag?
				if ($c0 == '{') {
					$c2 = $content{$i + 2};
					$c3 = $content{$i + 3};
					$c4 = $content{$i + 4};
					$c5 = $content{$i + 5};
					$c6 = $content{$i + 6};
					$c7 = $content{$i + 7};
					// {* (comment)
					if ($c1 == '*') {
						if ($ptype & _ETS_CODEs) {
							$this->error(0, 1, 'comment', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_COMMENT;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						++$i;
						++$nspecial;
						$saveline = $line;
					// {# (cdata)
					} elseif ($c1 == '#') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 2, 'cdata', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_CDATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						++$i;
						++$nspecial;
						$saveline = $line;
					// {loop:   (formerly "{mask:")
					} elseif ($c1 == 'l' && $c2 == 'o' && $c3 == 'o' && $c4 == 'p' && $c5 == ':') {
						if ($ptype & _ETS_GROUP2) {
							$this->error(0, 3, 'template element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_TEMPLATE;
						$i += 5;
					// {mask:
					} elseif ($c1 == 'm' && $c2 == 'a' && $c3 == 's' && $c4 == 'k' && $c5 == ':') {
						if ($ptype & _ETS_GROUP2) {
							$this->error(0, 3, 'template element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_TEMPLATE;
						$i += 5;
					// {call:
					} elseif ($c1 == 'c' && $c2 == 'a' && $c3 == 'l' && $c4 == 'l' && $c5 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 4, 'call element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 6;
						$index = _ETS_CALL . ':' . $i;
						$elts[$index]['template'] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						$elts[$index]['args'] = $this->parse(_ETS_CALL, $i, $line, $ncontent, $content);
					// {const:
					} elseif ($c1 == 'c' && $c2 == 'o' && $c3 == 'n' && $c4 == 's' && $c5 == 't' && $c6 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 5, 'constant element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 7;
						$elts[_ETS_CONST . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {set:
					} elseif ($c1 == 's' && $c2 == 'e' && $c3 == 't' && $c4 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 6, 'set element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_SET;
						$i += 4;
					// {mis:
					} elseif ($c1 == 'm' && $c2 == 'i' && $c3 == 's' && $c4 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 7, 'missing element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_MIS;
						$i += 4;
					// {choose:
					} elseif ($c1 == 'c' && $c2 == 'h' && $c3 == 'o' && $c4 == 'o' && $c5 == 's' && $c6 == 'e' && $c7 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 8, 'choose element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_CHOOSEVAR;
						$i += 7;
					// {arg:
					} elseif ($c1 == 'a' && $c2 == 'r' && $c3 == 'g' && $c4 == ':') {
						if ($ptype == _ETS_CALL) {
							$mode = _ETS_NAME;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = _ETS_ARG;
							$i += 4;
						} else {
							$this->error(0, 9, 'argument element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {reduce:
					} elseif ($c1 == 'r' && $c2 == 'e' && $c3 == 'd' && $c4 == 'u' && $c5 == 'c' && $c6 == 'e' && $c7 == ':') {
						if ($ptype == _ETS_ROOT) {
							if (!isset($elts['0reduce']) || $elts['0reduce'] == _ETS_REDUCE_NULL) {
								$mode = _ETS_NAME;
								$ntext = $nname = $nvalue = $nspace = 0;
								$ctext = $cname = $cvalue = '';
								$nametype = _ETS_REDUCE;
								$i += 7;
							} else {
								$this->error(7, 10, 'reduce element already used', $line);
							}
						} else {
							$this->error(0, 11, 'reduce element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {include:
					} elseif ($c1 == 'i' && $c2 == 'n' && $c3 == 'c' && $c4 == 'l' && $c5 == 'u' && $c6 == 'd' && $c7 == 'e' && $content{$i + 8} == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 15, 'include element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 9;
						$elts[_ETS_INCLUDE . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {insert:
					} elseif ($c1 == 'i' && $c2 == 'n' && $c3 == 's' && $c4 == 'e' && $c5 == 'r' && $c6 == 't' && $c7 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 14, 'insert element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 8;
						$elts[_ETS_INSERT . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {eval:
					} elseif ($c1 == 'e' && $c2 == 'v' && $c3 == 'a' && $c4 == 'l' && $c5 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 15, 'eval element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 6;
						$elts[_ETS_EVAL . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {safe:
					} elseif ($c1 == 's' && $c2 == 'a' && $c3 == 'f' && $c4 == 'e' && $c5 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 16, 'safe eval element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 6;
						$elts[_ETS_SAFE . ':' . $i] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						--$i;
					// {when:
					} elseif ($c1 == 'w' && $c2 == 'h' && $c3 == 'e' && $c4 == 'n' && $c5 == ':') {
						// of of whentest
						if ($ptype == _ETS_CHOOSE) {
							$mode = _ETS_DATA;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 6;
							$index = _ETS_WHENTEST . ':' . $i;
							$elts['when'][$index]['test'] = $this->parse(_ETS_CODE,     $i, $line, $ncontent, $content);
							$elts['when'][$index]['true'] = $this->parse(_ETS_WHENTEST, $i, $line, $ncontent, $content);
						}
						// of whenval
						elseif ($ptype == _ETS_CHOOSEVAR) {
							$mode = _ETS_VALUE;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = _ETS_WHENVAL;
							switch ($c6) {
								case '\'':	$quotetype = 1; $i += 6; break;
								case '"':	$quotetype = 2; $i += 6; break;
								default:	$quotetype = 0; $i += 5; break;
							}
						} else {
							$this->error(0, 17, 'when element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {if:
					} elseif ($c1 == 'i' && $c2 == 'f' && $c3 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 18, 'if element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 4;
						$index = _ETS_IF . ':' . $i;
						$elts[$index]['test'] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						$elts[$index]['true'] = $this->parse(_ETS_IF, $i, $line, $ncontent, $content);
					// {repeat:
					} elseif ($c1 == 'r' && $c2 == 'e' && $c3 == 'p' && $c4 == 'e' && $c5 == 'a' && $c6 == 't' && $c7 == ':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 19, 'repeat element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_DATA;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = NULL;
						$i += 8;
						$index = _ETS_REPEAT . ':' . $i;
						$elts[$index]['loops'] = $this->parse(_ETS_CODE, $i, $line, $ncontent, $content);
						$elts[$index]['repeated'] = $this->parse(_ETS_REPEAT, $i, $line, $ncontent, $content);
					// {rss:
					} elseif ($c1 =='r' && $c2 =='s' && $c3=='s' && $c4==':') {
						if ($ptype & _ETS_GROUP1) {
							$this->error(0, 19, 'rss element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cname = $cvalue = '';
						$nametype = _ETS_RSS;
						$i += 4;
					// simple tag with absolute path
					} elseif ($c1 == '/' && $c2 == '/') {
						if ($ptype & _ETS_GROUP3) {
							$this->error(0, 20, 'simple tag element with absolute path', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
						$this->store_text($elts, $i, $ptype, $ntext, $ctext);
						$mode = _ETS_NAME;
						$ntext = $nname = $nvalue = $nspace = 0;
						$ctext = $cvalue = '';
						$cname = '//';
						$nametype = _ETS_TAG;
						$i += 2;
					// other simple tag
					} elseif ($c1 != '/' && !$this->is_space($c1)) {
						// {else
						if ($c1 == 'e' && $c2 == 'l' && $c3 == 's' && $c4 == 'e' && ($this->is_space($c5) || $c5 == '}' )) {
							if ($ptype & _ETS_CHOOSEs) {
								$mode = _ETS_NAME;
								$ntext = $nvalue = $nspace = 0;
								$nname = 1;
								$ctext = $cvalue = '';
								$cname = 'else';
								$nametype = _ETS_TAG;
								$i += 4;
							} else {
								$this->error(0, 21, 'else element', $line, $ptype);
								$this->skip = TRUE;
								return array();
							}
						} elseif ($ptype & _ETS_GROUP3) {
							$this->error(0, 22, 'simple tag element', $line, $ptype);
							$this->skip = TRUE;
							return array();
						// other
						} else {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_NAME;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = _ETS_TAG;
						}
					// {/loop     (formerly "{/mask")
					} elseif ($c1 == '/' && $c2 == 'l' && $c3 == 'o' && $c4 == 'o' && $c5 == 'p') {
						if ($ptype == _ETS_TEMPLATE) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 23, 'loop', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/mask
					} elseif ($c1 == '/' && $c2 == 'm' && $c3 == 'a' && $c4 == 's' && $c5 == 'k') {
						if ($ptype == _ETS_TEMPLATE) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 23, 'mask', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/set
					} elseif ($c1 == '/' && $c2 == 's' && $c3 == 'e' && $c4 == 't') {
						if ($ptype & _ETS_SETs) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 24, 'set', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/mis
					} elseif ($c1 == '/' && $c2 == 'm' && $c3 == 'i' && $c4 == 's') {
						if ($ptype & _ETS_MISs) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 25, 'mis', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/php
					} elseif ($c1 == '/' && $c2 == 'p' && $c3 == 'h' && $c4 == 'p') {
						if ($ptype == _ETS_PHP) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 26, 'PHP', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/if
					} elseif ($c1 == '/' && $c2 == 'i' && $c3 == 'f') {
						if ($ptype == _ETS_IF) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 3;
						} else {
							$this->error(1, 27, 'if', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/choose
					} elseif ($c1 == '/' && $c2 == 'c' && $c3 == 'h' && $c4 == 'o' && $c5 == 'o' && $c6 == 's' && $c7 == 'e') {
						if ($ptype & _ETS_CHOOSEs) {
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 7;
						} else {
							$this->error(1, 28, 'choose', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/call
					} elseif ($c1 == '/' && $c2 == 'c' && $c3 == 'a' && $c4 == 'l' && $c5 == 'l') {
						if ($ptype == _ETS_CALL) {
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 29, 'call', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/arg
					} elseif ($c1 == '/' && $c2 == 'a' && $c3 == 'r' && $c4 == 'g') {
						if ($ptype == _ETS_ARG) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 30, 'arg', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/when
					} elseif ($c1 == '/' && $c2 == 'w' && $c3 == 'h' && $c4 == 'e' && $c5 == 'n') {
						// of when val
						if ($ptype == _ETS_WHENVAL) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						// of when test
						} elseif ($ptype == _ETS_WHENTEST) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 31, 'when', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/else
					} elseif ($c1 == '/' && $c2 == 'e' && $c3 == 'l' && $c4 == 's' && $c5 == 'e') {
						if ($ptype == _ETS_ELSE) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 5;
						} else {
							$this->error(1, 32, 'else', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/repeat
					} elseif ($c1 == '/' && $c2 == 'r' && $c3 == 'e' && $c4 == 'p' && $c5 == 'e' && $c6 == 'a' && $c7 == 't') {
						if ($ptype == _ETS_REPEAT) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 7;
						} else {
							$this->error(1, 33, 'repeat', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/rss
					} elseif ($c1 == '/' && $c2 =='r' && $c3 == 's' && $c4 == 's') {
						if ($ptype == _ETS_RSS) {
							$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							$i += 4;
						} else {
							$this->error(1, 35, 'rss', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// {/ (simplified closing tag)
					} elseif ($c1 == '/' && ($c2 == '}' || $this->is_space($c2))) {
						if ($ptype != _ETS_ROOT) {
							if (!($ptype & _ETS_GROUP4)) {
								$this->store_text($elts, $i, $ptype, $ntext, $ctext);
							}
							$mode = _ETS_CLOSING_TAG;
							$ntext = $nname = $nvalue = $nspace = 0;
							$ctext = $cname = $cvalue = '';
							$nametype = NULL;
							++$i;
						} else {
							$this->error(1, 34, '', $line, $ptype);
							$this->skip = TRUE;
							return array();
						}
					// text
					} elseif (!($ptype & _ETS_GROUP3)) {
						$ctext .= $c0;
						$ntext = 1;
					}
				// end of code element
				} elseif ($c0 == '}' && $ptype == _ETS_CODE) {
					$this->store_text($elts, $i, $ptype, $ntext, $ctext);
					++$i;
					return $elts;
				// escape } with \} in code acquisition
				} elseif ($c0 == '\\' && $c1 == '}' && $ptype == _ETS_CODE) {
					$ctext .= '}';
					$ntext = 1;
					++$i;
				// no text in choosevar element
				} elseif ($ptype == _ETS_CHOOSE && !$is_space0) {
					$this->error(2, 35, "unexpected character '$c0' in choose element", $line);
					$this->skip = TRUE;
					return array();
				// no text in choose element
				} elseif ($ptype == _ETS_CHOOSEVAR && !$is_space0) {
					$this->error(2, 36, "unexpected character '$c0' in choose-variable element", $line);
					$this->skip = TRUE;
					return array();
				// no text in call element
				} elseif ($ptype == _ETS_CALL && !$is_space0) {
					$this->error(2, 37, "unexpected character '$c0' in call element", $line);
					$this->skip = TRUE;
					return array();
				// text
				} elseif ($ptype != _ETS_ROOT) {
					$ctext .= $c0;
					$ntext = 1;
				}
			// name acquisition
			} elseif ($mode == _ETS_NAME) {
				// end of name acquisition
				if ($c0 == '}' && $nname == 1) {
					// reduce
					if ($nametype == _ETS_REDUCE) {
						if (!isset($elts['0reduce']) || $elts['0reduce'] == _ETS_REDUCE_NULL) {
							if (!$this->store_reduce($elts, $cname)) {
								$this->error(7, 38, "invalid value $cname for reduce element", $line);
							}
						}
					// template
					} elseif ($nametype == _ETS_TEMPLATE) {
						++$i;
						if ($ptype != _ETS_ROOT) {
							$this->store_node($elts, _ETS_TEMPLATE, $i, $line, $cname, NULL, $ncontent, $content);
						} elseif (isset($elts[$cname])) {
							$this->error(2, 39, "template $cname already defined", $line);
							$this->skip = TRUE;
							return $elts; //array();
						} else {
							$elts[$cname] = $this->parse(_ETS_TEMPLATE, $i, $line, $ncontent, $content);
						}
					// set
					} elseif ($nametype == _ETS_SET) {
						++$i;
						$this->store_node($elts, _ETS_SET, $i, $line, $cname, NULL, $ncontent, $content);
					// mis
					} elseif ($nametype == _ETS_MIS) {
						++$i;
						$this->store_node($elts, _ETS_MIS, $i, $line, $cname, NULL, $ncontent, $content);
					// tag?
					} elseif ($nametype == _ETS_TAG) {
						// php
						if ($cname == 'php') {
							++$i;
							$elts[_ETS_PHP . ':' . $i] = $this->parse(_ETS_PHP, $i, $line, $ncontent, $content);
						// choose
						} elseif ($cname == 'choose') {
							++$i;
							$elts[_ETS_CHOOSE . ':' . $i] = $this->parse(_ETS_CHOOSE, $i, $line, $ncontent, $content);
						// else
						} elseif (($ptype == _ETS_CHOOSE || $ptype == _ETS_CHOOSEVAR) && $cname == 'else') {
							if (isset($elts['else'])) {
								$this->error(2, 40, 'else element already exists in ' . $this->elt_label($ptype), $line);
								$this->skip = TRUE;
								return array();
							} else {
								++$i;
								$elts['else'] = $this->parse(_ETS_ELSE, $i, $line, $ncontent, $content);
							}
						// tag!
						} else {
							$this->store_leaf($elts, _ETS_TAG, $i, $cname);
						}
					// choose var
					} elseif ($nametype == _ETS_CHOOSEVAR) {
						++$i;
						$this->store_node($elts, _ETS_CHOOSEVAR, $i, $line, $cname, NULL, $ncontent, $content);
					// arg
					} elseif ($nametype == _ETS_ARG) {
						++$i;
						$this->store_node($elts, _ETS_ARG, $i, $line, $cname, NULL, $ncontent, $content);
					}
					$mode = _ETS_DATA;
				// space in name acquisition
				} elseif ($is_space0) {
					if ($nname == 1) {
						$nspace = 1;
					} else {
						$this->error(2, 41, "unexpected space before name", $line);
						$this->skip = TRUE;
						return array();
					}
				// start of value acquisition
				} elseif ($c0 == ':' && $nname == 1 && ($nametype == _ETS_SET || $nametype == _ETS_MIS)) {
					$cvalue = '';
					$nvalue = 0;
					$nspace = 0;
					$mode = _ETS_VALUE;
					switch ($c1) {
						case '\'':	$quotetype = 1; ++$i; break;
						case '"':	$quotetype = 2; ++$i; break;
						default:	$quotetype = 0;       break;
					}
				// start of second name acquisition
				} elseif ($c0 == ':' && $nametype == _ETS_TAG && $nname == 1 && $nvalue == 0) {
					++$i;
					$this->store_node($elts, _ETS_ALT_TAG, $i, $line, $cname, $cvalue, $ncontent, $content, TRUE);
					--$i;
					$mode = _ETS_DATA;
					$ntext = $nname = $nvalue = $nspace = 0;
					$ctext = $cname = $cvalue = '';
					$nametype = NULL;
				// data after trailing spaces
				} elseif ($nspace == 1) {
					$this->error(2, 42, "unexpected character '$c0' after spaces in name", $line);
					$this->skip = TRUE;
					return array();
				// name acquisition
				} elseif (($nname == 0 && preg_match('/[a-zA-Z_\.\x7f-\xff]/', $c0)) || ($nname == 1 && preg_match('/[\[\]\'\/a-zA-Z0-9_\.\x7f-\xff]/', $c0))) {
					$cname .= $c0;
					$nname = 1;
				// absolute path at the beginning of name acquisition
				} elseif ($c0 == '/' && $c1 == '/' && $nname == 0) {
					$cname = '//';
					++$i;
				// error in name acquisition
				} else {
					$this->error(2, 43, "unexpected character '$c0' in name", $line);
					$this->skip = TRUE;
					return array();
				}
			// end of closing tag
			} elseif ($mode == _ETS_CLOSING_TAG) {
				if ($c0 == '}') {
					$this->store_text($elts, $i, $ptype, $ntext, $ctext);
					return $elts;
				} elseif (!$is_space0) {
					$this->error(2, 44, "unexpected character '$c0' in closing tag", $line);
					$this->skip = TRUE;
					return array();
				}
			// value acquisition
			} elseif ($mode == _ETS_VALUE) {
				// end of value acquisition
				if ($c0 == '}' && $nvalue == 1 && ($quotetype == 0 || $nspace == 1 || $nspace == 2)) {
					if ($nametype == _ETS_SET) {
						++$i;
						$this->store_node($elts, _ETS_SETVAL, $i, $line, $cname, $cvalue, $ncontent, $content);
					} elseif ($nametype == _ETS_MIS) {
						++$i;
						$this->store_node($elts, _ETS_MISVAL, $i, $line, $cname, $cvalue, $ncontent, $content);
					} elseif ($nametype == _ETS_WHENVAL) {
						++$i;
						$elts['when'][_ETS_WHENVAL . ':' . $i . '::' . $cvalue] = $this->parse(_ETS_WHENVAL, $i, $line, $ncontent, $content);
					}
					$mode = _ETS_DATA;
				// no more character after space for single quoted string
				} elseif ($c0 == '\'' && $quotetype == 1 && $nspace == 0) {
					$nspace = 2;
				// no more character after space for double quoted string
				} elseif ($c0 == '"' && $quotetype == 2 && $nspace == 0) {
					$nspace = 2;
				// space in value acquisition
				} elseif ($is_space0) {
					if ($nvalue == 0 && $quotetype == 0) { // no value without quote
						$this->error(2, 45, "unexpected space at the beginning of a value", $line);
						$this->skip = TRUE;
						return array();
					} else { // value found or with quotes
						if ($quotetype == 0) { // no quote
							$nspace = 1;
						} else { // with quotes
							if ($nspace == 2) { // after quotes
								$nspace = 1;
							} else {			// in quotes
								$cvalue .= $c0;
								$nvalue = 1;
							}
						}
					}
				// escape } with \} in value acquisition without quote
				} elseif ($c0 == '\\' && $c1 == '}' && $nspace == 0) {
					$cvalue .= '}';
					$nvalue = 1;
					++$i;
				// espace ' with \' in value acquisition for single quoted string
				} elseif ($c0 == '\\' && $c1 == '\'' && $quotetype == 1 && $nspace == 0) {
					$cvalue .= '\'';
					$nvalue = 1;
					++$i;
				// espace " with \" in value acquisition for single quoted string
				} elseif ($c0 == '\\' && $c1 == '"' && $quotetype == 2 && $nspace == 0) {
					$cvalue .= '"';
					$nvalue = 1;
					++$i;
				// value acquisition
				} elseif ($nspace == 0) {
					$cvalue .= $c0;
					$nvalue = 1;
				// error in value acquisition
				} else {
					$this->error(2, 46, "unexpected character '$c0' in value", $line);
					$this->skip = TRUE;
					return array();
				}
			// comment
			} elseif ($mode == _ETS_COMMENT) {
				// nested
				if ($c0 == '{' && $c1 == '*') {
					++$i;
					++$nspecial;
				// end
				} elseif ($c0 == '*' && $c1 == '}') {
					++$i;
					--$nspecial;
					// last end
					if ($nspecial == 0) {
						$mode = _ETS_DATA;
					}
				}
			// cdata
			} elseif ($mode == _ETS_CDATA) {
				// nested
				if ($c0 == '{' && $c1 == '#') {
					++$i;
					++$nspecial;
				// end
				} elseif ($c0 == '#' && $c1 == '}') {
					++$i;
					--$nspecial;
					// last end
					if ($nspecial == 0) {
						$mode = _ETS_DATA;
					}
				// text acquisition
				} else {
					switch ($c0) {
						case "\n": $ctext .= "\1n\1"; break;
						case "\r": $ctext .= "\1r\1"; break;
						case "\t": $ctext .= "\1t\1"; break;
						case " " : $ctext .= "\1s\1"; break;
						default  : $ctext .= $c0; break;
					}
					$ntext = 1;
				}
			}
		}
		// end
		if ($mode & _ETS_GROUP0) {
			$this->error(3, 47, '', $saveline, $mode);
			$this->skip = TRUE;
			return array();
		}
		if ($ptype == _ETS_ROOT_EVAL) {
			$this->store_text($elts, $i, $ptype, $ntext, $ctext);
		} elseif ($ptype != _ETS_ROOT) {
			$this->error(4, 48, '', $saveline, $ptype);
			$this->skip = TRUE;
			return array();
		}
		return $elts;
	}
Recursively parse template