Location: PHPKode > scripts > Siviglia Templating > siviglia-templates/templating/Grammar.class.php
<?php

class Grammar {
	var $pointcuts = array();
	var $errors = array();
	function Grammar($params) {
		$this->params = & $params;
		foreach (array_keys($this->params['nt']) as $k) {
			$this->params['nt'][$k]->setParent($this, $this);
		}
	}
	function &get($name) {
		return $this->params['nt'][$name];
	}
	function addPointCuts($ps){
		$this->setPointcuts(array_merge($this->pointcuts,$ps));
	}
	function setPointCuts($ps){
		$this->pointcuts=$ps;
	}
	function &getGrammar() {
		return $this;
	}
	function &getProcessor($name) {
		return $this->pointcuts[$name];
	}
	function &getRoot() {
		$root = & new SubParser($this->params['root']);
		$root->setParent($this,$this);
		return $root;
	}
	function &process($name, &$data){
		$p =& $this->getProcessor($name);
		if ($p===null){
			return $data;
		} else {
			return $p->callWith($data);
		}
	}
	function &compile($str) {
		$this->errors = array();
		$this->input = $str;
		$root =& $this->getRoot();
		$this->res = $root->parse(new ParseInput($str));  
              
		if (preg_match('~^[\s\t\n]*$~',$this->res[1]->str)){
			return $root->process($this->res[0]->match);//$this->process($this->params['root'],$res1);
		} else {
            
			return $this->getError($str);
		}
	}
	function isError(){
		return empty($this->errors);
	}
	function &getError(){
		$str = $this->input;
		$ret = '';
		foreach ($this->errors as $remaining=> $symbol){
			if ($remaining==0){
				$rem = 'EOF';
				$prev = $str;
			} else {
				$rem = '"'.substr($str, -$remaining, 1). '"';
				$prev = substr($str,0, -$remaining);
			}
			$lines = explode("\n",$prev);
			$nl = count($lines);
			$ret .="\n".'Unexpected '.$rem.', expecting '.$symbol.
			' on line '.$nl. ',character '.(strlen(array_pop($lines))+1);
		}

		return $ret;
	}
	function setError($err){
		$this->errors= $err;
	}
	function print_tree() {
		$ret =  "<".$this->params['root']."(\n   ";
		foreach (array_keys($this->params['nt']) as $k) {
			$ret.= $k . '::='.
				$this->params['nt'][$k]->print_tree().
				".\n   ";
		}
		return $ret . ")>";
	}
}


class AltParser extends Parser {
	var $errorBuffer = array();
	function AltParser($children, $backtrack=false) {
		if (!is_array($children)) {
			echo 'NOT ARRAY!';
			print_r($children);
			exit;
		}
		parent :: Parser();
		$this->backtrack = $backtrack;
		$this->children =& $children;
	}
	function setParent(&$parent, &$grammar){
		parent :: setParent($parent, $grammar);
		foreach (array_keys($this->children) as $k) {
			$this->children[$k]->setParent($this, $grammar);
		}
	}
	function parse($tks) {
		$return = array(ParseResult::fail(), $tks);
		foreach (array_keys($this->children) as $k) {
			$c = & $this->children[$k];
			$res = $c->parse($tks);
			if (!$res[0]->failed() && !$res[0]->isLambda()) {
				//if ($res[0]->isLambda()) print_backtrace(strtolower(get_class($c). ' '.htmlentities($this->print_tree()));
				if ($res[1]->isBetterMatchThan($return[1])) {
					$res[0]= ParseResult::match(array('selector'=>$k,'result'=>$res[0]->match));
					$return =  $res;
				}
			}
		}
		if ($return[0]->failed()){
			parent::setError($this->errorBuffer);
		}
		$this->errorBuffer=array();
		return $return;

	}
	function print_tree() {
		foreach (array_keys($this->children) as $k) {
			$c = & $this->children[$k];
			$t = $c->print_tree();
			if (strtolower(get_class($c))=='altparser'){
				$t = '('.$t.')';
			}
			if (is_numeric($k)){
				$ret []= $t;
			} else {
				$ret []= $k.'=>'.$t;
			}
		 }
		 return implode('|',$ret);
	}
	function &process($result) {
		if (!$this->children[$result['selector']]) {echo 'wrong alternative:';var_dump($result);}
		$rets =&$this->children[$result['selector']]->process($result['result']);
		 $arr = array('selector'=>$result['selector'],'result'=>$rets);
		return $arr;
	}
	function setError($err){
		$this->errorBuffer= array_merge($err,$this->errorBuffer);
	}
}


class FunctionObject
{
    var $target;
    var $method_name;
    var $params;

    function FunctionObject(&$target, $method_name, $params=array()) {
        #@gencheck
        if($target == null)
        {
        	//if(!function_exists($method_name)) { print_backtrace('Function ' . $method_name . ' does not exist');        }
        }
        else {
            if(!method_exists($target, $method_name)) { print_backtrace('Method ' . $method_name . ' does not exist in ' . getClass($target));        }
        }//@#

        $this->setTarget($target);
        $this->method_name = $method_name;
        $this->params = $params;
    }

    function getMethodName() {
    	return $this->method_name;
    }

    function getParams() {
    	return $this->params;
    }

	function setTarget(&$target){
		$this->target =& $target;
	}
	function &getTarget(){
		return $this->target;
	}
    function &call() {
        
      	$method_name = $this->method_name;
      	$ret = '';
        if($this->target==null)
            $ret = call_user_func($method_name,$this->params);
        else
            $ret=call_user_func(array($this->target,$method_name),$this->params);
        
       	//eval($this->callString($method_name) . '($this->params);');
       	return $ret;
    }
	function execute() {
        
        call_user_func($this->method_name,$this->params);
       	//eval($this->executeString($method_name) . '($this->params);');
    }
	function executeWith(&$params) {
        
      	//$method_name = $this->method_name;
        call_user_func($this->method_name,$params,$this->params);
       	//eval($this->executeString($method_name) . '($params, $this->params);');
    }

	function executeWithWith(&$param1, &$param2) {
        
      	//$method_name = $this->method_name;
        call_user_func($this->method_name,$param1,$param2,$this->params);
       	//eval($this->executeString($method_name) . '($param1, $param2, $this->params);');
    }

    function callString($method) {
        
    	if ($this->target === null) {
    		return '$ret =& '. $method;
    	}
    	else {
       		return '$t =& $this->getTarget(); $ret =& $t->' . $method;
    	}
    }
    function executeString($method) {
        
    	if ($this->target === null) {
    		return $method;
    	}
    	else {
       		return '$t =& $this->getTarget(); $t->' . $method;
    	}
    }
	/**
	 *  Permission checking
	 */
	function hasPermissions(){
		$m = $this->method_name;
		$msg = 'check'.ucfirst($m).'Permissions';
		if (method_exists($this->target, $msg)){
			return $this->target->$msg($this->params);
		} else {
			return true;
		}
	}

    function &callWith(&$params) {

		$method_name = $this->method_name;
		$ret ='';
        if($this->target==null)
            $ret = call_user_func($method_name,$params,$this->params);
        else
            $ret= call_user_func(array($this->target,$method_name),$params,$this->params);

    	//eval($this->callString($method_name) . '($params, $this->params);');
    	return $ret;
    }

    function &callWithWith(&$param1, &$param2) {
        
    	$method_name = $this->method_name;
    	$ret ='';
    	//eval($this->callString($method_name) . '($param1, $param2, $this->params);');

        if($this->target==null)
            $ret = call_user_func($method_name,$param1,$param2,$this->params);
        else
            $ret= call_user_func(array($this->target,$method_name),$param1,$param2,$this->params);
    	return $ret;
    }

    /* We may want to use function objects as ValueHolders. Similar to Aspect adaptors */

    function &getValue() {
    	return $this->call();
    }

    function setValue(&$value) {
    	return $this->callWith($value);
    }

    function primPrintString($str){
        return '[' . getClass($this) . ' ' . $str .']';
    }

    function printString() {
        return $this->primPrintString($this->target->printString() . '->' . $this->method_name);
    }

    function debugPrintString() {
    	return $this->primPrintString($this->target->debugPrintString() . '->' . $this->method_name);
    }
}

function &callback(&$target, $selector) {
	return new FunctionObject($target, $selector);
}




class ListParser extends Parser {
	var $errorBuffer = array();
	function ListParser(& $parser, & $separator) {
		parent :: Parser();
		$this->sep = & $separator;
		$this->parser = & $parser;
	}
	function setParent(&$parent, &$grammar){
		parent :: setParent($parent, $grammar);
		$this->sep->setParent($this, $grammar);
		$this->parser->setParent($this, $grammar);
	}
	function parse($tks) {
		/*first, we parse the list*/
		$mp = & new MultiParser(new SeqParser(array (
			$this->parser,
			$this->sep
		)));
		$mp->setParent($this, $this->grammar);
		$res = $mp->parse($tks);
		/* then, we parse again, in the tail of the list */
		$res1 = $this->parser->parse($res[1]);
		/* if the tail failed, the parse failed */
		if ($res1[0]->failed() || $res1[0]->isLambda()) {
			parent::setError($this->errorBuffer);
			return array (
				ParseResult::fail(),
				$tks
			);
		}
		/* we collect the last parsed token, in the first position of the subarray (as all the other ones) */
		$res[0]->match[] = array (
			$res1[0]->match
		);
		return array (
			ParseResult::match($res[0]->match),
			$res1[1]
		);
	}
	function setError($err){
		$this->errorBuffer= array_merge($err,$this->errorBuffer);
	}
	function print_tree() {
		return '{'.
		$this->parser->print_tree().
		';'.
		$this->sep->print_tree().
		'}';
	}
	function &process($res) {
		for ($i=0; $i<count($res);$i++){
			$ret []=&$this->parser->process($res[$i][0]);
			if(isset($res[$i][1]))
				$ret []=&$this->sep->process($res[$i][1]);
		}
		return $ret;
	}

}


class MaybeParser extends Parser {
	function MaybeParser(& $parser) {
		parent :: Parser();
		$this->parser = & $parser;
	}
	function setParent(&$parent, &$grammar){
		parent :: setParent($parent, $grammar);
		$this->parser->setParent($this, $grammar);
	}
	function parse($tks) {
		$res = $this->parser->parse($tks);
		if ($res[0]->failed()) {
			return array (
				ParseResult::lambda(),
				$tks
			);
		} else {
			return $res;
		}
	}
	function print_tree() {
		return  '['.
		$this->parser->print_tree().
		']';
	}
	function &process($result) {
		if ($result!=null) return $this->parser->process($result); else {return $result;}
	}
	function setError($err){}
}



class MultiParser extends Parser {
	function MultiParser(& $parser) {
		parent :: Parser();
		$this->parser = & $parser;
	}
	function setParent(&$parent, &$grammar){
		parent :: setParent($parent, $grammar);
		$this->parser->setParent($this, $grammar);
	}
	function parse($tks) {
		$res = $this->parser->parse($tks);
		$ret = array();
		while ((!$res[0]->failed()) && !$res[0]->isLambda()) {
			$ret[] = $res[0]->match;
			$res = $this->parser->parse($res[1]);
		}
		if (empty($ret)){
			return array (ParseResult::lambda(),$tks);
		} else {
			return array (ParseResult::match($ret),	$res[1]);
		}
	}
	function print_tree() {
		return '('.$this->parser->print_tree(). ')*';
	}
	function &process($res) {
		$ret = array();
        
		foreach($res as $r){
			$ret []=&$this->parser->process($r);
		}
		return $ret;
	}
	function setError($err){$this->buffer = $err;}
}

class MultiOneParser extends MultiParser{
	function parse($tks) {
		$res = parent::parse($tks);
		if (count($res[0])==0){
			parent::setError($this->buffer);
			return array(ParseResult::fail(), $tks);
		} else {
			return $res;
		}
	}
	function print_tree() {
		return '('.$this->parser->print_tree(). ')+';
	}
}



class SeqParser extends Parser {
	function SeqParser($children) {
		if (!is_array($children)) {
			print_r($children);
			exit;
		}
		parent :: Parser();
		$this->children =& $children;
	}
	function setParent(&$parent, &$grammar){
		parent :: setParent($parent, $grammar);
		foreach (array_keys($this->children) as $k) {
			$this->children[$k]->setParent($this, $grammar);
		}
	}

	function &process($result) {
		foreach (array_keys($this->children) as $k) {
			$rets [$k]=&$this->children[$k]->process($result[$k]);
		}
		return $rets;
	}
	function parse($tks) {
		$res = array (FALSE,$tks);
		$ret = array();
		foreach (array_keys($this->children) as $k) {
			$res = $this->children[$k]->parse($res[1]);
			if ($res[0]->failed()) {
				return array (ParseResult::fail(),$tks);
			}
			$ret[$k] = $res[0]->match;
		}
		return array (ParseResult::match($ret),$res[1]);
	}
	function print_tree() {
		foreach (array_keys($this->children) as $k) {
			$c = & $this->children[$k];
			$t = $c->print_tree();
			if (strtolower(get_class($c))=='altparser'){
				$t = '('.$t.')';
			}
			if (is_numeric($k)){
				$ret []= $t;
			} else {
				$ret []= $k.'->'.$t;
			}
		}
		return implode(' ',$ret);
	}
}



class SubParser extends Parser {
	function SubParser($name) {
		parent :: Parser();
		$this->subName = $name;
	}
	function parse($tks) {
		if (($res = $tks->getPartial($this->subName))===null){
			if ($tks->includesNonTerminal($this->subName)){
				$tks->reDescendNonTerminal($this->subName);
				return array(ParseResult::fail(), $tks);
			}
			$p = & $this->get($this->subName);
			$g =& $this->getGrammar();
			if ($p===null) {print_backtrace_and_exit($this->subName .' does not exist');}
			$tks->pushNonTerminal($this->subName);
			$p->setErrorHandler($this);
			$res = $p->parse($tks);
			$tks->popNonTerminal($this->subName);
			$next = $res;
			$str = $tks->str;
			$tks->str = '';
			$parts = $tks->partials;
			$tks->partials = array();
			while ($tks->shouldReDescend($this->subName) && !$next[0]->failed() && !$next[0]->isLambda()){
				$tks->addPartial($this->subName, $res);
				$res =$next;
				$next = $p->parse($tks);
			}
			$tks->str = $str;
			$tks->partials = $parts ;
			$tks->addPartial($this->subName, $res);
			$p->popErrorHandler();
		}
		return $res;
	}
	function setError($err){
		$eh =& $this->popErrorHandler();
		$eh->setError($err);
		$this->setErrorHandler($eh);
	}
	function &getParser(){
		return $this->get($this->subName);
	}
	function print_tree() {
		return '<' . $this->subName . '>';
	}
	function &process($res){
		$p = & $this->get($this->subName);
		$ret =& $p->process($res);
		$g =& $this->getGrammar();
		return $g->process($this->subName, $ret);
	}
}

class EregSymbol extends Parser {
	function EregSymbol($sym) {
		parent :: Parser();
                
                $escapeChar=$sym[0];
                $this->preg=$escapeChar."^".substr($sym,1);
		/*$bars = explode($escapeChar,$sym);
		$mods = array_pop($bars);
		array_shift($bars);               
                   $spaces='[\s\t\n]*';*
		$this->preg = $escapeChar.'^'.$spaces.'('.implode($escapeChar,$bars).')'.$spaces.$escapeChar.$mods;
                echo $this->preg."<br>";*/
                //$this->preg = $sym;
		$this->sym = $sym;
	}
	function parse($tks) {
	
		if (preg_match($this->preg, $tks->str, $matches)) {
                //    echo "<b>[".htmlentities($this->preg)."] ".htmlentities($tks->str)." (".strlen($matches[0]).")</b><br>";
			return array (ParseResult::match($matches[0]),new ParseInput(substr($tks->str,strlen($matches[0]))));
		} else {
                  //  echo "[".htmlentities($this->preg)."] ".htmlentities($tks->str)."<br>";
			$this->setError(array((string)strlen($tks->str)=>$this->sym));
			return array (ParseResult::fail(),$tks);
		}
	}
	function print_tree() {
		return $this->sym;
	}
}

class Symbol extends EregSymbol {
	function Symbol($ss) {
		parent :: EregSymbol('~'.preg_quote($ss).'~');
		$this->sym='"'.$ss.'"';
	}
}


/**************************************************************************************************************************************/
class Parser {
	function Parser() {}
	function &get($name) {
		$gr =& $this->getGrammar();
		return $gr->get($name);
	}
	function &getGrammar() {
		return $this->grammar;
	}
	function setParent(&$parent, &$grammar){
		$this->grammar =& $grammar;
		$this->setErrorHandler($parent);
	}
	function setErrorHandler(&$eh){
		$this->errorHandler[] =& $eh;
	}
	function &popErrorHandler(){
		$eh =& array_pop($this->errorHandler);
		return $eh;
	}
	function &process($result){return $result;}
	function setError($err){
		//echo "<br/>setting $err from ".get_class($this) . " to ".get_class($this->errorHandler[count($this->errorHandler)-1]);
		$this->errorHandler[count($this->errorHandler)-1]->setError($err);
	}
}

class ParseInput{
	var $partials = array();
	var $nts=array();
	function ParseInput($str){
		$this->str = $str;
	}
	function addPartial($name, $res){
		$this->partials[$name]=$res;
	}
	function getPartial($name){
		return @$this->partials[$name];
	}
	function pushNonTerminal($nt){
		array_push($this->nts,$nt);
	}
	function popNonTerminal($nt){
		array_pop($this->nts);
	}
	function includesNonTerminal($nt){
		return in_array($nt,$this->nts);
	}
	function redescendNonTerminal($nt){
		$this->rdnts[$nt] = $nt;
	}
	function shouldReDescend($nt){
		$b = isset($this->rdnts[$nt]);
		//unset($this->rdnts[$nt]);
		return $b;
	}
	function isBetterMatchThan($input){
		return strlen($this->str) < strlen($input->str);
	}
}

class ParseResult{
	function fail(){
		//return ParseResult::match(FALSE);
		$pr = new ParseResult();
		$pr->failed=true;
		return $pr;
	}
	function match($result){
		$pr = new ParseResult();
		$pr->match=$result;
		return $pr;
	}
	function lambda(){
		$pr = new ParseResult();
		$pr->lambda=true;
		$pr->match=null;
		return $pr;
	}
	function isLambda(){
		return isset($this->lambda);
	}
	function failed(){
		return isset($this->failed);
	}
}
?>
Return current item: Siviglia Templating