mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Split parseExp (for #3)
This commit is contained in:
parent
f1d252a3cc
commit
c27df81545
@ -32,6 +32,15 @@ class Compiler
|
|||||||
*/
|
*/
|
||||||
public static function tagInclude(Tokenizer $tokens, Template $tpl)
|
public static function tagInclude(Tokenizer $tokens, Template $tpl)
|
||||||
{
|
{
|
||||||
|
$name = false;
|
||||||
|
// if($tokens->is('[')) {
|
||||||
|
// $tokens->next();
|
||||||
|
// if(!$name && $tokens->is(T_CONSTANT_ENCAPSED_STRING)) {
|
||||||
|
// if($tpl->getStorage()->templateExists($_name = substr($tokens->getAndNext(), 1, -1))) {
|
||||||
|
// $name = $_name;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
$cname = $tpl->parsePlainArg($tokens, $name);
|
$cname = $tpl->parsePlainArg($tokens, $name);
|
||||||
$p = $tpl->parseParams($tokens);
|
$p = $tpl->parseParams($tokens);
|
||||||
if ($p) { // if we have additionally variables
|
if ($p) { // if we have additionally variables
|
||||||
|
@ -603,135 +603,165 @@ class Template extends Render
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse expressions. The mix of math operations, boolean operations, scalars, arrays and variables.
|
* Parse expressions. The mix of operations and terms.
|
||||||
*
|
*
|
||||||
* @static
|
|
||||||
* @param Tokenizer $tokens
|
* @param Tokenizer $tokens
|
||||||
* @param bool $required
|
* @param bool $required
|
||||||
* @throws TokenizeException
|
|
||||||
* @throws UnexpectedTokenException
|
|
||||||
* @throws \Exception
|
|
||||||
* @return string
|
* @return string
|
||||||
|
* @throws Error\UnexpectedTokenException
|
||||||
*/
|
*/
|
||||||
public function parseExp(Tokenizer $tokens, $required = false)
|
public function parseExp(Tokenizer $tokens, $required = false) {
|
||||||
{
|
$exp = array();
|
||||||
$_exp = array(); // expression as PHP code
|
$var = false; // last term was: true - variable, false - mixed
|
||||||
$term = false; // last item was variable or value.
|
$op = false; // last exp was operator
|
||||||
// 0 - was operator, but trem required
|
$cond = false; // was conditional operator
|
||||||
// false - was operator or no one term
|
while($tokens->valid()) {
|
||||||
// true - was trem
|
// parse term
|
||||||
// 1 - term is strict varaible
|
$term = $this->parseTerm($tokens, $var);
|
||||||
$cond = false; // last item was operator
|
if($term !== false) {
|
||||||
while ($tokens->valid()) {
|
$exp[] = $term;
|
||||||
if (!$term && $tokens->is(Tokenizer::MACRO_SCALAR, '"', '`', T_ENCAPSED_AND_WHITESPACE)) { // like quoted string
|
$op = false;
|
||||||
$_exp[] = $this->parseScalar($tokens, true);
|
} else {
|
||||||
$term = true;
|
break;
|
||||||
} elseif (!$term && $tokens->is(Tokenizer::MACRO_INCDEC)) { // like variable
|
}
|
||||||
$_exp[] = $this->parseVariable($tokens);
|
|
||||||
$term = true;
|
if(!$tokens->valid()) {
|
||||||
} elseif (!$term && $tokens->is(T_VARIABLE)) { // like variable too
|
break;
|
||||||
$var = $this->parseVar($tokens);
|
}
|
||||||
if ($tokens->is(Tokenizer::MACRO_EQUALS)) {
|
|
||||||
$_exp[] = $var;
|
// parse operator
|
||||||
if ($tokens->isLast()) {
|
if($tokens->is(Tokenizer::MACRO_BINARY)) {
|
||||||
break;
|
|
||||||
}
|
|
||||||
$_exp[] = $tokens->getAndNext();
|
|
||||||
$term = 0;
|
|
||||||
} elseif ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?", '(')) {
|
|
||||||
$_exp[] = $this->parseVariable($tokens, 0, $var);
|
|
||||||
$term = true;
|
|
||||||
} else {
|
|
||||||
$_exp[] = $var;
|
|
||||||
$term = 1;
|
|
||||||
}
|
|
||||||
} elseif (!$term && $tokens->is("(")) { // open bracket
|
|
||||||
$tokens->next();
|
|
||||||
$_exp[] = "(" . $this->parseExp($tokens, true) . ")";
|
|
||||||
$tokens->get(")");
|
|
||||||
$tokens->next();
|
|
||||||
$term = 1;
|
|
||||||
} elseif ($tokens->is(T_STRING)) {
|
|
||||||
if ($term) { // parse 'in' or 'is' operators
|
|
||||||
if (!$_exp) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$operator = $tokens->current();
|
|
||||||
if ($operator == "is") {
|
|
||||||
$item = array_pop($_exp);
|
|
||||||
$_exp[] = $this->parseIs($tokens, $item, $term === 1);
|
|
||||||
} elseif ($operator == "in" || ($operator == "not" && $tokens->isNextToken("in"))) {
|
|
||||||
$item = array_pop($_exp);
|
|
||||||
$_exp[] = $this->parseIn($tokens, $item, $term === 1);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else { // function or special value
|
|
||||||
if ($tokens->isSpecialVal()) {
|
|
||||||
$_exp[] = $tokens->getAndNext();
|
|
||||||
} elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) {
|
|
||||||
$func = $this->_fenom->getModifier($tokens->current(), $this);
|
|
||||||
if (!$func) {
|
|
||||||
throw new \Exception("Function " . $tokens->getAndNext() . " not found");
|
|
||||||
}
|
|
||||||
$tokens->next();
|
|
||||||
$func = $func . $this->parseArgs($tokens);
|
|
||||||
if ($tokens->is('|')) {
|
|
||||||
$_exp[] = $this->parseModifier($tokens, $func);
|
|
||||||
} else {
|
|
||||||
$_exp[] = $func;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$term = true;
|
|
||||||
}
|
|
||||||
} elseif (!$term && $tokens->is(T_ISSET, T_EMPTY)) { // empty and isset operators
|
|
||||||
$func = $tokens->getAndNext();
|
|
||||||
if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) {
|
|
||||||
$tokens->next();
|
|
||||||
$_exp[] = $func . "(" . $this->parseVar($tokens) . ")";
|
|
||||||
$tokens->need(')')->next();
|
|
||||||
} else {
|
|
||||||
throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables");
|
|
||||||
}
|
|
||||||
$term = true;
|
|
||||||
} elseif (!$term && $tokens->is(Tokenizer::MACRO_UNARY)) {
|
|
||||||
if (!$tokens->isNext(T_VARIABLE, T_DNUMBER, T_LNUMBER, T_STRING, T_ISSET, T_EMPTY, '(')) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$_exp[] = $tokens->getAndNext();
|
|
||||||
$term = 0;
|
|
||||||
} elseif ($tokens->is(Tokenizer::MACRO_BINARY)) { // like binary operator, see Tokenizer::MACRO_BINARY
|
|
||||||
if (!$term) {
|
|
||||||
throw new UnexpectedTokenException($tokens);
|
|
||||||
}
|
|
||||||
if ($tokens->isLast()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ($tokens->is(Tokenizer::MACRO_COND)) {
|
if ($tokens->is(Tokenizer::MACRO_COND)) {
|
||||||
if ($cond) {
|
if ($cond) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$cond = true;
|
$cond = true;
|
||||||
} elseif ($tokens->is(Tokenizer::MACRO_BOOLEAN)) {
|
|
||||||
$cond = false;
|
|
||||||
}
|
}
|
||||||
$_exp[] = " " . $tokens->getAndNext() . " ";
|
$op = $tokens->getAndNext();
|
||||||
$term = 0;
|
} elseif($tokens->is(Tokenizer::MACRO_EQUALS)) {
|
||||||
} elseif ($tokens->is('[')) {
|
if(!$var) {
|
||||||
$_exp[] = $this->parseArray($tokens);
|
break;
|
||||||
|
}
|
||||||
|
$op = $tokens->getAndNext();
|
||||||
|
} elseif($tokens->is(T_STRING)) {
|
||||||
|
if (!$exp) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$operator = $tokens->current();
|
||||||
|
if ($operator == "is") {
|
||||||
|
$item = array_pop($exp);
|
||||||
|
$exp[] = $this->parseIs($tokens, $item, $var);
|
||||||
|
} elseif ($operator == "in" || ($operator == "not" && $tokens->isNextToken("in"))) {
|
||||||
|
$item = array_pop($exp);
|
||||||
|
$exp[] = $this->parseIn($tokens, $item, $var);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} elseif($tokens->is('~')) {
|
||||||
|
// string concat coming soon
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if($op) {
|
||||||
|
$exp[] = $op;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($term === 0) {
|
|
||||||
|
if ($op) {
|
||||||
throw new UnexpectedTokenException($tokens);
|
throw new UnexpectedTokenException($tokens);
|
||||||
}
|
}
|
||||||
if ($required && !$_exp) {
|
if ($required && !$exp) {
|
||||||
throw new UnexpectedTokenException($tokens);
|
throw new UnexpectedTokenException($tokens);
|
||||||
}
|
}
|
||||||
return implode('', $_exp);
|
return implode(' ', $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse any term: -2, ++$var, 'adf'|mod:4
|
||||||
|
*
|
||||||
|
* @param Tokenizer $tokens
|
||||||
|
* @param bool $is_var
|
||||||
|
* @return bool|string
|
||||||
|
* @throws Error\UnexpectedTokenException
|
||||||
|
* @throws Error\TokenizeException
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function parseTerm(Tokenizer $tokens, &$is_var = false) {
|
||||||
|
$is_var = false;
|
||||||
|
$unary = "";
|
||||||
|
term: {
|
||||||
|
if($tokens->is(T_LNUMBER, T_DNUMBER)) {
|
||||||
|
return $unary.$this->parseScalar($tokens, true);
|
||||||
|
} elseif($tokens->is(T_CONSTANT_ENCAPSED_STRING, '"', T_ENCAPSED_AND_WHITESPACE)) {
|
||||||
|
if($unary) {
|
||||||
|
throw new UnexpectedTokenException($tokens->back());
|
||||||
|
}
|
||||||
|
return $this->parseScalar($tokens, true);
|
||||||
|
} elseif($tokens->is(T_VARIABLE)) {
|
||||||
|
$var = $this->parseVar($tokens);
|
||||||
|
if ($tokens->is(Tokenizer::MACRO_INCDEC, "|", "!", "?")) {
|
||||||
|
return $unary.$this->parseVariable($tokens, 0, $var);
|
||||||
|
} elseif($tokens->is("(") && $tokens->hasBackList(T_STRING)) { // method call
|
||||||
|
return $unary.$this->parseVariable($tokens, 0, $var);
|
||||||
|
} elseif($unary) {
|
||||||
|
return $unary.$var;
|
||||||
|
} else {
|
||||||
|
$is_var = true;
|
||||||
|
return $var;
|
||||||
|
}
|
||||||
|
} elseif($tokens->is(Tokenizer::MACRO_INCDEC)) {
|
||||||
|
return $unary.$this->parseVariable($tokens);
|
||||||
|
} elseif($tokens->is("(")) {
|
||||||
|
$tokens->next();
|
||||||
|
$exp = $unary."(" . $this->parseExp($tokens, true).")";
|
||||||
|
$tokens->need(")")->next();
|
||||||
|
return $exp;
|
||||||
|
} elseif($tokens->is(Tokenizer::MACRO_UNARY)) {
|
||||||
|
if($unary) {
|
||||||
|
throw new UnexpectedTokenException($tokens);
|
||||||
|
}
|
||||||
|
$unary = $tokens->getAndNext();
|
||||||
|
goto term;
|
||||||
|
} elseif($tokens->is(T_STRING)) {
|
||||||
|
if ($tokens->isSpecialVal()) {
|
||||||
|
return $unary.$tokens->getAndNext();
|
||||||
|
} elseif ($tokens->isNext("(") && !$tokens->getWhitespace()) {
|
||||||
|
$func = $this->_fenom->getModifier($tokens->current(), $this);
|
||||||
|
if (!$func) {
|
||||||
|
throw new \Exception("Function " . $tokens->getAndNext() . " not found");
|
||||||
|
}
|
||||||
|
$tokens->next();
|
||||||
|
$func = $func . $this->parseArgs($tokens);
|
||||||
|
if ($tokens->is('|')) {
|
||||||
|
return $unary.$this->parseModifier($tokens, $func);
|
||||||
|
} else {
|
||||||
|
return $unary.$func;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} elseif($tokens->is(T_ISSET, T_EMPTY)) {
|
||||||
|
$func = $tokens->getAndNext();
|
||||||
|
if ($tokens->is("(") && $tokens->isNext(T_VARIABLE)) {
|
||||||
|
$tokens->next();
|
||||||
|
$exp = $func . "(" . $this->parseVar($tokens) . ")";
|
||||||
|
$tokens->need(')')->next();
|
||||||
|
return $unary.$exp;
|
||||||
|
} else {
|
||||||
|
throw new TokenizeException("Unexpected token " . $tokens->getNext() . ", isset() and empty() accept only variables");
|
||||||
|
}
|
||||||
|
} elseif($tokens->is('[')) {
|
||||||
|
if($unary) {
|
||||||
|
throw new UnexpectedTokenException($tokens->back());
|
||||||
|
}
|
||||||
|
return $this->parseArray($tokens);
|
||||||
|
} elseif($unary) {
|
||||||
|
$tokens->back();
|
||||||
|
throw new UnexpectedTokenException($tokens);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1031,7 +1061,7 @@ class Template extends Render
|
|||||||
break;
|
break;
|
||||||
case T_ENCAPSED_AND_WHITESPACE:
|
case T_ENCAPSED_AND_WHITESPACE:
|
||||||
case '"':
|
case '"':
|
||||||
$_scalar .= $this->parseSubstr($tokens);
|
$_scalar .= $this->parseQuote($tokens);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new TokenizeException("Unexpected scalar token '" . $tokens->current() . "'");
|
throw new TokenizeException("Unexpected scalar token '" . $tokens->current() . "'");
|
||||||
@ -1050,7 +1080,7 @@ class Template extends Render
|
|||||||
* @throws UnexpectedTokenException
|
* @throws UnexpectedTokenException
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function parseSubstr(Tokenizer $tokens)
|
public function parseQuote(Tokenizer $tokens)
|
||||||
{
|
{
|
||||||
if ($tokens->is('"', "`")) {
|
if ($tokens->is('"', "`")) {
|
||||||
$stop = $tokens->current();
|
$stop = $tokens->current();
|
||||||
@ -1108,6 +1138,16 @@ class Template extends Render
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Tokenizer $tokens
|
||||||
|
* @param null $first_member
|
||||||
|
*/
|
||||||
|
public function parseConcat(Tokenizer $tokens, $first_member = null) {
|
||||||
|
$concat = array();
|
||||||
|
if($first_member) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse modifiers
|
* Parse modifiers
|
||||||
* |modifier:1:2.3:'string':false:$var:(4+5*$var3)|modifier2:"str {$var+3} ing":$arr.item
|
* |modifier:1:2.3:'string':false:$var:(4+5*$var3)|modifier2:"str {$var+3} ing":$arr.item
|
||||||
@ -1137,7 +1177,7 @@ class Template extends Render
|
|||||||
} elseif ($tokens->is(T_VARIABLE)) {
|
} elseif ($tokens->is(T_VARIABLE)) {
|
||||||
$args[] = $this->parseVariable($tokens, self::DENY_MODS);
|
$args[] = $this->parseVariable($tokens, self::DENY_MODS);
|
||||||
} elseif ($tokens->is('"', '`', T_ENCAPSED_AND_WHITESPACE)) {
|
} elseif ($tokens->is('"', '`', T_ENCAPSED_AND_WHITESPACE)) {
|
||||||
$args[] = $this->parseSubstr($tokens);
|
$args[] = $this->parseQuote($tokens);
|
||||||
} elseif ($tokens->is('(')) {
|
} elseif ($tokens->is('(')) {
|
||||||
$args[] = $this->parseExp($tokens, true);
|
$args[] = $this->parseExp($tokens, true);
|
||||||
} elseif ($tokens->is('[')) {
|
} elseif ($tokens->is('[')) {
|
||||||
@ -1187,7 +1227,7 @@ class Template extends Render
|
|||||||
$key = false;
|
$key = false;
|
||||||
$val = true;
|
$val = true;
|
||||||
} elseif ($tokens->is('"') && !$val) {
|
} elseif ($tokens->is('"') && !$val) {
|
||||||
$_arr .= $this->parseSubstr($tokens);
|
$_arr .= $this->parseQuote($tokens);
|
||||||
$key = false;
|
$key = false;
|
||||||
$val = true;
|
$val = true;
|
||||||
} elseif ($tokens->is(T_DOUBLE_ARROW) && $val) {
|
} elseif ($tokens->is(T_DOUBLE_ARROW) && $val) {
|
||||||
|
@ -328,7 +328,7 @@ class Tokenizer
|
|||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws UnexpectedTokenException
|
* @throws UnexpectedTokenException
|
||||||
*/
|
*/
|
||||||
public function getAndNext()
|
public function getAndNext(/* $token1, ... */)
|
||||||
{
|
{
|
||||||
if ($this->curr) {
|
if ($this->curr) {
|
||||||
$cur = $this->curr[1];
|
$cur = $this->curr[1];
|
||||||
|
@ -186,9 +186,10 @@ class TemplateTest extends TestCase
|
|||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
array('If: {-"hi"} end', 'Fenom\Error\CompileException', "Unexpected token '-'"),
|
array('If: {-"hi"} end', 'Fenom\Error\CompileException', "Unexpected token '-'"),
|
||||||
|
array('If: {-[1,2]} end', 'Fenom\Error\CompileException', "Unexpected token '-'"),
|
||||||
array('If: {($a++)++} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
array('If: {($a++)++} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
||||||
array('If: {$a + * $c} end', 'Fenom\Error\CompileException', "Unexpected token '*'"),
|
array('If: {$a + * $c} end', 'Fenom\Error\CompileException', "Unexpected token '*'"),
|
||||||
array('If: {$a + } end', 'Fenom\Error\CompileException', "Unexpected token '+'"),
|
array('If: {$a + } end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||||
array('If: {$a + =} end', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
array('If: {$a + =} end', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
||||||
array('If: {$a + 1 =} end', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
array('If: {$a + 1 =} end', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
||||||
array('If: {$a + 1 = 6} end', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
array('If: {$a + 1 = 6} end', 'Fenom\Error\CompileException', "Unexpected token '='"),
|
||||||
@ -334,7 +335,7 @@ class TemplateTest extends TestCase
|
|||||||
array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
array('Create: {var $v = $a|upper++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
||||||
array('Create: {var $v = max($a,2)++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
array('Create: {var $v = max($a,2)++} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '++'"),
|
||||||
array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\Error\CompileException', "Function max not found", Fenom::DENY_NATIVE_FUNCS),
|
array('Create: {var $v = max($a,2)} Result: {$v} end', 'Fenom\Error\CompileException', "Function max not found", Fenom::DENY_NATIVE_FUNCS),
|
||||||
array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '*'"),
|
array('Create: {var $v = 4*} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||||
array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '\$a'"),
|
array('Create: {var $v = ""$a} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token '\$a'"),
|
||||||
array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
array('Create: {var $v = [1,2} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected end of expression"),
|
||||||
array('Create: {var $v = empty(2)} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token 2, isset() and empty() accept only variables"),
|
array('Create: {var $v = empty(2)} Result: {$v} end', 'Fenom\Error\CompileException', "Unexpected token 2, isset() and empty() accept only variables"),
|
||||||
@ -672,7 +673,7 @@ class TemplateTest extends TestCase
|
|||||||
public function _testSandbox()
|
public function _testSandbox()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
var_dump($this->fenom->compileCode('{$one.two->three[e]()}')->getBody());
|
var_dump($this->fenom->compileCode('{if 0 is empty} block1 {else} block2 {/if}')->getBody());
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
print_r($e->getMessage() . "\n" . $e->getTraceAsString());
|
print_r($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user