diff options
-rw-r--r-- | docu/language_definition.html | 5 | ||||
-rw-r--r-- | stupid_template_engine.php | 820 | ||||
-rw-r--r-- | tests/test_short/comment | 1 | ||||
-rw-r--r-- | tests/test_short/want | 2 |
4 files changed, 395 insertions, 433 deletions
diff --git a/docu/language_definition.html b/docu/language_definition.html index 77cb680..934042d 100644 --- a/docu/language_definition.html +++ b/docu/language_definition.html @@ -223,7 +223,6 @@ <p><code><ste:if>condition<ste:then>then</ste:then><ste:else>else</ste:else></ste:if></code></p> <p><code>?</code>, <code>{</code>, <code>|</code> and <code>}</code> can be <a href="#escaping">escaped</a></p> <p>In this variant, the else part <strong>is not optional</strong>!</p> - <p><strong>WARNING:</strong> short if-clauses can not be nested!</p> <h3 id="builtin_cmp">ste:cmp</h3> <p>With the ste:cmp tag you can compare two values.</p> @@ -285,9 +284,7 @@ <p><code>~{a|operator|b}</code></p> <p>This is equivalent to:</p> <p><code><ste:cmp text_a="a" op="operator" text_b="b" /></code></p> - <p><code>~</code>, <code>{</code>, <code>|</code> and <code>}</code> can be <a href="#escaping">escaped</a></p> - <p>Because this is implemented as a simple substitution, you can only use <a href="#basic_elems_text">Text</a> and <a href="#basic_elems_variable">Variables</a>. And <code>"</code> must be escaped.</p> - <p><strong>WARNING:</strong> short comparisons can not be nested! They can be inside <a href="#builtin_if_short">short if-clauses</a>, but not the other way around!</p> + <p><code>~</code>, <code>{</code>, <code>|</code> and <code>}</code> can be <a href="#escaping">escaped</a>.</p> <h3 id="builtin_not">ste:not</h3> <p>The ste:not Tag will logically invert its content. If it is an empty text (i.e. false), it will return a non-empty text (i.e. true) and vice versa.</p> diff --git a/stupid_template_engine.php b/stupid_template_engine.php index 2cfb9d0..cfb5882 100644 --- a/stupid_template_engine.php +++ b/stupid_template_engine.php @@ -15,19 +15,16 @@ */ namespace ste; -abstract class ASTNode -{ +abstract class ASTNode { public $tpl; public $offset; - public function __construct($tpl, $off) - { + public function __construct($tpl, $off) { $this->tpl = $tpl; $this->offset = $off; } } -class TextNode extends ASTNode -{ +class TextNode extends ASTNode { public $text; public function __construct($tpl, $off, $text = "") { parent::__construct($tpl, $off); @@ -35,8 +32,7 @@ class TextNode extends ASTNode } } -class TagNode extends ASTNode -{ +class TagNode extends ASTNode { public $name; public $params = array(); public $sub = array(); @@ -46,53 +42,44 @@ class TagNode extends ASTNode } } -class VariableNode extends ASTNode -{ +class VariableNode extends ASTNode { public $name; public $arrayfields = array(); - public function transcompile() - { + public function transcompile() { $varaccess = '@$ste->vars[' . (is_numeric($this->name) ? $this->name : '"' . escape_text($this->name) . '"'). ']'; - foreach($this->arrayfields as $af) - { - if((count($af) == 1) and ($af[0] instanceof TextNode) and is_numeric($af[0]->text)) + foreach($this->arrayfields as $af) { + if((count($af) == 1) and ($af[0] instanceof TextNode) and is_numeric($af[0]->text)) { $varaccess .= '[' . $af->text . ']'; - else - $varaccess .= '[' . implode(".", - array_map( - function($node) - { - if($node instanceof TextNode) - return "\"" . escape_text($node->text) . "\""; - else if($node instanceof VariableNode) - return $node->transcompile(); - }, $af - ) - ). ']'; + } else { + $varaccess .= '[' . implode(".", array_map(function($node) { + if($node instanceof TextNode) { + return "\"" . escape_text($node->text) . "\""; + } else if($node instanceof VariableNode) { + return $node->transcompile(); + } + }, $af)). ']'; + } } return $varaccess; } } -class ParseCompileError extends \Exception -{ +class ParseCompileError extends \Exception { public $msg; public $tpl; public $off; - public function __construct($msg, $tpl, $offset, $code = 0, $previous = NULL) - { + 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) - { + 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)"; - $this->is_rewritten = True; + $this->is_rewritten = true; } } @@ -558,20 +545,15 @@ class Parser { } } -function indent_code($code) -{ +function indent_code($code) { return implode( "\n", - array_map( - function($line) { return "\t$line"; }, - explode("\n", $code) - ) + array_map(function($line) { return "\t$line"; }, explode("\n", $code)) ); } /* We could also just eval() the $infix_math code, but this is much cooler :-D (Parser inception) */ -function shunting_yard($infix_math) -{ +function shunting_yard($infix_math) { $operators = array( "+" => array("l", 2), "-" => array("l", 2), @@ -584,165 +566,156 @@ function shunting_yard($infix_math) ); 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") or (!empty($x)); }); + $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(); - foreach($tokens_raw as $token) - { + foreach($tokens_raw as $token) { $priority = isset($operators[$token]) ? $operators[$token][1] : -1; - if(($token == "-") and (($lastpriority === NULL) or ($lastpriority >= 0))) - { + if(($token == "-") && (($lastpriority === NULL) || ($lastpriority >= 0))) { $priority = $operators["_"][1]; $tokens[] = "_"; - } - else + } else { $tokens[] = $token; + } $lastpriority = $priority; } - while(!empty($tokens)) - { + while(!empty($tokens)) { $token = array_shift($tokens); - if(is_numeric($token)) + if(is_numeric($token)) { $output_queue[] = $token; - else if($token == "(") + } else if($token == "(") { $op_stack[] = $token; - else if($token == ")") - { - $lbr_found = False; - while(!empty($op_stack)) - { + } else if($token == ")") { + $lbr_found = false; + while(!empty($op_stack)) { $op = array_pop($op_stack); - if($op == "(") - { - $lbr_found = True; + if($op == "(") { + $lbr_found = true; break; } $output_queue[] = $op; } - if(!$lbr_found) + if(!$lbr_found) { throw new RuntimeError("Bracket mismatch."); - } - else if(!isset($operators[$token])) + } + } else if(!isset($operators[$token])) { throw new RuntimeError("Invalid token ($token): Not a number, bracket or operator. Stop."); - else - { + } else { $priority = $operators[$token][1]; - if($operators[$token][0] == "l") - while((!empty($op_stack)) and ($priority <= $operators[$op_stack[count($op_stack)-1]][1])) + if($operators[$token][0] == "l") { + while((!empty($op_stack)) and ($priority <= $operators[$op_stack[count($op_stack)-1]][1])) { $output_queue[] = array_pop($op_stack); - else - while((!empty($op_stack)) and ($priority < $operators[$op_stack[count($op_stack)-1]][1])) + } + } else { + while((!empty($op_stack)) and ($priority < $operators[$op_stack[count($op_stack)-1]][1])) { $output_queue[] = array_pop($op_stack); + } + } $op_stack[] = $token; } } - while(!empty($op_stack)) - { + while(!empty($op_stack)) { $op = array_pop($op_stack); - if($op == "(") + if($op == "(") { throw new RuntimeError("Bracket mismatch..."); + } $output_queue[] = $op; } return $output_queue; } -function pop2(&$array) -{ +function pop2(&$array) { $rv = array(array_pop($array), array_pop($array)); - if(array_search(NULL, $rv, True) !== False) + if(array_search(NULL, $rv, true) !== false) { throw new RuntimeError("Not enough numbers on stack. Invalid formula."); + } return $rv; } -function calc_rpn($rpn) -{ +function calc_rpn($rpn) { $stack = array(); - foreach($rpn as $token) - { - switch($token) - { - case "+": - list($b, $a) = pop2($stack); - $stack[] = $a + $b; - break; - case "-": - list($b, $a) = pop2($stack); - $stack[] = $a - $b; - break; - case "*": - list($b, $a) = pop2($stack); - $stack[] = $a * $b; - break; - case "/": - list($b, $a) = pop2($stack); - $stack[] = $a / $b; - break; - case "^": - list($b, $a) = pop2($stack); - $stack[] = pow($a, $b); - break; - case "_": - $a = array_pop($stack); - if($a === NULL) - throw new RuntimeError("Not enough numbers on stack. Invalid formula."); - $stack[] = -$a; - break; - default: - $stack[] = $token; - break; + foreach($rpn as $token) { + switch($token) { + case "+": + list($b, $a) = pop2($stack); + $stack[] = $a + $b; + break; + case "-": + list($b, $a) = pop2($stack); + $stack[] = $a - $b; + break; + case "*": + list($b, $a) = pop2($stack); + $stack[] = $a * $b; + break; + case "/": + list($b, $a) = pop2($stack); + $stack[] = $a / $b; + break; + case "^": + list($b, $a) = pop2($stack); + $stack[] = pow($a, $b); + break; + case "_": + $a = array_pop($stack); + if($a === NULL) { + throw new RuntimeError("Not enough numbers on stack. Invalid formula."); + } + $stack[] = -$a; + break; + default: + $stack[] = $token; + break; } } return array_pop($stack); } -function loopbody($code) -{ +function loopbody($code) { return "try\n{\n" . indent_code($code) . "\n}\ncatch(\ste\BreakException \$e) { break; }\ncatch(\ste\ContinueException \$e) { continue; }\n"; } $ste_builtins = array( - "if" => function($ast) - { + "if" => function($ast) { $output = ""; $condition = array(); $then = NULL; $else = NULL; - foreach($ast->sub as $node) - { - if(($node instanceof TagNode) and ($node->name == "then")) + foreach($ast->sub as $node) { + if(($node instanceof TagNode) and ($node->name == "then")) { $then = $node->sub; - else if(($node instanceof TagNode) and ($node->name == "else")) + } else if(($node instanceof TagNode) and ($node->name == "else")) { $else = $node->sub; - else + } else { $condition[] = $node; + } } - if($then === NULL) + if($then === NULL) { throw new ParseCompileError("Transcompile error: Missing <ste:then> in <ste:if>.", $ast->tpl, $ast->offset); + } $output .= "\$outputstack[] = \"\";\n\$outputstack_i++;\n"; $output .= _transcompile($condition); $output .= "\$outputstack_i--;\nif(\$ste->evalbool(array_pop(\$outputstack)))\n{\n"; $output .= indent_code(_transcompile($then)); $output .= "\n}\n"; - if($else !== NULL) - { + if($else !== NULL) { $output .= "else\n{\n"; $output .= indent_code(_transcompile($else)); $output .= "\n}\n"; } return $output; }, - "cmp" => function($ast) - { + "cmp" => function($ast) { $operators = array( array('eq', '=='), array('neq', '!='), @@ -754,42 +727,48 @@ $ste_builtins = array( $code = ""; - if(isset($ast->params["var_b"])) - $b = '$ste->get_var_by_name(' . _transcompile($ast->params["var_b"], True) . ')'; - else if(isset($ast->params["text_b"])) - $b = _transcompile($ast->params["text_b"], True); - else + if(isset($ast->params["var_b"])) { + list($val, $pre) = _transcompile($ast->params["var_b"], true); + $code .= $pre; + $b = '$ste->get_var_by_name(' . $val . ')'; + } else if(isset($ast->params["text_b"])) { + list($b, $pre) = _transcompile($ast->params["text_b"], true); + $code .= $pre; + } else { throw new ParseCompileError("Transcompile error: neiter var_b nor text_b set in <ste:cmp>.", $ast->tpl, $ast->offset); + } - if(isset($ast->params["var_a"])) - $a = '$ste->get_var_by_name(' . _transcompile($ast->params["var_a"], True) . ')'; - else if(isset($ast->params["text_a"])) - $a = _transcompile($ast->params["text_a"], True); - else + if(isset($ast->params["var_a"])) { + list($val, $pre) = _transcompile($ast->params["var_a"], true); + $code .= $pre; + $a = '$ste->get_var_by_name(' . $val . ')'; + } else if(isset($ast->params["text_a"])) { + list($a, $pre) = _transcompile($ast->params["text_a"], true); + $code .= $pre; + } else { throw new ParseCompileError("Transcompile error: neiter var_a nor text_a set in <ste:cmp>.", $ast->tpl, $ast->offset); + } - if(!isset($ast->params["op"])) + if(!isset($ast->params["op"])) { throw new ParseCompileError("Transcompile error: op not given in <ste:cmp>.", $ast->tpl, $ast->offset); - if((count($ast->params["op"]) == 1) and ($ast->params["op"][0] instanceof TextNode)) - { + } + if((count($ast->params["op"]) == 1) and ($ast->params["op"][0] instanceof TextNode)) { /* Operator is known at compile time, this saves *a lot* of output code! */ $op = trim($ast->params["op"][0]->text); $op_php = NULL; - foreach($operators as $v) - { - if($v[0] == $op) - { + foreach($operators as $v) { + if($v[0] == $op) { $op_php = $v[1]; break; } } - if($op_php === NULL) + if($op_php === NULL) { throw new ParseCompileError("Transcompile Error: Unknown operator in <ste:cmp>", $ast->tpl, $ast->offset); + } $code .= "\$outputstack[\$outputstack_i] .= (($a) $op_php ($b)) ? 'yes' : '';\n"; - } - else - { - $code .= "switch(trim(" . _transcompile($ast->params["op"], True) . "))\n{\n\t"; + } else { + list($val, $pre) = _transcompile($ast->params["op"], true); + $code .= $pre . "switch(trim(" . $val . "))\n{\n\t"; $code .= implode("", array_map( function($op) use ($a,$b) { @@ -801,49 +780,57 @@ $ste_builtins = array( } return $code; }, - "not" => function($ast) - { + "not" => function($ast) { $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->sub); $code .= "\$outputstack_i--;\n\$outputstack[\$outputstack_i] .= (!\$ste->evalbool(array_pop(\$outputstack))) ? 'yes' : '';\n"; return $code; }, - "even" => function($ast) - { + "even" => function($ast) { $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->sub); $code .= "\$outputstack_i--;\n\$tmp_even = array_pop(\$outputstack);\n\$outputstack[\$outputstack_i] .= (is_numeric(\$tmp_even) and (\$tmp_even % 2 == 0)) ? 'yes' : '';\n"; return $code; }, - "for" => function($ast) - { + "for" => function($ast) { $code = ""; - $loopname = "forloop_" . str_replace(".", "_", uniqid("",True)); - if(empty($ast->params["start"])) + $loopname = "forloop_" . str_replace(".", "_", uniqid("",true)); + if(empty($ast->params["start"])) { throw new ParseCompileError("Transcompile error: Missing 'start' parameter in <ste:for>.", $ast->tpl, $ast->offset); - $code .= "\$${loopname}_start = " . _transcompile($ast->params["start"], True) . ";\n"; + } + list($val, $pre) = _transcompile($ast->params["start"], true); + $code .= $pre; + $code .= "\$${loopname}_start = " . $val . ";\n"; - if(empty($ast->params["stop"])) + if(empty($ast->params["stop"])) { throw new ParseCompileError("Transcompile error: Missing 'end' parameter in <ste:for>.", $ast->tpl, $ast->offset); - $code .= "\$${loopname}_stop = " . _transcompile($ast->params["stop"], True) . ";\n"; + } + list($val, $pre) = _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"])) + if(empty($ast->params["step"])) { $step = 1; - else if((count($ast->params["step"]) == 1) and ($ast->params["step"][0] instanceof TextNode)) + } else if((count($ast->params["step"]) == 1) and ($ast->params["step"][0] instanceof TextNode)) { $step = $ast->params["step"][0]->text + 0; - else - $code .= "\$${loopname}_step = " . _transcompile($ast->params["step"], True) . ";\n"; + } else { + list($val, $pre) = _transcompile($ast->params["step"], true); + $code .= $pre; + $code .= "\$${loopname}_step = " . $val . ";\n"; + } - if(!empty($ast->params["counter"])) - $code .= "\$${loopname}_countername = " . _transcompile($ast->params["counter"], True) . ";\n"; + if(!empty($ast->params["counter"])) { + list($val, $pre) = _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 .= _transcompile($ast->sub); $loopbody = indent_code("{\n" . loopbody(indent_code($loopbody)) . "\n}\n"); - if($step === NULL) - { + if($step === NULL) { $code .= "if(\$${loopname}_step == 0)\n\tthrow new \\ste\\RuntimeError('step can not be 0 in <ste:for>.');\n"; $code .= "if(\$${loopname}_step > 0)\n{\n"; $code .= "\tfor(\$${loopname}_counter = \$${loopname}_start; \$${loopname}_counter <= \$${loopname}_stop; \$${loopname}_counter += \$${loopname}_step)\n"; @@ -852,47 +839,58 @@ $ste_builtins = array( $code .= "\tfor(\$${loopname}_counter = \$${loopname}_start; \$${loopname}_counter >= \$${loopname}_stop; \$${loopname}_counter += \$${loopname}_step)\n"; $code .= $loopbody; $code .= "\n}\n"; - } - else if($step == 0) + } else if($step == 0) { throw new ParseCompileError("Transcompile Error: step can not be 0 in <ste:for>.", $ast->tpl, $ast->offset); - else if($step > 0) + } else if($step > 0) { $code .= "for(\$${loopname}_counter = \$${loopname}_start; \$${loopname}_counter <= \$${loopname}_stop; \$${loopname}_counter += $step)\n$loopbody\n"; - else + } else { $code .= "for(\$${loopname}_counter = \$${loopname}_start; \$${loopname}_counter >= \$${loopname}_stop; \$${loopname}_counter += $step)\n$loopbody\n"; + } return $code; }, - "foreach" => function($ast) - { - $loopname = "foreachloop_" . str_replace(".", "_", uniqid("",True)); + "foreach" => function($ast) { + $loopname = "foreachloop_" . str_replace(".", "_", uniqid("",true)); $code = ""; - if(empty($ast->params["array"])) + if(empty($ast->params["array"])) { throw new ParseCompileError("Transcompile Error: array not given in <ste:foreach>.", $ast->tpl, $ast->offset); - $code .= "\$${loopname}_arrayvar = " . _transcompile($ast->params["array"], True) . ";\n"; + } + list($val, $pre) = _transcompile($ast->params["array"], true); + $code .= $pre; + $code .= "\$${loopname}_arrayvar = " . $val . ";\n"; - if(empty($ast->params["value"])) + if(empty($ast->params["value"])) { throw new ParseCompileError("Transcompile Error: value not given in <ste:foreach>.", $ast->tpl, $ast->offset); - $code .= "\$${loopname}_valuevar = " . _transcompile($ast->params["value"], True) . ";\n"; - - if(!empty($ast->params["key"])) - $code .= "\$${loopname}_keyvar = " . _transcompile($ast->params["key"], True) . ";\n"; + } + list($val, $pre) = _transcompile($ast->params["value"], true); + $code .= $pre; + $code .= "\$${loopname}_valuevar = " . $val . ";\n"; + + if(!empty($ast->params["key"])) { + list($val, $pre) = _transcompile($ast->params["key"], true); + $code .= $pre; + $code .= "\$${loopname}_keyvar = " . $val . ";\n"; + } - if(!empty($ast->params["counter"])) - $code .= "\$${loopname}_countervar = " . _transcompile($ast->params["counter"], True) . ";\n"; + if(!empty($ast->params["counter"])) { + list($val, $pre) = _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"; - if(!empty($ast->params["counter"])) - { + if(!empty($ast->params["counter"])) { $code .= "\$${loopname}_counter = -1;\n"; $loopbody .= "\$${loopname}_counter++;\n\$ste->set_var_by_name(\$${loopname}_countervar, \$${loopname}_counter);\n"; } $loopbody .= "\$ste->set_var_by_name(\$${loopname}_valuevar, \$${loopname}_value);\n"; - if(!empty($ast->params["key"])) + if(!empty($ast->params["key"])) { $loopbody .= "\$ste->set_var_by_name(\$${loopname}_keyvar, \$${loopname}_key);\n"; + } $loopbody .= "\n"; $loopbody .= _transcompile($ast->sub); $loopbody = "{\n" . loopbody(indent_code($loopbody)) . "\n}\n"; @@ -901,58 +899,57 @@ $ste_builtins = array( return $code; }, - "infloop" => function($ast) - { - return "while(True)\n{\n" . indent_code(loopbody(indent_code(_transcompile($ast->sub)))) . "\n}\n"; + "infloop" => function($ast) { + return "while(true)\n{\n" . indent_code(loopbody(indent_code(_transcompile($ast->sub)))) . "\n}\n"; }, - "break" => function($ast) - { + "break" => function($ast) { return "throw new \\ste\\BreakException();\n"; }, - "continue" => function($ast) - { + "continue" => function($ast) { return "throw new \\ste\\ContinueException();\n"; }, - "block" => function($ast) - { - if(empty($ast->params["name"])) + "block" => function($ast) { + if(empty($ast->params["name"])) { throw new ParseCompileError("Transcompile Error: name missing in <ste:block>.", $ast->tpl, $ast->offset); + } - $blknamevar = "blockname_" . str_replace(".", "_", uniqid("", True)); + $blknamevar = "blockname_" . str_replace(".", "_", uniqid("", true)); - $code = "\$${blknamevar} = " . _transcompile($ast->params["name"], True) . ";\n"; + list($val, $code) = _transcompile($ast->params["name"], true); + $code .= "\$${blknamevar} = " . $val . ";\n"; - $tmpblk = uniqid("", True); + $tmpblk = uniqid("", true); $code .= "\$ste->blocks['$tmpblk'] = array_pop(\$outputstack);\n\$ste->blockorder[] = '$tmpblk';\n\$outputstack = array('');\n\$outputstack_i = 0;\n"; $code .= _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"; + $code .= "if(array_search(\$${blknamevar}, \$ste->blockorder) === false)\n\t\$ste->blockorder[] = \$${blknamevar};\n\$outputstack = array('');\n\$outputstack_i = 0;\n"; return $code; }, - "load" => function($ast) - { - if(empty($ast->params["name"])) + "load" => function($ast) { + if(empty($ast->params["name"])) { throw new ParseCompileError("Transcompile Error: name missing in <ste:load>.", $ast->tpl, $ast->offset); + } - return "\$outputstack[\$outputstack_i] .= \$ste->load(" . _transcompile($ast->params["name"], True) . ");\n"; + list($val, $code) = _transcompile($ast->params["name"], true); + $code .= "\$outputstack[\$outputstack_i] .= \$ste->load(" . $val . ");\n"; + return $code; }, - "mktag" => function($ast) - { + "mktag" => function($ast) { $code = ""; - if(empty($ast->params["name"])) + if(empty($ast->params["name"])) { throw new ParseCompileError("Transcompile Error: name missing in <ste:mktag>.", $ast->tpl, $ast->offset); - - $tagname = _transcompile($ast->params["name"], True); + } $fxbody = "\$outputstack = array(''); \$outputstack_i = 0;\$ste->vars['_tag_parameters'] = \$params;\n"; + list($tagname, $tagname_pre) = _transcompile($ast->params["name"], true); + $usemandatory = ""; - if(!empty($ast->params["mandatory"])) - { + if(!empty($ast->params["mandatory"])) { $usemandatory = " use (\$mandatory_params)"; $code .= "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->params["mandatory"]); @@ -965,36 +962,39 @@ $ste_builtins = array( $fxbody .= "return array_pop(\$outputstack);"; $code .= "\$tag_fx = function(\$ste, \$params, \$sub)" . $usemandatory . "\n{\n" . indent_code($fxbody) . "\n};\n"; + $code .= $tagname_pre; $code .= "\$ste->register_tag($tagname, \$tag_fx);\n"; return $code; }, - "tagcontent" => function($ast) - { + "tagcontent" => function($ast) { return "\$outputstack[\$outputstack_i] .= \$sub(\$ste);"; }, - "set" => function($ast) - { - if(empty($ast->params["var"])) + "set" => function($ast) { + if(empty($ast->params["var"])) { throw new ParseCompileError("Transcompile Error: var missing in <ste:set>.", $ast->tpl, $ast->offset); + } $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->sub); $code .= "\$outputstack_i--;\n"; - $code .= "\$ste->set_var_by_name(" . _transcompile($ast->params["var"], True) . ", array_pop(\$outputstack));\n"; + list($val, $pre) = _transcompile($ast->params["var"], true); + $code .= $pre; + $code .= "\$ste->set_var_by_name(" . $val . ", array_pop(\$outputstack));\n"; return $code; }, - "get" => function($ast) - { - if(empty($ast->params["var"])) + "get" => function($ast) { + if(empty($ast->params["var"])) { throw new ParseCompileError("Transcompile Error: var missing in <ste:get>.", $ast->tpl, $ast->offset); + } - return "\$outputstack[\$outputstack_i] .= \$ste->get_var_by_name(" . _transcompile($ast->params["var"], True) . ");"; + list($val, $pre) = _transcompile($ast->params["var"], true); + $code .= $pre; + return "\$outputstack[\$outputstack_i] .= \$ste->get_var_by_name(" . $val . ");"; }, - "calc" => function($ast) - { + "calc" => function($ast) { $code = "\$outputstack[] = '';\n\$outputstack_i++;\n"; $code .= _transcompile($ast->sub); $code .= "\$outputstack_i--;\n\$outputstack[\$outputstack_i] .= \$ste->calc(array_pop(\$outputstack));\n"; @@ -1003,40 +1003,36 @@ $ste_builtins = array( } ); -function escape_text($text) -{ +function escape_text($text) { return addcslashes($text, "\r\n\t\$\0..\x1f\\\"\x7f..\xff"); } -function _transcompile($ast, $no_outputstack = False) /* The real transcompile function, does not add boilerplate code. */ -{ +function _transcompile($ast, $avoid_outputstack = false) { /* The real transcompile function, does not add boilerplate code. */ $code = ""; global $ste_builtins; $text_and_var_buffer = array(); - foreach($ast as $node) - { - if($node instanceof TextNode) + foreach($ast as $node) { + if($node instanceof TextNode) { $text_and_var_buffer[] = '"' . escape_text($node->text) . '"'; - else if($node instanceof VariableNode) + } else if($node instanceof VariableNode) { $text_and_var_buffer[] = $node->transcompile(); - else if($node instanceof TagNode) - { - if(!empty($text_and_var_buffer)) - { + } else if($node instanceof TagNode) { + if(!empty($text_and_var_buffer)) { $code .= "\$outputstack[\$outputstack_i] .= " . implode (" . ", $text_and_var_buffer) . ";\n"; $text_and_var_buffer = array(); } - if(isset($ste_builtins[$node->name])) + if(isset($ste_builtins[$node->name])) { $code .= $ste_builtins[$node->name]($node); - else - { - $paramarray = "parameters_" . str_replace(".", "_", uniqid("", True)); + } else { + $paramarray = "parameters_" . str_replace(".", "_", uniqid("", true)); $code .= "\$$paramarray = array();\n"; - foreach($node->params as $pname => $pcontent) - $code .= "\$${paramarray}['" . escape_text($pname) . "'] = " . _transcompile($pcontent, True) . ";\n"; + foreach($node->params as $pname => $pcontent) { + list($pval, $pre) = _transcompile($pcontent, true); + $code .= $pre . "\$${paramarray}['" . escape_text($pname) . "'] = " . $pval . ";\n"; + } $code .= "\$outputstack[\$outputstack_i] .= \$ste->call_tag('" . escape_text($node->name) . "', \$${paramarray}, "; $code .= empty($node->sub) ? "function(\$ste) { return ''; }" : transcompile($node->sub); @@ -1045,18 +1041,19 @@ function _transcompile($ast, $no_outputstack = False) /* The real transcompile f } } - if(!empty($text_and_var_buffer)) - { - if(!$no_outputstack) - $code .= "\$outputstack[\$outputstack_i] .= "; - $code .= implode (" . ", $text_and_var_buffer); - if(!$no_outputstack) - $code .= ";\n"; - $text_and_var_buffer = array(); + if($avoid_outputstack && ($code == "")) { + return array(implode (" . ", $text_and_var_buffer), ""); } - else if($no_outputstack) - { - $code = "\"\""; + + if(!empty($text_and_var_buffer)) { + $code .= "\$outputstack[\$outputstack_i] .= ". implode (" . ", $text_and_var_buffer) . ";\n"; + } + + if($avoid_outputstack) { + $tmpvar = "tmp_" . str_replace(".", "_", uniqid("",true)); + $code = "\$outputstack[] = '';\n\$outputstack_i++;" . $code; + $code .= "\$$tmpvar = array_pop(\$outputstack);\n\$outputstack_i--;\n"; + return array("\$$tmpvar", $code); } return $code; @@ -1075,8 +1072,7 @@ $ste_transc_boilerplate = "\$outputstack = array('');\n\$outputstack_i = 0;\n"; * Returns: * PHP code. The PHP code is an anonymous function expecting a <STECore> instance as its parameter and returns a string (everything that was not pached into a section). */ -function transcompile($ast) /* Transcompile and add some boilerplate code. */ -{ +function transcompile($ast) { /* Transcompile and add some boilerplate code. */ global $ste_transc_boilerplate; return "function(\$ste)\n{\n" . indent_code($ste_transc_boilerplate . _transcompile($ast) . "return array_pop(\$outputstack);") . "\n}"; } @@ -1110,8 +1106,7 @@ class CantSaveTemplate extends StorageAccessFailure { } * A StorageAccess implementation is used to access the templates from any storage. * This means, that you are not limited to store the Templates inside directories, you can also use a database or something else. */ -interface StorageAccess -{ +interface StorageAccess { /* * Function: load * Loading a template. @@ -1149,8 +1144,7 @@ interface StorageAccess * Class: FilesystemStorageAccess * The default <StorageAccess> implementation for loading / saving templates into a directory structure. */ -class FilesystemStorageAccess implements StorageAccess -{ +class FilesystemStorageAccess implements StorageAccess { protected $sourcedir; protected $transcompileddir; @@ -1161,62 +1155,51 @@ class FilesystemStorageAccess implements StorageAccess * $src - The directory with the sources (Writing permissions are not mandatory, because STE does not save template sources). * $transc - The directory with the transcompiled templates (the PHP instance / the HTTP Server needs writing permissions to this directory). */ - public function __construct($src, $transc) - { + public function __construct($src, $transc) { $this->sourcedir = $src; $this->transcompileddir = $transc; } - public function load($tpl, &$mode) - { + public function load($tpl, &$mode) { $src_fn = $this->sourcedir . "/" . $tpl; $transc_fn = $this->transcompileddir . "/" . $tpl . ".php"; - if($mode == MODE_SOURCE) - { + if($mode == MODE_SOURCE) { $content = @file_get_contents($src_fn); - if($content === False) + if($content === false) { throw new CantLoadTemplate("Template not found."); + } return $content; } $src_stat = @stat($src_fn); $transc_stat = @stat($transc_fn); - if(($src_stat === False) and ($transc_stat === False)) + if(($src_stat === false) and ($transc_stat === false)) { throw new CantLoadTemplate("Template not found."); - else if($transc_stat === False) - { + } else if($transc_stat === false) { $mode = MODE_SOURCE; return file_get_contents($src_fn); - } - else if($src_stat === False) - { + } else if($src_stat === false) { include($transc_fn); return $transcompile_fx; - } - else - { - if($src_stat["mtime"] > $transc_stat["mtime"]) - { + } else { + if($src_stat["mtime"] > $transc_stat["mtime"]) { $mode = MODE_SOURCE; return file_get_contents($src_fn); - } - else - { + } else { include($transc_fn); return $transcompile_fx; } } } - public function save($tpl, $data, $mode) - { + public function save($tpl, $data, $mode) { $fn = (($mode == MODE_SOURCE) ? $this->sourcedir : $this->transcompileddir) . "/" . $tpl . (($mode == MODE_TRANSCOMPILED) ? ".php" : ""); - @mkdir(dirname($fn), 0777, True); - if(file_put_contents($fn, "<?php \$transcompile_fx = $data; ?>") === False) + @mkdir(dirname($fn), 0777, true); + if(file_put_contents($fn, "<?php \$transcompile_fx = $data; ?>") === false) { throw new CantSaveTemplate("Unable to save template."); - + } } } @@ -1227,8 +1210,7 @@ class ContinueException extends \Exception { } * Class: STECore * The Core of STE */ -class STECore -{ +class STECore { private $tags; private $storage_access; private $cur_tpl_dir; @@ -1239,14 +1221,14 @@ class STECore * $blocks - Associative array of blocks (see the language definition). * $blockorder - The order of the blocks (an array) * $vars - Associative array of all template variables. Use this to pass data to your templates. - * $mute_runtime_errors - If True (default) a <RuntimeError> exception will result in no output from the tag, if False a error message will be written to output. - * $fatal_error_on_missing_tag - If True, STE will throw a <FatalRuntimeError> if a tag was called that was not registered, otherwise (default) a regular <RuntimeError> will be thrown and automatically handled by STE (see <$mute_runtime_errors>). + * $mute_runtime_errors - If true (default) a <RuntimeError> exception will result in no output from the tag, if false a error message will be written to output. + * $fatal_error_on_missing_tag - If true, STE will throw a <FatalRuntimeError> if a tag was called that was not registered, otherwise (default) a regular <RuntimeError> will be thrown and automatically handled by STE (see <$mute_runtime_errors>). */ public $blocks; public $blockorder; public $vars; - public $mute_runtime_errors = True; - public $fatal_error_on_missing_tag = False; + public $mute_runtime_errors = true; + public $fatal_error_on_missing_tag = false; /* * Constructor: __construct @@ -1254,8 +1236,7 @@ class STECore * Parameters: * $storage_access - An Instance of a <StorageAccess> implementation. */ - public function __construct($storage_access) - { + public function __construct($storage_access) { $this->storage_access = $storage_access; $this->cur_tpl_dir = "/"; STEStandardLibrary::_register_lib($this); @@ -1275,12 +1256,13 @@ class STECore * Throws: * An Exception if the tag could not be registered (if $callback is not callable or if $name is empty) */ - public function register_tag($name, $callback) - { - if(!is_callable($callback)) + public function register_tag($name, $callback) { + if(!is_callable($callback)) { throw new \Exception("Can not register tag \"$name\", not callable."); - if(empty($name)) + } + if(empty($name)) { throw new \Exception("Can not register tag, empty name."); + } $this->tags[$name] = $callback; } @@ -1299,28 +1281,24 @@ class STECore * Returns: * The output of the tag or, if a <RuntimeError> was thrown, the appropiate result (see <$mute_runtime_errors>). */ - public function call_tag($name, $params, $sub) - { - try - { - if(!isset($this->tags[$name])) - { - if($this->fatal_error_on_missing_tag) + public function call_tag($name, $params, $sub) { + try { + if(!isset($this->tags[$name])) { + if($this->fatal_error_on_missing_tag) { throw new FatalRuntimeError("Can not call tag \"$name\": Does not exist."); - else + } else { throw new RuntimeError("Can not call tag \"$name\": Does not exist."); + } } return call_user_func($this->tags[$name], $this, $params, $sub); - } - catch(RuntimeError $e) - { - if(!$this->mute_runtime_errors) + } catch(RuntimeError $e) { + if(!$this->mute_runtime_errors) { return "RuntimeError occurred on tag '$name': " . $e->getMessage(); + } } } - public function calc($expression) - { + public function calc($expression) { return calc_rpn(shunting_yard($expression)); } @@ -1340,13 +1318,13 @@ class STECore * Returns: * The output of the template. */ - public function exectemplate($tpl) - { + public function exectemplate($tpl) { $output = ""; $lastblock = $this->load($tpl); - foreach($this->blockorder as $blockname) + foreach($this->blockorder as $blockname) { $output .= $this->blocks[$blockname]; + } return $output . $lastblock; } @@ -1367,47 +1345,39 @@ class STECore * A Reference to the variable. */ - public function &get_var_reference($name, $create_if_not_exist) - { + public function &get_var_reference($name, $create_if_not_exist) { $ref = &$this->_get_var_reference($this->vars, $name, $create_if_not_exist); return $ref; } - private function &_get_var_reference(&$from, $name, $create_if_not_exist) - { + private function &_get_var_reference(&$from, $name, $create_if_not_exist) { $bracket_open = strpos($name, "["); - if($bracket_open === False) - { - if(isset($from[$name]) or $create_if_not_exist) - { + if($bracket_open === false) { + if(isset($from[$name]) or $create_if_not_exist) { $ref = &$from[$name]; return $ref; - } - else + } else { return NULL; - } - else - { + } + } else { $old_varname = $varname; $bracket_close = strpos($name, "]", $bracket_open); - if($bracket_close === FALSE) + if($bracket_close === false) { throw new RuntimeError("Invalid varname \"$varname\". Missing closing \"]\"."); + } $varname = substr($name, 0, $bracket_open); $name = substr($name, $bracket_open + 1, $bracket_close - $bracket_open - 1) . substr($name, $bracket_close + 1); - if(!is_array($from[$varname])) - { - if($create_if_not_exist) + if(!is_array($from[$varname])) { + if($create_if_not_exist) { $from[$varname] = array(); - else + } else { return NULL; + } } - try - { + try { $ref = &$this->_get_var_reference($from[$varname], $name, $create_if_not_exist); return $ref; - } - catch(Exception $e) - { + } catch(Exception $e) { throw new RuntimeError("Invalid varname \"$old_varname\". Missing closing \"]\"."); } } @@ -1425,9 +1395,8 @@ class STECore * Throws: * <RuntimeError> if the variable name can not be parsed (e.g. unbalanced brackets). */ - public function set_var_by_name($name, $val) - { - $ref = &$this->_get_var_reference($this->vars, $name, True); + public function set_var_by_name($name, $val) { + $ref = &$this->_get_var_reference($this->vars, $name, true); $ref = $val; } @@ -1445,9 +1414,8 @@ class STECore * Returns: * The variables value. */ - public function get_var_by_name($name) - { - $ref = $this->_get_var_reference($this->vars, $name, False); + public function get_var_by_name($name) { + $ref = $this->_get_var_reference($this->vars, $name, false); return $ref === NULL ? "" : $ref; } @@ -1468,43 +1436,38 @@ class STECore * Returns: * The result of the template (if $quiet == false). */ - public function load($tpl, $quiet=False) - { + 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] != "/") + if($tpl[0] != "/") { $tpl = $this->cur_tpl_dir . "/" . $tpl; + } $pathex = array_filter(explode("/", $tpl), function($s) { return ($s != ".") and (!empty($s)); }); $pathex = array_merge($pathex); - while(($i = array_search("..", $pathex)) !== False) - { - if($i == 0) + while(($i = array_search("..", $pathex)) !== false) { + if($i == 0) { $pathex = array_slice($pathex, 1); - else + } else { $pathex = array_merge(array_slice($pathex, 0, $i), array_slice($pathex, $i + 2)); + } } $tpl = implode("/", $pathex); $this->cur_tpl_dir = dirname($tpl); - if($quiet) - { + if($quiet) { $blocks_back = clone $this->blocks; $blockorder_back = clone $this->blockorder; } $mode = MODE_TRANSCOMPILED; $content = $this->storage_access->load($tpl, $mode); - if($mode == MODE_SOURCE) - { - try - { + if($mode == MODE_SOURCE) { + try { $ast = Parser::parse($content, $tpl); $transc = transcompile($ast); - } - catch(ParseCompileError $e) - { + } catch(ParseCompileError $e) { $e->rewrite($content); throw $e; } @@ -1516,13 +1479,12 @@ class STECore $this->cur_tpl_dir = $tpldir_b4; - if($quiet) - { + if($quiet) { $this->blocks = $blocks_back; $this->blockorder = $blockorder_back; - } - else + } else { return $output; + } } /* @@ -1535,145 +1497,149 @@ class STECore * Returns: * true/false. */ - public function evalbool($txt) - { + public function evalbool($txt) { return trim($txt . "") != ""; } } -class STEStandardLibrary -{ - static public function _register_lib($ste) - { - foreach(get_class_methods(__CLASS__) as $method) - if($method[0] != "_") +class STEStandardLibrary { + static public function _register_lib($ste) { + foreach(get_class_methods(__CLASS__) as $method) { + if($method[0] != "_") { $ste->register_tag($method, array(__CLASS__, $method)); + } + } } - static public function escape($ste, $params, $sub) - { - if($ste->evalbool($params["lines"])) + static public function escape($ste, $params, $sub) { + if($ste->evalbool($params["lines"])) { return nl2br(htmlspecialchars(str_replace("\r\n", "\n", $sub($ste)))); - else + } else { return htmlspecialchars($sub($ste)); + } } - static public function strlen($ste, $params, $sub) - { + static public function strlen($ste, $params, $sub) { return strlen($sub($ste)); } - static public function arraylen($ste, $params, $sub) - { - if(empty($params["array"])) + static public function arraylen($ste, $params, $sub) { + if(empty($params["array"])) { throw new RuntimeError("Missing array parameter in <ste:arraylen>."); - $a = $ste->get_var_by_name($params["array"], False); + } + $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"])) + static public function inc($ste, $params, $sub) { + if(empty($params["var"])) { throw new RuntimeError("Missing var parameter in <ste:inc>."); - $ref = &$ste->get_var_reference($params["var"], True); + } + $ref = &$ste->get_var_reference($params["var"], true); $ref++; } - static public function dec($ste, $params, $sub) - { - if(empty($params["var"])) + static public function dec($ste, $params, $sub) { + if(empty($params["var"])) { throw new RuntimeError("Missing var parameter in <ste:dec>."); - $ref = &$ste->get_var_reference($params["var"], True); + } + $ref = &$ste->get_var_reference($params["var"], true); $ref--; } - static public function date($ste, $params, $sub) - { + 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"])) + static public function in_array($ste, $params, $sub) { + if(empty($params["array"])) { throw new RuntimeError("Missing array parameter in <ste:in_array>."); - $ar = &$ste->get_var_reference($params["array"], False); - if(!is_array($ar)) + } + $ar = &$ste->get_var_reference($params["array"], false); + if(!is_array($ar)) { return ""; + } return in_array($sub($ste), $ar) ? "y" : ""; } - static public function join($ste, $params, $sub) - { - if(empty($params["array"])) + 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"])) + static public function split($ste, $params, $sub) { + if(empty($params["array"])) { throw new RuntimeError("Missing array parameter in <ste:split>."); - if(empty($params["delim"])) + } + if(empty($params["delim"])) { throw new RuntimeError("Missing delim parameter in <ste:split>."); + } $ste->set_var_by_name($params["array"], explode($params["delim"], $sub($ste))); } - static public function array_add($ste, $params, $sub) - { - if(empty($params["array"])) + 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 = &$ste->get_var_reference($params["array"], true); + if(empty($params["key"])) { $ar[] = $sub($ste); - else + } else { $ar[$params["key"]] = $sub($ste); + } } static public function array_filter($ste, $params, $sub) { - if(empty($params["array"])) + 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)) + 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)) + if(!empty($params["keep_by_keys"])) { + $keep_by_keys = &$ste->get_var_reference($params["keep_by_keys"], false); + if(!is_array($keep_by_keys)) { throw new RuntimeError("Variable at 'keep_by_keys' is not an array."); + } $delkeys = array_filter($keys, function($k) use ($keep_by_keys) { return !in_array($k, $keep_by_keys); }); - foreach($delkeys as $dk) + foreach($delkeys as $dk) { unset($ar[$dk]); + } $keys = array_keys($ar); } - if(!empty($params["keep_by_values"])) - { - $keep_by_values = &$ste->get_var_reference($params["keep_by_values"], False); - if(!is_array($keep_by_values)) + if(!empty($params["keep_by_values"])) { + $keep_by_values = &$ste->get_var_reference($params["keep_by_values"], false); + if(!is_array($keep_by_values)) { throw new RuntimeError("Variable at 'keep_by_values' is not an array."); + } $ar = array_filter($ar, function($v) use ($keep_by_values) { return in_array($v, $keep_by_values); }); $keys = array_keys($ar); } - if(!empty($params["delete_by_keys"])) - { - $delete_by_keys = &$ste->get_var_reference($params["delete_by_keys"], False); - if(!is_array($delete_by_keys)) + if(!empty($params["delete_by_keys"])) { + $delete_by_keys = &$ste->get_var_reference($params["delete_by_keys"], false); + if(!is_array($delete_by_keys)) { throw new RuntimeError("Variable at 'delete_by_keys' is not an array."); + } $delkeys = array_filter($keys, function($k) use ($delete_by_keys) { return in_array($k, $delete_by_keys); }); - foreach($delkeys as $dk) + foreach($delkeys as $dk) { unset($ar[$dk]); + } $keys = array_keys($ar); } - if(!empty($params["delete_by_values"])) - { - $delete_by_values = &$ste->get_var_reference($params["delete_by_values"], False); - if(!is_array($delete_by_values)) + if(!empty($params["delete_by_values"])) { + $delete_by_values = &$ste->get_var_reference($params["delete_by_values"], false); + if(!is_array($delete_by_values)) { throw new RuntimeError("Variable at 'delete_by_values' is not an array."); + } $ar = array_filter($ar, function($v) use ($delete_by_values) { return !in_array($v, $delete_by_values); }); $keys = array_keys($ar); } diff --git a/tests/test_short/comment b/tests/test_short/comment deleted file mode 100644 index 12d7494..0000000 --- a/tests/test_short/comment +++ /dev/null @@ -1 +0,0 @@ -This currently does not work, since the compiler does not allow tags in tag parameters. diff --git a/tests/test_short/want b/tests/test_short/want index a0aba93..d86bac9 100644 --- a/tests/test_short/want +++ b/tests/test_short/want @@ -1 +1 @@ -OK
\ No newline at end of file +OK |