VMXTemplate/template.lime

356 lines
11 KiB
Plaintext
Raw Permalink Normal View History

2013-04-19 23:26:52 +04:00
# Контекстно-свободная LIME-грамматика шаблонизатора
#
# Для корректной работы нужен патченый LIME со следующими изменениями:
# (*) Подменой лексемы 'lit' на 'str' в метаграмматике.
2013-04-19 23:26:52 +04:00
# Это нужно, чтобы можно было юзать строковые лексемы типа '<!--'.
# (*) Для корректной обработки ошибок нужно, чтобы метод eat() возвращал
# false при ошибке и true при успехе. Т.к. подразумевается, что лексический
# анализатор зависим от работы синтаксического, знает о его состоянии и
# соответственно выдаёт либо лексемы "внутри" блоков кода, либо литералы
# "вне" оных.
2013-04-22 03:24:36 +04:00
# Взять таковой можно здесь: https://github.com/vitalif/lime
2013-08-26 02:20:13 +04:00
# Компилить так: php -d xdebug.max_nesting_level=200 lime.php template.lime > template.class
#
# {{ двойные скобки }} нужно исключительно чтобы маркеры начала и конца подстановки
# были уникальны в грамматике. Вместо них обычно используются { одинарные }, а
# выбор корректной лексемы - скобки или маркера - делает лексический анализатор.
# Но зато вместо { фигурных скобок } можно выбрать себе любые другие маркеры!
#
2013-04-23 23:08:58 +04:00
# Все выражения представляются массивом из двух значений: [ код выражения, флаг экранирования ]
# Флаг экранирования == true, если это выражение HTML-безопасно. При включённом auto_escape
# небезопасные выражения прогоняются через экранирование.
#
# Кстати:
2013-04-23 23:09:00 +04:00
# * Олдстайл BEGIN .. END ликвидирован
# * Возможно, нужно добавить в каком-то виде foreach ... as key => value
#
# PHP старее 5.4 не поддерживается из-за следующих причин:
# * используется $a ?: $b в выражении {a || b}
# * используется короткий синтаксис массивов [ $a, $b ]
# * используется синтаксис ($array_expression)[$key]
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'"
2013-04-20 03:42:30 +04:00
%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 '>='"
2019-12-22 02:29:47 +03:00
%token "?" "ternary operator '? :'"
%token ":" "ternary operator '? :'"
2013-04-20 03:42:30 +04:00
%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'"
2013-04-20 03:42:30 +04:00
%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"
%token "{{" "substitution begin"
%token "}}" "substitution end"
2013-04-19 23:26:52 +04:00
2013-04-19 19:55:42 +04:00
%left ".."
2020-01-01 17:30:23 +03:00
%nonassoc "?" ":"
%left "||" "OR" "XOR"
%left "&&" "AND"
2020-01-01 17:30:23 +03:00
%nonassoc "==" "!=" "<" ">" "<=" ">="
2013-04-19 19:55:42 +04:00
%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
.
2013-06-18 02:28:10 +04:00
chunks = {
$$ = '';
2013-04-20 03:42:30 +04:00
}
| chunks chunk {
2017-02-24 14:07:33 +03:00
$$ = $1 . "# line ".$this->template->lexer->lineno."\n" . $2;
2013-04-19 19:55:42 +04:00
}
.
2013-04-20 03:42:30 +04:00
chunk = literal {
2015-02-17 16:02:54 +03:00
$$ = ($1 != "''" && $1 != '""' ? '$t .= ' . $1 . ";\n" : '');
2013-04-19 19:55:42 +04:00
}
| "<!--" code_chunk/c "-->" {
$$ = $c;
}
| "{{" exp/e "}}" {
2013-04-23 23:08:58 +04:00
$$ = '$t .= ' . ($e[1] || !$this->template->options->auto_escape ? $e[0] : $this->template->compile_function($this->template->options->auto_escape, [ $e ])[0]) . ";\n";
2013-04-19 19:55:42 +04:00
}
2013-04-20 03:42:30 +04:00
| error/e {
$$ = '';
}
.
2013-04-23 23:08:58 +04:00
code_chunk = c_if/$ | c_set/$ | c_fn/$ | c_for/$ | exp/e {
$$ = '$t .= ' . ($e[1] || !$this->template->options->auto_escape ? $e[0] : $this->template->compile_function($this->template->options->auto_escape, [ $e ])[0]) . ";\n";
2013-04-19 23:26:52 +04:00
}
2013-04-19 19:55:42 +04:00
.
c_if = "IF" exp/e "-->" chunks/if "<!--" "END" {
2013-04-23 23:08:58 +04:00
$$ = "if (" . $e[0] . ") {\n" . $if . "}\n";
2013-04-19 19:55:42 +04:00
}
| "IF" exp/e "-->" chunks/if "<!--" "ELSE" "-->" chunks/else "<!--" "END" {
2013-04-23 23:08:58 +04:00
$$ = "if (" . $e[0] . ") {\n" . $if . "} else {\n" . $else . "}\n";
2013-04-19 19:55:42 +04:00
}
| "IF" exp/e "-->" chunks/if c_elseifs/ei chunks/ec "<!--" "END" {
2013-04-23 23:08:58 +04:00
$$ = "if (" . $e[0] . ") {\n" . $if . $ei . $ec . "}\n";
2013-04-19 19:55:42 +04:00
}
| "IF" exp/e "-->" chunks/if c_elseifs/ei chunks/ec "<!--" "ELSE" "-->" chunks/else "<!--" "END" {
2013-04-23 23:08:58 +04:00
$$ = "if (" . $e[0] . ") {\n" . $if . $ei . $ec . "} else {\n" . $else . "}\n";
2013-04-19 19:55:42 +04:00
}
.
c_elseifs = "<!--" elseif exp/e "-->" {
2013-04-23 23:08:58 +04:00
$$ = "} elseif (" . $e[0] . ") {\n";
2013-04-19 19:55:42 +04:00
}
| c_elseifs/p chunks/cs "<!--" elseif exp/e "-->" {
2013-04-23 23:08:58 +04:00
$$ = $p . $cs . "} elseif (" . $e[0] . ") {\n";
2013-04-19 19:55:42 +04:00
}
.
c_set = "SET" varref/v "=" exp/e {
2013-04-23 23:08:58 +04:00
$$ = $v[0] . ' = ' . $e[0] . ";\n";
2013-04-19 19:55:42 +04:00
}
| "SET" varref/v "-->" chunks/cs "<!--" "END" {
2013-04-23 23:08:58 +04:00
$$ = "\$stack[] = \$t;\n\$t = '';\n" . $cs . $v[0] . " = \$t;\n\$t = array_pop(\$stack);\n";
2013-04-19 19:55:42 +04:00
}
.
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,
'body' => 'function fn_'.$name." () {\nreturn ".$exp[0].";\n}\n",
2013-04-19 23:26:52 +04:00
//'line' => $line, Ой, я чо - аргументы не юзаю?
2013-04-19 19:55:42 +04:00
//'pos' => $pos,
);
$$ = '';
}
| fn name/name "(" arglist/args ")" "-->" chunks/cs "<!--" "END" {
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." () {\$stack = array();\n\$t = '';\n".$cs."\nreturn \$t;\n}\n",
2013-04-19 19:55:42 +04:00
//'line' => $line,
//'pos' => $pos,
);
$$ = '';
}
.
c_for = for varref/varref "=" exp/exp "-->" chunks/cs "<!--" "END" {
2013-04-23 23:08:58 +04:00
$varref_index = substr($varref[0], 0, -1) . ".'_index']";
$$ = "\$stack[] = ".$varref[0].";
2013-04-19 19:55:42 +04:00
\$stack[] = ".$varref_index.";
\$stack[] = 0;
foreach ((array)($exp[0]) as \$item) {
2013-04-23 23:08:58 +04:00
".$varref[0]." = \$item;
2013-04-19 19:55:42 +04:00
".$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);
2013-04-23 23:08:58 +04:00
".$varref[0]." = array_pop(\$stack);
2013-04-19 19:55:42 +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 {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' . ' . $b[0] . ')', $a[1] && $b[1] ];
2013-04-19 19:55:42 +04:00
}
| exp/a "||" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' ?: ' . $b[0] . ')', $a[1] && $b[1] ];
2013-04-19 19:55:42 +04:00
}
| exp/a "OR" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' ?: ' . $b[0] . ')', $a[1] && $b[1] ];
2013-04-19 19:55:42 +04:00
}
| exp/a "XOR" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' XOR ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "&&" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' && ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "AND" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' && ' . $b[0] . ')', true ];
2019-12-22 02:29:47 +03:00
}
| exp/a "?" exp/b ":" exp/c {
$$ = [ '(' . $a[0] . ' ? ' . $b[0] . ' : ' . $c[0] . ')', $b[1] && $c[1] ];
2013-04-19 19:55:42 +04:00
}
| exp/a "==" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' == ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "!=" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' != ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "<" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' < ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a ">" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' > ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "<=" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' <= ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a ">=" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' >= ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "+" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' + ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "-" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' - ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "&" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' & ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "*" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' * ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "/" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' / ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
| exp/a "%" exp/b {
2013-04-23 23:08:58 +04:00
$$ = [ '(' . $a[0] . ' % ' . $b[0] . ')', true ];
2013-04-19 19:55:42 +04:00
}
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-23 23:08:58 +04:00
$$ = [ '(-'.$a[0].')', true ];
2013-04-19 19:55:42 +04:00
}
.
p11: nonbrace
| '(' exp/e ')' varpath/p {
$$ = [ ($p !== '' ? 'self::noop('.$e[0].')'.$p : '('.$e[0].')'), false ];
2013-04-19 19:55:42 +04:00
}
| '!' p11/a {
2013-04-23 23:08:58 +04:00
$$ = [ '(!'.$a[0].')', true ];
2013-04-19 19:55:42 +04:00
}
| "NOT" p11/a {
2013-04-23 23:08:58 +04:00
$$ = [ '(!'.$a[0].')', true ];
2013-04-19 19:55:42 +04:00
}
.
nonbrace: '{' hash/h '}' {
2013-04-23 23:08:58 +04:00
$$ = [ 'array(' . $h . ')', true ];
}
| literal {
$$ = [ $1, true ];
2013-04-19 19:55:42 +04:00
}
2013-04-19 23:26:52 +04:00
| 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-23 23:08:58 +04:00
$$ = [ "\$this->parent->call_block('".addcslashes($f, "'\\")."', array(".$args."), '".addcslashes($this->template->lexer->errorinfo(), "'\\")."')", true ];
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
}
.
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;
}
| {
$$ = '';
}
.
2014-09-25 15:58:18 +04:00
gthash: gtpair/$
2013-04-19 19:55:42 +04:00
| gtpair/p ',' gthash/h {
$$ = $p . ', ' . $h;
}
.
pair: exp/k ',' exp/v {
2013-04-23 23:08:58 +04:00
$$ = $k[0] . ' => ' . $v[0];
2013-04-19 19:55:42 +04:00
}
2013-04-19 23:26:52 +04:00
| gtpair/$
2013-04-19 19:55:42 +04:00
.
gtpair: exp/k "=>" exp/v {
2013-04-23 23:08:58 +04:00
$$ = $k[0] . ' => ' . $v[0];
2013-04-19 19:55:42 +04:00
}
.
varref: name/n {
2013-04-23 23:08:58 +04:00
$$ = [ "\$this->tpldata['".addcslashes($n, "\\\'")."']", false ];
2013-04-19 19:55:42 +04:00
}
| varref/v varpart/p {
2013-04-23 23:08:58 +04:00
$$ = [ $v[0] . $p, false ];
2013-04-19 19:55:42 +04:00
}
.
2013-08-26 02:20:13 +04:00
varpart: '.' namekw/n {
2013-04-20 03:42:30 +04:00
$$ = "['".addcslashes($n, "\\\'")."']";
2013-04-19 19:55:42 +04:00
}
| '[' exp/e ']' {
2013-04-23 23:08:58 +04:00
$$ = '['.$e[0].']';
2013-04-19 19:55:42 +04:00
}
| '.' namekw/n '(' ')' {
2014-10-12 15:55:02 +04:00
$$ = '->'.$n.'()';
}
| '.' namekw/n '(' list/l ')' {
2014-10-12 15:55:02 +04:00
$argv = [];
foreach ($l as $a) {
2014-10-12 15:55:02 +04:00
$argv[] = $a[0];
}
$$ = '->'.$n.'('.implode(', ', $argv).')';
}
2013-04-19 19:55:42 +04:00
.
varpath: {
$$ = '';
}
| varpath/a varpart/p {
$$ = $a . $p;
}
.
2013-08-26 02:20:13 +04:00
namekw: name
| "IF" | "END" | "ELSE" | "ELSIF" | "ELSEIF"
| "SET" | "OR" | "XOR" | "AND" | "NOT"
| "FUNCTION" | "BLOCK" | "MACRO" | "FOR" | "FOREACH"
.