/** * Function that evaluates a given syntax tree * @param array $params * @param array $env Environment to be used * @return mixed Result */ public function evaluate($params) { if (is_numeric($params)) { return (double) $params; } elseif (is_string($params)) { $ret = Common::findInEnv($this->env, $params); if ($ret === Common::NONE) { throw new Exception("undefined atom {$params}"); } else { return $ret; } } elseif (!is_array($params)) { return $params; } elseif ($params[0] == 'quote') { if (count($params) != 2) { throw new Exception("bad syntax for quote"); } return $params[1]; } elseif ($params[0] == 'lambda') { if (count($params) != 3) { throw new Exception("bad syntax for lambda"); } $parms = $params[1]; $body = $params[2]; $env = $this->env; return function ($args) use($parms, $body, $env) { $newEnv = new Environment(Common::zip($parms, $args)); $x = new TinyLisp(array($newEnv, $env)); return $x->evaluate($body); }; } elseif ($params[0] == 'if') { if (count($params) != 4) { throw new Exception("bad syntax for if"); } $ret = $this->evaluate($params[1]) ? $this->evaluate($params[2]) : $this->evaluate($params[3]); return $ret; } elseif ($params[0] == 'define') { if (count($params) != 3) { throw new Exception("bad syntax for define"); } $multiple_scoped = false; foreach ($this->env as $key => $value) { if ($value instanceof Environment) { $multiple_scoped = true; break; } } $param = is_array($params[2]) ? $this->evaluate($params[2]) : $params[2]; if ($multiple_scoped) { // multiple scopes, apply this only to the top most $this->env->v[0][$params[1]] = $param; } else { $this->env->v[$params[1]] = $param; } } elseif ($params[0] == 'print') { if (count($params) != 2) { throw new Exception("bad syntax for print"); } print_r($this->evaluate($params[1])); } else { $proc = $this->evaluate($params[0]); $args = array(); // for (define first car), $proc may return 'car', in which case we need to re-evaluate if (is_string($proc)) { array_shift($params); return $this->evaluate(array_merge(array($proc), $params)); } for ($i = 1; $i < count($params); $i++) { $args[] = $this->evaluate($params[$i]); } if (is_callable($proc)) { return $proc($args); } elseif (is_array($proc) && method_exists($proc[0], $proc[1])) { return call_user_func_array($proc, array($args)); } else { throw new Exception("Can't parse " . print_r($params, true)); } } }