Skip to content

Commit

Permalink
use backfill to resolve else chain
Browse files Browse the repository at this point in the history
  • Loading branch information
Zordius Chen committed Jun 12, 2016
1 parent 091ee87 commit b6a7bd4
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 41 deletions.
20 changes: 8 additions & 12 deletions src/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Compiler extends Validator
public static function compileTemplate(&$context, $template) {
array_unshift($context['parsed'], array());
Validator::verify($context, $template);
static::$lastParsed = $context['parsed'];

if (count($context['error'])) {
return;
Expand All @@ -66,7 +67,7 @@ public static function compileTemplate(&$context, $template) {
}
}

static::$lastParsed = array_shift($context['parsed']);
array_shift($context['parsed']);

return $code;
}
Expand Down Expand Up @@ -448,38 +449,33 @@ protected static function blockCustomHelper(&$context, $vars, $inverted = false)
*/
protected static function blockEnd(&$context, &$vars, $matchop = NULL) {
$pop = $context['stack'][count($context['stack']) - 1];
$elsifend = isset($vars[0][-1]) ? str_repeat($context['ops']['cnd_nend'], $vars[0][-1]) : '';
switch ($context['currentToken'][Token::POS_INNERTAG]) {
case 'if':
case 'unless':
if ($pop === ':') {
array_pop($context['stack']);
return "$elsifend{$context['ops']['cnd_end']}";
return "{$context['ops']['cnd_end']}";
}
if (!$context['flags']['nohbh']) {
return "{$context['ops']['cnd_else']}''$elsifend{$context['ops']['cnd_end']}";
return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
}
break;
case 'with':
if (!$context['flags']['nohbh']) {
return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
}
}

if ($pop === ':') {
array_pop($context['stack']);
return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
}

if ($elsifend !== '') {
$elsifend = "{$context['ops']['cnd_else']}''$elsifend";
return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
}

switch($pop) {
case '#':
return "$elsifend{$context['ops']['f_end']}}){$context['ops']['seperator']}";
return "{$context['ops']['f_end']}}){$context['ops']['seperator']}";
case '^':
return "$elsifend{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
return "{$context['ops']['cnd_else']}''{$context['ops']['cnd_end']}";
}
}

Expand Down
1 change: 1 addition & 0 deletions src/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Token
const POS_ENDTAG = 10;
const POS_RSPACE = 11;
const POS_ROTHER = 12;
const POS_BACKFILL = 13;

/**
* Setup delimiter by default or provided string
Expand Down
52 changes: 24 additions & 28 deletions src/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ protected static function delimiter($token, &$context) {
* @return boolean|integer|null Return true when invalid or detected
*
* @expect null when input '', array(), array()
* @expect 2 when input '^', array('usedFeature' => array('isec' => 1), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'helperresolver' => 0), array(array('foo'))
* @expect 2 when input '^', array('usedFeature' => array('isec' => 1), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'elselvl' => array(), 'flags' => array('spvar' => 0), 'elsechain' => false, 'helperresolver' => 0), array(array('foo'))
* @expect true when input '/', array('stack' => array('[with]', '#'), 'level' => 1, 'currentToken' => array(0,0,0,0,0,0,0,'with'), 'flags' => array('nohbh' => 0)), array(array())
* @expect 4 when input '#', array('usedFeature' => array('sec' => 3), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('x'))
* @expect 5 when input '#', array('usedFeature' => array('if' => 4), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0, 'nohbh' => 0), 'elsechain' => false, 'elselvl' => array(), 'helperresolver' => 0), array(array('if'))
Expand Down Expand Up @@ -197,9 +197,10 @@ protected static function operator($operator, &$context, &$vars) {
}
}

static::doElseChain($context);

if (static::isBlockHelper($context, $vars)) {
static::pushStack($context, '#', $vars);
array_unshift($context['elselvl'], 0);
return static::blockCustomHelper($context, $vars, true);
}

Expand All @@ -208,22 +209,21 @@ protected static function operator($operator, &$context, &$vars) {

case '/':
$r = static::blockEnd($context, $vars);
array_pop($context['stack']);
array_pop($context['stack']);
array_pop($context['stack']);
if ($r !== Token::POS_BACKFILL) {
array_pop($context['stack']);
array_pop($context['stack']);
array_pop($context['stack']);
}
return $r;

case '#':
static::doElseChain($context);
static::pushStack($context, '#', $vars);

if (static::isBlockHelper($context, $vars)) {
static::pushStack($context, '#', $vars);
array_unshift($context['elselvl'], 0);
return static::blockCustomHelper($context, $vars);
}

if (!$context['elsechain']) {
static::pushStack($context, '#', $vars);
}

return static::blockBegin($context, $vars);
}
}
Expand Down Expand Up @@ -307,15 +307,15 @@ protected static function partialBlock(&$context, $vars) {
}

/**
* handle else if
* handle else chain
*
* @param array<string,array|string|integer> $context current compile context
*/
protected static function doElseIf(&$context) {
protected static function doElseChain(&$context) {
if ($context['elsechain']) {
$context['elsechain'] = false;
} else {
array_unshift($context['elselvl'], 0);
array_unshift($context['elselvl'], array());
}
}

Expand All @@ -330,17 +330,14 @@ protected static function doElseIf(&$context) {
protected static function blockBegin(&$context, $vars) {
switch ((isset($vars[0][0]) && is_string($vars[0][0])) ? $vars[0][0] : null) {
case 'with':
array_unshift($context['elselvl'], 0);
return static::with($context, $vars);
case 'each':
array_unshift($context['elselvl'], 0);
return static::section($context, $vars, true);
case 'unless':
return static::unless($context, $vars);
case 'if':
return static::doIf($context, $vars);
default:
array_unshift($context['elselvl'], 0);
return static::section($context, $vars);
}
}
Expand Down Expand Up @@ -407,7 +404,6 @@ protected static function with(&$context, $vars) {
* @return boolean Return true always
*/
protected static function unless(&$context, $vars) {
static::doElseIf($context);
static::builtin($context, $vars);
return true;
}
Expand All @@ -421,7 +417,6 @@ protected static function unless(&$context, $vars) {
* @return boolean Return true always
*/
protected static function doIf(&$context, $vars) {
static::doElseIf($context);
static::builtin($context, $vars);
return true;
}
Expand Down Expand Up @@ -484,20 +479,21 @@ protected static function blockEnd(&$context, &$vars, $match = null) {
}

switch($pop) {
case '#>':
case '#*':
case '#':
case '^':
$elsechain = array_shift($context['elselvl']);
if (isset($elsechain[0])) {
$context['currentToken'][Token::POS_RSPACE] = $context['currentToken'][Token::POS_BACKFILL] = '{{/' . implode('}}{{/', $elsechain) . '}}' . Token::toString($context['currentToken']) . $context['currentToken'][Token::POS_RSPACE];
return Token::POS_BACKFILL;
}
case '#>':
case '#*':
list($levels, $spvar, $var) = Expression::analyze($context, $vars[0]);
$v = Expression::toString($levels, $spvar, $var);
if ($pop2 !== $v) {
$context['error'][] = 'Unexpect token ' . Token::toString($context['currentToken']) . " ! Previous token {{{$pop}$pop2}} is not closed";
return;
}
if (count($context['elselvl']) > 0) {
$vars[0][-1] = $context['elselvl'][0];
array_shift($context['elselvl']);
}
return true;
default:
$context['error'][] = 'Unexpect token: ' . Token::toString($context['currentToken']) . ' !';
Expand Down Expand Up @@ -617,7 +613,7 @@ protected static function token(&$token, &$context) {
static::spacing($token, $context, (($token[Token::POS_OP] === '') || ($token[Token::POS_OP] === '&')) && (!$context['flags']['else'] || !isset($vars[0][0]) || ($vars[0][0] !== 'else')) || ($context['flags']['nostd'] > 0));

if (static::operator($token[Token::POS_OP], $context, $vars)) {
return array($raw, $vars);
return isset($token[Token::POS_BACKFILL]) ? null : array($raw, $vars);
}

if (count($vars) == 0) {
Expand Down Expand Up @@ -669,11 +665,11 @@ protected static function doElse(&$context, $vars) {
$context['error'][] = '{{else}} only valid in if, unless, each, and #section context';
}

if (isset($vars[1][0]) && (($vars[1][0] === 'if') || ($vars[1][0] === 'unless'))) {
if (isset($vars[1][0])) {
$token = $context['currentToken'];
$context['currentToken'][Token::POS_RSPACE] = '{{#' . $vars[1][0] . ' ' . preg_replace('/^\\s*else\\s+' . $vars[1][0] . '\\s+/', '', $token[Token::POS_INNERTAG]) . '}}' . $context['currentToken'][Token::POS_RSPACE];
array_unshift($context['elselvl'][0], $vars[1][0]);
$context['elsechain'] = true;
$context['elselvl'][0]++;
}

return ++$context['usedFeature']['else'];
Expand Down
2 changes: 1 addition & 1 deletion tests/ValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function testOn_operator() {
'', array(), array()
))));
$this->assertEquals(2, $method->invokeArgs(null, array_by_ref(array(
'^', array('usedFeature' => array('isec' => 1), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'flags' => array('spvar' => 0), 'helperresolver' => 0), array(array('foo'))
'^', array('usedFeature' => array('isec' => 1), 'level' => 0, 'currentToken' => array(0,0,0,0,0,0,0,0), 'elselvl' => array(), 'flags' => array('spvar' => 0), 'elsechain' => false, 'helperresolver' => 0), array(array('foo'))
))));
$this->assertEquals(true, $method->invokeArgs(null, array_by_ref(array(
'/', array('stack' => array('[with]', '#'), 'level' => 1, 'currentToken' => array(0,0,0,0,0,0,0,'with'), 'flags' => array('nohbh' => 0)), array(array())
Expand Down

0 comments on commit b6a7bd4

Please sign in to comment.