Browse Source

Everywhere: Initial implementation of step3

master
Riyyi 2 years ago
parent
commit
58584f5bba
  1. 4
      CMakeLists.txt
  2. 1
      src/ast.cpp
  3. 62
      src/environment.cpp
  4. 137
      src/environment.h
  5. 109
      src/eval.cpp
  6. 14
      src/eval.h
  7. 119
      src/functions.cpp
  8. 2
      src/step2_eval.cpp
  9. 108
      src/step3_env.cpp

4
CMakeLists.txt

@ -91,3 +91,7 @@ add_dependencies(test1 ${PROJECT})
add_custom_target(test2
COMMAND env STEP=step_eval MAL_IMPL=js ../vendor/mal/runtest.py --deferrable --optional ../vendor/mal/tests/step2_eval.mal -- ./${PROJECT})
add_dependencies(test2 ${PROJECT})
add_custom_target(test3
COMMAND env STEP=step_env MAL_IMPL=js ../vendor/mal/runtest.py --deferrable --optional ../vendor/mal/tests/step3_env.mal -- ./${PROJECT})
add_dependencies(test3 ${PROJECT})

1
src/ast.cpp

@ -74,6 +74,7 @@ Function::Function(Lambda lambda)
void Formatter<blaze::ASTNodePtr>::format(Builder& builder, blaze::ASTNodePtr value) const
{
// printf("ASDJASJKDASJKDNAJK\n");
blaze::Printer printer;
return Formatter<std::string>::format(builder, printer.printNoErrorCheck(value));
}

62
src/environment.cpp

@ -0,0 +1,62 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include "ruc/format/print.h"
#include "ast.h"
#include "environment.h"
namespace blaze {
Environment::Environment(EnvironmentPtr outer)
: m_outer(outer)
{
}
// -----------------------------------------
bool Environment::exists(const std::string& symbol)
{
return m_values.find(symbol) != m_values.end();
}
ASTNodePtr Environment::set(const std::string& symbol, ASTNodePtr value)
{
if (exists(symbol)) {
m_values.erase(symbol);
}
m_values.emplace(symbol, value);
return value;
}
ASTNodePtr Environment::get(const std::string& symbol)
{
m_current_key = symbol;
if (exists(symbol)) {
return m_values[symbol];
}
if (m_outer) {
return m_outer->get(symbol);
}
return nullptr;
}
// -----------------------------------------
GlobalEnvironment::GlobalEnvironment()
{
add();
sub();
mul();
div();
}
} // namespace blaze

137
src/environment.h

@ -6,146 +6,43 @@
#pragma once
#include <cstdint> // int64_t
#include <iostream>
#include <memory>
#include <span>
#include <string_view>
#include <string>
#include <unordered_map>
#include <vector>
#include "error.h"
#include "ruc/format/color.h"
#include "ruc/singleton.h"
#include "ast.h"
#include "types.h"
namespace blaze {
class Environment;
typedef std::shared_ptr<Environment> EnvironmentPtr;
class Environment {
public:
Environment() = default;
Environment(EnvironmentPtr outer);
virtual ~Environment() = default;
ASTNodePtr lookup(const std::string& symbol)
{
m_current_key = symbol;
return m_values.find(symbol) != m_values.end() ? m_values[symbol] : nullptr;
}
bool exists(const std::string& symbol);
ASTNodePtr set(const std::string& symbol, ASTNodePtr value);
ASTNodePtr get(const std::string& symbol);
protected:
std::string m_current_key;
std::unordered_map<std::string, ASTNodePtr> m_values;
EnvironmentPtr m_outer { nullptr };
};
class GlobalEnvironment final : public Environment {
public:
GlobalEnvironment()
{
// TODO: Add more native functions
// TODO: Move the functions to their own file
auto add = [](std::span<ASTNodePtr> nodes) -> ASTNodePtr {
int64_t result = 0;
for (auto node : nodes) {
if (!is<Number>(node.get())) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
result += static_pointer_cast<Number>(node)->number();
}
return makePtr<Number>(result);
};
auto sub = [](std::span<ASTNodePtr> nodes) -> ASTNodePtr {
int64_t result = 0;
if (nodes.size() == 0) {
return makePtr<Number>(0);
}
for (auto node : nodes) {
if (!is<Number>(node.get())) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
}
// Start with the first number
result += static_pointer_cast<Number>(nodes[0])->number();
// Skip the first node
for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) {
result -= static_pointer_cast<Number>(*it)->number();
}
return makePtr<Number>(result);
};
auto mul = [](std::span<ASTNodePtr> nodes) -> ASTNodePtr {
int64_t result = 1;
for (auto node : nodes) {
if (!is<Number>(node.get())) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
result *= static_pointer_cast<Number>(node)->number();
}
return makePtr<Number>(result);
};
auto div = [this](std::span<ASTNodePtr> nodes) -> ASTNodePtr {
double result = 0;
if (nodes.size() == 0) {
Error::the().addError(format("wrong number of arguments: {}, 0", m_current_key));
return nullptr;
}
for (auto node : nodes) {
if (!is<Number>(node.get())) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
}
// Start with the first number
result += static_pointer_cast<Number>(nodes[0])->number();
// Skip the first node
for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) {
result /= static_pointer_cast<Number>(*it)->number();
}
return makePtr<Number>((int64_t)result);
};
m_values.emplace("+", makePtr<Function>(add));
m_values.emplace("-", makePtr<Function>(sub));
m_values.emplace("*", makePtr<Function>(mul));
m_values.emplace("/", makePtr<Function>(div));
}
GlobalEnvironment();
virtual ~GlobalEnvironment() = default;
private:
// TODO: Add more native functions
void add();
void sub();
void mul();
void div();
};
} // namespace blaze
// associative data structure that maps symbols (the keys) to values
// values = anything, including other symbols.
// an environment is like a hash table
// value can map to:
// list
// vector
// hash-map
// symbol
// number
// string
// function

109
src/eval.cpp

@ -4,17 +4,20 @@
* SPDX-License-Identifier: MIT
*/
#include <span> // std::span
#include <memory> // std::static_pointer_cast
#include <span> // std::span
#include <string>
#include "ast.h"
#include "environment.h"
#include "error.h"
#include "eval.h"
#include "ruc/meta/assert.h"
#include "types.h"
namespace blaze {
Eval::Eval(ASTNodePtr ast, Environment* env)
Eval::Eval(ASTNodePtr ast, EnvironmentPtr env)
: m_ast(ast)
, m_env(env)
{
@ -25,37 +28,46 @@ void Eval::eval()
m_ast = evalImpl(m_ast, m_env);
}
ASTNodePtr Eval::evalImpl(ASTNodePtr ast, Environment* env)
ASTNodePtr Eval::evalImpl(ASTNodePtr ast, EnvironmentPtr env)
{
if (!is<List>(ast.get())) {
return evalAst(ast, env);
}
if (static_cast<List*>(ast.get())->empty()) {
auto list = std::static_pointer_cast<List>(ast);
if (list->empty()) {
return ast;
}
return apply(static_pointer_cast<List>(evalAst(ast, env)));
// Environment
auto nodes = list->nodes();
if (is<Symbol>(nodes[0].get())) {
auto symbol = std::static_pointer_cast<Symbol>(nodes[0])->symbol();
if (symbol == "def!") {
return evalDef(nodes, env);
}
if (symbol == "let*") {
return evalLet(nodes, env);
}
}
return apply(std::static_pointer_cast<List>(evalAst(ast, env)));
}
ASTNodePtr Eval::evalAst(ASTNodePtr ast, Environment* env)
ASTNodePtr Eval::evalAst(ASTNodePtr ast, EnvironmentPtr env)
{
ASTNode* ast_raw_ptr = ast.get();
if (is<Symbol>(ast_raw_ptr)) {
auto result = env->lookup(static_pointer_cast<Symbol>(ast)->symbol());
auto result = env->get(std::static_pointer_cast<Symbol>(ast)->symbol());
if (!result) {
// TODO: Maybe add backlink to parent nodes?
if (is<List>(m_ast)) {
Error::the().addError(format("symbol's function definition is void: {}", ast_raw_ptr));
}
else {
Error::the().addError(format("symbol's value as variable is void: {}", ast_raw_ptr));
}
Error::the().addError(format("'{}' not found", ast));
}
return result;
}
else if (is<List>(ast_raw_ptr)) {
auto result = makePtr<List>();
auto nodes = static_pointer_cast<List>(ast)->nodes();
auto nodes = std::static_pointer_cast<List>(ast)->nodes();
for (auto node : nodes) {
result->addNode(evalImpl(node, env));
}
@ -63,7 +75,7 @@ ASTNodePtr Eval::evalAst(ASTNodePtr ast, Environment* env)
}
else if (is<Vector>(ast_raw_ptr)) {
auto result = makePtr<Vector>();
auto nodes = static_pointer_cast<Vector>(ast)->nodes();
auto nodes = std::static_pointer_cast<Vector>(ast)->nodes();
for (auto node : nodes) {
result->addNode(evalImpl(node, env));
}
@ -71,7 +83,7 @@ ASTNodePtr Eval::evalAst(ASTNodePtr ast, Environment* env)
}
else if (is<HashMap>(ast_raw_ptr)) {
auto result = makePtr<HashMap>();
auto elements = static_pointer_cast<HashMap>(ast)->elements();
auto elements = std::static_pointer_cast<HashMap>(ast)->elements();
for (auto& element : elements) {
result->addElement(element.first, evalImpl(element.second, env));
}
@ -81,6 +93,67 @@ ASTNodePtr Eval::evalAst(ASTNodePtr ast, Environment* env)
return ast;
}
ASTNodePtr Eval::evalDef(const std::vector<ASTNodePtr>& nodes, EnvironmentPtr env)
{
if (nodes.size() != 3) {
Error::the().addError(format("wrong number of arguments: def!, {}", nodes.size() - 1));
return nullptr;
}
// First element needs to be a Symbol
if (!is<Symbol>(nodes[1].get())) {
Error::the().addError(format("wrong type argument: symbol, {}", nodes[1]));
return nullptr;
}
std::string symbol = std::static_pointer_cast<Symbol>(nodes[1])->symbol();
// Modify existing environment
return env->set(symbol, evalImpl(nodes[2], env));
}
ASTNodePtr Eval::evalLet(const std::vector<ASTNodePtr>& nodes, EnvironmentPtr env)
{
if (nodes.size() != 3) {
Error::the().addError(format("wrong number of arguments: let*, {}", nodes.size() - 1));
return nullptr;
}
// Create new environment
auto let_env = makePtr<Environment>(env);
// First argument needs to be a List
if (!is<List>(nodes[1].get())) {
Error::the().addError(format("wrong type argument: list, {}", nodes[1]));
return nullptr;
}
// List needs to have an even number of elements
auto bindings = std::static_pointer_cast<List>(nodes[1]);
auto binding_nodes = bindings->nodes();
if (bindings->nodes().size() % 2 != 0) {
// FIXME: Print correct value
Error::the().addError("FIXME");
// Error::the().addError(format("wrong number of arguments: {}, {}", bindings, bindings->nodes().size()));
}
size_t count = binding_nodes.size();
for (size_t i = 0; i < count; i += 2) {
// First element needs to be a Symbol
if (!is<Symbol>(binding_nodes[i].get())) {
Error::the().addError(format("wrong type argument: symbol, {}", binding_nodes[i]));
}
std::string key = std::static_pointer_cast<Symbol>(binding_nodes[i])->symbol();
ASTNodePtr value = evalAst(binding_nodes[i + 1], let_env);
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(nodes[2], let_env);
}
ASTNodePtr Eval::apply(std::shared_ptr<List> evaluated_list)
{
auto nodes = evaluated_list->nodes();
@ -91,7 +164,7 @@ ASTNodePtr Eval::apply(std::shared_ptr<List> evaluated_list)
}
// car
auto lambda = static_pointer_cast<Function>(nodes[0])->lambda();
auto lambda = std::static_pointer_cast<Function>(nodes[0])->lambda();
// cdr
std::span<ASTNodePtr> span { nodes.data() + 1, nodes.size() - 1 };

14
src/eval.h

@ -6,6 +6,8 @@
#pragma once
#include <vector>
#include "ast.h"
#include "environment.h"
@ -13,7 +15,7 @@ namespace blaze {
class Eval {
public:
Eval(ASTNodePtr ast, Environment* env);
Eval(ASTNodePtr ast, EnvironmentPtr env);
virtual ~Eval() = default;
void eval();
@ -21,12 +23,14 @@ public:
ASTNodePtr ast() const { return m_ast; }
private:
ASTNodePtr evalImpl(ASTNodePtr ast, Environment* env);
ASTNodePtr evalAst(ASTNodePtr ast, Environment* env);
ASTNodePtr evalImpl(ASTNodePtr ast, EnvironmentPtr env);
ASTNodePtr evalAst(ASTNodePtr ast, EnvironmentPtr env);
ASTNodePtr evalDef(const std::vector<ASTNodePtr>& nodes, EnvironmentPtr env);
ASTNodePtr evalLet(const std::vector<ASTNodePtr>& nodes, EnvironmentPtr env);
ASTNodePtr apply(std::shared_ptr<List> evaluated_list);
ASTNodePtr m_ast { nullptr };
Environment* m_env { nullptr };
ASTNodePtr m_ast;
EnvironmentPtr m_env;
};
} // namespace blaze

119
src/functions.cpp

@ -0,0 +1,119 @@
/*
* Copyright (C) 2023 Riyi
*
* SPDX-License-Identifier: MIT
*/
#include <memory> // std::static_pointer_cast
#include "ruc/format/format.h"
#include "ast.h"
#include "environment.h"
#include "error.h"
#include "types.h"
namespace blaze {
void GlobalEnvironment::add()
{
auto add = [](std::span<ASTNodePtr> nodes) -> ASTNodePtr {
int64_t result = 0;
for (auto node : nodes) {
if (!is<Number>(node.get())) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
result += std::static_pointer_cast<Number>(node)->number();
}
return makePtr<Number>(result);
};
m_values.emplace("+", makePtr<Function>(add));
}
void GlobalEnvironment::sub()
{
auto sub = [](std::span<ASTNodePtr> nodes) -> ASTNodePtr {
int64_t result = 0;
if (nodes.size() == 0) {
return makePtr<Number>(0);
}
for (auto node : nodes) {
if (!is<Number>(node.get())) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
}
// Start with the first number
result += std::static_pointer_cast<Number>(nodes[0])->number();
// Skip the first node
for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) {
result -= std::static_pointer_cast<Number>(*it)->number();
}
return makePtr<Number>(result);
};
m_values.emplace("-", makePtr<Function>(sub));
}
void GlobalEnvironment::mul()
{
auto mul = [](std::span<ASTNodePtr> nodes) -> ASTNodePtr {
int64_t result = 1;
for (auto node : nodes) {
if (!is<Number>(node.get())) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
result *= std::static_pointer_cast<Number>(node)->number();
}
return makePtr<Number>(result);
};
m_values.emplace("*", makePtr<Function>(mul));
}
void GlobalEnvironment::div()
{
auto div = [this](std::span<ASTNodePtr> nodes) -> ASTNodePtr {
double result = 0;
if (nodes.size() == 0) {
Error::the().addError(format("wrong number of arguments: {}, 0", m_current_key));
return nullptr;
}
for (auto node : nodes) {
if (!is<Number>(node.get())) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
}
// Start with the first number
result += std::static_pointer_cast<Number>(nodes[0])->number();
// Skip the first node
for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) {
result /= std::static_pointer_cast<Number>(*it)->number();
}
return makePtr<Number>((int64_t)result);
};
m_values.emplace("/", makePtr<Function>(div));
}
} // namespace blaze

2
src/step2_eval.cpp

@ -15,7 +15,7 @@
#include "readline.h"
#include "settings.h"
#if 1
#if 0
auto read(std::string_view input) -> blaze::ASTNodePtr
{
blaze::Lexer lexer(input);

108
src/step3_env.cpp

@ -0,0 +1,108 @@
#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 "lexer.h"
#include "printer.h"
#include "reader.h"
#include "readline.h"
#include "settings.h"
#if 1
static blaze::EnvironmentPtr env = blaze::makePtr<blaze::GlobalEnvironment>();
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();
}
auto eval(blaze::ASTNodePtr ast) -> blaze::ASTNodePtr
{
blaze::Eval eval(ast, env);
eval.eval();
return eval.ast();
}
auto print(blaze::ASTNodePtr exp) -> std::string
{
blaze::Printer printer;
return printer.print(exp);
}
auto rep(std::string_view input) -> std::string
{
blaze::Error::the().clearErrors();
blaze::Error::the().setInput(input);
return print(eval(read(input)));
}
static auto cleanup(int signal) -> void
{
print("\033[0m\n");
std::exit(signal);
}
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);
blaze::Readline readline(pretty_print, history_path);
std::string input;
while (readline.get(input)) {
if (pretty_print) {
print("\033[0m");
}
print("{}\n", rep(input));
}
if (pretty_print) {
print("\033[0m");
}
return 0;
}
#endif
Loading…
Cancel
Save