mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
Add string concatenation (#3)
This commit is contained in:
@ -81,6 +81,10 @@ Operators
|
|||||||
* `--$a` - decrement the variable and use it
|
* `--$a` - decrement the variable and use it
|
||||||
* `$a--` - use the variable and decrement it
|
* `$a--` - use the variable and decrement it
|
||||||
|
|
||||||
|
### String operator
|
||||||
|
|
||||||
|
* `$a ~ $b` - return concatenation of variables `$a` and `$b`
|
||||||
|
|
||||||
### Ternary operator
|
### Ternary operator
|
||||||
|
|
||||||
* `$a ? $b : $c` - returns `$b` if `$a` is not empty, and `$c` otherwise
|
* `$a ? $b : $c` - returns `$b` if `$a` is not empty, and `$c` otherwise
|
||||||
|
@ -615,36 +615,34 @@ class Template extends Render
|
|||||||
$exp = array();
|
$exp = array();
|
||||||
$var = false; // last term was: true - variable, false - mixed
|
$var = false; // last term was: true - variable, false - mixed
|
||||||
$op = false; // last exp was operator
|
$op = false; // last exp was operator
|
||||||
$cond = false; // was conditional operator
|
$cond = false; // was comparison operator
|
||||||
while ($tokens->valid()) {
|
while ($tokens->valid()) {
|
||||||
// parse term
|
// parse term
|
||||||
$term = $this->parseTerm($tokens, $var);
|
$term = $this->parseTerm($tokens, $var); // term of the expression
|
||||||
if ($term !== false) {
|
if ($term !== false) {
|
||||||
$exp[] = $term;
|
$exp[] = $term;
|
||||||
$op = false;
|
$op = false;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$tokens->valid()) {
|
if (!$tokens->valid()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse operator
|
// parse operator
|
||||||
if ($tokens->is(Tokenizer::MACRO_BINARY)) {
|
if ($tokens->is(Tokenizer::MACRO_BINARY)) { // binary operator: $a + $b, $a <= $b, ...
|
||||||
if ($tokens->is(Tokenizer::MACRO_COND)) {
|
if ($tokens->is(Tokenizer::MACRO_COND)) { // comparison operator
|
||||||
if ($cond) {
|
if ($cond) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$cond = true;
|
$cond = true;
|
||||||
}
|
}
|
||||||
$op = $tokens->getAndNext();
|
$op = $tokens->getAndNext();
|
||||||
} elseif ($tokens->is(Tokenizer::MACRO_EQUALS)) {
|
} elseif ($tokens->is(Tokenizer::MACRO_EQUALS)) { // assignment operator: $a = 4, $a += 3, ...
|
||||||
if (!$var) {
|
if (!$var) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$op = $tokens->getAndNext();
|
$op = $tokens->getAndNext();
|
||||||
} elseif ($tokens->is(T_STRING)) {
|
} elseif ($tokens->is(T_STRING)) { // test or containment operator: $a in $b, $a is set, ...
|
||||||
if (!$exp) {
|
if (!$exp) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -658,8 +656,17 @@ class Template extends Render
|
|||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} elseif ($tokens->is('~')) {
|
} elseif ($tokens->is('~')) { // string concatenation operator: 'asd' ~ $var
|
||||||
// string concat coming soon
|
$concat = array(array_pop($exp));
|
||||||
|
while($tokens->is('~')) {
|
||||||
|
$tokens->next();
|
||||||
|
if($tokens->is(T_LNUMBER, T_DNUMBER)) {
|
||||||
|
$concat[] = "strval(".$this->parseTerm($tokens).")";
|
||||||
|
} else {
|
||||||
|
$concat[] = $this->parseTerm($tokens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$exp[] = "(".implode(".", $concat).")";
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -692,79 +699,79 @@ class Template extends Render
|
|||||||
$is_var = false;
|
$is_var = false;
|
||||||
$unary = "";
|
$unary = "";
|
||||||
term: {
|
term: {
|
||||||
if ($tokens->is(T_LNUMBER, T_DNUMBER)) {
|
if ($tokens->is(T_LNUMBER, T_DNUMBER)) {
|
||||||
return $unary . $this->parseScalar($tokens, true);
|
return $unary . $this->parseScalar($tokens, true);
|
||||||
} elseif ($tokens->is(T_CONSTANT_ENCAPSED_STRING, '"', T_ENCAPSED_AND_WHITESPACE)) {
|
} elseif ($tokens->is(T_CONSTANT_ENCAPSED_STRING, '"', T_ENCAPSED_AND_WHITESPACE)) {
|
||||||
if ($unary) {
|
if ($unary) {
|
||||||
throw new UnexpectedTokenException($tokens->back());
|
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();
|
return $this->parseScalar($tokens, true);
|
||||||
$func = $func . $this->parseArgs($tokens);
|
} elseif ($tokens->is(T_VARIABLE)) {
|
||||||
if ($tokens->is('|')) {
|
$var = $this->parseVar($tokens);
|
||||||
return $unary . $this->parseModifier($tokens, $func);
|
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 {
|
} else {
|
||||||
return $unary . $func;
|
$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 {
|
} else {
|
||||||
return false;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse simple variable (without modifier etc)
|
* Parse simple variable (without modifier etc)
|
||||||
|
@ -34,7 +34,9 @@ class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
"obj" => new \StdClass,
|
"obj" => new \StdClass,
|
||||||
"list" => array(
|
"list" => array(
|
||||||
"a" => 1,
|
"a" => 1,
|
||||||
"b" => 2
|
"one" => 1,
|
||||||
|
"b" => 2,
|
||||||
|
"two" => 2
|
||||||
),
|
),
|
||||||
0 => "empty value",
|
0 => "empty value",
|
||||||
1 => "one value",
|
1 => "one value",
|
||||||
@ -204,7 +206,11 @@ class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
public function providerVariables()
|
public function providerVariables()
|
||||||
{
|
{
|
||||||
return array();
|
return array(
|
||||||
|
array('$one', 1),
|
||||||
|
array('$list.one', 1),
|
||||||
|
array('$list[$$.DEVELOP]', 1),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function providerObjects()
|
public static function providerObjects()
|
||||||
|
@ -676,10 +676,20 @@ class TemplateTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function providerConcat() {
|
||||||
|
return array(
|
||||||
|
array('{"string" ~ $one ~ up("end")}', "string1END"),
|
||||||
|
array('{"string" ~ $one++ ~ "end"}', "string1end"),
|
||||||
|
array('{"string" ~ ++$one ~ "end"}', "string2end"),
|
||||||
|
array('{"string" ~ "one" ~ "end"}', "stringoneend"),
|
||||||
|
array('{"string" ~ 1 ~ "end"}', "string1end"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function _testSandbox()
|
public function _testSandbox()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
var_dump($this->fenom->compileCode('{$a!"no way":"right"}')->getBody());
|
var_dump($this->fenom->compileCode('{$a++~"hi"~time("Y:m:d")}')->getBody());
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
print_r($e->getMessage() . "\n" . $e->getTraceAsString());
|
print_r($e->getMessage() . "\n" . $e->getTraceAsString());
|
||||||
}
|
}
|
||||||
@ -891,5 +901,13 @@ class TemplateTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->exec($code, self::getVars(), $result);
|
$this->exec($code, self::getVars(), $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerConcat
|
||||||
|
*/
|
||||||
|
public function testConcat($code, $result)
|
||||||
|
{
|
||||||
|
$this->exec($code, self::getVars(), $result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user