From 56c80b9aaff7dd7e94c05f1c967cfbac0ee3a8bc Mon Sep 17 00:00:00 2001 From: Pekka Laiho Date: Wed, 27 May 2020 20:48:31 +0700 Subject: [PATCH] improvements --- classes.php | 10 +++--- lib.php | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++-- lisp.php | 19 +++++++++-- 3 files changed, 112 insertions(+), 9 deletions(-) diff --git a/classes.php b/classes.php index 7c35663..17ff3a8 100644 --- a/classes.php +++ b/classes.php @@ -49,11 +49,6 @@ class MLHash extends MLCollection throw new MadLispException("hash does not contain key $key"); } - - public function set(string $key, $value): void - { - $this->data[$key] = $value; - } } class MLEnv extends MLHash @@ -75,6 +70,11 @@ class MLEnv extends MLHash throw new MadLispException("symbol $key not defined"); } + + public function set(string $key, $value): void + { + $this->data[$key] = $value; + } } class MLSymbol diff --git a/lib.php b/lib.php index f68e953..b55156a 100644 --- a/lib.php +++ b/lib.php @@ -6,7 +6,35 @@ function ml_get_env(): MLEnv { $env = new MLEnv(); - // basic arithmetic + // logic + + $env->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); @@ -56,8 +84,68 @@ function ml_get_env(): MLEnv // types $env->set('type?', function ($a) { - return gettype($a); + if ($a instanceof Closure) { + return 'function'; + } elseif ($a instanceof MLList) { + return 'list'; + } elseif ($a instanceof MLHash) { + return 'hash'; + } elseif ($a instanceof MLSymbol) { + return 'symbol'; + } elseif ($a === true || $a === false) { + return 'bool'; + } elseif ($a === null) { + return 'null'; + } elseif (is_int($a)) { + return 'int'; + } elseif (is_float($a)) { + return 'float'; + } else { + return 'string'; + } }); + $env->set('fn?', fn ($a) => $a instanceof Closure); + $env->set('list?', fn ($a) => $a instanceof MLList); + $env->set('hash?', fn ($a) => $a instanceof MLHash); + $env->set('sym?', fn ($a) => $a instanceof MLSymbol); + $env->set('bool?', fn ($a) => $a === true || $a === false); + $env->set('true?', fn ($a) => $a == true); // not strict + $env->set('false?', fn ($a) => $a == false); // not strict + $env->set('null?', fn ($a) => $a === null); + $env->set('int?', fn ($a) => is_int($a)); + $env->set('float?', fn ($a) => is_float($a)); + $env->set('str?', fn ($a) => is_string($a)); + + // collections + + $env->set('list', function (...$args) { + return new MLList($args); + }); + + $env->set('hash', function (...$args) { + if (count($args) % 2 == 1) { + throw new MadLispException('uneven number of arguments for hash'); + } + + $data = []; + + for ($i = 0; $i < count($args) - 1; $i += 2) { + $key = $args[$i]; + $val = $args[$i + 1]; + + if (!is_string($key)) { + throw new MadLispException('invalid key for hash (not string)'); + } + + $data[$key] = $val; + } + + return new MLHash($data); + }); + + // for debugging! + $env->set('eval', fn ($a) => ml_eval($a, $env)); + return $env; } diff --git a/lisp.php b/lisp.php index b7784a7..1acd136 100644 --- a/lisp.php +++ b/lisp.php @@ -149,14 +149,23 @@ function ml_eval($expr, MLEnv $env) // Evaluate list contents $results = array_map(fn ($a) => ml_eval($a, $env), $expr->getData()); - if ($results[0] instanceof Closure) { + $fn = $results[0]; + + if ($fn instanceof Closure) { // If the first item is a function, call it $args = array_slice($results, 1); - return ($results[0])(...$args); + return $fn(...$args); } else { // Otherwise return new list with evaluated contents return new MLList($results); } + } elseif ($expr instanceof MLHash) { + // Hash: return new hash with all items evaluated + $items = []; + foreach ($expr->getData() as $key => $val) { + $items[$key] = ml_eval($val, $env); + } + return new MLHash($items); } elseif ($expr instanceof MLSymbol) { return $env->get($expr->name()); } @@ -170,6 +179,12 @@ function ml_print($a): string return ''; } elseif ($a instanceof MLList) { return '(' . implode(' ', array_map('ml_print', $a->getData())) . ')'; + } elseif ($a instanceof MLHash) { + $items = []; + foreach ($a->getData() as $key => $val) { + $items[] = ml_print($key) . ':' . ml_print($val); + } + return '{' . implode(' ', $items) . '}'; } elseif ($a instanceof MLSymbol) { return $a->name(); } elseif ($a === true) {