Browse Source

Eval: Change stack-based TCO to loop-based TCO

master
Riyyi 1 year ago
parent
commit
63a19170ef
  1. 52
      src/eval-special-form.cpp
  2. 38
      src/eval.cpp
  3. 4
      src/eval.h
  4. 6
      src/functions.cpp

52
src/eval-special-form.cpp

@ -30,8 +30,8 @@ ValuePtr Eval::evalDef(const ValueVector& nodes, EnvironmentPtr env)
VALUE_CAST(symbol, Symbol, nodes.front()); VALUE_CAST(symbol, Symbol, nodes.front());
// Eval second argument // Eval second argument
m_ast_stack.push(*std::next(nodes.begin())); m_ast = *std::next(nodes.begin());
m_env_stack.push(env); m_env = env;
ValuePtr value = evalImpl(); ValuePtr value = evalImpl();
// Dont overwrite symbols after an error // Dont overwrite symbols after an error
@ -52,8 +52,8 @@ ValuePtr Eval::evalDefMacro(const ValueVector& nodes, EnvironmentPtr env)
VALUE_CAST(symbol, Symbol, nodes.front()); VALUE_CAST(symbol, Symbol, nodes.front());
// Eval second argument // Eval second argument
m_ast_stack.push(*std::next(nodes.begin())); m_ast = *std::next(nodes.begin());
m_env_stack.push(env); m_env = env;
ValuePtr value = evalImpl(); ValuePtr value = evalImpl();
VALUE_CAST(lambda, Lambda, value); VALUE_CAST(lambda, Lambda, value);
@ -118,14 +118,14 @@ void Eval::evalDo(const ValueVector& nodes, EnvironmentPtr env)
// Evaluate all nodes except the last // Evaluate all nodes except the last
for (auto it = nodes.begin(); it != std::prev(nodes.end(), 1); ++it) { for (auto it = nodes.begin(); it != std::prev(nodes.end(), 1); ++it) {
m_ast_stack.push(*it); m_ast = *it;
m_env_stack.push(env); m_env = env;
evalImpl(); evalImpl();
} }
// Eval last node // Eval last node
m_ast_stack.push(nodes.back()); m_ast = nodes.back();
m_env_stack.push(env); m_env = env;
return; // TCO return; // TCO
} }
@ -138,18 +138,18 @@ void Eval::evalIf(const ValueVector& nodes, EnvironmentPtr env)
auto second_argument = *std::next(nodes.begin()); auto second_argument = *std::next(nodes.begin());
auto third_argument = (nodes.size() == 3) ? *std::next(std::next(nodes.begin())) : makePtr<Constant>(Constant::Nil); auto third_argument = (nodes.size() == 3) ? *std::next(std::next(nodes.begin())) : makePtr<Constant>(Constant::Nil);
m_ast_stack.push(first_argument); m_ast = first_argument;
m_env_stack.push(env); m_env = env;
auto first_evaluated = evalImpl(); auto first_evaluated = evalImpl();
if (!is<Constant>(first_evaluated.get()) if (!is<Constant>(first_evaluated.get())
|| std::static_pointer_cast<Constant>(first_evaluated)->state() == Constant::True) { || std::static_pointer_cast<Constant>(first_evaluated)->state() == Constant::True) {
m_ast_stack.push(second_argument); m_ast = second_argument;
m_env_stack.push(env); m_env = env;
return; // TCO return; // TCO
} }
m_ast_stack.push(third_argument); m_ast = third_argument;
m_env_stack.push(env); m_env = env;
return; // TCO return; // TCO
} }
@ -173,16 +173,16 @@ void Eval::evalLet(const ValueVector& nodes, EnvironmentPtr env)
VALUE_CAST(elt, Symbol, (*it), void()); VALUE_CAST(elt, Symbol, (*it), void());
std::string key = elt->symbol(); std::string key = elt->symbol();
m_ast_stack.push(*std::next(it)); m_ast = *std::next(it);
m_env_stack.push(let_env); m_env = let_env;
ValuePtr value = evalImpl(); ValuePtr value = evalImpl();
let_env->set(key, value); let_env->set(key, value);
} }
// TODO: Remove limitation of 3 arguments // TODO: Remove limitation of 3 arguments
// Eval all arguments in this new env, return last sexp of the result // Eval all arguments in this new env, return last sexp of the result
m_ast_stack.push(*std::next(nodes.begin())); m_ast = *std::next(nodes.begin());
m_env_stack.push(let_env); m_env = let_env;
return; // TCO return; // TCO
} }
@ -277,8 +277,8 @@ void Eval::evalQuasiQuote(const ValueVector& nodes, EnvironmentPtr env)
auto result = evalQuasiQuoteImpl(nodes.front()); auto result = evalQuasiQuoteImpl(nodes.front());
m_ast_stack.push(result); m_ast = result;
m_env_stack.push(env); m_env = env;
return; // TCO return; // TCO
} }
@ -288,8 +288,8 @@ void Eval::evalTry(const ValueVector& nodes, EnvironmentPtr env)
CHECK_ARG_COUNT_AT_LEAST("try*", nodes.size(), 1, void()); CHECK_ARG_COUNT_AT_LEAST("try*", nodes.size(), 1, void());
// Try 'x' // Try 'x'
m_ast_stack.push(nodes.front()); m_ast = nodes.front();
m_env_stack.push(env); m_env = env;
auto result = evalImpl(); auto result = evalImpl();
// Catch // Catch
@ -317,13 +317,13 @@ void Eval::evalTry(const ValueVector& nodes, EnvironmentPtr env)
catch_env->set(catch_binding->symbol(), error); catch_env->set(catch_binding->symbol(), error);
// Evaluate 'z' using the new Environment // Evaluate 'z' using the new Environment
m_ast_stack.push(catch_nodes.back()); m_ast = catch_nodes.back();
m_env_stack.push(catch_env); m_env = catch_env;
return; // TCO return; // TCO
} }
m_ast_stack.push(result); m_ast = result;
m_env_stack.push(env); m_env = env;
return; // TCO return; // TCO
} }

38
src/eval.cpp

@ -23,6 +23,7 @@ namespace blaze {
Eval::Eval(ValuePtr ast, EnvironmentPtr env) Eval::Eval(ValuePtr ast, EnvironmentPtr env)
: m_ast(ast) : m_ast(ast)
, m_env(env) , m_env(env)
, m_outer_env(env)
{ {
} }
@ -30,11 +31,6 @@ Eval::Eval(ValuePtr ast, EnvironmentPtr env)
void Eval::eval() void Eval::eval()
{ {
m_ast_stack = std::stack<ValuePtr>();
m_env_stack = std::stack<EnvironmentPtr>();
m_ast_stack.push(m_ast);
m_env_stack.push(m_env);
m_ast = evalImpl(); m_ast = evalImpl();
} }
@ -48,20 +44,13 @@ ValuePtr Eval::evalImpl()
return nullptr; return nullptr;
} }
if (m_ast_stack.size() == 0) { ast = m_ast;
return nullptr; env = m_env;
}
if (m_env_stack.size() == 0) { if (env == nullptr) {
m_env_stack.push(m_env); env = m_outer_env;
} }
ast = m_ast_stack.top();
env = m_env_stack.top();
m_ast_stack.pop();
m_env_stack.pop();
if (!is<List>(ast.get())) { if (!is<List>(ast.get())) {
return evalAst(ast, env); return evalAst(ast, env);
} }
@ -133,8 +122,8 @@ ValuePtr Eval::evalImpl()
if (is<Lambda>(evaluated_list->front().get())) { if (is<Lambda>(evaluated_list->front().get())) {
auto lambda = std::static_pointer_cast<Lambda>(evaluated_list->front()); auto lambda = std::static_pointer_cast<Lambda>(evaluated_list->front());
m_ast_stack.push(lambda->body()); m_ast = lambda->body();
m_env_stack.push(Environment::create(lambda, evaluated_list->rest())); m_env = Environment::create(lambda, evaluated_list->rest());
continue; // TCO continue; // TCO
} }
@ -165,8 +154,8 @@ ValuePtr Eval::evalAst(ValuePtr ast, EnvironmentPtr env)
auto evaluated_nodes = ValueVector(count); auto evaluated_nodes = ValueVector(count);
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
m_ast_stack.push(nodes[i]); m_ast = nodes[i];
m_env_stack.push(env); m_env = env;
ValuePtr eval_node = evalImpl(); ValuePtr eval_node = evalImpl();
if (eval_node == nullptr) { if (eval_node == nullptr) {
return nullptr; return nullptr;
@ -184,8 +173,8 @@ ValuePtr Eval::evalAst(ValuePtr ast, EnvironmentPtr env)
const auto& elements = std::static_pointer_cast<HashMap>(ast)->elements(); const auto& elements = std::static_pointer_cast<HashMap>(ast)->elements();
Elements evaluated_elements; Elements evaluated_elements;
for (const auto& element : elements) { for (const auto& element : elements) {
m_ast_stack.push(element.second); m_ast = element.second;
m_env_stack.push(env); m_env = env;
ValuePtr element_node = evalImpl(); ValuePtr element_node = evalImpl();
if (element_node == nullptr) { if (element_node == nullptr) {
return nullptr; return nullptr;
@ -231,11 +220,12 @@ ValuePtr Eval::macroExpand(ValuePtr ast, EnvironmentPtr env)
{ {
while (isMacroCall(ast, env)) { while (isMacroCall(ast, env)) {
auto list = std::static_pointer_cast<List>(ast); auto list = std::static_pointer_cast<List>(ast);
auto value = env->get(std::static_pointer_cast<Symbol>(list->front())->symbol()); auto value = env->get(std::static_pointer_cast<Symbol>(list->front())->symbol());
auto lambda = std::static_pointer_cast<Lambda>(value); auto lambda = std::static_pointer_cast<Lambda>(value);
m_ast_stack.push(lambda->body()); m_ast = lambda->body();
m_env_stack.push(Environment::create(lambda, list->rest())); m_env = Environment::create(lambda, list->rest());
ast = evalImpl(); ast = evalImpl();
} }

4
src/eval.h

@ -48,9 +48,7 @@ private:
ValuePtr m_ast; ValuePtr m_ast;
EnvironmentPtr m_env; EnvironmentPtr m_env;
EnvironmentPtr m_outer_env;
std::stack<ValuePtr> m_ast_stack;
std::stack<EnvironmentPtr> m_env_stack;
}; };
} // namespace blaze } // namespace blaze

6
src/functions.cpp

@ -518,7 +518,7 @@ ADD_FUNCTION(
return makePtr<List>(collection->rest()); return makePtr<List>(collection->rest());
}); });
// (apply + 1 2 (list 3 4)) -> (+ 1 2 3 4) // (apply + 1 2 (list 3 4)) -> (+ 1 2 3 4) -> 10
ADD_FUNCTION( ADD_FUNCTION(
"apply", "apply",
{ {
@ -984,8 +984,8 @@ ADD_FUNCTION(
void installFunctions(EnvironmentPtr env) void installFunctions(EnvironmentPtr env)
{ {
for (const auto& [name, lambda] : s_functions) { for (const auto& [name, function] : s_functions) {
env->set(name, makePtr<Function>(name, lambda)); env->set(name, makePtr<Function>(name, function));
} }
} }

Loading…
Cancel
Save