Add standalone line whitespace handling to Parser.

This commit is contained in:
Justin Hileman
2013-05-15 13:37:05 -07:00
parent de9941d271
commit 8f5be9e470
+108 -3
View File
@@ -16,6 +16,8 @@
*/ */
class Mustache_Parser class Mustache_Parser
{ {
private $lineNum;
private $lineTokens;
/** /**
* 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.
@@ -26,6 +28,9 @@ class Mustache_Parser
*/ */
public function parse(array $tokens = array()) public function parse(array $tokens = array())
{ {
$this->lineNum = -1;
$this->lineTokens = 0;
return $this->buildTree($tokens); return $this->buildTree($tokens);
} }
@@ -46,12 +51,21 @@ class Mustache_Parser
while (!empty($tokens)) { while (!empty($tokens)) {
$token = array_shift($tokens); $token = array_shift($tokens);
if ($token === null) { if ($token[Mustache_Tokenizer::LINE] === $this->lineNum) {
continue; $this->lineTokens++;
} else { } else {
$this->lineNum = $token[Mustache_Tokenizer::LINE];
$this->lineTokens = 0;
}
switch ($token[Mustache_Tokenizer::TYPE]) { switch ($token[Mustache_Tokenizer::TYPE]) {
case Mustache_Tokenizer::T_DELIM_CHANGE:
$this->clearStandaloneLines($nodes, $tokens);
break;
case Mustache_Tokenizer::T_SECTION: case Mustache_Tokenizer::T_SECTION:
case Mustache_Tokenizer::T_INVERTED: case Mustache_Tokenizer::T_INVERTED:
$this->clearStandaloneLines($nodes, $tokens);
$nodes[] = $this->buildTree($tokens, $token); $nodes[] = $this->buildTree($tokens, $token);
break; break;
@@ -66,18 +80,33 @@ class Mustache_Parser
throw new Mustache_Exception_SyntaxException($msg, $token); throw new Mustache_Exception_SyntaxException($msg, $token);
} }
$this->clearStandaloneLines($nodes, $tokens);
$parent[Mustache_Tokenizer::END] = $token[Mustache_Tokenizer::INDEX]; $parent[Mustache_Tokenizer::END] = $token[Mustache_Tokenizer::INDEX];
$parent[Mustache_Tokenizer::NODES] = $nodes; $parent[Mustache_Tokenizer::NODES] = $nodes;
return $parent; return $parent;
break; break;
case Mustache_Tokenizer::T_PARTIAL:
case Mustache_Tokenizer::T_PARTIAL_2:
// store the whitespace prefix for laters!
if ($indent = $this->clearStandaloneLines($nodes, $tokens)) {
$token[Mustache_Tokenizer::INDENT] = $indent[Mustache_Tokenizer::VALUE];
}
$nodes[] = $token;
break;
case Mustache_Tokenizer::T_PRAGMA:
case Mustache_Tokenizer::T_COMMENT:
$this->clearStandaloneLines($nodes, $tokens);
$nodes[] = $token;
break;
default: default:
$nodes[] = $token; $nodes[] = $token;
break; break;
} }
} }
};
if (isset($parent)) { if (isset($parent)) {
$msg = sprintf('Missing closing tag: %s', $parent[Mustache_Tokenizer::NAME]); $msg = sprintf('Missing closing tag: %s', $parent[Mustache_Tokenizer::NAME]);
@@ -86,4 +115,80 @@ class Mustache_Parser
return $nodes; return $nodes;
} }
/**
* Clear standalone line tokens.
*
* Returns a whitespace token for indenting partials, if applicable.
*
* @param array $nodes Parsed nodes.
* @param array $tokens Tokens to be parsed.
*
* @return array Resulting indent token, if any.
*/
private function clearStandaloneLines(array &$nodes, array &$tokens)
{
if ($this->lineTokens > 1) {
// this is the third or later node on this line, so it can't be standalone
return;
}
$prev = null;
if ($this->lineTokens === 1) {
// this is the second node on this line, so it can't be standalone
// unless the previous node is whitespace.
if ($prev = end($nodes)) {
if (!$this->tokenIsWhitespace($prev)) {
return;
}
}
}
$next = null;
if ($next = reset($tokens)) {
// If we're on a new line, bail.
if ($next[Mustache_Tokenizer::LINE] !== $this->lineNum) {
return;
}
// If the next token isn't whitespace, bail.
if (!$this->tokenIsWhitespace($next)) {
return;
}
if (count($tokens) !== 1) {
// Unless it's the last token in the template, the next token
// must end in newline for this to be standalone.
if (substr($next[Mustache_Tokenizer::VALUE], -1) !== "\n") {
return;
}
}
// Discard the whitespace suffix
array_shift($tokens);
}
if ($prev) {
// Return the whitespace prefix, if any
return array_pop($nodes);
}
}
/**
* Check whether token is a whitespace token.
*
* True if token type is T_TEXT and value is all whitespace characters.
*
* @param array $token
*
* @return boolean True if token is a whitespace token
*/
private function tokenIsWhitespace(array $token)
{
if ($token[Mustache_Tokenizer::TYPE] == Mustache_Tokenizer::T_TEXT) {
return preg_match('/^\s*$/', $token[Mustache_Tokenizer::VALUE]);
}
return false;
}
} }