From: Rex Feany Date: Mon, 30 Aug 2004 04:03:48 +0000 (+0000) Subject: Initial revision X-Git-Tag: yatt-1 X-Git-Url: https://git.rexfeany.com/?p=yatt.git;a=commitdiff_plain;h=a4518480d7a53eaae3ce8ef1d6f155f46fdb5fcb Initial revision --- a4518480d7a53eaae3ce8ef1d6f155f46fdb5fcb diff --git a/php/YATT.class.php b/php/YATT.class.php new file mode 100644 index 0000000..c6c9ff9 --- /dev/null +++ b/php/YATT.class.php @@ -0,0 +1,215 @@ +errors, vsprintf($format, $args)); + return count($this->errors); + } + + # INTERNAL: act as a callback for preg_replace_callback below + function pp_callback($matches) { + return $this->preprocess($matches[1]); + } + + # INTERNAL: read file, preprocess for includes, strip out comments + function preprocess($fname) { + if (! ($data = file_get_contents($fname))) { + $this->error('INCLUDE(%s): can not open file!', $fname); + return ''; + } + + # strip all comments + $data = preg_replace('/[ \t]*\%\[#\].*$/m', '', $data); + + # fetch all includes (recursive!) + return preg_replace_callback( + '/^[ \t]*\%include[ \t]+\[([A-Za-z-_.]+)\][ \t]*$/im', + array(&$this, 'pp_callback'), + $data); + } + + # load template file on class creation + # You can load multiple files + function load($fname) { + $data = $this->preprocess($fname); + $stack = array(); + + $cur = &$this->obj; + $nchunks = preg_match_all( + '/(.*?)[ \t]*\%(begin|end)[\t ]+\[([A-Za-z-_]+)\][\t ]*$/ism', + $data, $matches, PREG_SET_ORDER); + + # array[1] == text, array[2] == (begin|end), array[3] == NAME + for ($i = 0; $i < $nchunks; $i++) { + $text = $matches[$i][1]; + $type = strtolower($matches[$i][2]); + $name = $matches[$i][3]; + + if ($text && (strlen($text) > 0)) { + array_push($cur, $text); + } + + if (strcasecmp($type, 'begin') == 0) { + $new = array($name, '', ''); + $cur[] =& $new; + $stack[] =& $cur; + $cur = &$new; + unset($new); + } else if (strcasecmp($type, 'end') == 0) { + if (strcmp($cur[YATT_NAME], $name)) { + return $this->error('LOAD(%s): Mismatched begin/end: got %s, wanted %s! aborting!', + $fname, $name, $cur[YATT_NAME]); + } + if (! ($cur =& my_pop($stack))) { + $cur = &$this->obj; + } + } else { + return $this->error('LOAD(%s): unknown tag type %s, aborting!', $fname, $type); + } + } + if (count($stack)) { + return $this->error('LOAD(%s): mismatched begin/end pairs at EOF!', $fname); + } + return count($this->errors); + } + + # INTERNAL: Find the node that corrosponds to an OID + function &find_node($path) { + $oid = explode('.', $path); + $node = &$this->obj; + + while ($cmp = array_shift($oid)) { + $old = &$node; + for ($i = YATT_DSTART; $i < count($node); $i++) { + if (is_array($node[$i]) && (strcmp($node[$i][YATT_NAME], $cmp) == 0)) { + $node = &$node[$i]; + break; + } + } + if ($old == $node) { + $this->error('FIND(%s): Could not find node %s', $path, $cmp); + return FALSE; + } + } + return $node; + } + + # INTERNAL: Substitute some stuff! + function subst($matches) { + if (!isset($this->vars[$matches[1]])) { + $this->error('PARSE(): unbound variable %s', $matches[1]); + return ''; + } + return $this->vars[$matches[1]]; + } + + # INTERNAL: Build the output for this node + function build_output(&$root, &$node) { + $out = ''; + + for ($i = YATT_DSTART; $i < count($node); $i++) { + if (is_array($node[$i])) { + $out .= $this->return_output($node[$i]); + } else { + $buf = $node[$i]; + $pass = 0; + + while (preg_match('/\%\[([^][%]+)\]/', $buf)) { + if ($pass++ > 10) { + # TODO: give the user more info when this happens. + $this->error('PARSE(): recursive subst in node %s?', $node[YATT_NAME]); + break; + } + $buf = preg_replace_callback('/\%\[([^][%]+)\]/', array(&$root, 'subst'), $buf); + } + $out .= $buf; + } + } + return $out; + } + + # INTERNAL: Return all of the generated output + function return_output(&$node) { + $out = $node[YATT_OUTPUT]; + $node[YATT_OUTPUT] = ''; + + for ($i = YATT_DSTART; $i < count($node); $i++) { + if (is_array($node[$i])) { + $out .= $this->return_output($node[$i]); + } + } + return $out; + } + + # Create a new YATT instance. + function YATT() { + $this->errors = array(); + $this->obj = array('ROOT', '', ''); + } + + # Return output, starting at a given node + function output($path=NULL) { + $obj =& $this->find_node($path); + return $obj ? $this->return_output($obj) : FALSE; + } + + # Generate text from an object tree + function parse($path) { + if ($obj =& $this->find_node($path)) { + $obj[YATT_OUTPUT] .= $this->build_output($this, $obj); + } + } + + # Set a variable to some value + function set($var, $value=NULL) { + if (is_array($var)) { + $this->vars = array_merge($this->vars, $var); + } else { + $this->vars[$var] = $value; + } + } + + # Get errors, or return FALSE if there are none. + # Resets error list to zero! + function get_errors() { + $err = $this->errors; + $this->errors = array(); + return count($err) ? $err : FALSE; + } +} + +?> diff --git a/php/example.php b/php/example.php new file mode 100644 index 0000000..e4e8098 --- /dev/null +++ b/php/example.php @@ -0,0 +1,56 @@ +load("example.yatt"); + +# set a variable +$n->set('CRACK', 'eat me'); + +# or like this +$n->set(array("FOO" => "something else", "BAR" => "barorific!")); + +# Variable substitution is recursive, see how this works in the template +$n->set('FNORK', 'baz'); +$n->set('CORK_baz', 'shout off and die'); + +# Parse a single block of text +$n->parse('faz.test'); + +# Maybe build a table +foreach (array("ONE", "TWO", "THREE") as $value) { + $n->set('ROW_NAME', $value); + $n->parse('faz.table.row'); +} +$n->parse('faz.table'); + +# Comment this out and look at the output. +# Notice that everything *but* what is in faz is displayed. +# Only nodes that you parse() actually display things. +$n->parse('faz'); + +# print the output for everything. +# You could also tell it where to start, like output('faz.table') +# to only print out stuff from table. +print "--------------------------------------------------------------\n"; +print "Output of the template:\n"; +print "--------------------------------------------------------------\n"; +print $n->output(); + +# If there were any errors, print them out +# You should probably check this *before* you print out the page +# because these errors will likely cause the page not to be +# built correctly. It is done this way so that you only have to +# check once, and so you can do something cool with the errors +# instead of die(). +if (($e = $n->get_errors())) { + print "--------------------------------------------------------------\n"; + print "errors:\n"; + print "--------------------------------------------------------------\n"; + + print_r($e); +} + +?> diff --git a/php/example.yatt b/php/example.yatt new file mode 100644 index 0000000..6a329b1 --- /dev/null +++ b/php/example.yatt @@ -0,0 +1,49 @@ +%[#] +%[#] This is a template comment +%[#] This is stripped out by the template parser +%[#] + +%[#] This is an include statement, it loads the file as if it were +%[#] a part of this one. This should generate an error since missing.yatt +%[#] isn't actually included with the example. +%include [missing.yatt] + +%[#] This begins a block of text, blocks can be inside of other blocks. +%begin [faz] + +Here is some static text to be printed when 'faz' is parsed. + +This is a variable: %[CRACK] + +%[#] Another random block of text: parsed with 'faz.test' +%begin [test] + +recursive variable substitution in action: +this is recursive: %[CORK_%[FNORK]] wheee! + +%end [test] + + +some other stuff: %[FOO] +some other stuff: %[BAR] + +undefined variables are bad: this should generate an error: %[UNDEFINED] + +%[#] How about another example? +%begin [table] + +foo table header +-------------- +%begin [row] +FF: %[ROW_NAME] +%end [row] +-------------- +foo table footer + +%end [table] + + +and some stuff down here! + +%end [faz] +