'int', 'intval' => 'int', 'lower' => 'lc', 'lowercase' => 'lc', 'upper' => 'uc', 'uppercase' => 'uc', 'addslashes' => 'quote', 'q' => 'quote', 'sq' => 'sql_quote', 're_quote' => 'requote', 'preg_quote' => 'requote', 'uri_escape' => 'urlencode', 'uriquote' => 'urlencode', 'substring' => 'substr', 'htmlspecialchars' => 'html', 's' => 'html', 'strip_tags' => 'strip', 't' => 'strip', 'h' => 'strip_unsafe', 'implode' => 'join', 'truncate' => 'strlimit', 'hash_keys' => 'keys', 'array_keys' => 'keys', 'array_slice' => 'subarray', 'hget' => 'get', 'aget' => 'get', 'var_dump' => 'dump', 'process' => 'parse', 'include' => 'parse', 'process_inline' => 'parse_inline', 'include_inline' => 'parse_inline', ); // Functions that do escape HTML, for safe mode const Q_ALWAYS = -1; const Q_IF_ALL = -2; const Q_ALL_BUT_FIRST = -3; static $functionSafeness = array( 'int' => self::Q_ALWAYS, 'raw' => self::Q_ALWAYS, 'html' => self::Q_ALWAYS, 'strip' => self::Q_ALWAYS, 'strip_unsafe' => self::Q_ALWAYS, 'parse' => self::Q_ALWAYS, 'parse_inline' => self::Q_ALWAYS, 'exec' => self::Q_ALWAYS, 'exec_from' => self::Q_ALWAYS, 'exec_from_inline' => self::Q_ALWAYS, 'quote' => self::Q_ALWAYS, 'sql_quote' => self::Q_ALWAYS, 'requote' => self::Q_ALWAYS, 'urlencode' => self::Q_ALWAYS, 'and' => self::Q_ALWAYS, 'or' => self::Q_IF_ALL, 'not' => self::Q_ALWAYS, 'add' => self::Q_ALWAYS, 'sub' => self::Q_ALWAYS, 'mul' => self::Q_ALWAYS, 'div' => self::Q_ALWAYS, 'mod' => self::Q_ALWAYS, 'min' => self::Q_IF_ALL, 'max' => self::Q_IF_ALL, 'round' => self::Q_ALWAYS, 'log' => self::Q_ALWAYS, 'even' => self::Q_ALWAYS, 'odd' => self::Q_ALWAYS, 'eq' => self::Q_ALWAYS, 'ne' => self::Q_ALWAYS, 'gt' => self::Q_ALWAYS, 'lt' => self::Q_ALWAYS, 'ge' => self::Q_ALWAYS, 'le' => self::Q_ALWAYS, 'seq' => self::Q_ALWAYS, 'sne' => self::Q_ALWAYS, 'sgt' => self::Q_ALWAYS, 'slt' => self::Q_ALWAYS, 'sge' => self::Q_ALWAYS, 'sle' => self::Q_ALWAYS, 'neq' => self::Q_ALWAYS, 'nne' => self::Q_ALWAYS, 'ngt' => self::Q_ALWAYS, 'nlt' => self::Q_ALWAYS, 'nge' => self::Q_ALWAYS, 'nle' => self::Q_ALWAYS, 'strlen' => self::Q_ALWAYS, 'strftime' => self::Q_ALWAYS, 'str_replace' => self::Q_ALL_BUT_FIRST, 'substr' => 1, // parameter number to take safeness from 'trim' => 1, 'split' => 1, 'nl2br' => 1, 'concat' => self::Q_IF_ALL, 'join' => self::Q_IF_ALL, 'subst' => self::Q_IF_ALL, 'strlimit' => 1, 'plural_ru' => self::Q_ALL_BUT_FIRST, 'hash' => self::Q_IF_ALL, 'keys' => 1, 'values' => 1, 'sort' => 1, 'pairs' => 1, 'array' => self::Q_IF_ALL, 'range' => self::Q_ALWAYS, 'is_array' => self::Q_ALWAYS, 'count' => self::Q_ALWAYS, 'subarray' => 1, 'subarray_divmod' => 1, 'set' => 2, 'array_merge' => self::Q_IF_ALL, 'shift' => 1, 'pop' => 1, 'unshift' => self::Q_ALWAYS, 'push' => self::Q_ALWAYS, 'void' => self::Q_ALWAYS, 'json' => self::Q_ALWAYS, 'map' => self::Q_ALL_BUT_FIRST, 'yesno' => self::Q_ALL_BUT_FIRST, ); var $options, $st, $lexer, $parser; function __construct($options) { $this->options = $options; } /** * Translate a template to PHP * * @param $code full template code * @param $filename input filename for error reporting * @param $func_ns suffix for class name (Template_SUFFIX) */ function parse_all($code, $filename, $func_ns) { $this->st = new VMXTemplateState(); $this->options->input_filename = $filename; $this->st->functions['main'] = array( 'name' => 'main', 'args' => array(), 'body' => '', ); if (!$this->lexer) { $this->lexer = new VMXTemplateLexer($this->options); $this->parser = new parse_engine(new VMXTemplateParser()); $this->parser->parser->template = $this; } $this->lexer->set_code($code); $this->lexer->feed($this->parser); if ($this->st->functions['main']['body'] === '') { // Parse error unset($this->st->functions['main']); } // Generate code for functions $code = ''; $functions = []; $smap = []; $l = 9; foreach ($this->st->functions as $n => $f) { $ms = []; preg_match_all('/(?:^|\n)# line (\d+)|\n/', $f['body'], $ms, PREG_SET_ORDER); foreach ($ms as $m) { $l++; if (!empty($m[1])) $smap[] = [ $l, $m[1] ]; } $code .= $f['body']; $functions[$n] = $f['args']; } // Assemble the class code $functions = var_export($functions, true); $smap = var_export($smap, true); $rfn = addcslashes($this->options->input_filename, '\\\''); $code = "options->input_filename} class Template_$func_ns extends VMXTemplate { static \$template_filename = '$rfn'; static \$version = ".VMXTemplate::CODE_VERSION."; function __construct(\$t) { \$this->tpldata = &\$t->tpldata; \$this->parent = &\$t; } $code static \$functions = $functions; static \$smap = $smap; } "; return $code; } function compile_function($fn, $args) { $fn = strtolower($fn); if (isset(self::$functions[$fn])) { // Function alias $fn = self::$functions[$fn]; } $argv = []; $q = isset(self::$functionSafeness[$fn]) ? self::$functionSafeness[$fn] : false; if ($q > 0) { $q = isset($args[$q-1]) ? $args[$q-1][1] : true; } elseif ($q == self::Q_ALWAYS) { $q = true; } elseif ($q == self::Q_IF_ALL || $q == self::Q_ALL_BUT_FIRST) { $q = true; $n = count($args); for ($i = ($q == self::Q_ALL_BUT_FIRST ? 1 : 0); $i < $n; $i++) { $q = $q && $args[$i][1]; } } foreach ($args as $a) { $argv[] = $a[0]; } // Check if function is not disabled if (!empty($this->options->compiletime_functions[$fn]) || !isset($this->options->compiletime_functions[$fn]) && method_exists($this, "function_$fn")) { if (!empty($this->options->compiletime_functions[$fn])) { // Custom compile-time function call $r = call_user_func($this->options->compiletime_functions[$fn], $this, $argv); } else { // Builtin function call using name $r = call_user_func_array(array($this, "function_$fn"), $argv); } } else { // A block reference or unknown function $r = "\$this->parent->call_block_list('$fn', array(".implode(', ', $argv)."), '".addcslashes($this->lexer->errorinfo(), "'\\")."')"; $q = true; } return [ $r, $q ]; } /*** Functions ***/ /** Utilities for function parsing **/ // Code for operator-like function static function fmop($op, $args) { return "((" . join(") $op (", $args) . "))"; } /** Числа, логические операции **/ /* логические операции */ function function_or() { $a = func_get_args(); return "self::perlish_or(".join(",", $a).")"; } function function_and() { $a = func_get_args(); return self::fmop('&&', $a); } function function_not($e) { return "!($e)"; } /* арифметические операции */ function function_add() { $a = func_get_args(); return self::fmop('+', $a); } function function_sub() { $a = func_get_args(); return self::fmop('-', $a); } function function_mul() { $a = func_get_args(); return self::fmop('*', $a); } function function_div() { $a = func_get_args(); return self::fmop('/', $a); } function function_mod($a,$b) { return "(($a) % ($b))"; } /* минимум и максимум, округление */ function function_min() { $a = func_get_args(); return "min([ ".implode(', ', $a)." ])"; } function function_max() { $a = func_get_args(); return "max([ ".implode(', ', $a)." ])"; } function function_round($a) { return "round($a)"; } /* логарифм */ function function_log($e) { return "log($e)"; } /* чётный, нечётный */ function function_even($e) { return "!(($e) & 1)"; } function function_odd($e) { return "(($e) & 1)"; } /* приведение к целому числу */ function function_int($e) { return "intval($e)"; } /* сравнения: == != > < >= <= (аргументов как строк если оба строки, иначе как чисел) */ function function_eq($a,$b) { return "(($a) == ($b))"; } function function_ne($a,$b) { return "(($a) != ($b))"; } function function_gt($a,$b) { return "(($a) > ($b))"; } function function_lt($a,$b) { return "(($a) < ($b))"; } function function_ge($a,$b) { return "(($a) >= ($b))"; } function function_le($a,$b) { return "(($a) <= ($b))"; } /* сравнения: == != > < >= <= (аргументов как строк) */ function function_seq($a,$b) { return "((\"$a\") == (\"$b\"))"; } function function_sne($a,$b) { return "((\"$a\") != (\"$b\"))"; } function function_sgt($a,$b) { return "((\"$a\") > (\"$b\"))"; } function function_slt($a,$b) { return "((\"$a\") < (\"$b\"))"; } function function_sge($a,$b) { return "((\"$a\") >= (\"$b\"))"; } function function_sle($a,$b) { return "((\"$a\") <= (\"$b\"))"; } /* сравнения: == != > < >= <= (аргументов как чисел) */ function function_neq($a,$b) { return "((0+$a) == ($b))"; } function function_nne($a,$b) { return "((0+$a) != ($b))"; } function function_ngt($a,$b) { return "((0+$a) > ($b))"; } function function_nlt($a,$b) { return "((0+$a) < ($b))"; } function function_nge($a,$b) { return "((0+$a) >= ($b))"; } function function_nle($a,$b) { return "((0+$a) <= ($b))"; } /* тернарный оператор $1 ? $2 : $3 */ function function_yesno($a,$b,$c) { return "(($a) ? ($b) : ($c))"; } /** Строки **/ /* нижний регистр */ function function_lc($e) { return ($this->options->use_utf8 ? "mb_" : "") . "strtolower($e)"; } /* верхний регистр */ function function_uc($e) { return ($this->options->use_utf8 ? "mb_" : "") . "strtoupper($e)"; } /* нижний регистр первого символа */ function function_lcfirst($e) { return ($this->options->use_utf8 ? "self::mb_" : "") . "lcfirst($e)"; } /* верхний регистр первого символа */ function function_ucfirst($e) { return ($this->options->use_utf8 ? "self::mb_" : "") . "ucfirst($e)"; } /* экранирование кавычек */ function function_quote($e) { return "str_replace(array(\"\\n\",\"\\r\"),array(\"\\\\n\",\"\\\\r\"),addslashes($e))"; } /* экранирование кавычек в SQL- или CSV- стиле (кавычка " превращается в двойную кавычку "") */ function function_sql_quote($e) { return "str_replace('\"','\"\"',$e)"; } /* экранирование символов, специальных для регулярного выражения */ function function_requote($e) { return "preg_quote($e)"; } /* экранирование в стиле URL */ function function_urlencode($e) { return "urlencode($e)"; } /* замены - по регулярке и по подстроке */ function function_replace($re, $sub, $v) { return "preg_replace('#'.str_replace('#','\\\\#',$re).'#s', $sub, $v)"; } function function_str_replace($s, $sub, $v) { return "str_replace($s, $sub, $v)"; } /* длина строки */ function function_strlen($s) { return ($this->options->use_utf8 ? "mb_" : "") . "strlen($s)"; } /* подстрока */ function function_substr($s, $start, $length = NULL) { return ($this->options->use_utf8 ? "mb_" : "") . "substr($s, $start" . ($length !== NULL ? ", $length" : "") . ")"; } /* убиение пробелов в начале и конце */ function function_trim($s) { return "trim($s)"; } /* разбиение строки по регулярному выражению */ function function_split($re, $v, $limit = -1) { return "preg_split('#'.str_replace('#','\\\\#',$re).'#s', $v, $limit)"; } /* пустое преобразование, для отмены автоэкранирования HTML */ function function_raw($e) { return $e; } /* преобразование символов <>&'" в HTML-сущности < > & ' " */ function function_html($e) { return "htmlspecialchars($e,ENT_QUOTES)"; } /* удаление всех или заданных тегов */ function function_strip($e, $t='') { return "self::strip_tags($e".($t?",$t":"").")"; } /* удаление "небезопасных" HTML-тегов */ /* TODO: м.б исправлять некорректную разметку? */ function function_strip_unsafe($e) { return "self::strip_tags($e, self::\$safe_tags)"; } /* заменить \n на
*/ function function_nl2br($s) { return "nl2br($s)"; } /* конкатенация строк */ function function_concat() { $a = func_get_args(); return self::fmop('.', $a); } /* объединение всех скаляров и всех элементов аргументов-массивов */ function function_join() { $a = func_get_args(); $sep = array_shift($a); return "call_user_func('implode', $sep, self::merge_to_array(".implode(', ', $a)."))"; } /* подставляет на места $1, $2 и т.п. в строке аргументы */ function function_subst() { $a = func_get_args(); return "call_user_func_array('VMXTemplate::exec_subst', self::merge_to_array(".implode(', ', $a)."))"; } /* sprintf */ function function_sprintf() { $a = func_get_args(); return "call_user_func_array('sprintf', self::merge_to_array(".implode(', ', $a)."))"; } /* strftime */ function function_strftime($fmt, $date = NULL) { if ($date !== NULL) { $date = ", self::timestamp($date)"; } return "strftime($fmt$date)"; } /* ограничение длины строки $maxlen символами на границе пробелов и добавление '...', если что. */ /* strlimit(string, length, dots = '...') */ function function_strlimit($a) { $a = func_get_args(); return "self::" . ($this->options->use_utf8 ? "mb_" : "") . "strlimit(".join(",", $a).")"; } /* выбор правильной формы множественного числа для русского языка */ function function_plural_ru($count, $one, $few, $many) { return "self::plural_ru($count, $one, $few, $many)"; } /** Массивы и хеши **/ /* создание хеша */ function function_hash() { $a = func_get_args(); if (count($a) == 1) return "self::exec_hash(".$a[0].")"; $s = "array("; $i = 0; $d = ''; foreach ($a as $v) { $s .= $d; $s .= $v; $i++; if ($i & 1) $d = '=>'; else $d = ','; } $s .= ")"; return $s; } /* ключи хеша или массива */ function function_keys($a) { return "array_keys(is_array($a) ? $a : array())"; } /* значения хеша или массива */ function function_values($a) { return "array_values(is_array($a) ? $a : array())"; } /* сортировка массива/массивов */ function function_sort() { $a = func_get_args(); return "call_user_func('VMXTemplate::exec_sort', self::merge_to_array(".implode(', ', $a)."))"; } /* пары key => ключ, value => значение для ассоциативного массива */ function function_pairs() { $a = func_get_args(); return "self::exec_pairs(".implode(', ', $a).")"; } /* создание массива */ function function_array() { $a = func_get_args(); return "array(" . join(",", $a) . ")"; } /* диапазон от $1 до $2 */ function function_range($a, $b) { return "range($a,$b)"; } /* проверка, массив это или нет? */ function function_is_array($a) { return "is_array($a)"; } /* число элементов в массиве */ function function_count($e) { return "self::array_count($e)"; } /* подмассив по номерам элементов */ function function_subarray() { $a = func_get_args(); return "array_slice(" . join(",", $a) . ")"; } /* подмассив по кратности номеров элементов */ function function_subarray_divmod() { $a = func_get_args(); return "self::exec_subarray_divmod(" . join(",", $a) . ")"; } /* 0) получить "корневую" переменную по неконстантному ключу 1) получить элемент хеша/массива по неконстантному ключу (например get(iteration.array, rand(5))) по-моему, это лучше, чем Template Toolkit'овский ад - hash.key.${another.hash.key}.зюка.хрюка и т.п. 2) получить элемент выражения-массива - в PHP < 5.4 не работает (...expression...)['key'], к примеру не работает range(1,10)[0] но у нас-то это поддерживается... */ function function_get($a, $k = NULL) { if ($k === NULL) return "\$this->tpldata[$a]"; if (PHP_VERSION_ID > 50400) return $a."[$k]"; return "self::exec_get($a, $k)"; } /* присваивание (только lvalue) */ function function_set($l, $r) { return "self::void($l = $r)"; } /* объединение массивов */ function function_array_merge() { $a = func_get_args(); return "array_merge(" . join(",", $a) . ")"; } /* shift, unshift, pop, push */ function function_shift($a) { return "array_shift($a)"; } function function_pop($a) { return "array_pop($a)"; } function function_unshift($a, $v) { return "array_unshift($a, $v)"; } function function_push($a, $v) { return "array_push($a, $v)"; } /** Прочее **/ /* игнорирование результата (а-ля js) */ function function_void($a) { return "self::void($a)"; } /* дамп переменной */ function function_dump($var) { return "var_export($var, true)"; } /* JSON-кодирование */ function function_json($v) { return "json_encode($v, JSON_UNESCAPED_UNICODE)"; } /* Аргументы для функций включения аргументы ::= hash(ключ => значение, ...) | ключ => значение, ... */ protected function auto_hash($args) { if (!($n = count($args))) $args = ', $this->tpldata, false'; elseif ($n == 1) $args = ", ".$args[0]; else $args = ", ".call_user_func_array(array($this, 'function_hash'), $args); return $args; } /* включение другого файла: parse('файл'[, аргументы]) */ function function_parse() { $args = func_get_args(); $file = array_shift($args); $p = $args ? 'parse_discard' : 'parse_real'; $args = $this->auto_hash($args); return "\$this->parent->$p($file, NULL, 'main'$args)"; } /* включение блока из текущего файла: exec('блок'[, аргументы]) */ function function_exec() { $args = func_get_args(); $block = array_shift($args); $p = $args ? 'parse_discard' : 'parse_real'; $args = $this->auto_hash($args); return "\$this->parent->$p(self::\$template_filename, NULL, $block$args)"; } /* включение блока из другого файла: exec_from('файл', 'блок'[, аргументы]) */ function function_exec_from() { $args = func_get_args(); $file = array_shift($args); $block = array_shift($args); $p = $args ? 'parse_discard' : 'parse_real'; $args = $this->auto_hash($args); return "\$this->parent->$p($file, NULL, $block$args)"; } /* parse не из файла, хотя и не рекомендуется */ function function_parse_inline() { $args = func_get_args(); $code = array_shift($args); $p = $args ? 'parse_discard' : 'parse_real'; $args = $this->auto_hash($args); return "\$this->parent->$p(NULL, $code, 'main'$args)"; } /* сильно не рекомендуется, но возможно: включение блока не из файла: exec_from_inline('код', 'блок'[, аргументы]) */ function function_exec_from_inline() { $args = func_get_args(); $code = array_shift($args); $block = array_shift($args); $p = $args ? 'parse_discard' : 'parse_real'; $args = $this->auto_hash($args); return "\$this->parent->$p(NULL, $code, $block$args)"; } /* вызов функции объекта по вычисляемому имени: call(object, "method", arg1, arg2, ...) или call_array(object, "method", array(arg1, arg2, ...)) */ function function_call() { $a = func_get_args(); $o = array_shift($a); $m = array_shift($a); return "call_user_func_array(array($o, $m), array(".implode(", ", $a)."))"; } function function_call_array($o, $m, $a = NULL) { return "call_user_func_array(array($o, $m), ".($a ? $a : "array()").")"; } /* map() */ function function_map($f) { if (!preg_match('/^(["\'])([a-z_]+)\1$/s', $f, $m)) { $this->lexer->warn("Non-constant function specified for map(): $f"); return 'false'; } $fn = $m[2]; if (isset(self::$functions[$fn])) { // Function alias $fn = self::$functions[$fn]; } if (!method_exists($this, "function_$fn")) { $this->lexer->warn("Unknown function specified for map(): $f"); return 'false'; } $fn = "function_$fn"; $fn = $this->$fn('$arg'); $args = func_get_args(); array_shift($args); return "array_map(function(\$arg) { return $fn; }, self::merge_to_array(".implode(", ", $args)."))"; } } /** * State object */ class VMXTemplateState { // Functions var $functions = array(); } /** * Lexical analyzer (~regexp) */ class VMXTemplateLexer { var $options; // Code (string) and current position inside it var $code, $codelen, $pos, $lineno; // Last directive start position, directive and substitution start/end counters var $last_start, $last_start_line, $in_code, $in_subst, $force_literal = 0; // Possible tokens consisting of special characters static $chartokens = '+ - = * / % ! ? : , . < > ( ) { } [ ] & .. || && == != <= >= =>'; // Reserved keywords static $keywords_str = 'OR XOR AND NOT IF ELSE ELSIF ELSEIF END SET FOR FOREACH FUNCTION BLOCK MACRO'; var $nchar, $lens, $keywords; function __construct(VMXTemplateOptions $options) { $this->options = $options; $this->nchar = array(); foreach (explode(' ', self::$chartokens) as $t) { $this->nchar[strlen($t)][$t] = true; } // Add code fragment finishing tokens $this->nchar[strlen($this->options->end_code)][$this->options->end_code] = true; if ($this->options->end_subst) { $this->nchar[strlen($this->options->end_subst)][$this->options->end_subst] = true; } // Reverse-sort lengths $this->lens = array_keys($this->nchar); rsort($this->lens); $this->keywords = array_flip(explode(' ', self::$keywords_str)); } function feed($parser) { try { $parser->reset(); $in = false; while ($t = $this->read_token()) { $success = $parser->eat($t[0], $t[1]); if (!$success) { // Pass $in from last step so we skip to the beginning // of directive even if it just ended and $this->in_* == 0 $this->skip_error(end($parser->parser->errors), $in); } $in = $this->in_code || $this->in_subst; } $parser->eat_eof(); } catch (parse_error $e) { $this->options->error($e->getMessage()); } } function set_code($code) { $this->code = $code; $this->codelen = strlen($this->code); $this->pos = $this->lineno = 0; } function errorinfo() { $linestart = strrpos($this->code, "\n", $this->pos-$this->codelen-1) ?: -1; $lineend = strpos($this->code, "\n", $this->pos) ?: $this->codelen; $line = substr($this->code, $linestart+1, $this->pos-$linestart-1); $line .= '^^^'; $line .= substr($this->code, $this->pos, $lineend-$this->pos); return " in {$this->options->input_filename}, line ".($this->lineno+1).", byte {$this->pos}, marked by ^^^ in $line"; } function warn($text) { $this->options->error($text.$this->errorinfo()); } /** * Skip a directive */ function skip_error($e, $force = false) { if (substr($e, 0, 18) !== 'error not expected') { $this->warn($e); if ($this->in_code || $this->in_subst || $force) { $this->in_code = $this->in_subst = 0; $this->pos = $this->last_start; $this->lineno = $this->last_start_line; $this->force_literal = 1; } } } /** * Read next token from the stream * Returns array($token, $value) or false for EOF */ function read_token() { if ($this->pos >= $this->codelen) { // End of code return false; } if ($this->in_code <= 0 && $this->in_subst <= 0) { $was_code = true; $code_pos = strpos($this->code, $this->options->begin_code, $this->pos+$this->force_literal); $subst_pos = strpos($this->code, $this->options->begin_subst, $this->pos+$this->force_literal); $this->force_literal = 0; if ($code_pos === false && $subst_pos === false) { $r = array('literal', "'".addcslashes(substr($this->code, $this->pos), "'\\")."'"); $this->lineno += substr_count($r[1], "\n"); $this->pos = $this->codelen; } elseif ($subst_pos === false || $code_pos !== false && $subst_pos > $code_pos) { // Code starts closer if ($code_pos > $this->pos) { // We didn't yet reach the code beginning $str = substr($this->code, $this->pos, $code_pos-$this->pos); $this->lineno += substr_count($str, "\n"); if ($this->options->eat_code_line) { $str = preg_replace('/\n[ \t]*$/s', $was_code ? '' : "\n", $str); } $r = array('literal', "'".addcslashes($str, "'\\")."'"); $this->pos = $code_pos; } elseif ($code_pos !== false) { // We are at the code beginning ($this->pos == $code_pos) $i = $this->pos+strlen($this->options->begin_code); while ($i < $this->codelen && (($c = $this->code{$i}) == ' ' || $c == "\t")) { $i++; } if ($i < $this->codelen && $this->code{$i} == '#') { // Strip comment $i = strpos($this->code, $this->options->end_code, $i); $this->pos = $i ? $i+strlen($this->options->end_code) : $this->codelen; return $this->read_token(); } $r = array('', $t); } } elseif ($this->in_subst) { $this->in_subst += ($t === $this->options->begin_subst); $this->in_subst -= ($t === $this->options->end_subst); if (!$this->in_subst) { return array('}}', $t); } } return array($t, false); } } // Unknown character $this->skip_error( "Unexpected character '".$this->code{$this->pos}."'" ); return array('error', false); } } } /** * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ define('LIME_CALL_PROTOCOL', '$tokens, &$result'); abstract class lime_parser { } /** * The input doesn't match the grammar */ class parse_error extends Exception { } /** * Bug, I made a mistake */ class parse_bug extends Exception { } class parse_unexpected_token extends parse_error { public function __construct($type, $state) { parent::__construct("Unexpected token of type {$type}"); $this->type = $type; $this->state = $state; } } class parse_premature_eof extends parse_error { public function __construct(array $expect) { parent::__construct('Premature EOF'); } } class parse_stack { public $q; public $qs = array(); /** * Stack of semantic actions */ public $ss = array(); public function __construct($qi) { $this->q = $qi; } public function shift($q, $semantic) { $this->ss[] = $semantic; $this->qs[] = $this->q; $this->q = $q; // echo "Shift $q -- $semantic\n"; } public function top_n($n) { if (!$n) { return array(); } return array_slice($this->ss, 0 - $n); } public function pop_n($n) { if (!$n) { return array(); } $qq = array_splice($this->qs, 0 - $n); $this->q = $qq[0]; return array_splice($this->ss, 0 - $n); } public function occupied() { return !empty($this->ss); } public function index($n) { if ($n) { $this->q = $this->qs[count($this->qs) - $n]; } } public function text() { return $this->q . ' : ' . implode(' . ', array_reverse($this->qs)); } } class parse_engine { public $debug = false; public $parser; public $qi; public $rule; public $step; public $descr; /** * @var boolean */ public $accept; /** * @var parse_stack */ public $stack; public function __construct($parser) { $this->parser = $parser; $this->qi = $parser->qi; $this->rule = $parser->a; $this->step = $parser->i; $this->descr = $parser->d; $this->reset(); } public function reset() { $this->accept = false; $this->stack = new parse_stack($this->qi); $this->parser->errors = array(); } private function enter_error_tolerant_state() { while ($this->stack->occupied()) { if ($this->has_step_for('error')) { return true; } if ($this->debug) echo "Dropped an item from the stack, {" . implode(', ', $this->get_steps()) . "} left\n"; if ($this->debug) echo 'Currently in state ' . $this->state() . "\n"; $this->drop(); } return false; } private function drop() { $this->stack->pop_n(1); } /* * So that I don't get any brilliant misguided ideas: * * The "accept" step happens when we try to eat a start symbol. * That happens because the reductions up the stack at the end * finally (and symetrically) tell the parser to eat a symbol * representing what they've just shifted off the end of the stack * and reduced. However, that doesn't put the parser into any * special different state. Therefore, it's back at the start * state. * * That being said, the parser is ready to reduce an EOF to the * empty program, if given a grammar that allows them. * * So anyway, if you literally tell the parser to eat an EOF * symbol, then after it's done reducing and accepting the prior * program, it's going to think it has another symbol to deal with. * That is the EOF symbol, which means to reduce the empty program, * accept it, and then continue trying to eat the terminal EOF. * * This infinte loop quickly runs out of memory. * * That's why the real EOF algorithm doesn't try to pretend that * EOF is a terminal. Like the invented start symbol, it's special. * * Instead, we pretend to want to eat EOF, but never actually * try to get it into the parse stack. (It won't fit.) In short, * we look up what reduction is indicated at each step in the * process of rolling up the parse stack. * * The repetition is because one reduction is not guaranteed to * cascade into another and clean up the entire parse stack. * Rather, it will instead shift each partial production as it * is forced to completion by the EOF lookahead. */ public function eat_eof() { // We must reduce as if having read the EOF symbol do { // and we have to try at least once, because if nothing // has ever been shifted, then the stack will be empty // at the start. list($opcode, $operand) = $this->step_for('#'); switch ($opcode) { case 'r': $this->reduce($operand); break; case 'e': $this->premature_eof(); break; default: throw new parse_bug(); } } while ($this->stack->occupied()); // If the sentence is well-formed according to the grammar, then // this will eventually result in eating a start symbol, which // causes the "accept" instruction to fire. Otherwise, the // step('#') method will indicate an error in the syntax, which // here means a premature EOF. // // Incidentally, some tremendous amount of voodoo with the parse // stack might help find the beginning of some unfinished // production that the sentence was cut off during, but as a // general rule that would require deeper knowledge. if (!$this->accept) { throw new parse_bug(); } return $this->semantic; } private function premature_eof() { $seen = array(); $expect = $this->get_steps(); while ($this->enter_error_tolerant_state() || $this->has_step_for('error')) { if (isset($seen[$this->state()])) { // This means that it's pointless to try here. // We're guaranteed that the stack is occupied. $this->drop(); continue; } $seen[$this->state()] = true; $this->eat('error', 'Premature EOF'); if ($this->has_step_for('#')) { // Good. We can continue as normal. return; } else { // That attempt to resolve the error condition // did not work. There's no point trying to // figure out how much to slice off the stack. // The rest of the algorithm will make it happen. } } throw new parse_premature_eof($expect); } private function current_row() { return $this->step[$this->state()]; } private function step_for($type) { $row = $this->current_row(); if (isset($row[$type])) { return explode(' ', $row[$type]); } if (isset($row[''])) { return explode(' ', $row['']); } return array('e', $this->stack->q); } private function get_steps() { $out = array(); foreach($this->current_row() as $type => $row) { foreach($this->rule as $rule) { if ($rule['symbol'] == $type) { continue 2; } } list($opcode) = explode(' ', $row, 2); if ($opcode != 'e') { $out[] = $type === '' ? '$default' : $type; } } return $out; } private function has_step_for($type) { $row = $this->current_row(); return isset($row[$type]); } private function state() { return $this->stack->q; } function eat($type, $semantic) { // assert('$type == trim($type)'); if ($this->debug) echo "Trying to eat a ($type)\n"; list($opcode, $operand) = $this->step_for($type); switch ($opcode) { case 's': if ($this->debug) echo "shift $type to state $operand\n"; $this->stack->shift($operand, $semantic); // echo $this->stack->text()." shift $type
\n"; break; case 'r': if ($this->debug) echo "Reducing $type via rule $operand\n"; $this->reduce($operand); // Yes, this is tail-recursive. It's also the simplest way. return $this->eat($type, $semantic); case 'a': if ($this->stack->occupied()) { throw new parse_bug('Accept should happen with empty stack.'); } $this->accept = true; if ($this->debug) echo ("Accept\n\n"); $this->semantic = $semantic; break; case 'e': // This is thought to be the uncommon, exceptional path, so // it's OK that this algorithm will cause the stack to // flutter while the parse engine waits for an edible token. if ($this->debug) echo "($type) causes a problem.\n"; // get these before doing anything $expected = $this->get_steps(); $this->parser->errors[] = $this->descr($type, $semantic) . ' not expected, expected one of ' . implode(', ', $expected); if ($this->debug) echo "Possibilities before error fixing: {" . implode(', ', $expected) . "}\n"; if ($this->enter_error_tolerant_state() || $this->has_step_for('error')) { $this->eat('error', end($this->parser->errors)); if ($this->has_step_for($type)) { $this->eat($type, $semantic); } return false; } else { // If that didn't work, give up: throw new parse_error('Parse Error: ' . $this->descr($type, $semantic) . ' not expected, expected one of ' . implode(', ', $expected)); } break; default: throw new parse_bug("Bad parse table instruction " . htmlspecialchars($opcode)); } return true; } private function descr($type, $semantic) { if (isset($this->descr[$type])) { return $this->descr[$type]; } elseif ("$semantic" !== "") { return $type . ' (' . $semantic . ')'; } else { return $type; } } private function reduce($rule_id) { $rule = $this->rule[$rule_id]; $len = $rule['len']; $semantic = $this->perform_action($rule_id, $this->stack->top_n($len)); //echo $semantic.br(); if ($rule['replace']) { $this->stack->pop_n($len); } else { $this->stack->index($len); } $this->eat($rule['symbol'], $semantic); } private function perform_action($rule_id, $slice) { // we have this weird calling convention.... $result = null; $method = $this->parser->method[$rule_id]; //if ($this->debug) echo "rule $id: $method\n"; $this->parser->$method($slice, $result); return $result; } } /* *** DON'T EDIT THIS FILE! *** * * This file was automatically generated by the Lime parser generator. * The real source code you should be looking at is in one or more * grammar files in the Lime format. * * THE ONLY REASON TO LOOK AT THIS FILE is to see where in the grammar * file that your error happened, because there are enough comments to * help you debug your grammar. * If you ignore this warning, you're shooting yourself in the brain, * not the foot. */ class VMXTemplateParser extends lime_parser { public $qi = 0; public $i = array( array( 'chunks' => 's 1', 'template' => 's 191', "'start'" => "a 'start'", 'literal' => 'r 1', '' => 's 6' ), array( '' => 'r 4' ), array( '' => 'r 7' ), array( '' => 'r 8' ), array( '' => 'r 9' ), array( '' => 'r 10' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 11' ), array( 'exp' => 's 13', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 31', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 31', ':' => 'r 31', ')' => 'r 31', ',' => 'r 31', '=>' => 'r 31', ']' => 'r 31', '}}' => 'r 31', '}' => 'r 31' ), array( 'exp' => 's 15', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 32', '||' => 'r 32', 'OR' => 'r 32', 'XOR' => 'r 32', '&&' => 's 20', 'AND' => 's 22', '?' => 'r 32', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 32', ':' => 'r 32', ')' => 'r 32', ',' => 'r 32', '=>' => 'r 32', ']' => 'r 32', '}}' => 'r 32', '}' => 'r 32' ), array( 'exp' => 's 17', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 33', '||' => 'r 33', 'OR' => 'r 33', 'XOR' => 'r 33', '&&' => 's 20', 'AND' => 's 22', '?' => 'r 33', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 33', ':' => 'r 33', ')' => 'r 33', ',' => 'r 33', '=>' => 'r 33', ']' => 'r 33', '}}' => 'r 33', '}' => 'r 33' ), array( 'exp' => 's 19', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 34', '||' => 'r 34', 'OR' => 'r 34', 'XOR' => 'r 34', '&&' => 's 20', 'AND' => 's 22', '?' => 'r 34', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 34', ':' => 'r 34', ')' => 'r 34', ',' => 'r 34', '=>' => 'r 34', ']' => 'r 34', '}}' => 'r 34', '}' => 'r 34' ), array( 'exp' => 's 21', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 35', '||' => 'r 35', 'OR' => 'r 35', 'XOR' => 'r 35', '&&' => 'r 35', 'AND' => 'r 35', '?' => 'r 35', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 35', ':' => 'r 35', ')' => 'r 35', ',' => 'r 35', '=>' => 'r 35', ']' => 'r 35', '}}' => 'r 35', '}' => 'r 35' ), array( 'exp' => 's 23', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 36', '||' => 'r 36', 'OR' => 'r 36', 'XOR' => 'r 36', '&&' => 'r 36', 'AND' => 'r 36', '?' => 'r 36', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 36', ':' => 'r 36', ')' => 'r 36', ',' => 'r 36', '=>' => 'r 36', ']' => 'r 36', '}}' => 'r 36', '}' => 'r 36' ), array( 'exp' => 's 25', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', ':' => 's 26', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50' ), array( 'exp' => 's 27', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 37', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 37', ':' => 'r 37', ')' => 'r 37', ',' => 'r 37', '=>' => 'r 37', ']' => 'r 37', '}}' => 'r 37', '}' => 'r 37' ), array( 'exp' => 's 29', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 38', '||' => 'r 38', 'OR' => 'r 38', 'XOR' => 'r 38', '&&' => 'r 38', 'AND' => 'r 38', '?' => 'r 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 38', ':' => 'r 38', ')' => 'r 38', ',' => 'r 38', '=>' => 'r 38', ']' => 'r 38', '}}' => 'r 38', '}' => 'r 38' ), array( 'exp' => 's 31', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 39', '||' => 'r 39', 'OR' => 'r 39', 'XOR' => 'r 39', '&&' => 'r 39', 'AND' => 'r 39', '?' => 'r 39', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 39', ':' => 'r 39', ')' => 'r 39', ',' => 'r 39', '=>' => 'r 39', ']' => 'r 39', '}}' => 'r 39', '}' => 'r 39' ), array( 'exp' => 's 33', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 40', '||' => 'r 40', 'OR' => 'r 40', 'XOR' => 'r 40', '&&' => 'r 40', 'AND' => 'r 40', '?' => 'r 40', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 40', ':' => 'r 40', ')' => 'r 40', ',' => 'r 40', '=>' => 'r 40', ']' => 'r 40', '}}' => 'r 40', '}' => 'r 40' ), array( 'exp' => 's 35', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 41', '||' => 'r 41', 'OR' => 'r 41', 'XOR' => 'r 41', '&&' => 'r 41', 'AND' => 'r 41', '?' => 'r 41', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 41', ':' => 'r 41', ')' => 'r 41', ',' => 'r 41', '=>' => 'r 41', ']' => 'r 41', '}}' => 'r 41', '}' => 'r 41' ), array( 'exp' => 's 37', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 42', '||' => 'r 42', 'OR' => 'r 42', 'XOR' => 'r 42', '&&' => 'r 42', 'AND' => 'r 42', '?' => 'r 42', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 42', ':' => 'r 42', ')' => 'r 42', ',' => 'r 42', '=>' => 'r 42', ']' => 'r 42', '}}' => 'r 42', '}' => 'r 42' ), array( 'exp' => 's 39', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 43', '||' => 'r 43', 'OR' => 'r 43', 'XOR' => 'r 43', '&&' => 'r 43', 'AND' => 'r 43', '?' => 'r 43', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 43', ':' => 'r 43', ')' => 'r 43', ',' => 'r 43', '=>' => 'r 43', ']' => 'r 43', '}}' => 'r 43', '}' => 'r 43' ), array( 'exp' => 's 41', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 44', '||' => 'r 44', 'OR' => 'r 44', 'XOR' => 'r 44', '&&' => 'r 44', 'AND' => 'r 44', '?' => 'r 44', '==' => 'r 44', '!=' => 'r 44', '<' => 'r 44', '>' => 'r 44', '<=' => 'r 44', '>=' => 'r 44', '+' => 'r 44', '-' => 'r 44', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 44', ':' => 'r 44', ')' => 'r 44', ',' => 'r 44', '=>' => 'r 44', ']' => 'r 44', '}}' => 'r 44', '}' => 'r 44' ), array( 'exp' => 's 43', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 45', '||' => 'r 45', 'OR' => 'r 45', 'XOR' => 'r 45', '&&' => 'r 45', 'AND' => 'r 45', '?' => 'r 45', '==' => 'r 45', '!=' => 'r 45', '<' => 'r 45', '>' => 'r 45', '<=' => 'r 45', '>=' => 'r 45', '+' => 'r 45', '-' => 'r 45', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 45', ':' => 'r 45', ')' => 'r 45', ',' => 'r 45', '=>' => 'r 45', ']' => 'r 45', '}}' => 'r 45', '}' => 'r 45' ), array( 'exp' => 's 45', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 'r 46', '||' => 'r 46', 'OR' => 'r 46', 'XOR' => 'r 46', '&&' => 'r 46', 'AND' => 'r 46', '?' => 'r 46', '==' => 'r 46', '!=' => 'r 46', '<' => 'r 46', '>' => 'r 46', '<=' => 'r 46', '>=' => 'r 46', '+' => 'r 46', '-' => 'r 46', '&' => 'r 46', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 46', ':' => 'r 46', ')' => 'r 46', ',' => 'r 46', '=>' => 'r 46', ']' => 'r 46', '}}' => 'r 46', '}' => 'r 46' ), array( 'exp' => 's 47', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '' => 'r 47' ), array( 'exp' => 's 49', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '' => 'r 48' ), array( 'exp' => 's 51', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '' => 'r 49' ), array( '' => 'r 50' ), array( '' => 'r 51' ), array( 'p11' => 's 55', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '' => 'r 52' ), array( '' => 'r 53' ), array( 'exp' => 's 58', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', ')' => 's 59' ), array( 'varpath' => 's 60', '.' => 'r 83', '[' => 'r 83', '%' => 'r 83', '/' => 'r 83', '*' => 'r 83', '&' => 'r 83', '-' => 'r 83', '+' => 'r 83', '>=' => 'r 83', '<=' => 'r 83', '>' => 'r 83', '<' => 'r 83', '!=' => 'r 83', '==' => 'r 83', '?' => 'r 83', 'AND' => 'r 83', '&&' => 'r 83', 'XOR' => 'r 83', 'OR' => 'r 83', '||' => 'r 83', '..' => 'r 83', '-->' => 'r 83', ':' => 'r 83', ')' => 'r 83', ',' => 'r 83', '=>' => 'r 83', ']' => 'r 83', '}}' => 'r 83', '}' => 'r 83' ), array( '.' => 's 61', '[' => 's 77', 'varpart' => 's 121', '%' => 'r 54', '/' => 'r 54', '*' => 'r 54', '&' => 'r 54', '-' => 'r 54', '+' => 'r 54', '>=' => 'r 54', '<=' => 'r 54', '>' => 'r 54', '<' => 'r 54', '!=' => 'r 54', '==' => 'r 54', '?' => 'r 54', 'AND' => 'r 54', '&&' => 'r 54', 'XOR' => 'r 54', 'OR' => 'r 54', '||' => 'r 54', '..' => 'r 54', '-->' => 'r 54', ':' => 'r 54', ')' => 'r 54', ',' => 'r 54', '=>' => 'r 54', ']' => 'r 54', '}}' => 'r 54', '}' => 'r 54' ), array( 'namekw' => 's 62', 'name' => 's 105', 'IF' => 's 106', 'END' => 's 107', 'ELSE' => 's 108', 'ELSIF' => 's 109', 'ELSEIF' => 's 110', 'SET' => 's 111', 'OR' => 's 112', 'XOR' => 's 113', 'AND' => 's 114', 'NOT' => 's 115', 'FUNCTION' => 's 116', 'BLOCK' => 's 117', 'MACRO' => 's 118', 'FOR' => 's 119', 'FOREACH' => 's 120' ), array( '(' => 's 63', '.' => 'r 79', '[' => 'r 79', '%' => 'r 79', '/' => 'r 79', '*' => 'r 79', '&' => 'r 79', '-' => 'r 79', '+' => 'r 79', '>=' => 'r 79', '<=' => 'r 79', '>' => 'r 79', '<' => 'r 79', '!=' => 'r 79', '==' => 'r 79', '?' => 'r 79', 'AND' => 'r 79', '&&' => 'r 79', 'XOR' => 'r 79', 'OR' => 'r 79', '||' => 'r 79', '..' => 'r 79', '-->' => 'r 79', ':' => 'r 79', ')' => 'r 79', ',' => 'r 79', '=>' => 'r 79', '=' => 'r 79', ']' => 'r 79', '}}' => 'r 79', '}' => 'r 79' ), array( 'exp' => 's 64', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80', ')' => 's 102', 'list' => 's 103' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', ',' => 's 65', ')' => 'r 64' ), array( 'exp' => 's 64', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80', 'list' => 's 101' ), array( 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'p11' => 's 67', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '' => 'r 55' ), array( 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', 'p11' => 's 69', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '' => 'r 56' ), array( 'exp' => 's 71', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'hash' => 's 95', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80', 'pair' => 's 97', 'gtpair' => 's 100', '}' => 'r 71' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', ',' => 's 72', '=>' => 's 83' ), array( 'exp' => 's 73', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', ',' => 'r 74', '}' => 'r 74' ), array( '' => 'r 58' ), array( 'varpart' => 's 76', '.' => 's 61', '[' => 's 77', '%' => 'r 59', '/' => 'r 59', '*' => 'r 59', '&' => 'r 59', '-' => 'r 59', '+' => 'r 59', '>=' => 'r 59', '<=' => 'r 59', '>' => 'r 59', '<' => 'r 59', '!=' => 'r 59', '==' => 'r 59', '?' => 'r 59', 'AND' => 'r 59', '&&' => 'r 59', 'XOR' => 'r 59', 'OR' => 'r 59', '||' => 'r 59', '..' => 'r 59', '-->' => 'r 59', ':' => 'r 59', ')' => 'r 59', ',' => 'r 59', '=>' => 'r 59', ']' => 'r 59', '}}' => 'r 59', '}' => 'r 59' ), array( '' => 'r 78' ), array( 'exp' => 's 78', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', ']' => 's 79' ), array( '' => 'r 80' ), array( '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80', '(' => 's 81', 'nonbrace' => 's 94', '.' => 'r 77', '[' => 'r 77', '%' => 'r 77', '/' => 'r 77', '*' => 'r 77', '&' => 'r 77', '-' => 'r 77', '+' => 'r 77', '>=' => 'r 77', '<=' => 'r 77', '>' => 'r 77', '<' => 'r 77', '!=' => 'r 77', '==' => 'r 77', '?' => 'r 77', 'AND' => 'r 77', '&&' => 'r 77', 'XOR' => 'r 77', 'OR' => 'r 77', '||' => 'r 77', '..' => 'r 77', '-->' => 'r 77', ':' => 'r 77', ')' => 'r 77', ',' => 'r 77', '=>' => 'r 77', ']' => 'r 77', '}}' => 'r 77', '}' => 'r 77' ), array( 'exp' => 's 82', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80', ')' => 's 85', 'list' => 's 86', 'gthash' => 's 88', 'gtpair' => 's 90' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', ',' => 's 65', '=>' => 's 83', ')' => 'r 64' ), array( 'exp' => 's 84', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', ',' => 'r 76', '}' => 'r 76', ')' => 'r 76' ), array( '' => 'r 60' ), array( ')' => 's 87' ), array( '' => 'r 61' ), array( ')' => 's 89' ), array( '' => 'r 62' ), array( ',' => 's 91', ')' => 'r 72' ), array( 'exp' => 's 92', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80', 'gtpair' => 's 90', 'gthash' => 's 93' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '=>' => 's 83' ), array( '' => 'r 73' ), array( '' => 'r 63' ), array( '}' => 's 96' ), array( '' => 'r 57' ), array( ',' => 's 98', '}' => 'r 69' ), array( 'exp' => 's 71', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80', 'pair' => 's 97', 'hash' => 's 99', 'gtpair' => 's 100', '}' => 'r 71' ), array( '' => 'r 70' ), array( '' => 'r 75' ), array( '' => 'r 65' ), array( '' => 'r 81' ), array( ')' => 's 104' ), array( '' => 'r 82' ), array( '' => 'r 85' ), array( '' => 'r 86' ), array( '' => 'r 87' ), array( '' => 'r 88' ), array( '' => 'r 89' ), array( '' => 'r 90' ), array( '' => 'r 91' ), array( '' => 'r 92' ), array( '' => 'r 93' ), array( '' => 'r 94' ), array( '' => 'r 95' ), array( '' => 'r 96' ), array( '' => 'r 97' ), array( '' => 'r 98' ), array( '' => 'r 99' ), array( '' => 'r 100' ), array( '' => 'r 84' ), array( 'exp' => 's 123', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '-->' => 's 124', '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50' ), array( 'chunks' => 's 125', 'literal' => 'r 1', '' => 's 129', 'IF' => 's 173' ), array( 'chunks' => 's 130', 'literal' => 'r 1', '' => 's 137', 'varpart' => 's 76', '.' => 's 61', '[' => 's 77' ), array( 'exp' => 's 136', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 18' ), array( 'chunks' => 's 138', 'literal' => 'r 1', '' => 's 148' ), array( 'exp' => 's 147', 'p10' => 's 52', 'p11' => 's 53', '-' => 's 54', 'nonbrace' => 's 56', '(' => 's 57', '!' => 's 66', 'NOT' => 's 68', '{' => 's 70', 'literal' => 's 74', 'varref' => 's 75', 'name' => 's 80' ), array( '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50', '-->' => 'r 20' ), array( 'chunks' => 's 149', 'literal' => 'r 1', '' => 's 156', '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50' ), array( 'chunks' => 's 157', 'literal' => 'r 1', '' => 's 176', '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50' ), array( '' => 'r 16' ), array( '' => 'r 29' ), array( '' => 'r 30' ), array( 'chunks' => 's 180', 'literal' => 'r 1', '' => 's 184', 'IF' => 's 173' ), array( 'chunks' => 's 185', 'literal' => 'r 1', '' => 's 190', '..' => 's 12', '||' => 's 14', 'OR' => 's 16', 'XOR' => 's 18', '&&' => 's 20', 'AND' => 's 22', '?' => 's 24', '==' => 's 28', '!=' => 's 30', '<' => 's 32', '>' => 's 34', '<=' => 's 36', '>=' => 's 38', '+' => 's 40', '-' => 's 42', '&' => 's 44', '*' => 's 46', '/' => 's 48', '%' => 's 50' ), array( '' => 'r 17' ), array( '' => 'r 101' ) ); public $d = array( '..' => "concatenation operator '..'", '||' => "OR operator '||'", 'OR' => "OR operator 'OR'", 'XOR' => "XOR operator 'XOR'", 'AND' => "AND operator 'AND'", '&&' => "AND operator '&&'", '&' => "bitwise AND operator '&'", '==' => "equality operator '=='", '!=' => "non-equality operator '!='", '<' => "less than operator '<'", '>' => "greater than operator '>'", '<=' => "less or equal operator '<='", '>=' => "greater or equal operator '>='", '?' => "ternary operator '? :'", ':' => "ternary operator '? :'", '+' => "plus operator '+'", '-' => "minus operator '-'", '*' => "multiply operator '*'", '/' => "divide operator '/'", '%' => "mod operator '%'", '(' => "left round brace '('", ')' => "right round brace '('", '!' => "NOT operator '!'", 'NOT' => "NOT operator 'NOT'", '{' => "left curly brace '{'", '}' => "right curly brace '}'", ',' => "comma ','", '=>' => "hash item operator '=>'", '[' => "left square brace '['", ']' => "right square brace ']'", '' => 'directive end', '{{' => 'substitution begin', '}}' => 'substitution end' ); public $errors = array(); function reduce_0_template_1($tokens, &$result) { // (0) template := chunks $result = reset($tokens); $this->template->st->functions['main']['body'] = "function fn_main() {\$stack = array();\n\$t = '';\n".$tokens[0]."\nreturn \$t;\n}\n"; $result = ''; } function reduce_1_chunks_1($tokens, &$result) { // (1) chunks := ε $result = reset($tokens); $result = ''; } function reduce_2_chunks_2($tokens, &$result) { // (2) chunks := chunks chunk $result = reset($tokens); $result = $tokens[0] . "# line ".$this->template->lexer->lineno."\n" . $tokens[1]; } function reduce_3_chunk_1($tokens, &$result) { // (3) chunk := literal $result = reset($tokens); $result = ($tokens[0] != "''" && $tokens[0] != '""' ? '$t .= ' . $tokens[0] . ";\n" : ''); } function reduce_4_chunk_2($tokens, &$result) { // (4) chunk := $result = reset($tokens); $c = &$tokens[1]; $result = $c; } function reduce_5_chunk_3($tokens, &$result) { // (5) chunk := {{ exp }} $result = reset($tokens); $e = &$tokens[1]; $result = '$t .= ' . ($e[1] || !$this->template->options->auto_escape ? $e[0] : $this->template->compile_function($this->template->options->auto_escape, [ $e ])[0]) . ";\n"; } function reduce_6_chunk_4($tokens, &$result) { // (6) chunk := error $result = reset($tokens); $e = &$tokens[0]; $result = ''; } function reduce_7_code_chunk_1($tokens, &$result) { // (7) code_chunk := c_if $result = $tokens[0]; } function reduce_8_code_chunk_2($tokens, &$result) { // (8) code_chunk := c_set $result = $tokens[0]; } function reduce_9_code_chunk_3($tokens, &$result) { // (9) code_chunk := c_fn $result = $tokens[0]; } function reduce_10_code_chunk_4($tokens, &$result) { // (10) code_chunk := c_for $result = $tokens[0]; } function reduce_11_code_chunk_5($tokens, &$result) { // (11) code_chunk := exp $result = reset($tokens); $e = &$tokens[0]; $result = '$t .= ' . ($e[1] || !$this->template->options->auto_escape ? $e[0] : $this->template->compile_function($this->template->options->auto_escape, [ $e ])[0]) . ";\n"; } function reduce_12_c_if_1($tokens, &$result) { // (12) c_if := IF exp --> chunks chunks chunks chunks c_elseifs chunks chunks c_elseifs chunks chunks $result = reset($tokens); $e = &$tokens[2]; $result = "} elseif (" . $e[0] . ") {\n"; } function reduce_17_c_elseifs_2($tokens, &$result) { // (17) c_elseifs := c_elseifs chunks $result = reset($tokens); $p = &$tokens[0]; $cs = &$tokens[1]; $e = &$tokens[4]; $result = $p . $cs . "} elseif (" . $e[0] . ") {\n"; } function reduce_18_c_set_1($tokens, &$result) { // (18) c_set := SET varref = exp $result = reset($tokens); $v = &$tokens[1]; $e = &$tokens[3]; $result = $v[0] . ' = ' . $e[0] . ";\n"; } function reduce_19_c_set_2($tokens, &$result) { // (19) c_set := SET varref --> chunks chunks chunks