2013-04-19 23:26:52 +04:00
|
|
|
|
# Контекстно-свободная LIME-грамматика шаблонизатора
|
|
|
|
|
#
|
|
|
|
|
# ЗАМЕЧАНИЯ:
|
|
|
|
|
# (!) Для разбора нужен патченый LIME, в котором лексема 'lit' подменена на 'str'.
|
|
|
|
|
# Это нужно, чтобы можно было юзать строковые лексемы типа '<!--'.
|
|
|
|
|
# (*) Подразумевается, что лексический анализатор зависим от работы синтаксического,
|
|
|
|
|
# знает о его состоянии и соответственно выдаёт либо лексемы "внутри" блоков кода,
|
|
|
|
|
# либо литералы "вне" оных
|
|
|
|
|
# (*) Олдстайл BEGIN .. END ликвидирован
|
|
|
|
|
# (*) Возможно, нужно добавить в каком-то виде foreach ... as key => value
|
2013-04-19 19:55:42 +04:00
|
|
|
|
|
|
|
|
|
%class VMXTemplateParser
|
2013-04-20 03:42:30 +04:00
|
|
|
|
%start template
|
2013-04-19 19:55:42 +04:00
|
|
|
|
|
|
|
|
|
%token literal
|
2013-04-20 03:42:30 +04:00
|
|
|
|
%token incorrect
|
2013-04-19 19:55:42 +04:00
|
|
|
|
%token name
|
2013-04-20 03:42:30 +04:00
|
|
|
|
%token comment
|
2013-04-19 19:55:42 +04:00
|
|
|
|
|
2013-04-20 03:42:30 +04:00
|
|
|
|
%token ".." "concatenation operator '..'"
|
|
|
|
|
%token "||" "OR operator '||'"
|
|
|
|
|
%token "or" "OR operator 'OR'"
|
|
|
|
|
%token "xor" "XOR operator 'XOR'"
|
|
|
|
|
%token "and" "AND operator 'AND'"
|
|
|
|
|
%token "&&" "AND operator '&&'"
|
|
|
|
|
%token "&" "bitwise AND operator '&'"
|
|
|
|
|
%token "==" "equality operator '=='"
|
|
|
|
|
%token "!=" "non-equality operator '!='"
|
|
|
|
|
%token "<" "less than operator '<'"
|
|
|
|
|
%token ">" "greater than operator '>'"
|
|
|
|
|
%token "<=" "less or equal operator '<='"
|
|
|
|
|
%token ">=" "greater or equal operator '>='"
|
|
|
|
|
%token "+" "plus operator '+'"
|
|
|
|
|
%token "-" "minus operator '-'"
|
|
|
|
|
%token "*" "multiply operator '*'"
|
|
|
|
|
%token "/" "divide operator '/'"
|
|
|
|
|
%token "%" "mod operator '%'"
|
|
|
|
|
%token "(" "left round brace '('"
|
|
|
|
|
%token ")" "right round brace '('"
|
|
|
|
|
%token "!" "NOT operator '!'"
|
|
|
|
|
%token "not" "NOT operator 'NOT'"
|
|
|
|
|
%token "{" "left curly brace '{'"
|
|
|
|
|
%token "}" "right curly brace '}'"
|
|
|
|
|
%token "," "comma ','"
|
|
|
|
|
%token "=>" "hash item operator '=>'"
|
|
|
|
|
%token "[" "left square brace '['"
|
|
|
|
|
%token "]" "right square brace ']'"
|
|
|
|
|
%token "<!--" "directive begin '<!--'"
|
|
|
|
|
%token "-->" "directive end '-->'"
|
2013-04-19 23:26:52 +04:00
|
|
|
|
|
2013-04-19 19:55:42 +04:00
|
|
|
|
%left ".."
|
2013-04-20 03:42:30 +04:00
|
|
|
|
%left "||" "or" "xor"
|
|
|
|
|
%left "&&" "and"
|
2013-04-19 19:55:42 +04:00
|
|
|
|
%nonassoc "==" "!=" "<" ">" "<=" ">="
|
|
|
|
|
%left "+" "-"
|
|
|
|
|
%left "&"
|
|
|
|
|
%left "*" "/" "%"
|
|
|
|
|
|
2013-04-19 23:26:52 +04:00
|
|
|
|
# Директивы
|
2013-04-19 19:55:42 +04:00
|
|
|
|
|
2013-04-20 03:42:30 +04:00
|
|
|
|
template = chunks {
|
|
|
|
|
$this->template->st->functions['main']['body'] = "function fn_main() {\$stack = array();\n\$t = '';\n".$1."\nreturn \$t;\n}\n";
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = '';
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
.
|
|
|
|
|
chunks = chunk {
|
|
|
|
|
$$ = $1;
|
|
|
|
|
}
|
|
|
|
|
| chunks chunk {
|
|
|
|
|
$$ = $1 . $2;
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
.
|
2013-04-20 03:42:30 +04:00
|
|
|
|
chunk = literal {
|
|
|
|
|
$$ = '$t .= ' . $1 . ";\n";
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
| "<!--" code_chunk/c "-->" {
|
|
|
|
|
$$ = $c;
|
|
|
|
|
}
|
|
|
|
|
| "{" exp/e "}" {
|
|
|
|
|
$$ = '$t .= ' . $e . ";\n";
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| error/e {
|
|
|
|
|
$this->template->lexer->skip_error($e);
|
|
|
|
|
$$ = '';
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
code_chunk = c_if/$ | c_set/$ | c_fn/$ | c_for/$ | exp {
|
|
|
|
|
$$ = '$t .= ' . $1 . ";\n";
|
2013-04-19 23:26:52 +04:00
|
|
|
|
}
|
2013-04-19 19:55:42 +04:00
|
|
|
|
.
|
2013-04-20 03:42:30 +04:00
|
|
|
|
c_if = "if" exp/e "-->" chunks/if "<!--" "end" {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = "if (" . $e . ") {\n" . $if . "}\n";
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| "if" exp/e "-->" chunks/if "<!--" "else" "-->" chunks/else "<!--" "end" {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = "if (" . $e . ") {\n" . $if . "} else {\n" . $else . "}\n";
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| "if" exp/e "-->" chunks/if c_elseifs/ei chunks/ec "<!--" "end" {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = "if (" . $e . ") {\n" . $if . $ei . $ec . "}\n";
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| "if" exp/e "-->" chunks/if c_elseifs/ei chunks/ec "<!--" "else" "-->" chunks/else "<!--" "end" {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = "if (" . $e . ") {\n" . $if . $ei . $ec . "} else {\n" . $else . "}\n";
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
c_elseifs = "<!--" elseif exp/e "-->" {
|
|
|
|
|
$$ = "} elseif (" . $e . ") {\n";
|
|
|
|
|
}
|
|
|
|
|
| c_elseifs/p chunks/cs "<!--" elseif exp/e "-->" {
|
|
|
|
|
$$ = $p . $cs . "} elseif (" . $e . ") {\n";
|
|
|
|
|
}
|
|
|
|
|
.
|
2013-04-20 03:42:30 +04:00
|
|
|
|
c_set = "set" varref/v "=" exp/e {
|
|
|
|
|
$$ = $v . ' = ' . $e . ";\n";
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| "set" varref/v "-->" chunks/cs "<!--" "end" {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = "\$stack[] = \$t;\n\$t = '';\n" . $cs . $v . " = \$t;\narray_pop(\$stack);\n";
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
c_fn = fn name/name "(" arglist/args ")" "=" exp/exp {
|
2013-04-20 03:42:30 +04:00
|
|
|
|
$this->template->st->functions[$name] = array(
|
2013-04-19 19:55:42 +04:00
|
|
|
|
'name' => $name,
|
|
|
|
|
'args' => $args,
|
2013-04-19 23:26:52 +04:00
|
|
|
|
'body' => 'function fn_'.$name." () {\nreturn ".$exp.";\n}\n",
|
|
|
|
|
//'line' => $line, Ой, я чо - аргументы не юзаю?
|
2013-04-19 19:55:42 +04:00
|
|
|
|
//'pos' => $pos,
|
|
|
|
|
);
|
|
|
|
|
$$ = '';
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| fn name/name "(" arglist/args ")" "-->" chunks/cs "<!--" "end" {
|
|
|
|
|
$this->template->st->functions[$name] = array(
|
2013-04-19 19:55:42 +04:00
|
|
|
|
'name' => $name,
|
|
|
|
|
'args' => $args,
|
2013-04-19 23:26:52 +04:00
|
|
|
|
'body' => 'function fn_'.$name." () {\$stack = array();\n\$t = '';\n".$cs."\nreturn \$t;\n}\n",
|
2013-04-19 19:55:42 +04:00
|
|
|
|
//'line' => $line,
|
|
|
|
|
//'pos' => $pos,
|
|
|
|
|
);
|
|
|
|
|
$$ = '';
|
|
|
|
|
}
|
|
|
|
|
.
|
2013-04-20 03:42:30 +04:00
|
|
|
|
c_for = for varref/varref "=" exp/exp "-->" chunks/cs "<!--" "end" {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$varref_index = substr($varref, 0, -1) . ".'_index']";
|
|
|
|
|
$$ = "\$stack[] = ".$varref.";
|
|
|
|
|
\$stack[] = ".$varref_index.";
|
|
|
|
|
\$stack[] = 0;
|
|
|
|
|
foreach (self::array1($exp) as \$item) {
|
|
|
|
|
".$varref." = \$item;
|
|
|
|
|
".$varref_index." = \$stack[count(\$stack)-1]++;
|
2013-04-20 03:42:30 +04:00
|
|
|
|
".$cs."}
|
2013-04-19 19:55:42 +04:00
|
|
|
|
array_pop(\$stack);
|
|
|
|
|
".$varref_index." = array_pop(\$stack);
|
|
|
|
|
".$varref." = array_pop(\$stack);
|
|
|
|
|
";
|
|
|
|
|
}
|
|
|
|
|
.
|
2013-04-20 03:42:30 +04:00
|
|
|
|
fn = "function" | "block" | "macro" .
|
|
|
|
|
for = "for" | "foreach" .
|
|
|
|
|
elseif = "else" "if" | "elsif" | "elseif" .
|
2013-04-19 19:55:42 +04:00
|
|
|
|
|
2013-04-19 23:26:52 +04:00
|
|
|
|
# Выражения
|
2013-04-19 19:55:42 +04:00
|
|
|
|
|
|
|
|
|
exp: exp/a ".." exp/b {
|
|
|
|
|
$$ = '(' . $a . ' . ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "||" exp/b {
|
|
|
|
|
$$ = 'self::perlish_or(' . $a . ', ' . $b . ')';
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| exp/a "or" exp/b {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = 'self::perlish_or(' . $a . ', ' . $b . ')';
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| exp/a "xor" exp/b {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = '(' . $a . ' XOR ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "&&" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' && ' . $b . ')';
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| exp/a "and" exp/b {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = '(' . $a . ' && ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "==" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' == ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "!=" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' != ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "<" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' < ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a ">" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' > ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "<=" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' <= ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a ">=" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' >= ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "+" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' + ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "-" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' - ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "&" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' & ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "*" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' * ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "/" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' / ' . $b . ')';
|
|
|
|
|
}
|
|
|
|
|
| exp/a "%" exp/b {
|
|
|
|
|
$$ = '(' . $a . ' % ' . $b . ')';
|
|
|
|
|
}
|
2013-04-19 23:26:52 +04:00
|
|
|
|
| p10/$
|
2013-04-19 19:55:42 +04:00
|
|
|
|
.
|
2013-04-19 23:26:52 +04:00
|
|
|
|
p10: p11/$
|
|
|
|
|
| '-' p11/a {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = '(-'.$a.')';
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
p11: nonbrace
|
|
|
|
|
| '(' exp/e ')' varpath/p {
|
|
|
|
|
$$ = '('.$e.')'.$p;
|
|
|
|
|
}
|
|
|
|
|
| '!' p11/a {
|
|
|
|
|
$$ = '(!'.$a.')';
|
|
|
|
|
}
|
2013-04-20 03:42:30 +04:00
|
|
|
|
| "not" p11/a {
|
2013-04-19 19:55:42 +04:00
|
|
|
|
$$ = '(!'.$a.')';
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
nonbrace: '{' hash/h '}' {
|
|
|
|
|
$$ = 'array(' . $h . ')';
|
|
|
|
|
}
|
2013-04-19 23:26:52 +04:00
|
|
|
|
| literal/$
|
|
|
|
|
| varref/$
|
2013-04-19 19:55:42 +04:00
|
|
|
|
| name/f '(' ')' {
|
2013-04-20 03:42:30 +04:00
|
|
|
|
$$ = $this->template->compile_function($f, []);
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
| name/f '(' list/args ')' {
|
2013-04-20 03:42:30 +04:00
|
|
|
|
$$ = $this->template->compile_function($f, $args);
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
| name/f '(' gthash/args ')' {
|
2013-04-20 03:42:30 +04:00
|
|
|
|
$$ = "\$this->parent->call_block('".addcslashes($f, "'\\")."', array(".$args."), '".addcslashes($this->template->lexer->errorinfo(), "'\\")."')";
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
| name/f nonbrace/arg {
|
2013-04-20 03:42:30 +04:00
|
|
|
|
$$ = $this->template->compile_function($f, [ $arg ]);
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
| method/f '(' ')' {
|
|
|
|
|
$$ = $f.'()';
|
|
|
|
|
}
|
|
|
|
|
| method/f '(' list/args ')' {
|
|
|
|
|
$$ = $f.'('.implode(', ', $args).')';
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
method: varref/v '.' name/m {
|
|
|
|
|
$$ = $v.'->'.$m;
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
list: exp/e {
|
|
|
|
|
$$ = [ $e ];
|
|
|
|
|
}
|
|
|
|
|
| exp/e ',' list/l {
|
|
|
|
|
$$ = $l;
|
|
|
|
|
array_unshift($$, $e);
|
|
|
|
|
}
|
|
|
|
|
.
|
2013-04-19 23:26:52 +04:00
|
|
|
|
arglist: name/n {
|
|
|
|
|
$$ = [ $n ];
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
2013-04-19 23:26:52 +04:00
|
|
|
|
| name/n ',' arglist/args {
|
|
|
|
|
$$ = $args;
|
|
|
|
|
array_unshift($$, $n);
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
| {
|
2013-04-19 23:26:52 +04:00
|
|
|
|
$$ = [];
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
.
|
2013-04-19 23:26:52 +04:00
|
|
|
|
hash: pair/$
|
2013-04-19 19:55:42 +04:00
|
|
|
|
| pair/p ',' hash/h {
|
|
|
|
|
$$ = $p . ', ' . $h;
|
|
|
|
|
}
|
|
|
|
|
| {
|
|
|
|
|
$$ = '';
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
gthash: gtpair/p {
|
|
|
|
|
$$ = $p;
|
|
|
|
|
}
|
|
|
|
|
| gtpair/p ',' gthash/h {
|
|
|
|
|
$$ = $p . ', ' . $h;
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
pair: exp/k ',' exp/v {
|
|
|
|
|
$$ = $k . ' => ' . $v;
|
|
|
|
|
}
|
2013-04-19 23:26:52 +04:00
|
|
|
|
| gtpair/$
|
2013-04-19 19:55:42 +04:00
|
|
|
|
.
|
|
|
|
|
gtpair: exp/k "=>" exp/v {
|
|
|
|
|
$$ = $k . ' => ' . $v;
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
varref: name/n {
|
2013-04-20 03:42:30 +04:00
|
|
|
|
$$ = "\$this->tpldata['".addcslashes($n, "\\\'")."']";
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
| varref/v varpart/p {
|
|
|
|
|
$$ = $v . $p;
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
varpart: '.' name/n {
|
2013-04-20 03:42:30 +04:00
|
|
|
|
$$ = "['".addcslashes($n, "\\\'")."']";
|
2013-04-19 19:55:42 +04:00
|
|
|
|
}
|
|
|
|
|
| '[' exp/e ']' {
|
|
|
|
|
$$ = '['.$e.']';
|
|
|
|
|
}
|
|
|
|
|
.
|
|
|
|
|
varpath: {
|
|
|
|
|
$$ = '';
|
|
|
|
|
}
|
|
|
|
|
| varpath/a varpart/p {
|
|
|
|
|
$$ = $a . $p;
|
|
|
|
|
}
|
|
|
|
|
.
|