Everywhere: Start implementation of step2
This commit is contained in:
@@ -87,3 +87,7 @@ add_dependencies(test0 ${PROJECT})
|
|||||||
add_custom_target(test1
|
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})
|
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_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})
|
||||||
|
|||||||
@@ -59,6 +59,13 @@ Value::Value(const std::string& value)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
Function::Function(Lambda lambda)
|
||||||
|
: m_lambda(lambda)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace blaze
|
} // namespace blaze
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint> // int64_t
|
#include <cstdint> // int64_t
|
||||||
|
#include <functional> // std::function
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <typeinfo> // typeid
|
#include <typeinfo> // typeid
|
||||||
@@ -51,6 +53,8 @@ public:
|
|||||||
void addNode(ASTNode* node);
|
void addNode(ASTNode* node);
|
||||||
|
|
||||||
const std::vector<ASTNode*>& nodes() const { return m_nodes; }
|
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:
|
protected:
|
||||||
Collection() {}
|
Collection() {}
|
||||||
@@ -173,6 +177,22 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::string m_value;
|
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<>
|
template<>
|
||||||
inline bool ASTNode::fastIs<Value>() const { return isValue(); }
|
inline bool ASTNode::fastIs<Value>() const { return isValue(); }
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline bool ASTNode::fastIs<Function>() const { return isFunction(); }
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
} // namespace blaze
|
} // namespace blaze
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
@@ -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
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#if 1
|
#if 0
|
||||||
auto read(std::string_view input) -> blaze::ASTNode*
|
auto read(std::string_view input) -> blaze::ASTNode*
|
||||||
{
|
{
|
||||||
blaze::Lexer lexer(input);
|
blaze::Lexer lexer(input);
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user