diff options
Diffstat (limited to 'src/ste/Parser.php')
-rw-r--r-- | src/ste/Parser.php | 118 |
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 */ |