Compare commits

..
4 Commits
5 changed files with 285 additions and 103 deletions
+4
View File
@@ -99,3 +99,7 @@ add_dependencies(test3 ${PROJECT})
add_custom_target(test4
COMMAND env STEP=step_env MAL_IMPL=js ../vendor/mal/runtest.py --deferrable --optional ../vendor/mal/tests/step4_if_fn_do.mal -- ./${PROJECT})
add_dependencies(test4 ${PROJECT})
add_custom_target(test5
COMMAND env STEP=step_env MAL_IMPL=js ../vendor/mal/runtest.py --deferrable --optional ../vendor/mal/tests/step5_tco.mal -- ./${PROJECT})
add_dependencies(test5 ${PROJECT})
+136 -98
View File
@@ -25,51 +25,96 @@ Eval::Eval(ASTNodePtr ast, EnvironmentPtr env)
{
}
// -----------------------------------------
void Eval::eval()
{
m_ast = evalImpl(m_ast, m_env);
m_ast_stack = std::stack<ASTNodePtr>();
m_env_stack = std::stack<EnvironmentPtr>();
m_ast_stack.push(m_ast);
m_env_stack.push(m_env);
m_ast = evalImpl();
}
ASTNodePtr Eval::evalImpl(ASTNodePtr ast, EnvironmentPtr env)
ASTNodePtr Eval::evalImpl()
{
if (ast == nullptr || env == nullptr) {
return nullptr;
ASTNodePtr ast = nullptr;
EnvironmentPtr env = nullptr;
while (true) {
if (m_ast_stack.size() == 0) {
return nullptr;
}
if (m_env_stack.size() == 0) {
m_env_stack.push(m_env);
}
ast = m_ast_stack.top();
env = m_env_stack.top();
m_ast_stack.pop();
m_env_stack.pop();
if (!is<List>(ast.get())) {
return evalAst(ast, env);
}
auto list = std::static_pointer_cast<List>(ast);
if (list->empty()) {
return ast;
}
// Special forms
auto nodes = list->nodes();
if (is<Symbol>(nodes.front().get())) {
auto symbol = std::static_pointer_cast<Symbol>(nodes.front())->symbol();
nodes.pop_front();
if (symbol == "def!") {
return evalDef(nodes, env);
}
if (symbol == "let*") {
evalLet(nodes, env);
continue; // TCO
}
if (symbol == "do") {
evalDo(nodes, env);
continue; // TCO
}
if (symbol == "if") {
evalIf(nodes, env);
continue; // TCO
}
if (symbol == "fn*") {
return evalFn(nodes, env);
}
}
auto evaluated_list = std::static_pointer_cast<List>(evalAst(ast, env));
if (evaluated_list == nullptr) {
return nullptr;
}
// Regular list
if (is<Lambda>(evaluated_list->nodes().front().get())) {
auto evaluated_nodes = evaluated_list->nodes();
// car
auto lambda = std::static_pointer_cast<Lambda>(evaluated_nodes.front());
// cdr
evaluated_nodes.pop_front();
m_ast_stack.push(lambda->body());
m_env_stack.push(Environment::create(lambda, evaluated_nodes));
continue; // TCO
}
// Function call
return apply(evaluated_list);
}
if (!is<List>(ast.get())) {
return evalAst(ast, env);
}
auto list = std::static_pointer_cast<List>(ast);
if (list->empty()) {
return ast;
}
// Environment
auto nodes = list->nodes();
if (is<Symbol>(nodes.front().get())) {
auto symbol = std::static_pointer_cast<Symbol>(nodes.front())->symbol();
nodes.pop_front();
if (symbol == "def!") {
return evalDef(nodes, env);
}
if (symbol == "let*") {
return evalLet(nodes, env);
}
if (symbol == "do") {
return evalDo(nodes, env);
}
if (symbol == "if") {
return evalIf(nodes, env);
}
if (symbol == "fn*") {
return evalFn(nodes, env);
}
}
// Function call
return apply(std::static_pointer_cast<List>(evalAst(ast, env)));
}
ASTNodePtr Eval::evalAst(ASTNodePtr ast, EnvironmentPtr env)
@@ -87,23 +132,14 @@ ASTNodePtr Eval::evalAst(ASTNodePtr ast, EnvironmentPtr env)
}
return result;
}
else if (is<List>(ast_raw_ptr)) {
auto result = makePtr<List>();
auto nodes = std::static_pointer_cast<List>(ast)->nodes();
else if (is<Collection>(ast_raw_ptr)) {
std::shared_ptr<Collection> result = nullptr;
(is<List>(ast_raw_ptr)) ? result = makePtr<List>() : result = makePtr<Vector>();
auto nodes = std::static_pointer_cast<Collection>(ast)->nodes();
for (auto node : nodes) {
ASTNodePtr eval_node = evalImpl(node, env);
if (eval_node == nullptr) {
return nullptr;
}
result->add(eval_node);
}
return result;
}
else if (is<Vector>(ast_raw_ptr)) {
auto result = makePtr<Vector>();
auto nodes = std::static_pointer_cast<Vector>(ast)->nodes();
for (auto node : nodes) {
ASTNodePtr eval_node = evalImpl(node, env);
m_ast_stack.push(node);
m_env_stack.push(env);
ASTNodePtr eval_node = evalImpl();
if (eval_node == nullptr) {
return nullptr;
}
@@ -115,7 +151,9 @@ ASTNodePtr Eval::evalAst(ASTNodePtr ast, EnvironmentPtr env)
auto result = makePtr<HashMap>();
auto elements = std::static_pointer_cast<HashMap>(ast)->elements();
for (auto& element : elements) {
ASTNodePtr element_node = evalImpl(element.second, env);
m_ast_stack.push(element.second);
m_env_stack.push(env);
ASTNodePtr element_node = evalImpl();
if (element_node == nullptr) {
return nullptr;
}
@@ -144,7 +182,9 @@ ASTNodePtr Eval::evalDef(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
}
std::string symbol = std::static_pointer_cast<Symbol>(first_argument)->symbol();
ASTNodePtr value = evalImpl(second_argument, env);
m_ast_stack.push(second_argument);
m_env_stack.push(env);
ASTNodePtr value = evalImpl();
// Dont overwrite symbols after an error
if (Error::the().hasAnyError()) {
@@ -155,11 +195,11 @@ ASTNodePtr Eval::evalDef(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
return env->set(symbol, value);
}
ASTNodePtr Eval::evalLet(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
void Eval::evalLet(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
{
if (nodes.size() != 2) {
Error::the().add(format("wrong number of arguments: let*, {}", nodes.size()));
return nullptr;
return;
}
auto first_argument = *nodes.begin();
@@ -168,7 +208,7 @@ ASTNodePtr Eval::evalLet(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
// First argument needs to be a List or Vector
if (!is<Collection>(first_argument.get())) {
Error::the().add(format("wrong argument type: collection, '{}'", first_argument));
return nullptr;
return;
}
// Get the nodes out of the List or Vector
@@ -180,7 +220,7 @@ ASTNodePtr Eval::evalLet(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
size_t count = binding_nodes.size();
if (count % 2 != 0) {
Error::the().add(format("wrong number of arguments: {}, {}", "let* bindings", count));
return nullptr;
return;
}
// Create new environment
@@ -190,58 +230,71 @@ ASTNodePtr Eval::evalLet(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
// First element needs to be a Symbol
if (!is<Symbol>(*it->get())) {
Error::the().add(format("wrong argument type: symbol, '{}'", *it));
return nullptr;
return;
}
std::string key = std::static_pointer_cast<Symbol>(*it)->symbol();
ASTNodePtr value = evalImpl(*std::next(it), let_env);
m_ast_stack.push(*std::next(it));
m_env_stack.push(let_env);
ASTNodePtr value = evalImpl();
let_env->set(key, value);
}
// TODO: Remove limitation of 3 arguments
// Eval all values in this new env, return last sexp of the result
return evalImpl(second_argument, let_env);
// Eval all values in this new env, return last sexp of the result
m_ast_stack.push(second_argument);
m_env_stack.push(let_env);
return; // TCO
}
ASTNodePtr Eval::evalDo(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
void Eval::evalDo(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
{
if (nodes.size() == 0) {
Error::the().add(format("wrong number of arguments: do, {}", nodes.size()));
return nullptr;
return;
}
// Evaluate all nodes except the last
for (auto it = nodes.begin(); it != std::prev(nodes.end(), 1); ++it) {
evalImpl(*it, env);
m_ast_stack.push(*it);
m_env_stack.push(env);
evalImpl();
}
// Eval and return last node
return evalImpl(nodes.back(), env);
// Eval last node
m_ast_stack.push(nodes.back());
m_env_stack.push(env);
return; // TCO
}
ASTNodePtr Eval::evalIf(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
void Eval::evalIf(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
{
if (nodes.size() != 2 && nodes.size() != 3) {
Error::the().add(format("wrong number of arguments: if, {}", nodes.size()));
return nullptr;
return;
}
auto first_argument = *nodes.begin();
auto second_argument = *std::next(nodes.begin());
auto third_argument = (nodes.size() == 3) ? *std::next(std::next(nodes.begin())) : makePtr<Value>(Value::Nil);
auto first_evaluated = evalImpl(first_argument, env);
m_ast_stack.push(first_argument);
m_env_stack.push(env);
auto first_evaluated = evalImpl();
if (!is<Value>(first_evaluated.get())
|| std::static_pointer_cast<Value>(first_evaluated)->state() == Value::True) {
return evalImpl(second_argument, env);
}
else {
return evalImpl(third_argument, env);
m_ast_stack.push(second_argument);
m_env_stack.push(env);
return; // TCO
}
m_ast_stack.push(third_argument);
m_env_stack.push(env);
return; // TCO
}
#define ARG_COUNT_CHECK(name, size, comparison) \
if (size comparison) { \
#define ARG_COUNT_CHECK(name, comparison, size) \
if (comparison) { \
Error::the().add(format("wrong number of arguments: {}, {}", name, size)); \
return nullptr; \
}
@@ -258,7 +311,7 @@ ASTNodePtr Eval::evalIf(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
ASTNodePtr Eval::evalFn(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env)
{
ARG_COUNT_CHECK("fn*", nodes.size(), != 2);
ARG_COUNT_CHECK("fn*", nodes.size() != 2, nodes.size());
auto first_argument = *nodes.begin();
auto second_argument = *std::next(nodes.begin());
@@ -284,32 +337,17 @@ ASTNodePtr Eval::apply(std::shared_ptr<List> evaluated_list)
auto nodes = evaluated_list->nodes();
if (!is<Function>(nodes.front().get()) && !is<Lambda>(nodes.front().get())) {
if (!is<Function>(nodes.front().get())) {
Error::the().add(format("invalid function: {}", nodes.front()));
return nullptr;
}
// Function
if (is<Function>(nodes.front().get())) {
// car
auto function = std::static_pointer_cast<Function>(nodes.front())->function();
// cdr
nodes.pop_front();
return function(nodes);
}
// Lambda
// car
auto lambda = std::static_pointer_cast<Lambda>(nodes.front());
auto function = std::static_pointer_cast<Function>(nodes.front())->function();
// cdr
nodes.pop_front();
auto lambda_env = Environment::create(lambda, nodes);
return evalImpl(lambda->body(), lambda_env);
return function(nodes);
}
} // namespace blaze
+8 -4
View File
@@ -7,6 +7,7 @@
#pragma once
#include <list>
#include <stack>
#include "environment.h"
#include "forward.h"
@@ -25,17 +26,20 @@ public:
ASTNodePtr ast() const { return m_ast; }
private:
ASTNodePtr evalImpl(ASTNodePtr ast, EnvironmentPtr env);
ASTNodePtr evalImpl();
ASTNodePtr evalAst(ASTNodePtr ast, EnvironmentPtr env);
ASTNodePtr evalDef(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env);
ASTNodePtr evalLet(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env);
ASTNodePtr evalDo(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env);
ASTNodePtr evalIf(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env);
void evalLet(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env);
void evalDo(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env);
void evalIf(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env);
ASTNodePtr evalFn(const std::list<ASTNodePtr>& nodes, EnvironmentPtr env);
ASTNodePtr apply(std::shared_ptr<List> evaluated_list);
ASTNodePtr m_ast;
EnvironmentPtr m_env;
std::stack<ASTNodePtr> m_ast_stack;
std::stack<EnvironmentPtr> m_env_stack;
};
} // namespace blaze
+1 -1
View File
@@ -17,7 +17,7 @@
#include "readline.h"
#include "settings.h"
#if 1
#if 0
static blaze::EnvironmentPtr s_outer_env = blaze::Environment::create();
static auto cleanup(int signal) -> void;
+136
View File
@@ -0,0 +1,136 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <csignal> // std::signal
#include <cstdlib> // std::exit
#include <string>
#include <string_view>
#include "ruc/argparser.h"
#include "ruc/format/color.h"
#include "ast.h"
#include "environment.h"
#include "error.h"
#include "eval.h"
#include "forward.h"
#include "lexer.h"
#include "printer.h"
#include "reader.h"
#include "readline.h"
#include "settings.h"
#if 1
static blaze::EnvironmentPtr s_outer_env = blaze::Environment::create();
static auto cleanup(int signal) -> void;
static auto installLambdas(blaze::EnvironmentPtr env) -> void;
static auto rep(std::string_view input, blaze::EnvironmentPtr env) -> std::string;
static auto read(std::string_view input) -> blaze::ASTNodePtr;
static auto eval(blaze::ASTNodePtr ast, blaze::EnvironmentPtr env) -> blaze::ASTNodePtr;
static auto print(blaze::ASTNodePtr exp) -> std::string;
auto main(int argc, char* argv[]) -> int
{
bool dump_lexer = false;
bool dump_reader = false;
bool pretty_print = false;
std::string_view history_path = "~/.mal-history";
// CLI arguments
ruc::ArgParser arg_parser;
arg_parser.addOption(dump_lexer, 'l', "dump-lexer", nullptr, nullptr);
arg_parser.addOption(dump_reader, 'r', "dump-reader", nullptr, nullptr);
arg_parser.addOption(pretty_print, 'c', "color", nullptr, nullptr);
arg_parser.addOption(history_path, 'h', "history", nullptr, nullptr, nullptr, ruc::ArgParser::Required::Yes);
arg_parser.parse(argc, argv);
// Set settings
blaze::Settings::the().set("dump-lexer", dump_lexer ? "1" : "0");
blaze::Settings::the().set("dump-reader", dump_reader ? "1" : "0");
blaze::Settings::the().set("pretty-print", pretty_print ? "1" : "0");
// Signal callbacks
std::signal(SIGINT, cleanup);
std::signal(SIGTERM, cleanup);
installFunctions(s_outer_env);
installLambdas(s_outer_env);
blaze::Readline readline(pretty_print, history_path);
std::string input;
while (readline.get(input)) {
if (pretty_print) {
print("\033[0m");
}
print("{}\n", rep(input, s_outer_env));
}
if (pretty_print) {
print("\033[0m");
}
return 0;
}
static auto cleanup(int signal) -> void
{
print("\033[0m\n");
std::exit(signal);
}
static std::string_view lambdaTable[] = {
"(def! not (fn* (cond) (if cond false true)))",
};
static auto installLambdas(blaze::EnvironmentPtr env) -> void
{
for (auto function : lambdaTable) {
rep(function, env);
}
}
static auto rep(std::string_view input, blaze::EnvironmentPtr env) -> std::string
{
blaze::Error::the().clearErrors();
blaze::Error::the().setInput(input);
return print(eval(read(input), env));
}
static auto read(std::string_view input) -> blaze::ASTNodePtr
{
blaze::Lexer lexer(input);
lexer.tokenize();
if (blaze::Settings::the().get("dump-lexer") == "1") {
lexer.dump();
}
blaze::Reader reader(std::move(lexer.tokens()));
reader.read();
if (blaze::Settings::the().get("dump-reader") == "1") {
reader.dump();
}
return reader.node();
}
static auto eval(blaze::ASTNodePtr ast, blaze::EnvironmentPtr env) -> blaze::ASTNodePtr
{
blaze::Eval eval(ast, env);
eval.eval();
return eval.ast();
}
static auto print(blaze::ASTNodePtr exp) -> std::string
{
blaze::Printer printer;
return printer.print(exp, true);
}
#endif