From 16808c7ff93d256806c6eb5639ac48c4bf9b9bff Mon Sep 17 00:00:00 2001 From: Pekka Laiho Date: Sat, 19 Dec 2020 17:45:18 +0700 Subject: [PATCH] add support for variable arguments using & --- src/Evaller.php | 4 +-- src/UserFunc.php | 5 +--- src/Util.php | 16 ++++++++++++ test/UtilTest.php | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/Evaller.php b/src/Evaller.php index 5bb1a8f..68b9bcb 100644 --- a/src/Evaller.php +++ b/src/Evaller.php @@ -241,9 +241,7 @@ class Evaller $closure = function (...$args) use ($bindings, $env, $astData, $depth) { $newEnv = new Env('closure', $env); - for ($i = 0; $i < count($bindings); $i++) { - $newEnv->set($bindings[$i]->getName(), $args[$i] ?? null); - } + Util::bindArguments($newEnv, $bindings, $args); return $this->eval($astData[2], $newEnv, $depth + 1); }; diff --git a/src/UserFunc.php b/src/UserFunc.php index 3c1f64f..e8a93f7 100644 --- a/src/UserFunc.php +++ b/src/UserFunc.php @@ -38,10 +38,7 @@ class UserFunc extends Func { $newEnv = new Env('apply', $this->tempEnv); - $bindings = $this->bindings->getData(); - for ($i = 0; $i < count($bindings); $i++) { - $newEnv->set($bindings[$i]->getName(), $args[$i] ?? null); - } + Util::bindArguments($newEnv, $this->bindings->getData(), $args); return $newEnv; } diff --git a/src/Util.php b/src/Util.php index 8f01e3e..416a857 100644 --- a/src/Util.php +++ b/src/Util.php @@ -9,6 +9,22 @@ namespace MadLisp; class Util { + public static function bindArguments(Env $env, array $bindings, array $args): void + { + for ($i = 0; $i < count($bindings); $i++) { + if ($bindings[$i]->getName() == '&') { + if ($i < count($bindings) - 1) { + $env->set($bindings[$i + 1]->getName(), new Vector(array_slice($args, $i))); + return; + } else { + throw new MadLispException('no binding after &'); + } + } else { + $env->set($bindings[$i]->getName(), $args[$i] ?? null); + } + } + } + public static function makeHash(array $args): Hash { if (count($args) % 2 == 1) { diff --git a/test/UtilTest.php b/test/UtilTest.php index f87af57..3fbbeb3 100644 --- a/test/UtilTest.php +++ b/test/UtilTest.php @@ -7,12 +7,74 @@ use PHPUnit\Framework\TestCase; +use MadLisp\Env; use MadLisp\Hash; use MadLisp\MadLispException; +use MadLisp\Symbol; use MadLisp\Util; +use MadLisp\Vector; class UtilTest extends TestCase { + public function testBindArguments() + { + $env = new Env('env'); + + Util::bindArguments($env, [ + new Symbol('a'), + new Symbol('b') + ], [ + 1, + 2 + ]); + + $this->assertSame(['a' => 1, 'b' => 2], $env->getData()); + } + + public function testBindArgumentsVariable() + { + $env = new Env('env'); + + Util::bindArguments($env, [ + new Symbol('a'), + new Symbol('b'), + new Symbol('&'), + new Symbol('c') + ], [ + 1, + 2, + 3, + 4 + ]); + + $data = $env->getData(); + $this->assertCount(3, $data); + $this->assertSame(1, $data['a']); + $this->assertSame(2, $data['b']); + $vec = $data['c']; + $this->assertInstanceOf(Vector::class, $vec); + $this->assertSame([3, 4], $vec->getData()); + } + + public function testBindArgumentsVariableInvalid() + { + $this->expectException(MadLispException::class); + $this->expectExceptionMessage('no binding after &'); + + $env = new Env('env'); + + Util::bindArguments($env, [ + new Symbol('a'), + new Symbol('b'), + new Symbol('&') + ], [ + 1, + 2, + 3, + 4 + ]); + } + public function testMakeHash() { $hash = Util::makeHash(['a', 1, 'b', 2]);