expr = new VTExpressionEvaluater($expr); $this->env = $env; } function get(){ if(!isset($this->val)){ $this->val = $this->expr->evaluate($this->env); } return $this->val; } } class VTExpressionEngineEnv implements VTEnv{ function __construct($data){ $this->data = $data; $this->vars = array(); } function bind($name, $expr){ $this->vars[$name] = new VTThunk($this, $expr); } function get($var){ if(array_key_exists($var, $this->vars)){ return $this->vars[$var]->get(); }else{ return $this->data[$var]; } } } class VTExpressionEngine{ function __construct(){ $this->data = array(); $this->expressions = array(); $this->unparsedExpressions = array(); $this->vars = array(); } /** * Load dynamic fields and their expressions * */ function loadExpressions($arr){ $this->unparsedExpressions = array_merge($this->unparsedExpressions, $arr); $this->expressions = array_merge($this->expressions, array_map(array($this, 'expression'), $arr)); $this->regenerate(); } function removeExpression($fieldName){ unset($this->unparsedExpressions[$fieldName]); unset($this->expressions[$fieldName]); $this->regenerate(); } private function regenerate(){ $this->vars = array_keys($this->expressions); $dyn_vars = array(); foreach($this->expressions as $var => $expr){ $syms = array_keys($this->getSyms($expr)); $dyn_vars[$var] = $syms; } $independents = array_keys(array_filter($dyn_vars, array($this, 'isEmpty'))); $arr = array(); foreach($dyn_vars as $var => $parents){ foreach($parents as $parent){ $arr[$parent][$var] = $var; } } $dependents = array_map('array_keys', $arr); $this->independents = $independents; $this->dependents = $dependents; $this->checkForCycles(); } /** * Evaluate the expressions using the data in the array * provided. * * @param $data An array of bound variables containing the * the variable name as the key and the value as data. * @return An array containing the variables bound to the expressions * and their values. */ function evaluate($data){ $env = new VTExpressionEngineEnv($data); foreach($this->expressions as $var=>$expr){ $env->bind($var, $expr); } $out = array(); foreach($this->vars as $var){ $out[$var] = $env->get($var); } return $out; } private function checkForCycles(){ foreach($this->independents as $independent){ $this->testCall($independent, array(), sizeof($this->dependents)); } } private function testCall($cur, $stack, $n){ if($n<0){ throw new VTExpressionException('There appears to be a loop in ('.implode(", ", $stack).')'); } $dependents = $this->dependents; if(array_key_exists($cur, $dependents)){ foreach($dependents[$cur] as $var){ $this->testCall($var, array_merge($stack, array($cur)), $n-1); } } } private function getSyms($expr){ if($expr instanceof VTTreeNode){ $params = $expr->getParams(); if(sizeof($params)!=0){ $sym_arr = array_map(array($this, "getSyms"), $params); if(sizeof($sym_arr)>1){ $syms = call_user_func_array('array_merge', $sym_arr); }else{ $syms = $sym_arr[0]; } }else{ $syms = array(); } return $syms; }else if($expr instanceof Symbol && in_array($expr->value, $this->vars)){ return array($expr->value=>$expr); }else{ return array(); } } private function loadExpression($var, $expr){ $this->expressions[$var] = $value; } private function parse($str){ $parser = new VTParser(new SpaceFilter(new VTTokenizer($str))); return $parser->expression(); } private function isEmpty($arr){ return sizeof($arr)!=0; } private function expression($str){ $parser = new VTParser(new SpaceFilter(new VTTokenizer($str))); return $parser->expression(); } } ?>