summaryrefslogtreecommitdiff
path: root/src/ste
diff options
context:
space:
mode:
Diffstat (limited to 'src/ste')
-rw-r--r--src/ste/Calc.php18
-rw-r--r--src/ste/FilesystemStorageAccess.php12
-rw-r--r--src/ste/ParseCompileError.php4
-rw-r--r--src/ste/Parser.php118
-rw-r--r--src/ste/STECore.php56
-rw-r--r--src/ste/STEStandardLibrary.php40
-rw-r--r--src/ste/Scope.php54
-rw-r--r--src/ste/StorageAccess.php4
-rw-r--r--src/ste/Transcompiler.php114
9 files changed, 210 insertions, 210 deletions
diff --git a/src/ste/Calc.php b/src/ste/Calc.php
index e8dba8d..595a7d6 100644
--- a/src/ste/Calc.php
+++ b/src/ste/Calc.php
@@ -5,7 +5,7 @@ namespace kch42\ste;
/* Class Calc contains static methods needed by <ste:calc /> */
class Calc {
private function __construct() {}
-
+
/* We could also just eval() the $infix_math code, but this is much cooler :-D (Parser inception) */
public static function shunting_yard($infix_math) {
$operators = array(
@@ -18,12 +18,12 @@ class Calc {
"(" => array("", 0),
")" => array("", 0)
);
-
+
preg_match_all("/\s*(?:(?:[+\\-\\*\\/\\^\\(\\)])|(\\d*[\\.]?\\d*))\\s*/s", $infix_math, $tokens, PREG_PATTERN_ORDER);
$tokens_raw = array_filter(array_map('trim', $tokens[0]), function($x) { return ($x === "0") || (!empty($x)); });
$output_queue = array();
$op_stack = array();
-
+
$lastpriority = NULL;
/* Make - unary, if neccessary */
$tokens = array();
@@ -37,7 +37,7 @@ class Calc {
}
$lastpriority = $priority;
}
-
+
while(!empty($tokens)) {
$token = array_shift($tokens);
if(is_numeric($token)) {
@@ -73,7 +73,7 @@ class Calc {
$op_stack[] = $token;
}
}
-
+
while(!empty($op_stack)) {
$op = array_pop($op_stack);
if($op == "(") {
@@ -81,10 +81,10 @@ class Calc {
}
$output_queue[] = $op;
}
-
+
return $output_queue;
}
-
+
public static function pop2(&$array) {
$rv = array(array_pop($array), array_pop($array));
if(array_search(NULL, $rv, true) !== false) {
@@ -92,7 +92,7 @@ class Calc {
}
return $rv;
}
-
+
public static function calc_rpn($rpn) {
$stack = array();
foreach($rpn as $token) {
@@ -131,7 +131,7 @@ class Calc {
}
return array_pop($stack);
}
-
+
public static function calc($expr) {
return self::calc_rpn(self::shunting_yard($expr));
}
diff --git a/src/ste/FilesystemStorageAccess.php b/src/ste/FilesystemStorageAccess.php
index cdbce8b..51945b8 100644
--- a/src/ste/FilesystemStorageAccess.php
+++ b/src/ste/FilesystemStorageAccess.php
@@ -12,7 +12,7 @@ namespace kch42\ste;
class FilesystemStorageAccess implements StorageAccess {
protected $sourcedir;
protected $transcompileddir;
-
+
/*
* Constructor: __construct
*
@@ -24,11 +24,11 @@ class FilesystemStorageAccess implements StorageAccess {
$this->sourcedir = $src;
$this->transcompileddir = $transc;
}
-
+
public function load($tpl, &$mode) {
$src_fn = $this->sourcedir . "/" . $tpl;
$transc_fn = $this->transcompileddir . "/" . $tpl . ".php";
-
+
if($mode == StorageAccess::MODE_SOURCE) {
$content = @file_get_contents($src_fn);
if($content === false) {
@@ -36,10 +36,10 @@ class FilesystemStorageAccess implements StorageAccess {
}
return $content;
}
-
+
$src_stat = @stat($src_fn);
$transc_stat = @stat($transc_fn);
-
+
if(($src_stat === false) and ($transc_stat === false)) {
throw new CantLoadTemplate("Template not found.");
} else if($transc_stat === false) {
@@ -58,7 +58,7 @@ class FilesystemStorageAccess implements StorageAccess {
}
}
}
-
+
public function save($tpl, $data, $mode) {
$fn = (($mode == StorageAccess::MODE_SOURCE) ? $this->sourcedir : $this->transcompileddir) . "/" . $tpl . (($mode == StorageAccess::MODE_TRANSCOMPILED) ? ".php" : "");
@mkdir(dirname($fn), 0777, true);
diff --git a/src/ste/ParseCompileError.php b/src/ste/ParseCompileError.php
index 5ee37c7..8a7c36b 100644
--- a/src/ste/ParseCompileError.php
+++ b/src/ste/ParseCompileError.php
@@ -6,14 +6,14 @@ class ParseCompileError extends \Exception {
public $msg;
public $tpl;
public $off;
-
+
public function __construct($msg, $tpl, $offset, $code = 0, $previous = NULL) {
$this->msg = $msg;
$this->tpl = $tpl;
$this->off = $offset;
$this->message = "$msg (Template $tpl, Offset $offset)";
}
-
+
public function rewrite($code) {
$line = substr_count(str_replace("\r\n", "\n", substr($code, 0, $this->off)), "\n") + 1;
$this->message = "{$this->msg} (Template {$this->tpl}, Line $line)";
diff --git a/src/ste/Parser.php b/src/ste/Parser.php
index 7e93e47..14ec33d 100644
--- a/src/ste/Parser.php
+++ b/src/ste/Parser.php
@@ -15,19 +15,19 @@ class Parser {
private $name;
private $off;
private $len;
-
+
const PARSE_SHORT = 1;
const PARSE_TAG = 2;
-
+
const ESCAPES_DEFAULT = '$?~{}|\\';
-
+
private function __construct($text, $name) {
$this->text = $text;
$this->name = $name;
$this->off = 0;
$this->len = mb_strlen($text);
}
-
+
private function next($n = 1) {
if($n <= 0) {
throw new \InvalidArgumentException("\$n must be > 0");
@@ -36,28 +36,28 @@ class Parser {
$this->off = min($this->off + $n, $this->len);
return $c;
}
-
+
private function eof() {
return ($this->off == $this->len);
}
-
+
private function back($n = 1) {
if($n <= 0) {
throw new \InvalidArgumentException("\$n must be > 0");
}
$this->off = max($this->off - $n, 0);
}
-
+
private function search_off($needle) {
return mb_strpos($this->text, $needle, $this->off);
}
-
+
private function search_multi($needles) {
$oldoff = $this->off;
-
+
$minoff = $this->len;
$which = NULL;
-
+
foreach($needles as $key => $needle) {
if(($off = $this->search_off($needle)) === false) {
continue;
@@ -68,25 +68,25 @@ class Parser {
$which = $key;
}
}
-
+
$this->off = $minoff + (($which === NULL) ? 0 : mb_strlen((string) $needles[$which]));
-
+
return array($which, $minoff, mb_substr($this->text, $oldoff, $minoff - $oldoff), $oldoff);
}
-
+
private function search($needle) {
$oldoff = $this->off;
-
+
$off = $this->search_off($needle);
if($off === false) {
$this->off = $this->len;
return array(false, mb_substr($this->text, $oldoff), $oldoff);
}
-
+
$this->off = $off + mb_strlen($needle);
return array($off, mb_substr($this->text, $oldoff, $off - $oldoff), $oldoff);
}
-
+
private function take_while($cb) {
$s = "";
while($c = $this->next()) {
@@ -98,11 +98,11 @@ class Parser {
}
return $s;
}
-
+
private function skip_ws() {
$this->take_while("ctype_space");
}
-
+
private function get_name() {
$off = $this->off;
$name = $this->take_while(function($c) { return ctype_alnum($c) || ($c == "_"); });
@@ -111,7 +111,7 @@ class Parser {
}
return $name;
}
-
+
/*
* Function: parse
* Parses the input into an AST.
@@ -136,13 +136,13 @@ class Parser {
);
return self::tidyup_ast($res[0]);
}
-
+
private static function tidyup_ast($ast) {
$out = array();
-
+
$prevtext = NULL;
$first = true;
-
+
foreach($ast as $node) {
if($node instanceof TextNode) {
if($prevtext === NULL) {
@@ -161,7 +161,7 @@ class Parser {
}
$prevtext = NULL;
$first = false;
-
+
if($node instanceof TagNode) {
$node->sub = self::tidyup_ast($node->sub);
foreach($node->params as $k => &$v) {
@@ -174,11 +174,11 @@ class Parser {
}
unset($v);
}
-
+
$out[] = $node;
}
}
-
+
if($prevtext !== NULL) {
if($first) {
$prevtext->text = ltrim($prevtext->text);
@@ -187,14 +187,14 @@ class Parser {
$out[] = $prevtext;
}
}
-
+
return $out;
}
-
+
private function parse_text($escapes, $flags, $breakon = NULL, $separator = NULL, $nullaction = NULL, $opentag = NULL, $openedat = -1) {
$elems = array();
$astlist = array();
-
+
$needles = array(
"commentopen" => "<ste:comment>",
"rawopen" => "<ste:rawtext>",
@@ -202,7 +202,7 @@ class Parser {
"varcurlyopen" => '${',
"var" => '$',
);
-
+
if($flags & self::PARSE_TAG) {
$needles["tagopen"] = '<ste:';
$needles["closetagopen"] = '</ste:';
@@ -211,19 +211,19 @@ class Parser {
$needles["shortifopen"] = '?{';
$needles["shortcompopen"] = '~{';
}
-
+
if($separator !== NULL) {
$needles["sep"] = $separator;
}
if($breakon !== NULL) {
$needles["break"] = $breakon;
}
-
+
for(;;) {
list($which, $off, $before, $offbefore) = $this->search_multi($needles);
-
+
$astlist[] = new TextNode($this->name, $offbefore, $before);
-
+
switch($which) {
case NULL:
if($nullaction === NULL) {
@@ -258,14 +258,14 @@ class Parser {
if($this->next() != ">") {
throw new ParseCompileError("Expected '>' in closing ste-Tag", $this->name, $off);
}
-
+
if($opentag === NULL) {
throw new ParseCompileError("Found closing ste:$name tag, but no tag was opened", $this->name, $off_start);
}
if($opentag != $name) {
throw new ParseCompileError("Open ste:$opentag was not closed", $this->name, $openedat);
}
-
+
$elems[] = $astlist;
return $elems;
case "escape":
@@ -282,22 +282,22 @@ class Parser {
if(count($shortelems) != 3) {
throw new ParseCompileError("A short if tag must have the form ?{..|..|..}", $this->name, $off);
}
-
+
list($cond, $then, $else) = $shortelems;
$thentag = new TagNode($this->name, $off);
$thentag->name = "then";
$thentag->sub = $then;
-
+
$elsetag = new TagNode($this->name, $off);
$elsetag->name = "else";
$elsetag->sub = $else;
-
+
$iftag = new TagNode($this->name, $off);
$iftag->name = "if";
$iftag->sub = $cond;
$iftag->sub[] = $thentag;
$iftag->sub[] = $elsetag;
-
+
$astlist[] = $iftag;
break;
case "shortcompopen":
@@ -305,7 +305,7 @@ class Parser {
if(count($shortelems) != 3) {
throw new ParseCompileError("A short comparasion tag must have the form ~{..|..|..}", $this->name, $off);
}
-
+
// TODO: What will happen, if a tag was in one of the elements?
list($a, $op, $b) = $shortelems;
$cmptag = new TagNode($this->name, $off);
@@ -313,7 +313,7 @@ class Parser {
$cmptag->params["text_a"] = $a;
$cmptag->params["op"] = $op;
$cmptag->params["text_b"] = $b;
-
+
$astlist[] = $cmptag;
break;
case "sep":
@@ -331,14 +331,14 @@ class Parser {
return $elems;
}
}
-
+
$elems[] = $astlist;
return $elems;
}
-
+
private function parse_short($shortname, $openedat) {
$tplname = $this->name;
-
+
return $this->parse_text(
self::ESCAPES_DEFAULT, /* Escapes */
self::PARSE_SHORT | self::PARSE_TAG, /* Flags */
@@ -351,25 +351,25 @@ class Parser {
$openedat /* Opened at */
);
}
-
+
private function parse_var($openedat, $curly) {
$varnode = new VariableNode($this->name, $openedat);
$varnode->name = $this->get_name();
if(!$this->eof()) {
$varnode->arrayfields = $this->parse_array();
}
-
+
if(($curly) && ($this->next() != "}")) {
throw new ParseCompileError("Unclosed '\${'", $this->name, $openedat);
}
return $varnode;
}
-
+
private function parse_array() {
$tplname = $this->name;
-
+
$arrayfields = array();
-
+
while($this->next() == "[") {
$openedat = $this->off - 1;
$res = $this->parse_text(
@@ -385,30 +385,30 @@ class Parser {
);
$arrayfields[] = $res[0];
}
-
+
$this->back();
return $arrayfields;
}
-
+
private function parse_tag($openedat) {
$tplname = $this->name;
-
+
$this->skip_ws();
$tag = new TagNode($this->name, $openedat);
$name = $tag->name = $this->get_name();
$tag->params = array();
$tag->sub = array();
-
+
for(;;) {
$this->skip_ws();
-
+
switch($this->next()) {
case '/': /* Self-closing tag */
$this->skip_ws();
if($this->next() != '>') {
throw new ParseCompileError("Unclosed opening <ste: tag (expected >)", $this->name, $openedat);
}
-
+
return $tag;
case '>':
$sub = $this->parse_text(
@@ -426,20 +426,20 @@ class Parser {
return $tag;
default:
$this->back();
-
+
$param = $this->get_name();
-
+
$this->skip_ws();
if($this->next() != '=') {
throw new ParseCompileError("Expected '=' after tag parameter name", $this->name, $this->off - 1);
}
$this->skip_ws();
-
+
$quot = $this->next();
if(($quot != '"') && ($quot != "'")) {
throw new ParseCompileError("Expected ' or \" after '=' of tag parameter", $this->name, $this->off - 1);
}
-
+
$off = $this->off - 1;
$paramval = $this->parse_text(
self::ESCAPES_DEFAULT . $quot, /* Escapes */
diff --git a/src/ste/STECore.php b/src/ste/STECore.php
index b021e3b..36e3b18 100644
--- a/src/ste/STECore.php
+++ b/src/ste/STECore.php
@@ -18,13 +18,13 @@ class STECore {
*/
const ESCAPE_NONE = "none";
const ESCAPE_HTML = "html";
-
+
private $tags;
private $storage_access;
private $cur_tpl_dir;
private $escape_method = self::ESCAPE_NONE;
public $scope;
-
+
/*
* Variables: Public variables
*
@@ -39,7 +39,7 @@ class STECore {
public $mute_runtime_errors = true;
public $fatal_error_on_missing_tag = false;
public $vars;
-
+
/*
* Constructor: __construct
*
@@ -50,17 +50,17 @@ class STECore {
public function __construct($storage_access, $escape_method=self::ESCAPE_NONE) {
$this->storage_access = $storage_access;
$this->escape_method = $escape_method;
-
+
$this->cur_tpl_dir = "/";
STEStandardLibrary::_register_lib($this);
$this->blockorder = array();
$this->blocks = array();
-
+
$this->vars = array();
$this->scope = new Scope();
$this->scope->vars =& $this->vars;
}
-
+
/*
* Function: register_tag
* Register a custom tag.
@@ -81,7 +81,7 @@ class STECore {
}
$this->tags[$name] = $callback;
}
-
+
/*
* Function: call_tag
* Calling a custom tag (builtin ones can not be called)
@@ -113,14 +113,14 @@ class STECore {
}
}
}
-
+
public function autoescape($content) {
if ($this->escape_method == self::ESCAPE_HTML) {
return htmlspecialchars($content);
}
return $content;
}
-
+
/*
* Evaluate a $sub function (representing the contents of a tag) with a different escaping method for autoescape.
*
@@ -138,11 +138,11 @@ class STECore {
$this->escape_method = $old_method;
return $retval;
}
-
+
public function calc($expression) {
return Calc::calc($expression);
}
-
+
/*
* Function: exectemplate
* Executes a template and returns the result. The huge difference to <load> is that this function will also output all blocks.
@@ -162,14 +162,14 @@ class STECore {
public function exectemplate($tpl) {
$output = "";
$lastblock = $this->load($tpl);
-
+
foreach($this->blockorder as $blockname) {
$output .= $this->blocks[$blockname];
}
-
+
return $output . $lastblock;
}
-
+
/*
* Function: get_var_reference
* Get a reference to a template variable using a variable name.
@@ -189,7 +189,7 @@ class STECore {
$ref = &$this->scope->get_var_reference($name, $create_if_not_exist);
return $ref;
}
-
+
/*
* Function: set_var_by_name
* Set a template variable by its name.
@@ -205,7 +205,7 @@ class STECore {
public function set_var_by_name($name, $val) {
$this->scope->set_var_by_name($name, $val);
}
-
+
/*
* Function: set_local_var
* Like <set_var_by_name>, but only sets the variable in the global scope (<set_var_by_name> will overwrite the variable in the parent scope, if it's defined there) .
@@ -220,7 +220,7 @@ class STECore {
public function set_local_var($name, $val) {
$this->scope->set_local_var($name, $val);
}
-
+
/*
* Function: get_var_by_name
* Get a template variable by its name.
@@ -238,7 +238,7 @@ class STECore {
public function get_var_by_name($name) {
return $this->scope->get_var_by_name($name);
}
-
+
/*
* Function: load
* Load a template and return its result (blocks not included, use <exectemplate> for this).
@@ -258,7 +258,7 @@ class STECore {
*/
public function load($tpl, $quiet = false) {
$tpldir_b4 = $this->cur_tpl_dir;
-
+
/* Resolve ".", ".." and protect from possible LFI */
$tpl = str_replace("\\", "/", $tpl);
if($tpl[0] != "/") {
@@ -275,12 +275,12 @@ class STECore {
}
$tpl = implode("/", $pathex);
$this->cur_tpl_dir = dirname($tpl);
-
+
if($quiet) {
$blocks_back = clone $this->blocks;
$blockorder_back = clone $this->blockorder;
}
-
+
$mode = StorageAccess::MODE_TRANSCOMPILED;
$content = $this->storage_access->load($tpl, $mode);
if($mode == StorageAccess::MODE_SOURCE) {
@@ -294,11 +294,11 @@ class STECore {
$this->storage_access->save($tpl, $transc, StorageAccess::MODE_TRANSCOMPILED);
eval("\$content = $transc;");
}
-
+
$output = $content($this);
-
+
$this->cur_tpl_dir = $tpldir_b4;
-
+
if($quiet) {
$this->blocks = $blocks_back;
$this->blockorder = $blockorder_back;
@@ -306,7 +306,7 @@ class STECore {
return $output;
}
}
-
+
/*
* Function: evalbool
* Test, if a text represents false (an empty / only whitespace text) or true (everything else).
@@ -320,17 +320,17 @@ class STECore {
public function evalbool($txt) {
return trim(@(string)$txt) != "";
}
-
+
public function make_closure($fx) {
$bound_scope = $this->scope;
return function() use($bound_scope, $fx) {
$args = func_get_args();
$ste = $args[0];
-
+
$prev = $ste->scope;
$scope = $bound_scope->new_subscope();
$ste->scope = $scope;
-
+
try {
$result = call_user_func_array($fx, $args);
$ste->scope = $prev;
diff --git a/src/ste/STEStandardLibrary.php b/src/ste/STEStandardLibrary.php
index 68ba06a..753ffd1 100644
--- a/src/ste/STEStandardLibrary.php
+++ b/src/ste/STEStandardLibrary.php
@@ -10,33 +10,33 @@ class STEStandardLibrary {
}
}
}
-
+
static public function escape($ste, $params, $sub) {
$content = $ste->eval_sub_with_escaping($sub, STECore::ESCAPE_NONE);
-
+
if($ste->evalbool(@$params["lines"])) {
return nl2br(htmlspecialchars(str_replace("\r\n", "\n", $content)));
} else {
return htmlspecialchars($content);
}
}
-
+
static public function raw($ste, $params, $sub) {
return $ste->eval_sub_with_escaping($sub, STECore::ESCAPE_NONE);
}
-
+
static public function autoescape($ste, $params, $sub) {
if(empty($params["mode"])) {
throw new RuntimeError("Missing mode parameter in <ste:arraylen>.");
}
-
+
return $content = $ste->eval_sub_with_escaping($sub, $params['mode']);
}
-
+
static public function strlen($ste, $params, $sub) {
return strlen($sub($ste));
}
-
+
static public function arraylen($ste, $params, $sub) {
if(empty($params["array"])) {
throw new RuntimeError("Missing array parameter in <ste:arraylen>.");
@@ -44,7 +44,7 @@ class STEStandardLibrary {
$a = $ste->get_var_by_name($params["array"], false);
return (is_array($a)) ? count($a) : "";
}
-
+
static public function inc($ste, $params, $sub) {
if(empty($params["var"])) {
throw new RuntimeError("Missing var parameter in <ste:inc>.");
@@ -52,7 +52,7 @@ class STEStandardLibrary {
$ref = &$ste->get_var_reference($params["var"], true);
$ref++;
}
-
+
static public function dec($ste, $params, $sub) {
if(empty($params["var"])) {
throw new RuntimeError("Missing var parameter in <ste:dec>.");
@@ -60,11 +60,11 @@ class STEStandardLibrary {
$ref = &$ste->get_var_reference($params["var"], true);
$ref--;
}
-
+
static public function date($ste, $params, $sub) {
return @strftime($sub($ste), empty($params["timestamp"]) ? @time() : (int) $params["timestamp"]);
}
-
+
static public function in_array($ste, $params, $sub) {
if(empty($params["array"])) {
throw new RuntimeError("Missing array parameter in <ste:in_array>.");
@@ -75,14 +75,14 @@ class STEStandardLibrary {
}
return in_array($sub($ste), $ar) ? "y" : "";
}
-
+
static public function join($ste, $params, $sub) {
if(empty($params["array"])) {
throw new RuntimeError("Missing array parameter in <ste:join>.");
}
return implode($sub($ste), $ste->get_var_by_name($params["array"]));
}
-
+
static public function split($ste, $params, $sub) {
if(empty($params["array"])) {
throw new RuntimeError("Missing array parameter in <ste:split>.");
@@ -92,12 +92,12 @@ class STEStandardLibrary {
}
$ste->set_var_by_name($params["array"], explode($params["delim"], $sub($ste)));
}
-
+
static public function array_add($ste, $params, $sub) {
if(empty($params["array"])) {
throw new RuntimeError("Missing array parameter in <ste:array_add>.");
}
-
+
$ar = &$ste->get_var_reference($params["array"], true);
if(empty($params["key"])) {
$ar[] = $sub($ste);
@@ -105,20 +105,20 @@ class STEStandardLibrary {
$ar[$params["key"]] = $sub($ste);
}
}
-
+
static public function array_filter($ste, $params, $sub)
{
if(empty($params["array"])) {
throw new RuntimeError("Missing array parameter in <ste:array_filter>.");
}
-
+
$ar = $ste->get_var_by_name($params["array"]);
if(!is_array($ar)) {
throw new RuntimeError("Variable at 'array' is not an array.");
}
-
+
$keys = array_keys($ar);
-
+
if(!empty($params["keep_by_keys"])) {
$keep_by_keys = &$ste->get_var_reference($params["keep_by_keys"], false);
if(!is_array($keep_by_keys)) {
@@ -157,7 +157,7 @@ class STEStandardLibrary {
$ar = array_filter($ar, function($v) use ($delete_by_values) { return !in_array($v, $delete_by_values); });
$keys = array_keys($ar);
}
-
+
$ste->set_var_by_name($params["array"], $ar);
}
}
diff --git a/src/ste/Scope.php b/src/ste/Scope.php
index 26481ac..5249158 100644
--- a/src/ste/Scope.php
+++ b/src/ste/Scope.php
@@ -5,49 +5,49 @@ namespace kch42\ste;
class Scope implements \ArrayAccess {
private $parent = NULL;
public $vars = array();
-
+
private static function parse_name($name) {
$remain = $name;
$fields = array();
-
+
while($remain !== "") {
$br_open = strpos($remain, '[');
if($br_open === false) {
$fields[] = $remain;
break;
}
-
+
$br_close = strpos($remain, ']', $br_open);
if($br_close === false) {
throw new RuntimeError("Invalid varname \"$name\". Missing closing \"]\".");
}
-
+
$fields[] = substr($remain, 0, $br_open);
-
+
$field = substr($remain, $br_open+1, $br_close-$br_open-1);
$more = substr($remain, $br_close+1);
-
+
if(strpos($field, '[') !== false) {
throw new RuntimeError("A variable field must not contain a '[' character.");
}
-
+
if((strlen($more) > 0) && ($more[0] !== '[')) {
// TODO: better error message, not very non-programmer friendly...
throw new RuntimeError("A variable name must be of format name('[' name ']')*.");
}
-
+
$remain = $field . $more;
}
-
+
return $fields;
}
-
+
private function &get_topvar_reference($name, $localonly) {
if(array_key_exists($name, $this->vars)) {
$ref = &$this->vars[$name];
return $ref;
}
-
+
if((!$localonly) && ($this->parent !== NULL)) {
$ref = &$this->parent->get_topvar_reference($name, $localonly);
return $ref;
@@ -55,17 +55,17 @@ class Scope implements \ArrayAccess {
throw new VarNotInScope();
}
-
+
public function &get_var_reference($name, $create_if_not_exist, $localonly=false) {
$nullref = NULL;
-
+
$fields = self::parse_name($name);
if(count($fields) == 0) {
return $nullref; // TODO: or should we throw an exception here?
}
-
+
$first = $fields[0];
-
+
$ref = NULL;
try {
$ref = &$this->get_topvar_reference($first, $localonly);
@@ -77,55 +77,55 @@ class Scope implements \ArrayAccess {
return $nullref;
}
}
-
+
for($i = 1; $i < count($fields); $i++) {
$field = $fields[$i];
-
+
if(!is_array($ref)) {
return $nullref;
}
-
+
if(!array_key_exists($field, $ref)) {
if(!$create_if_not_exist) {
return $nullref;
}
-
+
if($i < count($fields) - 1) {
$ref[$field] = array();
} else {
$ref[$field] = "";
}
}
-
+
$ref = &$ref[$field];
}
-
+
return $ref;
}
-
+
public function set_var_by_name($name, $val) {
$ref = &$this->get_var_reference($name, true);
$ref = $val;
}
-
+
public function set_local_var($name, $val) {
$ref = &$this->get_var_reference($name, true, true);
$ref = $val;
}
-
+
public function get_var_by_name($name) {
$ref = $this->get_var_reference($name, false);
return $ref === NULL ? "" : $ref;
}
-
+
public function new_subscope() {
$o = new self();
$o->parent = $this;
return $o;
}
-
+
/* implementing ArrayAccess */
-
+
public function offsetSet($offset, $value) {
$this->set_var_by_name($offset, $value);
}
diff --git a/src/ste/StorageAccess.php b/src/ste/StorageAccess.php
index 81f7439..d5bab83 100644
--- a/src/ste/StorageAccess.php
+++ b/src/ste/StorageAccess.php
@@ -20,7 +20,7 @@ interface StorageAccess {
*/
const MODE_SOURCE = 0;
const MODE_TRANSCOMPILED = 1;
-
+
/*
* Function: load
* Loading a template.
@@ -38,7 +38,7 @@ interface StorageAccess {
* Either the sourcecode or a callable function (first, and only parameter: an <STECore> instance).
*/
public function load($tpl, &$mode);
-
+
/*
* Function: save
* Saves a template.
diff --git a/src/ste/Transcompiler.php b/src/ste/Transcompiler.php
index 9961b15..2519406 100644
--- a/src/ste/Transcompiler.php
+++ b/src/ste/Transcompiler.php
@@ -11,7 +11,7 @@ namespace kch42\ste;
*/
class Transcompiler {
private static $builtins = NULL;
-
+
public static function tempvar($typ) {
return $typ . '_' . str_replace('.', '_', uniqid('', true));
}
@@ -20,7 +20,7 @@ class Transcompiler {
if(self::$builtins !== NULL) {
return;
}
-
+
self::$builtins = array(
"if" => array("\\kch42\\ste\\Transcompiler", "builtin_if"),
"cmp" => array("\\kch42\\ste\\Transcompiler", "builtin_cmp"),
@@ -41,13 +41,13 @@ class Transcompiler {
"calc" => array("\\kch42\\ste\\Transcompiler", "builtin_calc")
);
}
-
+
private static function builtin_if($ast) {
$output = "";
$condition = array();
$then = NULL;
$else = NULL;
-
+
foreach($ast->sub as $node) {
if(($node instanceof TagNode) and ($node->name == "then")) {
$then = $node->sub;
@@ -57,11 +57,11 @@ class Transcompiler {
$condition[] = $node;
}
}
-
+
if($then === NULL) {
throw new ParseCompileError("self::Transcompile error: Missing <ste:then> in <ste:if>.", $ast->tpl, $ast->offset);
}
-
+
$output .= "\$outputstack[] = \"\";\n\$outputstack_i++;\n";
$output .= self::_transcompile($condition);
$output .= "\$outputstack_i--;\nif(\$ste->evalbool(array_pop(\$outputstack)))\n{\n";
@@ -83,9 +83,9 @@ class Transcompiler {
array('gt', '>'),
array('gte', '>=')
);
-
+
$code = "";
-
+
if(isset($ast->params["var_b"])) {
list($val, $pre) = self::_transcompile($ast->params["var_b"], true);
$code .= $pre;
@@ -96,7 +96,7 @@ class Transcompiler {
} else {
throw new ParseCompileError("self::Transcompile error: neiter var_b nor text_b set in <ste:cmp>.", $ast->tpl, $ast->offset);
}
-
+
if(isset($ast->params["var_a"])) {
list($val, $pre) = self::_transcompile($ast->params["var_a"], true);
$code .= $pre;
@@ -107,7 +107,7 @@ class Transcompiler {
} else {
throw new ParseCompileError("self::Transcompile error: neiter var_a nor text_a set in <ste:cmp>.", $ast->tpl, $ast->offset);
}
-
+
if(!isset($ast->params["op"])) {
throw new ParseCompileError("self::Transcompile error: op not given in <ste:cmp>.", $ast->tpl, $ast->offset);
}
@@ -160,14 +160,14 @@ class Transcompiler {
list($val, $pre) = self::_transcompile($ast->params["start"], true);
$code .= $pre;
$code .= "\$${loopname}_start = " . $val . ";\n";
-
+
if(empty($ast->params["stop"])) {
throw new ParseCompileError("self::Transcompile error: Missing 'end' parameter in <ste:for>.", $ast->tpl, $ast->offset);
}
list($val, $pre) = self::_transcompile($ast->params["stop"], true);
$code .= $pre;
$code .= "\$${loopname}_stop = " . $val . ";\n";
-
+
$step = NULL; /* i.e. not known at compilation time */
if(empty($ast->params["step"])) {
$step = 1;
@@ -178,17 +178,17 @@ class Transcompiler {
$code .= $pre;
$code .= "\$${loopname}_step = " . $val . ";\n";
}
-
+
if(!empty($ast->params["counter"])) {
list($val, $pre) = self::_transcompile($ast->params["counter"], true);
$code .= $pre;
$code .= "\$${loopname}_countername = " . $val . ";\n";
}
-
+
$loopbody = empty($ast->params["counter"]) ? "" : "\$ste->set_var_by_name(\$${loopname}_countername, \$${loopname}_counter);\n";
$loopbody .= self::_transcompile($ast->sub);
$loopbody = self::indent_code("{\n" . self::loopbody(self::indent_code($loopbody)) . "\n}\n");
-
+
if($step === NULL) {
$code .= "if(\$${loopname}_step == 0)\n\tthrow new \\kch42\\ste\\RuntimeError('step can not be 0 in <ste:for>.');\n";
$code .= "if(\$${loopname}_step > 0)\n{\n";
@@ -205,39 +205,39 @@ class Transcompiler {
} else {
$code .= "for(\$${loopname}_counter = \$${loopname}_start; \$${loopname}_counter >= \$${loopname}_stop; \$${loopname}_counter += $step)\n$loopbody\n";
}
-
+
return $code;
}
private static function builtin_foreach($ast) {
$loopname = self::tempvar("foreachloop");
$code = "";
-
+
if(empty($ast->params["array"])) {
throw new ParseCompileError("self::Transcompile Error: array not given in <ste:foreach>.", $ast->tpl, $ast->offset);
}
list($val, $pre) = self::_transcompile($ast->params["array"], true);
$code .= $pre;
$code .= "\$${loopname}_arrayvar = " . $val . ";\n";
-
+
if(empty($ast->params["value"])) {
throw new ParseCompileError("self::Transcompile Error: value not given in <ste:foreach>.", $ast->tpl, $ast->offset);
}
list($val, $pre) = self::_transcompile($ast->params["value"], true);
$code .= $pre;
$code .= "\$${loopname}_valuevar = " . $val . ";\n";
-
+
if(!empty($ast->params["key"])) {
list($val, $pre) = self::_transcompile($ast->params["key"], true);
$code .= $pre;
$code .= "\$${loopname}_keyvar = " . $val . ";\n";
}
-
+
if(!empty($ast->params["counter"])) {
list($val, $pre) = self::_transcompile($ast->params["counter"], true);
$code .= $pre;
$code .= "\$${loopname}_countervar = " . $val . ";\n";
}
-
+
$loopbody = "";
$code .= "\$${loopname}_array = \$ste->get_var_by_name(\$${loopname}_arrayvar);\n";
$code .= "if(!is_array(\$${loopname}_array))\n\t\$${loopname}_array = array();\n";
@@ -245,7 +245,7 @@ class Transcompiler {
$code .= "\$${loopname}_counter = -1;\n";
$loopbody .= "\$${loopname}_counter++;\n\$ste->set_var_by_name(\$${loopname}_countervar, \$${loopname}_counter);\n";
}
-
+
$loop = array();
$else = array();
foreach($ast->sub as $node) {
@@ -255,7 +255,7 @@ class Transcompiler {
$loop[] = $node;
}
}
-
+
$loopbody .= "\$ste->set_var_by_name(\$${loopname}_valuevar, \$${loopname}_value);\n";
if(!empty($ast->params["key"])) {
$loopbody .= "\$ste->set_var_by_name(\$${loopname}_keyvar, \$${loopname}_key);\n";
@@ -263,14 +263,14 @@ class Transcompiler {
$loopbody .= "\n";
$loopbody .= self::_transcompile($loop);
$loopbody = "{\n" . self::loopbody(self::indent_code($loopbody)) . "\n}\n";
-
+
if(!empty($else)) {
$code .= "if(empty(\$${loopname}_array))\n{\n";
$code .= self::indent_code(self::_transcompile($else));
$code .= "\n}\nelse ";
}
$code .= "foreach(\$${loopname}_array as \$${loopname}_key => \$${loopname}_value)\n$loopbody\n";
-
+
return $code;
}
private static function builtin_infloop($ast) {
@@ -286,59 +286,59 @@ class Transcompiler {
if(empty($ast->params["name"])) {
throw new ParseCompileError("self::Transcompile Error: name missing in <ste:block>.", $ast->tpl, $ast->offset);
}
-
+
$blknamevar = self::tempvar("blockname");
-
+
list($val, $code) = self::_transcompile($ast->params["name"], true);
$code .= "\$${blknamevar} = " . $val . ";\n";
-
+
$tmpblk = uniqid("", true);
$code .= "\$ste->blocks['$tmpblk'] = array_pop(\$outputstack);\n\$ste->blockorder[] = '$tmpblk';\n\$outputstack = array('');\n\$outputstack_i = 0;\n";
-
+
$code .= self::_transcompile($ast->sub);
-
+
$code .= "\$ste->blocks[\$${blknamevar}] = array_pop(\$outputstack);\n";
$code .= "if(array_search(\$${blknamevar}, \$ste->blockorder) === false)\n\t\$ste->blockorder[] = \$${blknamevar};\n\$outputstack = array('');\n\$outputstack_i = 0;\n";
-
+
return $code;
}
private static function builtin_load($ast) {
if(empty($ast->params["name"])) {
throw new ParseCompileError("self::Transcompile Error: name missing in <ste:load>.", $ast->tpl, $ast->offset);
}
-
+
list($val, $code) = self::_transcompile($ast->params["name"], true);
$code .= "\$outputstack[\$outputstack_i] .= \$ste->load(" . $val . ");\n";
return $code;
}
private static function builtin_mktag($ast) {
$code = "";
-
+
if(empty($ast->params["name"])) {
throw new ParseCompileError("self::Transcompile Error: name missing in <ste:mktag>.", $ast->tpl, $ast->offset);
}
-
+
$fxbody = "\$outputstack = array(''); \$outputstack_i = 0;\$ste->set_local_var('_tag_parameters', \$params);\n";
-
+
list($tagname, $tagname_pre) = self::_transcompile($ast->params["name"], true);
-
+
$usemandatory = "";
if(!empty($ast->params["mandatory"])) {
$usemandatory = " use (\$mandatory_params)";
$code .= "\$outputstack[] = '';\n\$outputstack_i++;\n";
$code .= self::_transcompile($ast->params["mandatory"]);
$code .= "\$outputstack_i--;\n\$mandatory_params = explode('|', array_pop(\$outputstack));\n";
-
+
$fxbody .= "foreach(\$mandatory_params as \$mp)\n{\n\tif(!isset(\$params[\$mp]))\n\t\tthrow new \\kch42\\ste\\RuntimeError(\"\$mp missing in <ste:\" . $tagname . \">.\");\n}";
}
-
+
$fxbody .= self::_transcompile($ast->sub);
$fxbody .= "return array_pop(\$outputstack);";
-
+
$code .= "\$tag_fx = \$ste->make_closure(function(\$ste, \$params, \$sub)" . $usemandatory . "\n{\n" . self::indent_code($fxbody) . "\n});\n";
$code .= $tagname_pre;
$code .= "\$ste->register_tag($tagname, \$tag_fx);\n";
-
+
return $code;
}
private static function builtin_tagcontent($ast) {
@@ -348,37 +348,37 @@ class Transcompiler {
if(empty($ast->params["var"])) {
throw new ParseCompileError("self::Transcompile Error: var missing in <ste:set>.", $ast->tpl, $ast->offset);
}
-
+
$code = "\$outputstack[] = '';\n\$outputstack_i++;\n";
$code .= self::_transcompile($ast->sub);
$code .= "\$outputstack_i--;\n";
-
+
list($val, $pre) = self::_transcompile($ast->params["var"], true);
$code .= $pre;
$code .= "\$ste->set_var_by_name(" . $val . ", array_pop(\$outputstack));\n";
-
+
return $code;
}
private static function builtin_setlocal($ast) {
if(empty($ast->params["var"])) {
throw new ParseCompileError("self::Transcompile Error: var missing in <ste:set>.", $ast->tpl, $ast->offset);
}
-
+
$code = "\$outputstack[] = '';\n\$outputstack_i++;\n";
$code .= self::_transcompile($ast->sub);
$code .= "\$outputstack_i--;\n";
-
+
list($val, $pre) = self::_transcompile($ast->params["var"], true);
$code .= $pre;
$code .= "\$ste->set_local_var(" . $val . ", array_pop(\$outputstack));\n";
-
+
return $code;
}
private static function builtin_get($ast) {
if(empty($ast->params["var"])) {
throw new ParseCompileError("self::Transcompile Error: var missing in <ste:get>.", $ast->tpl, $ast->offset);
}
-
+
list($val, $pre) = self::_transcompile($ast->params["var"], true);
return "$pre\$outputstack[\$outputstack_i] .= \$ste->get_var_by_name(" . $val . ");";
}
@@ -386,10 +386,10 @@ class Transcompiler {
$code = "\$outputstack[] = '';\n\$outputstack_i++;\n";
$code .= self::_transcompile($ast->sub);
$code .= "\$outputstack_i--;\n\$outputstack[\$outputstack_i] .= \$ste->calc(array_pop(\$outputstack));\n";
-
+
return $code;
}
-
+
private static function indent_code($code) {
return implode(
"\n",
@@ -403,9 +403,9 @@ class Transcompiler {
private static function _transcompile($ast, $avoid_outputstack = false, $in_args = false) { /* The real self::transcompile function, does not add boilerplate code. */
$code = "";
-
+
$text_and_var_buffer = array();
-
+
foreach($ast as $node) {
if($node instanceof TextNode) {
$text_and_var_buffer[] = '"' . Misc::escape_text($node->text) . '"';
@@ -425,34 +425,34 @@ class Transcompiler {
} else {
$paramarray = self::tempvar("parameters");
$code .= "\$$paramarray = array();\n";
-
+
foreach($node->params as $pname => $pcontent) {
list($pval, $pre) = self::_transcompile($pcontent, true, true);
$code .= $pre . "\$${paramarray}['" . Misc::escape_text($pname) . "'] = " . $pval . ";\n";
}
-
+
$code .= "\$outputstack[\$outputstack_i] .= \$ste->call_tag('" . Misc::escape_text($node->name) . "', \$${paramarray}, ";
$code .= empty($node->sub) ? "function(\$ste) { return ''; }" : ("\$ste->make_closure(" . self::transcompile($node->sub) . ")");
$code .= ");\n";
}
}
}
-
+
if($avoid_outputstack && ($code == "")) {
return array(implode (" . ", $text_and_var_buffer), "");
}
-
+
if(!empty($text_and_var_buffer)) {
$code .= "\$outputstack[\$outputstack_i] .= ". implode (" . ", $text_and_var_buffer) . ";\n";
}
-
+
if($avoid_outputstack) {
$tmpvar = self::tempvar("tmp");
$code = "\$outputstack[] = '';\n\$outputstack_i++;" . $code;
$code .= "\$$tmpvar = array_pop(\$outputstack);\n\$outputstack_i--;\n";
return array("\$$tmpvar", $code);
}
-
+
return $code;
}