Browse Source

Everywhere: Start implementation of step2

master
Riyyi 2 years ago
parent
commit
da0b0a91a6
  1. 4
      CMakeLists.txt
  2. 7
      src/ast.cpp
  3. 23
      src/ast.h
  4. 148
      src/environment.h
  5. 90
      src/eval.cpp
  6. 32
      src/eval.h
  7. 2
      src/step1_read_print.cpp
  8. 113
      src/step2_eval.cpp

4
CMakeLists.txt

@ -87,3 +87,7 @@ add_dependencies(test0 ${PROJECT})
add_custom_target(test1
COMMAND env STEP=step1_read_print MAL_IMPL=js ../vendor/mal/runtest.py --deferrable --optional ../vendor/mal/tests/step1_read_print.mal -- ./${PROJECT})
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})

7
src/ast.cpp

@ -59,6 +59,13 @@ Value::Value(const std::string& value)
{
}
// -----------------------------------------
Function::Function(Lambda lambda)
: m_lambda(lambda)
{
}
} // namespace blaze
// -----------------------------------------

23
src/ast.h

@ -7,6 +7,8 @@
#pragma once
#include <cstdint> // int64_t
#include <functional> // std::function
#include <span>
#include <string>
#include <string_view>
#include <typeinfo> // typeid
@ -51,6 +53,8 @@ public:
void addNode(ASTNode* node);
const std::vector<ASTNode*>& nodes() const { return m_nodes; }
size_t size() const { return m_nodes.size(); }
bool empty() const { return m_nodes.size() == 0; }
protected:
Collection() {}
@ -173,6 +177,22 @@ public:
private:
std::string m_value;
};
// -----------------------------------------
using Lambda = std::function<ASTNode*(std::span<ASTNode*>)>;
class Function final : public ASTNode {
public:
Function(Lambda lambda);
virtual ~Function() = default;
virtual bool isFunction() const override { return true; }
Lambda lambda() const { return m_lambda; }
private:
Lambda m_lambda;
};
// -----------------------------------------
@ -204,6 +224,9 @@ inline bool ASTNode::fastIs<Symbol>() const { return isSymbol(); }
template<>
inline bool ASTNode::fastIs<Value>() const { return isValue(); }
template<>
inline bool ASTNode::fastIs<Function>() const { return isFunction(); }
// clang-format on
} // namespace blaze

148
src/environment.h

@ -0,0 +1,148 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint> // int64_t
#include <iostream>
#include <span>
#include <string_view>
#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 {
public:
Environment() = default;
virtual ~Environment() = default;
ASTNode* lookup(const std::string& symbol)
{
m_current_key = symbol;
return m_values.find(symbol) != m_values.end() ? m_values[symbol] : nullptr;
}
protected:
std::string m_current_key;
std::unordered_map<std::string, ASTNode*> m_values;
};
class GlobalEnvironment final : public Environment {
public:
GlobalEnvironment()
{
auto add = [](std::span<ASTNode*> nodes) -> ASTNode* {
int64_t result = 0;
for (auto node : nodes) {
if (!is<Number>(node)) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
result += static_cast<Number*>(node)->number();
}
return new Number(result);
};
auto sub = [](std::span<ASTNode*> nodes) -> ASTNode* {
int64_t result = 0;
if (nodes.size() == 0) {
return new Number(0);
}
for (auto node : nodes) {
if (!is<Number>(node)) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
}
// Start with the first number
result += static_cast<Number*>(nodes[0])->number();
// Skip the first node
for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) {
result -= static_cast<Number*>(*it)->number();
}
return new Number(result);
};
auto mul = [](std::span<ASTNode*> nodes) -> ASTNode* {
int64_t result = 1;
for (auto node : nodes) {
if (!is<Number>(node)) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
result *= static_cast<Number*>(node)->number();
}
return new Number(result);
};
auto div = [this](std::span<ASTNode*> nodes) -> ASTNode* {
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)) {
Error::the().addError(format("wrong type argument: number-or-marker-p, '{}'", node));
return nullptr;
}
}
// Start with the first number
result += static_cast<Number*>(nodes[0])->number();
// Skip the first node
for (auto it = std::next(nodes.begin()); it != nodes.end(); ++it) {
result /= static_cast<Number*>(*it)->number();
}
return new Number((int64_t)result);
};
m_values.emplace("+", new Function(add));
m_values.emplace("-", new Function(sub));
m_values.emplace("*", new Function(mul));
m_values.emplace("/", new Function(div));
}
virtual ~GlobalEnvironment() = default;
};
} // 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

90
src/eval.cpp

@ -0,0 +1,90 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <span> // std::span
#include "ast.h"
#include "environment.h"
#include "eval.h"
#include "ruc/meta/assert.h"
#include "types.h"
namespace blaze {
Eval::Eval(ASTNode* ast, Environment* env)
: m_ast(ast)
, m_env(env)
{
}
void Eval::eval()
{
m_ast = evalImpl(m_ast, m_env);
}
ASTNode* Eval::evalImpl(ASTNode* ast, Environment* env)
{
if (!is<List>(ast)) {
return evalAst(ast, env);
}
if (static_cast<List*>(ast)->empty()) {
return ast;
}
return apply(static_cast<List*>(evalAst(ast, env)));
}
ASTNode* Eval::evalAst(ASTNode* ast, Environment* env)
{
if (is<Symbol>(ast)) {
auto result = env->lookup(static_cast<Symbol*>(ast)->symbol());
if (!result) {
Error::the().addError(format("'{}' not found", ast));
}
return result;
}
else if (is<List>(ast)) {
auto result = new List();
auto nodes = static_cast<List*>(ast)->nodes();
for (auto node : nodes) {
result->addNode(evalImpl(node, env));
}
return result;
}
else if (is<Vector>(ast)) {
auto result = new Vector();
auto nodes = static_cast<Vector*>(ast)->nodes();
for (auto node : nodes) {
result->addNode(evalImpl(node, env));
}
return result;
}
else if (is<HashMap>(ast)) {
// TODO
VERIFY_NOT_REACHED();
return nullptr;
}
return ast;
}
ASTNode* Eval::apply(List* evaluated_list)
{
auto nodes = evaluated_list->nodes();
if (!is<Function>(nodes[0])) {
return nullptr;
}
// car
auto lambda = static_cast<Function*>(nodes[0])->lambda();
// cdr
std::span<ASTNode*> span { nodes.data() + 1, nodes.size() - 1 };
return lambda(span);
}
} // namespace blaze

32
src/eval.h

@ -0,0 +1,32 @@
/*
* Copyright (C) 2023 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "ast.h"
#include "environment.h"
namespace blaze {
class Eval {
public:
Eval(ASTNode* ast, Environment* env);
virtual ~Eval() = default;
void eval();
ASTNode* ast() const { return m_ast; }
private:
ASTNode* evalImpl(ASTNode* ast, Environment* env);
ASTNode* evalAst(ASTNode* ast, Environment* env);
ASTNode* apply(List* evaluated_list);
ASTNode* m_ast { nullptr };
Environment* m_env { nullptr };
};
} // namespace blaze

2
src/step1_read_print.cpp

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

113
src/step2_eval.cpp

@ -0,0 +1,113 @@
#include <csignal> // std::signal
#include <cstdlib> // std::exit
#include <iostream> // std::cin
#include <string> // std::getline
#include <string_view>
#include "environment.h"
#include "eval.h"
#include "ruc/argparser.h"
#include "ruc/format/color.h"
#include "ast.h"
#include "error.h"
#include "lexer.h"
#include "printer.h"
#include "reader.h"
#include "settings.h"
#if 1
auto read(std::string_view input) -> blaze::ASTNode*
{
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::ASTNode* ast) -> blaze::ASTNode*
{
blaze::GlobalEnvironment env;
blaze::Eval eval(ast, &env);
eval.eval();
return eval.ast();
}
auto print(blaze::ASTNode* exp) -> void
{
blaze::Printer printer(exp);
printer.dump();
}
auto rep(std::string_view input) -> void
{
blaze::Error::the().clearErrors();
blaze::Error::the().setInput(input);
print(eval(read(input)));
}
static auto cleanup(int signal) -> void
{
print("\033[0m");
std::exit(signal);
}
auto main(int argc, char* argv[]) -> int
{
bool dump_lexer = false;
bool dump_reader = false;
bool pretty_print = false;
// 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.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);
while (true) {
if (!pretty_print) {
print("user> ");
}
else {
print(fg(ruc::format::TerminalColor::Blue), "user>");
print(" \033[1m");
}
std::string line;
std::getline(std::cin, line);
if (pretty_print) {
print("\033[0m");
}
// Exit with Ctrl-D
if (std::cin.eof() || std::cin.fail()) {
break;
}
rep(line);
}
return 0;
}
#endif
// - Add AST node printing support to ruc::format
Loading…
Cancel
Save