diff --git a/bootstrap.php b/bootstrap.php index 8f0e37d..f292bb4 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -14,9 +14,9 @@ function ml_get_lisp(): array $env = new MadLisp\Env(); - $core = new MadLisp\Lib\Core(); - $core->register($env); + (new MadLisp\Lib\Math())->register($env); + /* $env->set('eval', function (...$args) use ($eval, $env) { $results = $eval->eval($args, $env); return $results[count($results) - 1]; @@ -26,6 +26,7 @@ function ml_get_lisp(): array $printer->print($args); return null; }); + */ return [$lisp, $env]; } diff --git a/src/CoreFunc.php b/src/CoreFunc.php new file mode 100644 index 0000000..9a98538 --- /dev/null +++ b/src/CoreFunc.php @@ -0,0 +1,44 @@ +name = $name; + $this->doc = $doc; + $this->minArgs = $minArgs; + $this->maxArgs = $maxArgs; + $this->closure = $closure; + } + + public function call(array $args) + { + $this->validateArgs(count($args)); + + return parent::call($args); + } + + private function validateArgs(int $count) + { + if ($this->minArgs >= 0 && $count < $this->minArgs) { + if ($this->minArgs == $this->maxArgs) { + throw new MadLispException(sprintf("%s requires exactly %s argument%s", $this->name, $this->minArgs, + $this->minArgs == 1 ? '' : 's')); + } else { + throw new MadLispException(sprintf("%s requires at least %s argument%s", $this->name, $this->minArgs, + $this->minArgs == 1 ? '' : 's')); + } + } elseif ($this->maxArgs >= 0 && $count > $this->maxArgs) { + throw new MadLispException(sprintf("%s requires at most %s argument%s", $this->name, $this->maxArgs, + $this->maxArgs == 1 ? '' : 's')); + } + } +} diff --git a/src/Evaller.php b/src/Evaller.php index dc0b119..ce0e5b6 100644 --- a/src/Evaller.php +++ b/src/Evaller.php @@ -1,8 +1,6 @@ get(0); - if (!($func instanceof Closure)) { + if (!($func instanceof Func)) { throw new MadLispException("first item of list is not function"); } $args = array_slice($ast->getData(), 1); - return $func(...$args); + return $func->call($args); } } diff --git a/src/Func.php b/src/Func.php new file mode 100644 index 0000000..1f88580 --- /dev/null +++ b/src/Func.php @@ -0,0 +1,24 @@ +closure = $closure; + } + + public function getClosure(): Closure + { + return $this->closure; + } + + public function call(array $args) + { + return ($this->closure)(...$args); + } +} diff --git a/src/Lib/Core.php b/src/Lib/Core.php index 0ce5e81..83eb5a3 100644 --- a/src/Lib/Core.php +++ b/src/Lib/Core.php @@ -1,8 +1,8 @@ set('or', function (...$args) { - // return first true - for ($i = 0; $i < count($args) - 1; $i++) { - if ($args[$i] == true) { - return $args[$i]; - } - } - - // return last - return $args[count($args) - 1]; - }); - - $env->set('and', function (...$args) { - // return first false - for ($i = 0; $i < count($args) - 1; $i++) { - if ($args[$i] == false) { - return $args[$i]; - } - } - - // return last - return $args[count($args) - 1]; - }); - - $env->set('not', fn ($a) => !$a); - // arithmetic - $env->set('+', function (...$args) { - return array_sum($args); - }); - - $env->set('-', function (...$args) { - $result = $args[0] ?? null; - for ($i = 1; $i < count($args); $i++) { - $result -= $args[$i]; - } - return $result; - }); - - $env->set('*', function (...$args) { - $result = $args[0] ?? null; - for ($i = 1; $i < count($args); $i++) { - $result *= $args[$i]; - } - return $result; - }); - - $env->set('/', function (...$args) { - $result = $args[0] ?? null; - for ($i = 1; $i < count($args); $i++) { - $result /= $args[$i]; - } - return $result; - }); - - $env->set('%', function (...$args) { - $result = $args[0] ?? null; - for ($i = 1; $i < count($args); $i++) { - $result %= $args[$i]; - } - return $result; - }); - // comparison $env->set('=', fn ($a, $b) => $a == $b); @@ -91,7 +27,7 @@ class Core implements ILib // types $env->set('type?', function ($a) { - if ($a instanceof Closure) { + if ($a instanceof Func) { return 'function'; } elseif ($a instanceof MList) { return 'list'; @@ -112,7 +48,7 @@ class Core implements ILib } }); - $env->set('fn?', fn ($a) => $a instanceof Closure); + $env->set('fn?', fn ($a) => $a instanceof Func); $env->set('list?', fn ($a) => $a instanceof MList); $env->set('hash?', fn ($a) => $a instanceof Hash); $env->set('sym?', fn ($a) => $a instanceof Symbol); @@ -124,14 +60,5 @@ class Core implements ILib $env->set('float?', fn ($a) => is_float($a)); $env->set('str?', fn ($a) => is_string($a)); - // collections - - $env->set('list', function (...$args) { - return new MList($args); - }); - - $env->set('hash', function (...$args) { - return Util::makeHash($args); - }); } } diff --git a/src/Lib/Math.php b/src/Lib/Math.php new file mode 100644 index 0000000..e66dcf5 --- /dev/null +++ b/src/Lib/Math.php @@ -0,0 +1,49 @@ +set('+', new CoreFunc('+', 'Return the sum of all arguments.', 2, -1, + function (...$args) { + return array_sum($args); + } + )); + + $env->set('-', new CoreFunc('-', 'Subtract the other arguments from the first.', 2, -1, + function (...$args) { + return array_reduce(array_slice($args, 1), fn ($a, $b) => $a - $b, $args[0]); + } + )); + + $env->set('*', new CoreFunc('*', 'Multiply the arguments.', 2, -1, + function (...$args) { + return array_reduce(array_slice($args, 1), fn ($a, $b) => $a * $b, $args[0]); + } + )); + + $env->set('/', new CoreFunc('/', 'Divide the arguments.', 2, -1, + function (...$args) { + return array_reduce(array_slice($args, 1), fn ($a, $b) => $a / $b, $args[0]); + } + )); + + $env->set('//', new CoreFunc('//', 'Divide the arguments using integer division.', 2, -1, + function (...$args) { + return array_reduce(array_slice($args, 1), fn ($a, $b) => intdiv($a, $b), $args[0]); + } + )); + + $env->set('%', new CoreFunc('%', 'Calculate the modulo of arguments.', 2, -1, + function (...$args) { + return array_reduce(array_slice($args, 1), fn ($a, $b) => $a % $b, $args[0]); + } + )); + } +} diff --git a/src/Printer.php b/src/Printer.php index 30e8883..d6ca9e9 100644 --- a/src/Printer.php +++ b/src/Printer.php @@ -1,8 +1,6 @@ '; } elseif ($a instanceof MList) { return '(' . implode(' ', array_map(fn ($b) => $this->doPrint($b), $a->getData())) . ')'; diff --git a/src/UserFunc.php b/src/UserFunc.php new file mode 100644 index 0000000..849e285 --- /dev/null +++ b/src/UserFunc.php @@ -0,0 +1,7 @@ +