public function testSkip() { $text = "1 foo: bar ( 3 + double ) "; $tokens = new Tokenizer($text); $tokens->skip()->skip(T_STRING)->skip(':'); try { $tokens->skip(T_STRING)->skip('(')->skip(':'); } catch (\Exception $e) { $this->assertInstanceOf('Fenom\\Error\\UnexpectedTokenException', $e); $this->assertStringStartsWith("Unexpected token '3' in expression, expect ':'", $e->getMessage()); } $this->assertTrue($tokens->valid()); $this->assertSame("3", $tokens->current()); $this->assertSame(T_LNUMBER, $tokens->key()); $this->assertSame($tokens, $tokens->next()); $tokens->next(); $this->assertSame("double", $tokens->getAndNext()); $this->assertSame(")", $tokens->current()); $this->assertTrue($tokens->isLast()); $this->assertSame($tokens, $tokens->next()); $tokens->p = 1000; $this->assertSame($tokens, $tokens->next()); $tokens->p = -1000; $this->assertSame($tokens, $tokens->back()); $this->assertNull($tokens->undef); }
public function __construct(Tokenizer $tokens, $expect = null, $where = null) { if ($expect && count($expect) == 1 && is_string($expect[0])) { $expect = ", expect '" . $expect[0] . "'"; } else { $expect = ""; } if (!$tokens->curr) { $this->message = "Unexpected end of " . ($where ?: "expression") . "{$expect}"; } else { $this->message = "Unexpected token '" . $tokens->current() . "' in " . ($where ?: "expression") . "{$expect}"; } }
/** * Import macros from templates * * @param Tokenizer $tokens * @param Template $tpl * @throws UnexpectedTokenException * @throws InvalidUsageException * @return string */ public static function tagImport(Tokenizer $tokens, Template $tpl) { $import = array(); if ($tokens->is('[')) { $tokens->next(); while ($tokens->valid()) { if ($tokens->is(Tokenizer::MACRO_STRING)) { $import[$tokens->current()] = true; $tokens->next(); } elseif ($tokens->is(']')) { $tokens->next(); break; } elseif ($tokens->is(',')) { $tokens->next(); } else { break; } } if ($tokens->current() != "from") { throw new UnexpectedTokenException($tokens); } $tokens->next(); } $tpl->parsePlainArg($tokens, $name); if (!$name) { throw new InvalidUsageException("Invalid usage tag {import}"); } if ($tokens->is(T_AS)) { $alias = $tokens->next()->get(Tokenizer::MACRO_STRING); if ($alias === "macro") { $alias = ""; } $tokens->next(); } else { $alias = ""; } $donor = $tpl->getStorage()->getRawTemplate()->load($name, true); if ($donor->macros) { foreach ($donor->macros as $name => $macro) { if ($p = strpos($name, ".")) { $name = substr($name, $p); } if ($import && !isset($import[$name])) { continue; } if ($alias) { $name = $alias . '.' . $name; } $tpl->macros[$name] = $macro; } $tpl->addDepend($donor); } return ''; }
/** * Parse string with or without variable * * @param Tokenizer $tokens * @throws UnexpectedTokenException * @return string */ public function parseQuote(Tokenizer $tokens) { if ($tokens->is('"')) { $stop = $tokens->current(); $_str = '"'; $tokens->next(); while ($t = $tokens->key()) { if ($t === T_ENCAPSED_AND_WHITESPACE) { $_str .= $tokens->current(); $tokens->next(); } elseif ($t === T_VARIABLE) { if (strlen($_str) > 1) { $_str .= '".'; } else { $_str = ""; } $_str .= '$var["' . substr($tokens->current(), 1) . '"]'; $tokens->next(); if ($tokens->is($stop)) { $tokens->skip(); return $_str; } else { $_str .= '."'; } } elseif ($t === T_CURLY_OPEN) { if (strlen($_str) > 1) { $_str .= '".'; } else { $_str = ""; } $tokens->getNext(T_VARIABLE); $_str .= '(' . $this->parseExpr($tokens) . ')'; if ($tokens->is($stop)) { $tokens->next(); return $_str; } else { $_str .= '."'; } } elseif ($t === "}") { $tokens->next(); } elseif ($t === $stop) { $tokens->next(); return $_str . '"'; } else { break; } } throw new UnexpectedTokenException($tokens); } elseif ($tokens->is(T_CONSTANT_ENCAPSED_STRING)) { return $tokens->getAndNext(); } elseif ($tokens->is(T_ENCAPSED_AND_WHITESPACE)) { throw new UnexpectedTokenException($tokens); } else { return ""; } }
/** * Define macro * * @param Tokenizer $tokens * @param Tag $scope * @throws InvalidUsageException */ public static function macroOpen(Tokenizer $tokens, Tag $scope) { $scope["name"] = $tokens->get(Tokenizer::MACRO_STRING); $scope["recursive"] = false; $args = array(); $defaults = array(); if (!$tokens->valid()) { return; } $tokens->next(); if ($tokens->is('(') || !$tokens->isNext(')')) { $tokens->next(); while ($tokens->is(Tokenizer::MACRO_STRING, T_VARIABLE)) { $param = $tokens->current(); if ($tokens->is(T_VARIABLE)) { $param = ltrim($param, '$'); } $tokens->next(); $args[] = $param; if ($tokens->is('=')) { $tokens->next(); if ($tokens->is(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER, T_DNUMBER) || $tokens->isSpecialVal()) { $defaults[$param] = $tokens->getAndNext(); } else { throw new InvalidUsageException("Macro parameters may have only scalar defaults"); } } $tokens->skipIf(','); } $tokens->skipIf(')'); } $scope["macro"] = array("name" => $scope["name"], "args" => $args, "defaults" => $defaults, "body" => "", "recursive" => false); return; }
/** * Parse modifiers * |modifier:1:2.3:'string':false:$var:(4+5*$var3)|modifier2:"str {$var+3} ing":$arr.item * * @param Tokenizer $tokens * @param $value * @throws \LogicException * @throws \Exception * @return string */ public function parseModifier(Tokenizer $tokens, $value) { while ($tokens->is("|")) { $mods = $this->_fenom->getModifier($tokens->getNext(Tokenizer::MACRO_STRING), $this); if (!$mods) { throw new \Exception("Modifier " . $tokens->current() . " not found"); } $tokens->next(); $args = array(); while ($tokens->is(":")) { if (!($args[] = $this->parseTerm($tokens->next()))) { throw new UnexpectedTokenException($tokens); } } if (!is_string($mods)) { // dynamic modifier $mods = 'call_user_func($tpl->getStorage()->getModifier("' . $mods . '"), '; } else { $mods .= "("; } if ($args) { $value = $mods . $value . ', ' . implode(", ", $args) . ')'; } else { $value = $mods . $value . ')'; } } return $value; }