diff --git a/src/environment.cpp b/src/environment.cpp index 025774d..3056e93 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -29,7 +29,7 @@ EnvironmentPtr Environment::create(EnvironmentPtr outer) return env; } -EnvironmentPtr Environment::create(const ValuePtr lambda, std::list arguments) +EnvironmentPtr Environment::create(const ValuePtr lambda, const std::list& arguments) { auto lambda_casted = std::static_pointer_cast(lambda); auto env = create(lambda_casted->env()); diff --git a/src/environment.h b/src/environment.h index 45ae5de..531c94a 100644 --- a/src/environment.h +++ b/src/environment.h @@ -21,7 +21,7 @@ public: // Factory functions instead of constructors because it can fail in the bindings/arguments case static EnvironmentPtr create(); static EnvironmentPtr create(EnvironmentPtr outer); - static EnvironmentPtr create(const ValuePtr lambda, std::list arguments); + static EnvironmentPtr create(const ValuePtr lambda, const std::list& arguments); bool exists(const std::string& symbol); ValuePtr set(const std::string& symbol, ValuePtr value); diff --git a/src/error.h b/src/error.h index 08c9884..4fd857e 100644 --- a/src/error.h +++ b/src/error.h @@ -10,6 +10,7 @@ #include "ruc/singleton.h" +#include "forward.h" #include "lexer.h" namespace blaze { @@ -23,23 +24,28 @@ public: { m_token_errors.clear(); m_other_errors.clear(); + m_exceptions.clear(); } void add(Token error) { m_token_errors.push_back(error); } void add(const std::string& error) { m_other_errors.push_back(error); } + void add(ValuePtr error) { m_exceptions.push_back(error); } bool hasTokenError() { return m_token_errors.size() > 0; } bool hasOtherError() { return m_other_errors.size() > 0; } - bool hasAnyError() { return hasTokenError() || hasOtherError(); } + bool hasException() { return m_exceptions.size() > 0; } + bool hasAnyError() { return hasTokenError() || hasOtherError() || hasException(); } void setInput(std::string_view input) { m_input = input; } Token tokenError() const { return m_token_errors[0]; } const std::string& otherError() const { return m_other_errors[0]; } + ValuePtr exception() const { return m_exceptions[0]; } private: std::string_view m_input; std::vector m_token_errors; std::vector m_other_errors; + std::vector m_exceptions; }; } // namespace blaze diff --git a/src/eval-special-form.cpp b/src/eval-special-form.cpp index 56758dd..cbe6d0c 100644 --- a/src/eval-special-form.cpp +++ b/src/eval-special-form.cpp @@ -4,10 +4,13 @@ * SPDX-License-Identifier: MIT */ +#include // std::next, std::prev #include #include +#include #include "ast.h" +#include "environment.h" #include "error.h" #include "eval.h" #include "forward.h" @@ -277,6 +280,51 @@ void Eval::evalQuasiQuote(const std::list& nodes, EnvironmentPtr env) return; // TCO } +// (try* x (catch* y z)) +void Eval::evalTry(const std::list& nodes, EnvironmentPtr env) +{ + CHECK_ARG_COUNT_AT_LEAST("try*", nodes.size(), 1, void()); + + // Try 'x' + m_ast_stack.push(nodes.front()); + m_env_stack.push(env); + auto result = evalImpl(); + + // Catch + if (nodes.size() == 2 && (Error::the().hasOtherError() || Error::the().hasException())) { + // Get the exception + auto error = (Error::the().hasOtherError()) + ? makePtr(Error::the().otherError()) + : Error::the().exception(); + Error::the().clearErrors(); + + VALUE_CAST(catch_list, List, nodes.back(), void()); + auto catch_nodes = catch_list->nodes(); + CHECK_ARG_COUNT_IS("catch*", catch_nodes.size() - 1, 2, void()); + + VALUE_CAST(catch_symbol, Symbol, catch_nodes.front(), void()); + if (catch_symbol->symbol() != "catch*") { + Error::the().add("catch block must begin with catch*"); + return; + } + + VALUE_CAST(catch_binding, Symbol, (*std::next(catch_nodes.begin())), void()); + + // Create new Environment that binds 'y' to the value of the exception + auto catch_env = Environment::create(env); + catch_env->set(catch_binding->symbol(), error); + + // Evaluate 'z' using the new Environment + m_ast_stack.push(catch_nodes.back()); + m_env_stack.push(catch_env); + return; // TCO + } + + m_ast_stack.push(result); + m_env_stack.push(env); + return; // TCO +} + // ----------------------------------------- } // namespace blaze diff --git a/src/eval.cpp b/src/eval.cpp index c8df127..82408fc 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -44,6 +44,10 @@ ValuePtr Eval::evalImpl() EnvironmentPtr env = nullptr; while (true) { + if (Error::the().hasAnyError()) { + return nullptr; + } + if (m_ast_stack.size() == 0) { return nullptr; } @@ -114,6 +118,10 @@ ValuePtr Eval::evalImpl() evalQuasiQuote(nodes, env); continue; // TCO } + if (symbol == "try*") { + evalTry(nodes, env); + continue; // TCO + } } auto evaluated_list = std::static_pointer_cast(evalAst(ast, env)); diff --git a/src/eval.h b/src/eval.h index 7e13561..b2dcd16 100644 --- a/src/eval.h +++ b/src/eval.h @@ -42,6 +42,7 @@ private: void evalIf(const std::list& nodes, EnvironmentPtr env); void evalLet(const std::list& nodes, EnvironmentPtr env); void evalQuasiQuote(const std::list& nodes, EnvironmentPtr env); + void evalTry(const std::list& nodes, EnvironmentPtr env); ValuePtr apply(std::shared_ptr evaluated_list); diff --git a/src/functions.cpp b/src/functions.cpp index b467127..bd0fbf1 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -570,6 +570,17 @@ ADD_FUNCTION( return result; }); +// (throw x) +ADD_FUNCTION( + "throw", + { + CHECK_ARG_COUNT_IS("throw", nodes.size(), 1); + + Error::the().add(nodes.front()); + + return nullptr; + }) + // ----------------------------------------- #define IS_CONSTANT(name, constant) \ diff --git a/src/printer.cpp b/src/printer.cpp index 0b0ca54..8ca2cf5 100644 --- a/src/printer.cpp +++ b/src/printer.cpp @@ -161,6 +161,10 @@ void Printer::printError() std::string error = Error::the().otherError(); m_print += format("{}", error); } + else if (Error::the().hasException()) { + ValuePtr error = Error::the().exception(); + m_print += format("{}", error); + } } } // namespace blaze