summaryrefslogtreecommitdiff
path: root/src/ste/Parser.php
diff options
context:
space:
mode:
Diffstat (limited to 'src/ste/Parser.php')
-rw-r--r--src/ste/Parser.php118
1 files changed, 59 insertions, 59 deletions
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 */