Optimization: refactor Evaller class, remove evalAst function

This commit is contained in:
Pekka Laiho 2020-12-06 21:11:36 +07:00
parent d1b412f913
commit 55da5fb9b6

View File

@ -3,10 +3,6 @@ namespace MadLisp;
class Evaller class Evaller
{ {
// Keep cache of macro names so we can skip
// macro expansion when possible.
protected static array $macros = [];
protected Tokenizer $tokenizer; protected Tokenizer $tokenizer;
protected Reader $reader; protected Reader $reader;
protected Printer $printer; protected Printer $printer;
@ -14,6 +10,10 @@ class Evaller
protected bool $debug = false; protected bool $debug = false;
// Keep cache of macro names so we can skip
// macro expansion when possible.
protected array $macros = [];
public function __construct(Tokenizer $tokenizer, Reader $reader, Printer $printer, bool $safemode) public function __construct(Tokenizer $tokenizer, Reader $reader, Printer $printer, bool $safemode)
{ {
$this->tokenizer = $tokenizer; $this->tokenizer = $tokenizer;
@ -28,44 +28,47 @@ class Evaller
$isTco = false; $isTco = false;
while (true) { while (true) {
// Show debug output
if ($this->debug) {
printf("%s %2d : ", $isTco ? ' tco' : 'eval', $depth);
$this->printer->print($ast);
print("\n");
$isTco = true;
}
// Return here after macro expansion // Return here after macro expansion
$expandMacros = true; $expandMacros = true;
beginning: beginning:
// Return fast for optimization if not list // Handle response for anything but a list
if (!($ast instanceof MList)) { if ($ast instanceof Symbol) {
if ($ast instanceof Symbol || $ast instanceof Collection) { // Lookup symbol from env
return $this->evalAst($ast, $env, $depth); return $env->get($ast->getName());
} else { } elseif ($ast instanceof Vector || $ast instanceof Hash) {
// This is not evaluated so we can just return it $newData = [];
// and save one extra call to evalAst. foreach ($ast->getData() as $key => $val) {
return $ast; $newData[$key] = $this->eval($val, $env, $depth + 1);
} }
return $ast::new($newData);
} elseif ($ast instanceof MList == false) {
return $ast;
} }
$astData = $ast->getData(); $astData = $ast->getData();
$astLength = count($astData); $astLength = count($astData);
// Empty list, return we can also return // Empty list, we can return
if ($astLength == 0) { if ($astLength == 0) {
return $ast; return $ast;
} }
// Show debug output here (before macro expansion)
if ($this->debug && $expandMacros) {
printf("%s %2d : ", $isTco ? ' tco' : 'eval', $depth);
$this->printer->print($ast);
print("\n");
$isTco = true;
}
// Handle special forms // Handle special forms
if ($astData[0] instanceof Symbol) { if ($astData[0] instanceof Symbol) {
$symbolName = $astData[0]->getName(); $symbolName = $astData[0]->getName();
// Handle macro expansion and go back to beginning to check // Handle macro expansion and go back to beginning to check
// again if ast is still something we need to evaluate or not. // if ast is still something we need to evaluate or not.
if ($expandMacros && array_key_exists($symbolName, self::$macros)) { if ($expandMacros && array_key_exists($symbolName, $this->macros)) {
$ast = $this->macroexpand($ast, $env); $ast = $this->macroexpand($ast, $env);
$expandMacros = false; $expandMacros = false;
goto beginning; goto beginning;
@ -126,7 +129,8 @@ class Evaller
// Save macros in cache // Save macros in cache
if ($value instanceof Func && $value->isMacro()) { if ($value instanceof Func && $value->isMacro()) {
self::$macros[$name] = $value; // value does not matter, we check for key
$this->macros[$name] = 1;
} }
return $env->set($name, $value); return $env->set($name, $value);
@ -322,19 +326,29 @@ class Evaller
} }
} }
// Get new evaluated list // Eval all items in list
$ast = $this->evalAst($ast, $env, $depth); $newData = [];
$astData = $ast->getData(); foreach ($astData as $a) {
if ($a instanceof Symbol) {
// Lookup symbol from env
$newData[] = $env->get($a->getName());
} elseif ($a instanceof Collection) {
$newData[] = $this->eval($a, $env, $depth + 1);
} else {
$newData[] = $a;
}
}
// First item is function, rest are arguments // First item is function, rest are arguments
$func = $astData[0]; $func = $newData[0];
$args = array_slice($astData, 1); $args = array_slice($newData, 1);
if ($func instanceof CoreFunc) { if ($func instanceof CoreFunc) {
return $func->call($args); return $func->call($args);
} elseif ($func instanceof UserFunc) { } elseif ($func instanceof UserFunc) {
$ast = $func->getAst(); $ast = $func->getAst();
$env = $func->getEnv($args); $env = $func->getEnv($args);
// tco
} else { } else {
throw new MadLispException("eval: first item of list is not function"); throw new MadLispException("eval: first item of list is not function");
} }
@ -351,28 +365,6 @@ class Evaller
$this->debug = $val; $this->debug = $val;
} }
private function evalAst($ast, Env $env, int $depth)
{
if ($ast instanceof Symbol) {
// Lookup symbol from env
return $env->get($ast->getName());
} elseif ($ast instanceof Seq) {
$results = [];
foreach ($ast->getData() as $val) {
$results[] = $this->eval($val, $env, $depth + 1);
}
return $ast::new($results);
} elseif ($ast instanceof Hash) {
$results = [];
foreach ($ast->getData() as $key => $val) {
$results[$key] = $this->eval($val, $env, $depth + 1);
}
return new Hash($results);
}
return $ast;
}
private function macroexpand($ast, Env $env) private function macroexpand($ast, Env $env)
{ {
while ($ast instanceof MList) { while ($ast instanceof MList) {