functions for collections

This commit is contained in:
Pekka Laiho 2020-06-02 09:51:21 +07:00
parent 9e4dffef2b
commit 64a7f19b8b
7 changed files with 126 additions and 7 deletions

View File

@ -17,8 +17,9 @@ function ml_get_lisp(): array
$lisp->register($env); $lisp->register($env);
// Register libraries // Register libraries
(new MadLisp\Lib\Math())->register($env); (new MadLisp\Lib\Collections())->register($env);
(new MadLisp\Lib\Compare())->register($env); (new MadLisp\Lib\Compare())->register($env);
(new MadLisp\Lib\Math())->register($env);
(new MadLisp\Lib\Types())->register($env); (new MadLisp\Lib\Types())->register($env);
return [$lisp, $env]; return [$lisp, $env];

View File

@ -10,6 +10,8 @@ while (true) {
$lisp->rep($input, $env); $lisp->rep($input, $env);
} catch (MadLisp\MadLispException $ex) { } catch (MadLisp\MadLispException $ex) {
print('error: ' . $ex->getMessage()); print('error: ' . $ex->getMessage());
} catch (TypeError $ex) {
print('error: invalid argument type: ' . $ex->getMessage());
} }
print(PHP_EOL); print(PHP_EOL);

View File

@ -82,7 +82,7 @@ class Evaller
} }
} }
return function (...$args) use ($bindings, $ast, $env) { return new UserFunc(function (...$args) use ($bindings, $ast, $env) {
$newEnv = new Env($env); $newEnv = new Env($env);
for ($i = 0; $i < count($bindings); $i++) { for ($i = 0; $i < count($bindings); $i++) {
@ -90,7 +90,7 @@ class Evaller
} }
return $this->doEval($ast->get(2), $newEnv); return $this->doEval($ast->get(2), $newEnv);
}; });
} elseif ($ast->get(0)->getName() == 'if') { } elseif ($ast->get(0)->getName() == 'if') {
if ($ast->count() < 3 || $ast->count() > 4) { if ($ast->count() < 3 || $ast->count() > 4) {
throw new MadLispException("if requires 2 or 3 arguments"); throw new MadLispException("if requires 2 or 3 arguments");

94
src/Lib/Collections.php Normal file
View File

@ -0,0 +1,94 @@
<?php
namespace MadLisp\Lib;
use MadLisp\Collection;
use MadLisp\CoreFunc;
use MadLisp\Env;
use MadLisp\Func;
use MadLisp\Hash;
use MadLisp\MList;
use MadLisp\Seq;
use MadLisp\Util;
use MadLisp\Vector;
class Collections implements ILib
{
public function register(Env $env): void
{
// Creation
$env->set('hash', new CoreFunc('hash', 'Return hash which contains the arguments.', 0, -1,
fn (...$args) => Util::makeHash($args)
));
$env->set('list', new CoreFunc('list', 'Return list which contains the arguments.', 0, -1,
fn (...$args) => new MList($args)
));
$env->set('vector', new CoreFunc('vector', 'Return vector which contains the arguments.', 0, -1,
fn (...$args) => new Vector($args)
));
// Other
$env->set('empty?', new CoreFunc('empty?', 'Return true if collection is empty.', 1, 1,
fn (Collection $a) => $a->count() == 0
));
$env->set('len', new CoreFunc('len', 'Return the length of string or number of elements in collection.', 1, 1,
function ($a) {
if ($a instanceof Collection) {
return $a->count();
} else {
return strlen($a);
}
}
));
$env->set('first', new CoreFunc('first', 'Return the first element of a sequence or null.', 1, 1,
fn (Seq $a) => $a->getData()[0] ?? null
));
$env->set('last', new CoreFunc('last', 'Return the last element of a sequence or null.', 1, 1,
function (Seq $a) {
if ($a->count() == 0) {
return null;
}
return $a->getData()[$a->count() - 1];
}
));
$env->set('head', new CoreFunc('head', 'Return new sequence containing all elements of argument except last.', 1, 1,
function (Seq $a) {
return $a::new(array_slice($a->getData(), 0, $a->count() - 1));
}
));
$env->set('tail', new CoreFunc('tail', 'Return new sequence containing all elements of argument except first.', 1, 1,
function (Seq $a) {
return $a::new(array_slice($a->getData(), 1));
}
));
$env->set('map', new CoreFunc('map', 'Apply the first argument (function) to all elements of second argument (sequence).', 2, 2,
function (Func $f, Seq $a) {
return $a::new(array_map($f->getClosure(), $a->getData()));
}
));
$env->set('reduce', new CoreFunc('reduce', 'Apply the first argument (function) to each element of second argument (sequence) incrementally. Optional third argument is the initial value to be used as first input for function and it defaults to null.', 2, 3,
function (Func $f, Seq $a, $initial = null) {
return array_reduce($a->getData(), $f->getClosure(), $initial);
}
));
$env->set('keys', new CoreFunc('keys', 'Return the keys of a hash-map as a list.', 1, 1,
fn (Hash $a) => new MList(array_keys($a->getData()))
));
$env->set('values', new CoreFunc('values', 'Return the values of a hash-map as a list.', 1, 1,
fn (Hash $a) => new MList(array_values($a->getData()))
));
}
}

View File

@ -28,7 +28,7 @@ class Types implements ILib
fn ($a) => intval($a) fn ($a) => intval($a)
)); ));
$env->set('str', new CoreFunc('str', 'Convert arguments to string and concatenate them together.', 1, -1, $env->set('str', new CoreFunc('str', 'Convert arguments to string and concatenate them together.', 0, -1,
fn (...$args) => implode('', array_map('strval', $args)) fn (...$args) => implode('', array_map('strval', $args))
)); ));
@ -111,5 +111,23 @@ class Types implements ILib
$env->set('str?', new CoreFunc('str?', 'Return true if argument is a string.', 1, 1, $env->set('str?', new CoreFunc('str?', 'Return true if argument is a string.', 1, 1,
fn ($a) => is_string($a) fn ($a) => is_string($a)
)); ));
// Helpers for numbers
$env->set('zero?', new CoreFunc('zero?', 'Return true if argument is an integer with value 0.', 1, 1,
fn ($a) => $a === 0
));
$env->set('one?', new CoreFunc('one?', 'Return true if argument is an integer with value 1.', 1, 1,
fn ($a) => $a === 1
));
$env->set('even?', new CoreFunc('even?', 'Return true if argument is divisible by 2.', 1, 1,
fn ($a) => $a % 2 === 0
));
$env->set('odd?', new CoreFunc('odd?', 'Return true if argument is not divisible by 2.', 1, 1,
fn ($a) => $a % 2 !== 0
));
} }
} }

View File

@ -28,7 +28,7 @@ class Printer
} elseif ($a === false) { } elseif ($a === false) {
return 'false'; return 'false';
} elseif ($a === null) { } elseif ($a === null) {
return ''; return 'null';
} elseif (is_string($a)) { } elseif (is_string($a)) {
return '"' . $a . '"'; return '"' . $a . '"';
} else { } else {

View File

@ -3,5 +3,9 @@ namespace MadLisp;
abstract class Seq extends Collection abstract class Seq extends Collection
{ {
public static function new(array $data = []): self
{
// late static binding
return new static($data);
}
} }