let and eval

This commit is contained in:
Pekka Laiho 2020-05-30 18:47:54 +07:00
parent c1224fb66f
commit 01e5c4b198
4 changed files with 67 additions and 30 deletions

View File

@ -1,22 +1,26 @@
<?php <?php
require('vendor/autoload.php'); require('vendor/autoload.php');
function ml_get_env(): MadLisp\Env function ml_get_lisp(): array
{
$env = new MadLisp\Env();
$core = new MadLisp\Lib\Core();
$core->register($env);
return $env;
}
function ml_get_lisp(): MadLisp\Lisp
{ {
$tokenizer = new MadLisp\Tokenizer(); $tokenizer = new MadLisp\Tokenizer();
$reader = new MadLisp\Reader(); $reader = new MadLisp\Reader();
$eval = new MadLisp\Evaller(); $eval = new MadLisp\Evaller();
$printer = new MadLisp\Printer(); $printer = new MadLisp\Printer();
return new MadLisp\Lisp($tokenizer, $reader, $eval, $printer); $lisp = new MadLisp\Lisp($tokenizer, $reader, $eval, $printer);
// environment
$env = new MadLisp\Env();
$core = new MadLisp\Lib\Core();
$core->register($env);
$env->set('eval', function (...$args) use ($eval, $env) {
$results = $eval->eval($args, $env);
return $results[count($results) - 1];
});
return [$lisp, $env];
} }

View File

@ -1,8 +1,7 @@
<?php <?php
require('bootstrap.php'); require('bootstrap.php');
$env = ml_get_env(); list($lisp, $env) = ml_get_lisp();
$lisp = ml_get_lisp();
while (true) { while (true) {
$input = readline('> '); $input = readline('> ');

View File

@ -21,8 +21,9 @@ class Env extends Hash
throw new MadLispException("symbol $key not defined in env"); throw new MadLispException("symbol $key not defined in env");
} }
public function set(string $key, $value): void public function set(string $key, $value)
{ {
$this->data[$key] = $value; $this->data[$key] = $value;
return $value;
} }
} }

View File

@ -40,21 +40,56 @@ class Evaller
public function doEval($ast, Env $env) public function doEval($ast, Env $env)
{ {
// Not list // Not list or empty list
if (!($ast instanceof MList)) { if (!($ast instanceof MList)) {
return $this->evalAst($ast, $env); return $this->evalAst($ast, $env);
} } elseif ($ast->count() == 0) {
// Empty list
if ($ast->count() == 0) {
return $ast; return $ast;
} }
$first = $ast->get(0);
// Handle special keywords // Handle special keywords
if ($first instanceof Symbol) { if ($ast->get(0) instanceof Symbol) {
if ($first->getName() == 'quote') { if ($ast->get(0)->getName() == 'def') {
if ($ast->count() != 3) {
throw new MadLispException("def requires exactly 2 arguments");
}
if (!($ast->get(1) instanceof Symbol)) {
throw new MadLispException("first argument to def is not symbol");
}
$value = $this->doEval($ast->get(2), $env);
return $env->set($ast->get(1)->getName(), $value);
} elseif ($ast->get(0)->getName() == 'let') {
if ($ast->count() != 3) {
throw new MadLispException("let requires exactly 2 arguments");
}
if (!($ast->get(1) instanceof MList)) {
throw new MadLispException("first argument to let is not list");
}
$bindings = $ast->get(1)->getData();
if (count($bindings) % 2 == 1) {
throw new MadLispException("uneven number of bindings for let");
}
$newEnv = new Env($env);
for ($i = 0; $i < count($bindings) - 1; $i += 2) {
$key = $bindings[$i];
if (!($key instanceof Symbol)) {
throw new MadLispException("binding key for let is not symbol");
}
$val = $this->doEval($bindings[$i + 1], $newEnv);
$newEnv->set($key->getName(), $val);
}
return $this->doEval($ast->get(2), $newEnv);
} elseif ($ast->get(0)->getName() == 'quote') {
if ($ast->count() != 2) { if ($ast->count() != 2) {
throw new MadLispException("quote requires exactly 1 argument"); throw new MadLispException("quote requires exactly 1 argument");
} }
@ -66,14 +101,12 @@ class Evaller
// Get new evaluated list // Get new evaluated list
$ast = $this->evalAst($ast, $env); $ast = $this->evalAst($ast, $env);
$first = $ast->get(0); // Call first argument as function
$func = $ast->get(0);
if (!($first instanceof Closure)) { if (!($func instanceof Closure)) {
throw new MadLispException("first item of list is not function"); throw new MadLispException("first item of list is not function");
} }
$args = array_slice($ast->getData(), 1); $args = array_slice($ast->getData(), 1);
return $func(...$args);
return $first(...$args);
} }
} }