+# We parse the template string into a tree of strings and sub-templates.
+# A template is a hash with a name string, a pieces array, and possibly
+# an args array.
+
+function parse_template($string) {
+ $tem =& tem_push();
+ $tem['pieces'] = array();
+ # note: for some reason this captures '<!--' but not '-->'.
+ $matches = preg_split("/(<!--)?(~[^~]*~)(?(1)-->)/", $string, -1, PREG_SPLIT_DELIM_CAPTURE);
+ foreach($matches as $match) {
+ if($match == '~~') $match = '~';
+ if(substr($match,0,1) == '~' and strlen($match) > 2) {
+ $args = explode(' ', substr($match,1,-1));
+
+ if(count($args) == 1 and $args[0] == '}') $name = '';
+ else $name = array_shift($args);
+
+ if(last($args) == '{') { # open block
+ array_pop($args); # drop '{'
+ if(!is_string(last($tem['pieces']))) $tem['pieces'][] = '';
+ $tem =& tem_push($tem); # create a new sub-template
+ $tem['parent']['pieces'][] =& $tem; # as a piece of the parent
+ $tem['name'] = $name;
+ $tem['pieces'] = array();
+ $tem['args'] = $args;
+ } elseif(last($args) == '}') { # close block
+ array_pop($args); # drop '}'
+ $cur = $tem['name'];
+ if($name && $name != $cur) {
+ die("Invalid template: tried to close '$name', but '$cur' is current.");
+ }
+ $tem =& $tem['parent'];
+ } else { # value slot
+ $tem['pieces'][] = array('name' => $name, 'args' => $args);
+ }
+ } elseif($match and $match != '<!--') { # static string
+ $tem['pieces'][] = $match;
+ }