Experimental template part tracking for data binding support

databind
Vitaliy Filippov 2015-04-10 14:32:42 +03:00
parent b94652978c
commit 2e50829f8c
4 changed files with 176 additions and 44 deletions

View File

@ -81,59 +81,73 @@
# Директивы
template = chunks {
$this->template->st->functions['main']['body'] = "function fn_main() {\$stack = array();\n\$t = '';\n".$1."\nreturn \$t;\n}\n";
$cs = $1;
$r = '';
foreach ($cs as $a)
{
if (is_array($a))
{
$r .= "\$t .= '".addcslashes($a[0], "'\\")."';\n";
$this->template->track_dom($a[0], strlen($r));
}
else
{
$r .= $a;
}
}
$this->template->st->functions['main']['body'] = "function fn_main() {\$stack = array();\n\$t = '';\n".$r."\nreturn \$t;\n}\n";
$$ = '';
}
.
chunks = {
$$ = '';
$$ = [];
}
| chunks chunk {
$$ = $1 . $2;
$$ = array_merge($1, $2);
}
.
chunk = literal {
$$ = ($1 != "''" && $1 != '""' ? '$t .= ' . $1 . ";\n" : '');
$$ = [ ($1 !== '' ? [ $1 ] : '') ];
}
| "<!--" code_chunk/c "-->" {
$$ = $c;
}
| "{{" exp/e "}}" {
$$ = '$t .= ' . ($e[1] || !$this->template->options->auto_escape ? $e[0] : $this->template->compile_function($this->template->options->auto_escape, [ $e ])[0]) . ";\n";
$$ = [ '$t .= ' . ($e[1] || !$this->template->options->auto_escape ? $e[0] : $this->template->compile_function($this->template->options->auto_escape, [ $e ])[0]) . ";\n" ];
}
| error/e {
$$ = '';
$$ = [];
}
.
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";
$$ = [ '$t .= ' . ($e[1] || !$this->template->options->auto_escape ? $e[0] : $this->template->compile_function($this->template->options->auto_escape, [ $e ])[0]) . ";\n" ];
}
.
c_if = "IF" exp/e "-->" chunks/if "<!--" "END" {
$$ = "if (" . $e[0] . ") {\n" . $if . "}\n";
$$ = array_merge([ "if (" . $e[0] . ") {\n" ], $if, [ "}\n" ]);
}
| "IF" exp/e "-->" chunks/if "<!--" "ELSE" "-->" chunks/else "<!--" "END" {
$$ = "if (" . $e[0] . ") {\n" . $if . "} else {\n" . $else . "}\n";
$$ = array_merge([ "if (" . $e[0] . ") {\n" ], $if, [ "} else {\n" ], $else, [ "}\n" ]);
}
| "IF" exp/e "-->" chunks/if c_elseifs/ei chunks/ec "<!--" "END" {
$$ = "if (" . $e[0] . ") {\n" . $if . $ei . $ec . "}\n";
$$ = array_merge([ "if (" . $e[0] . ") {\n" ], $if, $ei, $ec, [ "}\n" ]);
}
| "IF" exp/e "-->" chunks/if c_elseifs/ei chunks/ec "<!--" "ELSE" "-->" chunks/else "<!--" "END" {
$$ = "if (" . $e[0] . ") {\n" . $if . $ei . $ec . "} else {\n" . $else . "}\n";
$$ = array_merge([ "if (" . $e[0] . ") {\n" ], $if, $ei, $ec, [ "} else {\n" ], $else, [ "}\n" ]);
}
.
c_elseifs = "<!--" elseif exp/e "-->" {
$$ = "} elseif (" . $e[0] . ") {\n";
$$ = [ "} elseif (" . $e[0] . ") {\n" ];
}
| c_elseifs/p chunks/cs "<!--" elseif exp/e "-->" {
$$ = $p . $cs . "} elseif (" . $e[0] . ") {\n";
$$ = array_merge($p, $cs, [ "} elseif (" . $e[0] . ") {\n" ]);
}
.
c_set = "SET" varref/v "=" exp/e {
$$ = $v[0] . ' = ' . $e[0] . ";\n";
$$ = [ $v[0] . ' = ' . $e[0] . ";\n" ];
}
| "SET" varref/v "-->" chunks/cs "<!--" "END" {
$$ = "\$stack[] = \$t;\n\$t = '';\n" . $cs . $v[0] . " = \$t;\n\$t = array_pop(\$stack);\n";
$$ = array_merge([ "\$stack[] = \$t;\n\$t = '';\n" ], $cs, [ $v[0] . " = \$t;\n\$t = array_pop(\$stack);\n" ]);
}
.
c_fn = fn name/name "(" arglist/args ")" "=" exp/exp {
@ -144,9 +158,17 @@ c_fn = fn name/name "(" arglist/args ")" "=" exp/exp {
//'line' => $line, Ой, я чо - аргументы не юзаю?
//'pos' => $pos,
);
$$ = '';
$$ = [];
}
| fn name/name "(" arglist/args ")" "-->" chunks/cs "<!--" "END" {
foreach ($cs as &$a)
{
if (is_array($a))
{
$a = "\$t .= '".addcslashes($a[0], "'\\")."';\n";
}
}
$cs = implode('', $cs);
$this->template->st->functions[$name] = array(
'name' => $name,
'args' => $args,
@ -154,22 +176,22 @@ c_fn = fn name/name "(" arglist/args ")" "=" exp/exp {
//'line' => $line,
//'pos' => $pos,
);
$$ = '';
$$ = [];
}
.
c_for = for varref/varref "=" exp/exp "-->" chunks/cs "<!--" "END" {
$varref_index = substr($varref[0], 0, -1) . ".'_index']";
$$ = "\$stack[] = ".$varref[0].";
$$ = array_merge([ "\$stack[] = ".$varref[0].";
\$stack[] = ".$varref_index.";
\$stack[] = 0;
foreach ((array)($exp[0]) as \$item) {
".$varref[0]." = \$item;
".$varref_index." = \$stack[count(\$stack)-1]++;
".$cs."}
" ], $cs, [ "}
array_pop(\$stack);
".$varref_index." = array_pop(\$stack);
".$varref[0]." = array_pop(\$stack);
";
" ]);
}
.
fn = "FUNCTION" | "BLOCK" | "MACRO" .

View File

@ -658,6 +658,68 @@ $code
array_shift($args);
return "array_map(function(\$arg) { return $fn; }, self::merge_to_array(".implode(", ", $args)."))";
}
function track_dom($str, $pos)
{
static $st = 0;
static $quot = '';
static $tagre = '[a-z_:][a-z0-9\\.\\-_:]*';
static $tree = [];
print "code position $pos\n";
while ($str !== '' && $str !== false)
{
if ($st == 0 && preg_match(
"#^[^<]*<($tagre)(?:\s+$tagre\s*=\s*".
'(?:"[^"]*"|\'[^\']*\'|[^\s>"\'][^\s>]*))*(\s*(/?)>)?#is', $str, $m, PREG_OFFSET_CAPTURE))
{
// opening or enclosed tag + closed attributes
$st = !empty($m[2][0]) ? 0 : 1;
print (!empty($m[3][0]) ? 'enclosed' : ($st ? 'enter tag attrs' : 'open'))." <".$m[1][0].">\n";
$str = substr($str, $m[0][1]+strlen($m[0][0]));
}
elseif ($st == 0 && preg_match("#</($tagre)\s*>#is", $str, $m, PREG_OFFSET_CAPTURE))
{
// closing tag
print "leave <".$m[1][0].">\n";
$str = substr($str, $m[0][1]+strlen($m[0][0]));
}
elseif ($st == 1 && preg_match("#^\s+($tagre)\s*=\s*(\"[^\"]*|\'[^\']*)$#is", $str, $m, PREG_OFFSET_CAPTURE))
{
// enter attribute
$st = 2;
$quot = $m[2][0]{0};
print "enter attr '".$m[1][0]."', prefix=".substr($m[2][0], 1)."\n";
$str = substr($str, $m[0][1]+strlen($m[0][0]));
}
elseif ($st == 2)
{
if (preg_match("#^([^$quot]*)$quot#s", $str, $m, PREG_OFFSET_CAPTURE))
{
// leave attribute
$st = 1;
print "exit attr, suffix=".$m[1][0]."\n";
$str = substr($str, $m[0][1]+strlen($m[0][0]));
}
else
{
// continue attribute
print "continue attr, infix=$str\n";
$str = '';
}
}
elseif ($st == 1 && preg_match("#^(?:$tagre\s*=\s*".
'(?:"[^"]*"|\'[^\']*\'|[^\s>"\'][^\s>]*))*(\s*(/?)>)?$#is', $str, $m, PREG_OFFSET_CAPTURE) && $m[0])
{
$st = $m[1][0] ? 0 : 1;
print ($m[1][0] ? 'leave ' : 'continue ').($m[2][0] ? 'enclosed' : 'opening')." tag attrs\n";
$str = substr($str, $m[0][1]+strlen($m[0][0]));
}
else
{
$str = '';
}
}
}
}
/**
@ -794,7 +856,7 @@ class VMXTemplateLexer
$this->force_literal = 0;
if ($code_pos === false && $subst_pos === false)
{
$r = array('literal', "'".addcslashes(substr($this->code, $this->pos), "'\\")."'");
$r = array('literal', substr($this->code, $this->pos));
$this->lineno += substr_count($r[1], "\n");
$this->pos = $this->codelen;
}
@ -809,7 +871,7 @@ class VMXTemplateLexer
{
$str = preg_replace('/\n[ \t]*$/s', $was_code ? '' : "\n", $str);
}
$r = array('literal', "'".addcslashes($str, "'\\")."'");
$r = array('literal', $str);
$this->lineno += substr_count($r[1], "\n");
$this->pos = $code_pos;
}
@ -841,7 +903,7 @@ class VMXTemplateLexer
// Substitution is closer
if ($subst_pos > $this->pos)
{
$r = array('literal', "'".addcslashes(substr($this->code, $this->pos, $subst_pos-$this->pos), "'\\")."'");
$r = array('literal', substr($this->code, $this->pos, $subst_pos-$this->pos));
$this->lineno += substr_count($r[1], "\n");
$this->pos = $subst_pos;
}
@ -3446,7 +3508,21 @@ class VMXTemplateParser extends lime_parser {
// (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";
$cs = $tokens[0];
$r = '';
foreach ($cs as $a)
{
if (is_array($a))
{
$r .= "\$t .= '".addcslashes($a[0], "'\\")."';\n";
$this->template->track_dom($a[0], strlen($r));
}
else
{
$r .= $a;
}
}
$this->template->st->functions['main']['body'] = "function fn_main() {\$stack = array();\n\$t = '';\n".$r."\nreturn \$t;\n}\n";
$result = '';
}
@ -3454,21 +3530,21 @@ class VMXTemplateParser extends lime_parser {
// (1) chunks := ε
$result = reset($tokens);
$result = '';
$result = [];
}
function reduce_2_chunks_2($tokens, &$result) {
// (2) chunks := chunks chunk
$result = reset($tokens);
$result = $tokens[0] . $tokens[1];
$result = array_merge($tokens[0], $tokens[1]);
}
function reduce_3_chunk_1($tokens, &$result) {
// (3) chunk := literal
$result = reset($tokens);
$result = ($tokens[0] != "''" && $tokens[0] != '""' ? '$t .= ' . $tokens[0] . ";\n" : '');
$result = [ ($tokens[0] !== '' ? [ $tokens[0] ] : '') ];
}
function reduce_4_chunk_2($tokens, &$result) {
@ -3484,7 +3560,7 @@ class VMXTemplateParser extends lime_parser {
$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";
$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) {
@ -3492,7 +3568,7 @@ class VMXTemplateParser extends lime_parser {
$result = reset($tokens);
$e = &$tokens[0];
$result = '';
$result = [];
}
function reduce_7_code_chunk_1($tokens, &$result) {
@ -3529,7 +3605,7 @@ class VMXTemplateParser extends lime_parser {
$e = &$tokens[1];
$if = &$tokens[3];
$result = "if (" . $e[0] . ") {\n" . $if . "}\n";
$result = array_merge([ "if (" . $e[0] . ") {\n" ], $if, [ "}\n" ]);
}
function reduce_13_c_if_2($tokens, &$result) {
@ -3539,7 +3615,7 @@ class VMXTemplateParser extends lime_parser {
$if = &$tokens[3];
$else = &$tokens[7];
$result = "if (" . $e[0] . ") {\n" . $if . "} else {\n" . $else . "}\n";
$result = array_merge([ "if (" . $e[0] . ") {\n" ], $if, [ "} else {\n" ], $else, [ "}\n" ]);
}
function reduce_14_c_if_3($tokens, &$result) {
@ -3550,7 +3626,7 @@ class VMXTemplateParser extends lime_parser {
$ei = &$tokens[4];
$ec = &$tokens[5];
$result = "if (" . $e[0] . ") {\n" . $if . $ei . $ec . "}\n";
$result = array_merge([ "if (" . $e[0] . ") {\n" ], $if, $ei, $ec, [ "}\n" ]);
}
function reduce_15_c_if_4($tokens, &$result) {
@ -3562,7 +3638,7 @@ class VMXTemplateParser extends lime_parser {
$ec = &$tokens[5];
$else = &$tokens[9];
$result = "if (" . $e[0] . ") {\n" . $if . $ei . $ec . "} else {\n" . $else . "}\n";
$result = array_merge([ "if (" . $e[0] . ") {\n" ], $if, $ei, $ec, [ "} else {\n" ], $else, [ "}\n" ]);
}
function reduce_16_c_elseifs_1($tokens, &$result) {
@ -3570,7 +3646,7 @@ class VMXTemplateParser extends lime_parser {
$result = reset($tokens);
$e = &$tokens[2];
$result = "} elseif (" . $e[0] . ") {\n";
$result = [ "} elseif (" . $e[0] . ") {\n" ];
}
function reduce_17_c_elseifs_2($tokens, &$result) {
@ -3580,7 +3656,7 @@ class VMXTemplateParser extends lime_parser {
$cs = &$tokens[1];
$e = &$tokens[4];
$result = $p . $cs . "} elseif (" . $e[0] . ") {\n";
$result = array_merge($p, $cs, [ "} elseif (" . $e[0] . ") {\n" ]);
}
function reduce_18_c_set_1($tokens, &$result) {
@ -3589,7 +3665,7 @@ class VMXTemplateParser extends lime_parser {
$v = &$tokens[1];
$e = &$tokens[3];
$result = $v[0] . ' = ' . $e[0] . ";\n";
$result = [ $v[0] . ' = ' . $e[0] . ";\n" ];
}
function reduce_19_c_set_2($tokens, &$result) {
@ -3598,7 +3674,7 @@ class VMXTemplateParser extends lime_parser {
$v = &$tokens[1];
$cs = &$tokens[3];
$result = "\$stack[] = \$t;\n\$t = '';\n" . $cs . $v[0] . " = \$t;\n\$t = array_pop(\$stack);\n";
$result = array_merge([ "\$stack[] = \$t;\n\$t = '';\n" ], $cs, [ $v[0] . " = \$t;\n\$t = array_pop(\$stack);\n" ]);
}
function reduce_20_c_fn_1($tokens, &$result) {
@ -3615,7 +3691,7 @@ class VMXTemplateParser extends lime_parser {
//'line' => $line, Ой, я чо - аргументы не юзаю?
//'pos' => $pos,
);
$result = '';
$result = [];
}
function reduce_21_c_fn_2($tokens, &$result) {
@ -3625,6 +3701,14 @@ class VMXTemplateParser extends lime_parser {
$args = &$tokens[3];
$cs = &$tokens[6];
foreach ($cs as &$a)
{
if (is_array($a))
{
$a = "\$t .= '".addcslashes($a[0], "'\\")."';\n";
}
}
$cs = implode('', $cs);
$this->template->st->functions[$name] = array(
'name' => $name,
'args' => $args,
@ -3632,7 +3716,7 @@ class VMXTemplateParser extends lime_parser {
//'line' => $line,
//'pos' => $pos,
);
$result = '';
$result = [];
}
function reduce_22_c_for_1($tokens, &$result) {
@ -3643,17 +3727,17 @@ class VMXTemplateParser extends lime_parser {
$cs = &$tokens[5];
$varref_index = substr($varref[0], 0, -1) . ".'_index']";
$result = "\$stack[] = ".$varref[0].";
$result = array_merge([ "\$stack[] = ".$varref[0].";
\$stack[] = ".$varref_index.";
\$stack[] = 0;
foreach ((array)($exp[0]) as \$item) {
".$varref[0]." = \$item;
".$varref_index." = \$stack[count(\$stack)-1]++;
".$cs."}
" ], $cs, [ "}
array_pop(\$stack);
".$varref_index." = array_pop(\$stack);
".$varref[0]." = array_pop(\$stack);
";
" ]);
}
function reduce_23_fn_1($tokens, &$result) {
@ -4829,5 +4913,5 @@ class VMXTemplateParser extends lime_parser {
);
}
// Time: 4,2855579853058 seconds
// Memory: 11192016 bytes
// Time: 2,8588461875916 seconds
// Memory: 11894776 bytes

14
test.php Normal file
View File

@ -0,0 +1,14 @@
<?php
require 'template.php';
system('mkdir -p ./cache');
system('rm ./cache/tpl*.php');
$template = new VMXTemplate([
'cache_dir' => './cache',
'root' => '.',
'auto_escape' => 's',
]);
$template->parse('test.tpl');

12
test.tpl Normal file
View File

@ -0,0 +1,12 @@
<html>
<head>
</head>
<body>
<div>x{intro}y</div>
<select>
<!-- FOR o = options -->
<option value="{o.url}"<!-- IF o.selected --> selected="selected"<!-- END -->>{o.name}</option>
<!-- END -->
</select>
</body>
</html>