Update docs
  Add {import ... from ...}
  Improve parsing
This commit is contained in:
bzick
2013-03-14 21:45:00 +04:00
parent 26393ad22d
commit a3c5128aba
15 changed files with 221 additions and 101 deletions

View File

@ -25,7 +25,7 @@ class Aspect {
const DEFAULT_CLOSE_COMPILER = 'Aspect\Compiler::stdClose';
const DEFAULT_FUNC_PARSER = 'Aspect\Compiler::stdFuncParser';
const DEFAULT_FUNC_OPEN = 'Aspect\Compiler::stdFuncOpen';
const DEFAULT_FUNC_CLOSE = 'Aspect\Compiler::stdFuncOpen';
const DEFAULT_FUNC_CLOSE = 'Aspect\Compiler::stdFuncClose';
const SMART_FUNC_PARSER = 'Aspect\Compiler::smartFuncParser';
/**
@ -320,7 +320,18 @@ class Aspect {
return $this;
}
public function addCompilerSmart($class) {
/**
* @param string $compiler
* @param string|object $storage
* @return $this
*/
public function addCompilerSmart($compiler, $storage) {
if(method_exists($storage, "tag".$compiler)) {
$this->_actions[$compiler] = array(
'type' => self::INLINE_COMPILER,
'parser' => array($storage, "tag".$compiler)
);
}
return $this;
}
@ -329,7 +340,7 @@ class Aspect {
*
* @param string $compiler
* @param callable $open_parser
* @param callable $close_parser
* @param callable|string $close_parser
* @param array $tags
* @return Aspect
*/
@ -344,12 +355,40 @@ class Aspect {
}
/**
* @param string $class
* @param $compiler
* @param $storage
* @param array $tags
* @param array $floats
* @throws LogicException
* @return Aspect
*/
public function addBlockCompilerSmart($class, array $tags, array $floats = array()) {
public function addBlockCompilerSmart($compiler, $storage, array $tags, array $floats = array()) {
$c = array(
'type' => self::BLOCK_COMPILER,
"tags" => array(),
"float_tags" => array()
);
if(method_exists($storage, $compiler."Open")) {
$c["open"] = $compiler."Open";
} else {
throw new \LogicException("Open compiler {$compiler}Open not found");
}
if(method_exists($storage, $compiler."Close")) {
$c["close"] = $compiler."Close";
} else {
throw new \LogicException("Close compiler {$compiler}Close not found");
}
foreach($tags as $tag) {
if(method_exists($storage, "tag".$tag)) {
$c["tags"][ $tag ] = "tag".$tag;
if($floats && in_array($tag, $floats)) {
$c['float_tags'][ $tag ] = 1;
}
} else {
throw new \LogicException("Tag compiler $tag (tag{$compiler}) not found");
}
}
$this->_actions[$compiler] = $c;
return $this;
}
@ -362,7 +401,7 @@ class Aspect {
public function addFunction($function, $callback, $parser = self::DEFAULT_FUNC_PARSER) {
$this->_actions[$function] = array(
'type' => self::INLINE_FUNCTION,
'parser' => $parser ?: self::DEFAULT_FUNC_PARSER,
'parser' => $parser,
'function' => $callback,
);
return $this;
@ -385,16 +424,16 @@ class Aspect {
/**
* @param string $function
* @param callable $callback
* @param callable $parser_open
* @param callable $parser_close
* @param callable|string $parser_open
* @param callable|string $parser_close
* @return Aspect
*/
public function addBlockFunction($function, $callback, $parser_open = null, $parser_close = null) {
public function addBlockFunction($function, $callback, $parser_open = self::DEFAULT_FUNC_OPEN, $parser_close = self::DEFAULT_FUNC_CLOSE) {
$this->_actions[$function] = array(
'type' => self::BLOCK_FUNCTION,
'open' => $parser_open ?: 'Aspect\Compiler::stdFuncOpen',
'close' => $parser_close ?: 'Aspect\Compiler::stdFuncClose',
'function' => $callback,
'type' => self::BLOCK_FUNCTION,
'open' => $parser_open,
'close' => $parser_close,
'function' => $callback,
);
return $this;
}
@ -564,7 +603,7 @@ class Aspect {
return $this->_storage[ $template ];
}
} elseif($this->_options & self::FORCE_COMPILE) {
return $this->compile($template, false);
return $this->compile($template, $this->_options & self::DISABLE_CACHE);
} else {
return $this->_storage[ $template ] = $this->_load($template);
}
@ -583,7 +622,7 @@ class Aspect {
*
* @param string $tpl
* @throws \RuntimeException
* @return Aspect\Template|mixed
* @return Aspect\Render
*/
protected function _load($tpl) {
$file_name = $this->_getHash($tpl);
@ -591,7 +630,6 @@ class Aspect {
return $this->compile($tpl);
} else {
$aspect = $this;
/** @var Aspect\Render $tpl */
return include($this->_compile_dir."/".$file_name);
}
}
@ -604,7 +642,7 @@ class Aspect {
*/
private function _getHash($tpl) {
$hash = $tpl.":".$this->_options;
return sprintf("%s.%u.%d.php", basename($tpl), crc32($hash), strlen($hash));
return sprintf("%s.%u.%d.php", str_replace(":", "_", basename($tpl)), crc32($hash), strlen($hash));
}
/**

View File

@ -148,7 +148,6 @@ class Compiler {
$before = $before ? implode("; ", $before).";" : "";
$body = $body ? implode("; ", $body).";" : "";
$scope["after"] = $scope["after"] ? implode("; ", $scope["after"]).";" : "";
if($key) {
return "$prepend if($from) { $before foreach($from as $key => $value) { $body";
} else {
@ -161,8 +160,6 @@ class Compiler {
*
* @param Tokenizer $tokens
* @param Scope $scope
* @internal param $
* @param Scope $scope
* @return string
*/
public static function foreachElse($tokens, Scope $scope) {
@ -385,7 +382,7 @@ class Compiler {
return "";
} else { // dynamic extends
$tpl->_extends = $tpl_name;
return '$parent = $tpl->getStorage()->getTemplate('.$tpl_name.');';
return '$parent = $tpl->getStorage()->getTemplate("extend:".'.$tpl_name.');';
}
}
@ -561,7 +558,8 @@ class Compiler {
*/
public static function smartFuncParser($function, Tokenizer $tokens, Template $tpl) {
if(strpos($function, "::")) {
$ref = new \ReflectionMethod($function);
list($class, $method) = explode("::", $function, 2);
$ref = new \ReflectionMethod($class, $method);
} else {
$ref = new \ReflectionFunction($function);
}
@ -710,6 +708,24 @@ class Compiler {
* @throws ImproperUseException
*/
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;
}
}
if($tokens->current() != "from") {
throw new UnexpectedException($tokens);
}
$tokens->next();
}
$tpl->parseFirstArg($tokens, $name);
if(!$name) {
throw new ImproperUseException("Invalid usage tag {import}");

View File

@ -138,8 +138,8 @@ class Template extends Render {
while(($start = strpos($this->_src, '{', $pos)) !== false) { // search open-char of tags
switch($this->_src[$start + 1]) { // check next char
case "\n": case "\r": case "\t": case " ": case "}": // ignore the tag
$pos = $start + 1; // try find tags after the current char
continue 2;
$pos = $start + 1; // try find tags after the current char
continue 2;
case "*": // if comment block
$end = strpos($this->_src, '*}', $start); // finding end of the comment block
$_frag = substr($this->_src, $this->_pos, $start - $end); // read the comment block for precessing
@ -163,7 +163,7 @@ class Template extends Render {
$_tag = substr($tag, 1, -1);
$_frag = $frag;
}
if($this->_ignore) { // check ignore scope
if($this->_ignore) { // check ignore
if($_tag === '/ignore') {
$this->_ignore = false;
$this->_appendText($_frag);
@ -173,7 +173,12 @@ class Template extends Render {
}
} else {
$this->_appendText($_frag);
$this->_appendCode($this->_tag($_tag));
$tokens = new Tokenizer($_tag);
$this->_appendCode($this->_tag($tokens), $tag);
if($tokens->key()) { // if tokenizer still have tokens
throw new CompileException("Unexpected token '".$tokens->current()."' in {$this} line {$this->_line}, near '{".$tokens->getSnippetAsString(0,0)."' <- there", 0, E_ERROR, $this->_name, $this->_line);
}
}
$frag = "";
}
@ -195,7 +200,6 @@ class Template extends Render {
call_user_func_array($cb, array(&$this->_body, $this));
}
}
/*$this->_body = str_replace(array('?>'.PHP_EOL.'<?php ', '?><?php'), array(PHP_EOL, ' '), $this->_body);*/
}
/**
@ -207,7 +211,7 @@ class Template extends Render {
$this->_body .= str_replace("<?", '<?php echo "<?"; ?>'.PHP_EOL, $text);
}
public static function escapeCode($code) {
private function _escapeCode($code) {
$c = "";
foreach(token_get_all($code) as $token) {
if(is_string($token)) {
@ -225,12 +229,16 @@ class Template extends Render {
* Append PHP code to template body
*
* @param string $code
* @param $source
*/
private function _appendCode($code) {
private function _appendCode($code, $source) {
if(!$code) {
return;
} else {
$this->_body .= self::escapeCode($code);
if(strpos($code, '?>') !== false) {
$code = $this->_escapeCode($code); // paste PHP_EOL
}
$this->_body .= "<?php\n/* {$this->_name}:{$this->_line}: {$source} */\n $code ?>".PHP_EOL;
}
}
@ -259,7 +267,7 @@ class Template extends Render {
return "<?php \n".
"/** Aspect template '".$this->_name."' compiled at ".date('Y-m-d H:i:s')." */\n".
"return new Aspect\\Render(\$aspect, ".$this->_getClosureSource().", ".var_export(array(
//"options" => $this->_options,
"options" => $this->_options,
"provider" => $this->_scm,
"name" => $this->_name,
"base_name" => $this->_base_name,
@ -321,43 +329,28 @@ class Template extends Render {
/**
* Internal tags router
* @param string $src
* @param Tokenizer $tokens
* @throws UnexpectedException
* @throws CompileException
* @throws SecurityException
* @return string executable PHP code
*/
private function _tag($src) {
$tokens = new Tokenizer($src);
private function _tag(Tokenizer $tokens) {
try {
switch($src[0]) {
case '"':
case '\'':
case '$':
$code = "echo ".$this->parseExp($tokens).";";
break;
case '#':
$code = "echo ".$this->parseConst($tokens);
break;
case '/':
$code = $this->_end($tokens);
break;
default:
if($tokens->current() === "ignore") {
$this->_ignore = true;
$tokens->next();
$code = '';
} else {
$code = $this->_parseAct($tokens);
}
}
if($tokens->key()) { // if tokenizer still have tokens
throw new UnexpectedException($tokens);
}
if(!$code) {
return "";
if($tokens->is(Tokenizer::MACRO_STRING)) {
if($tokens->current() === "ignore") {
$this->_ignore = true;
$tokens->next();
return '';
} else {
return $this->_parseAct($tokens);
}
} elseif ($tokens->is('/')) {
return $this->_end($tokens);
} elseif ($tokens->is('#')) {
return "echo ".$this->parseConst($tokens).';';
} else {
return "<?php\n/* {$this->_name}:{$this->_line}: {$src} */\n {$code} ?>";
return $code = "echo ".$this->parseExp($tokens).";";
}
} catch (ImproperUseException $e) {
throw new CompileException($e->getMessage()." in {$this} line {$this->_line}", 0, E_ERROR, $this->_name, $this->_line, $e);
@ -376,6 +369,7 @@ class Template extends Render {
* @throws TokenizeException
*/
private function _end(Tokenizer $tokens) {
//return "end";
$name = $tokens->getNext(Tokenizer::MACRO_STRING);
$tokens->next();
if(!$this->_stack) {
@ -402,7 +396,7 @@ class Template extends Render {
if($tokens->is(Tokenizer::MACRO_STRING)) {
$action = $tokens->getAndNext();
} else {
return 'echo '.$this->parseExp($tokens).';'; // may be math and boolean expression
return 'echo '.$this->parseExp($tokens).';'; // may be math and/or boolean expression
}
if($tokens->is("(", T_NAMESPACE, T_DOUBLE_COLON)) { // just invoke function or static method