mirror of
https://github.com/peklaiho/madlisp.git
synced 2024-11-30 00:47:48 +00:00
Optimization: refactor Evaller class, remove evalAst function
This commit is contained in:
parent
d1b412f913
commit
55da5fb9b6
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user