Move filter logic from the compiler to the parser.

This commit is contained in:
Justin Hileman
2014-08-18 18:28:50 -07:00
parent d225afe032
commit 58a7726515
3 changed files with 120 additions and 83 deletions
+52 -68
View File
@@ -60,7 +60,7 @@ class Mustache_Compiler
* *
* @internal Users should set global pragmas in Mustache_Engine, not here :) * @internal Users should set global pragmas in Mustache_Engine, not here :)
* *
* @param array $pragmas * @param string[] $pragmas
*/ */
public function setPragmas(array $pragmas) public function setPragmas(array $pragmas)
{ {
@@ -95,6 +95,7 @@ class Mustache_Compiler
$code .= $this->section( $code .= $this->section(
$node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NODES],
$node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::NAME],
isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
$node[Mustache_Tokenizer::INDEX], $node[Mustache_Tokenizer::INDEX],
$node[Mustache_Tokenizer::END], $node[Mustache_Tokenizer::END],
$node[Mustache_Tokenizer::OTAG], $node[Mustache_Tokenizer::OTAG],
@@ -107,6 +108,7 @@ class Mustache_Compiler
$code .= $this->invertedSection( $code .= $this->invertedSection(
$node[Mustache_Tokenizer::NODES], $node[Mustache_Tokenizer::NODES],
$node[Mustache_Tokenizer::NAME], $node[Mustache_Tokenizer::NAME],
isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
$level $level
); );
break; break;
@@ -154,14 +156,24 @@ class Mustache_Compiler
case Mustache_Tokenizer::T_UNESCAPED: case Mustache_Tokenizer::T_UNESCAPED:
case Mustache_Tokenizer::T_UNESCAPED_2: case Mustache_Tokenizer::T_UNESCAPED_2:
$code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level); $code .= $this->variable(
$node[Mustache_Tokenizer::NAME],
isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
false,
$level
);
break; break;
case Mustache_Tokenizer::T_COMMENT: case Mustache_Tokenizer::T_COMMENT:
break; break;
case Mustache_Tokenizer::T_ESCAPED: case Mustache_Tokenizer::T_ESCAPED:
$code .= $this->variable($node[Mustache_Tokenizer::NAME], true, $level); $code .= $this->variable(
$node[Mustache_Tokenizer::NAME],
isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
true,
$level
);
break; break;
case Mustache_Tokenizer::T_TEXT: case Mustache_Tokenizer::T_TEXT:
@@ -279,8 +291,8 @@ class Mustache_Compiler
*/ */
private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level) private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level)
{ {
$key = $this->section($nodes, $id, $start, $end, $otag, $ctag, $level, true); $key = $this->section($nodes, $id, array(), $start, $end, $otag, $ctag, $level, true);
$id = var_export($id, true); $id = var_export($id, true);
return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key, $id, $this->flushIndent()); return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key, $id, $this->flushIndent());
} }
@@ -320,25 +332,20 @@ class Mustache_Compiler
/** /**
* Generate Mustache Template section PHP source. * Generate Mustache Template section PHP source.
* *
* @param array $nodes Array of child tokens * @param array $nodes Array of child tokens
* @param string $id Section name * @param string $id Section name
* @param int $start Section start offset * @param string[] $filters Array of filters
* @param int $end Section end offset * @param int $start Section start offset
* @param string $otag Current Mustache opening tag * @param int $end Section end offset
* @param string $ctag Current Mustache closing tag * @param string $otag Current Mustache opening tag
* @param int $level * @param string $ctag Current Mustache closing tag
* @param bool $arg (default: false) * @param int $level
* @param bool $arg (default: false)
* *
* @return string Generated section PHP source code * @return string Generated section PHP source code
*/ */
private function section($nodes, $id, $start, $end, $otag, $ctag, $level, $arg = false) private function section($nodes, $id, $filters, $start, $end, $otag, $ctag, $level, $arg = false)
{ {
$filters = '';
if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) {
list($id, $filters) = $this->getFilters($id, $level);
}
$source = var_export(substr($this->source, $start, $end - $start), true); $source = var_export(substr($this->source, $start, $end - $start), true);
$callable = $this->getCallable(); $callable = $this->getCallable();
@@ -357,8 +364,9 @@ class Mustache_Compiler
if ($arg === true) { if ($arg === true) {
return $key; return $key;
} else { } else {
$method = $this->getFindMethod($id); $method = $this->getFindMethod($id);
$id = var_export($id, true); $id = var_export($id, true);
$filters = $this->getFilters($filters, $level);
return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key); return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $method, $id, $filters, $key);
} }
@@ -374,22 +382,18 @@ class Mustache_Compiler
/** /**
* Generate Mustache Template inverted section PHP source. * Generate Mustache Template inverted section PHP source.
* *
* @param array $nodes Array of child tokens * @param array $nodes Array of child tokens
* @param string $id Section name * @param string $id Section name
* @param int $level * @param string[] $filters Array of filters
* @param int $level
* *
* @return string Generated inverted section PHP source code * @return string Generated inverted section PHP source code
*/ */
private function invertedSection($nodes, $id, $level) private function invertedSection($nodes, $id, $filters, $level)
{ {
$filters = ''; $method = $this->getFindMethod($id);
$id = var_export($id, true);
if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { $filters = $this->getFilters($filters, $level);
list($id, $filters) = $this->getFilters($id, $level);
}
$method = $this->getFindMethod($id);
$id = var_export($id, true);
return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $filters, $this->walk($nodes, $level)); return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $filters, $this->walk($nodes, $level));
} }
@@ -477,43 +481,23 @@ class Mustache_Compiler
/** /**
* Generate Mustache Template variable interpolation PHP source. * Generate Mustache Template variable interpolation PHP source.
* *
* @param string $id Variable name * @param string $id Variable name
* @param boolean $escape Escape the variable value for output? * @param string[] $filters Array of filters
* @param int $level * @param boolean $escape Escape the variable value for output?
* @param int $level
* *
* @return string Generated variable interpolation PHP source * @return string Generated variable interpolation PHP source
*/ */
private function variable($id, $escape, $level) private function variable($id, $filters, $escape, $level)
{ {
$filters = ''; $method = $this->getFindMethod($id);
$id = ($method !== 'last') ? var_export($id, true) : '';
if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { $filters = $this->getFilters($filters, $level);
list($id, $filters) = $this->getFilters($id, $level); $value = $escape ? $this->getEscape() : '$value';
}
$method = $this->getFindMethod($id);
$id = ($method !== 'last') ? var_export($id, true) : '';
$value = $escape ? $this->getEscape() : '$value';
return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value); return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value);
} }
/**
* Generate Mustache Template variable filtering PHP source.
*
* @param string $id Variable name
* @param int $level
*
* @return string Generated variable filtering PHP source
*/
private function getFilters($id, $level)
{
$filters = array_map('trim', explode('|', $id));
$id = array_shift($filters);
return array($id, $this->getFilter($filters, $level));
}
const FILTER = ' const FILTER = '
$filter = $context->%s(%s); $filter = $context->%s(%s);
if (!(%s)) { if (!(%s)) {
@@ -523,14 +507,14 @@ class Mustache_Compiler
'; ';
/** /**
* Generate PHP source for a single filter. * Generate Mustache Template variable filtering PHP source.
* *
* @param array $filters * @param string[] $filters Array of filters
* @param int $level * @param int $level
* *
* @return string Generated filter PHP source * @return string Generated filter PHP source
*/ */
private function getFilter(array $filters, $level) private function getFilters(array $filters, $level)
{ {
if (empty($filters)) { if (empty($filters)) {
return ''; return '';
@@ -542,7 +526,7 @@ class Mustache_Compiler
$callable = $this->getCallable('$filter'); $callable = $this->getCallable('$filter');
$msg = var_export($name, true); $msg = var_export($name, true);
return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilter($filters, $level)); return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilters($filters, $level));
} }
const LINE = '$buffer .= "\n";'; const LINE = '$buffer .= "\n";';
+57 -5
View File
@@ -21,6 +21,9 @@ class Mustache_Parser
private $pragmas; private $pragmas;
private $defaultPragmas = array(); private $defaultPragmas = array();
private $pragmaFilters;
private $pragmaBlocks;
/** /**
* Process an array of Mustache tokens and convert them into a parse tree. * Process an array of Mustache tokens and convert them into a parse tree.
* *
@@ -34,6 +37,9 @@ class Mustache_Parser
$this->lineTokens = 0; $this->lineTokens = 0;
$this->pragmas = $this->defaultPragmas; $this->pragmas = $this->defaultPragmas;
$this->pragmaFilters = isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS]);
$this->pragmaBlocks = isset($this->pragmas[Mustache_Engine::PRAGMA_BLOCKS]);
return $this->buildTree($tokens); return $this->buildTree($tokens);
} }
@@ -43,13 +49,13 @@ class Mustache_Parser
* *
* @internal Users should set global pragmas in Mustache_Engine, not here :) * @internal Users should set global pragmas in Mustache_Engine, not here :)
* *
* @param array $pragmas * @param string[] $pragmas
*/ */
public function setPragmas(array $pragmas) public function setPragmas(array $pragmas)
{ {
$this->pragmas = array(); $this->pragmas = array();
foreach ($pragmas as $pragma) { foreach ($pragmas as $pragma) {
$this->pragmas[$pragma] = true; $this->enablePragma($pragma);
} }
$this->defaultPragmas = $this->pragmas; $this->defaultPragmas = $this->pragmas;
} }
@@ -78,6 +84,14 @@ class Mustache_Parser
$this->lineTokens = 0; $this->lineTokens = 0;
} }
if ($this->pragmaFilters && isset($token[Mustache_Tokenizer::NAME])) {
list($name, $filters) = $this->getNameAndFilters($token[Mustache_Tokenizer::NAME]);
if (!empty($filters)) {
$token[Mustache_Tokenizer::NAME] = $name;
$token[Mustache_Tokenizer::FILTERS] = $filters;
}
}
switch ($token[Mustache_Tokenizer::TYPE]) { switch ($token[Mustache_Tokenizer::TYPE]) {
case Mustache_Tokenizer::T_DELIM_CHANGE: case Mustache_Tokenizer::T_DELIM_CHANGE:
$this->checkIfTokenIsAllowedInParent($parent, $token); $this->checkIfTokenIsAllowedInParent($parent, $token);
@@ -133,7 +147,7 @@ class Mustache_Parser
break; break;
case Mustache_Tokenizer::T_BLOCK_VAR: case Mustache_Tokenizer::T_BLOCK_VAR:
if (isset($this->pragmas[Mustache_Engine::PRAGMA_BLOCKS])) { if ($this->pragmaBlocks) {
// BLOCKS pragma is enabled, let's do this! // BLOCKS pragma is enabled, let's do this!
if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) { if ($parent[Mustache_Tokenizer::TYPE] === Mustache_Tokenizer::T_PARENT) {
$token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG; $token[Mustache_Tokenizer::TYPE] = Mustache_Tokenizer::T_BLOCK_ARG;
@@ -150,7 +164,7 @@ class Mustache_Parser
break; break;
case Mustache_Tokenizer::T_PRAGMA: case Mustache_Tokenizer::T_PRAGMA:
$this->pragmas[$token[Mustache_Tokenizer::NAME]] = true; $this->enablePragma($token[Mustache_Tokenizer::NAME]);
// no break // no break
case Mustache_Tokenizer::T_COMMENT: case Mustache_Tokenizer::T_COMMENT:
@@ -184,7 +198,7 @@ class Mustache_Parser
* @param array $nodes Parsed nodes. * @param array $nodes Parsed nodes.
* @param array $tokens Tokens to be parsed. * @param array $tokens Tokens to be parsed.
* *
* @return array Resulting indent token, if any. * @return array|null Resulting indent token, if any.
*/ */
private function clearStandaloneLines(array &$nodes, array &$tokens) private function clearStandaloneLines(array &$nodes, array &$tokens)
{ {
@@ -265,4 +279,42 @@ class Mustache_Parser
throw new Mustache_Exception_SyntaxException('Illegal content in < parent tag', $token); throw new Mustache_Exception_SyntaxException('Illegal content in < parent tag', $token);
} }
} }
/**
* Split a tag name into name and filters.
*
* @param string $name
*
* @return array {
* @type string Tag name
* @type string[] Array of filters
* }
*/
private function getNameAndFilters($name)
{
$filters = array_map('trim', explode('|', $name));
$name = array_shift($filters);
return array($name, $filters);
}
/**
* Enable a pragma.
*
* @param string $name
*/
private function enablePragma($name)
{
$this->pragmas[$name] = true;
switch ($name) {
case Mustache_Engine::PRAGMA_BLOCKS:
$this->pragmaBlocks = true;
break;
case Mustache_Engine::PRAGMA_FILTERS:
$this->pragmaFilters = true;
break;
}
}
} }
+11 -10
View File
@@ -61,16 +61,17 @@ class Mustache_Tokenizer
); );
// Token properties // Token properties
const TYPE = 'type'; const TYPE = 'type';
const NAME = 'name'; const NAME = 'name';
const OTAG = 'otag'; const OTAG = 'otag';
const CTAG = 'ctag'; const CTAG = 'ctag';
const LINE = 'line'; const LINE = 'line';
const INDEX = 'index'; const INDEX = 'index';
const END = 'end'; const END = 'end';
const INDENT = 'indent'; const INDENT = 'indent';
const NODES = 'nodes'; const NODES = 'nodes';
const VALUE = 'value'; const VALUE = 'value';
const FILTERS = 'filters';
private $state; private $state;
private $tagType; private $tagType;