From 7331cbe874652212d96205404595f05121a02aff Mon Sep 17 00:00:00 2001 From: Pekka Laiho Date: Fri, 5 Jun 2020 15:33:52 +0700 Subject: [PATCH] tail call optimization for function calls --- src/Evaller.php | 32 +++++++++++++------------------- src/UserFunc.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/Evaller.php b/src/Evaller.php index 21b8a1f..b544060 100644 --- a/src/Evaller.php +++ b/src/Evaller.php @@ -3,18 +3,8 @@ namespace MadLisp; class Evaller { - private $p = null; - public function eval($ast, Env $env) { - // Debug - if ($this->p == null) { - $this->p = new Printer(); - } - print("eval: "); - $this->p->print($ast); - print("\n"); - while (true) { // Not list or empty list @@ -108,7 +98,7 @@ class Evaller } } - return new UserFunc(function (...$args) use ($bindings, $ast, $env) { + $closure = function (...$args) use ($bindings, $ast, $env) { $newEnv = new Env($env); for ($i = 0; $i < count($bindings); $i++) { @@ -116,7 +106,9 @@ class Evaller } return $this->eval($ast->get(2), $newEnv); - }); + }; + + return new UserFunc($closure, $ast->get(2), $env, $bindings); } elseif ($ast->get(0)->getName() == 'if') { if ($ast->count() < 3 || $ast->count() > 4) { throw new MadLispException("if requires 2 or 3 arguments"); @@ -125,11 +117,9 @@ class Evaller $result = $this->eval($ast->get(1), $env); if ($result == true) { - echo "if tco\n"; $ast = $ast->get(2); continue; } elseif ($ast->count() == 4) { - echo "if tco\n"; $ast = $ast->get(3); continue; } else { @@ -192,14 +182,18 @@ class Evaller // Get new evaluated list $ast = $this->evalAst($ast, $env); - // Call first argument as function + // First item is function, rest are arguments $func = $ast->get(0); - if (!($func instanceof Func)) { + $args = array_slice($ast->getData(), 1); + + if ($func instanceof CoreFunc) { + return $func->call($args); + } elseif ($func instanceof UserFunc) { + $ast = $func->getAst(); + $env = $func->getEnv($args); + } else { throw new MadLispException("eval: first item of list is not function"); } - - $args = array_slice($ast->getData(), 1); - return $func->call($args); } } diff --git a/src/UserFunc.php b/src/UserFunc.php index 849e285..42448eb 100644 --- a/src/UserFunc.php +++ b/src/UserFunc.php @@ -1,7 +1,36 @@ ast = $ast; + $this->tempEnv = $tempEnv; + $this->bindings = $bindings; + } + + public function getAst() + { + return $this->ast; + } + + public function getEnv(array $args) + { + $newEnv = new Env($this->tempEnv); + + for ($i = 0; $i < count($this->bindings); $i++) { + $newEnv->set($this->bindings[$i]->getName(), $args[$i] ?? null); + } + + return $newEnv; + } }