Riyyi
2 years ago
8 changed files with 419 additions and 2 deletions
@ -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
|
@ -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
|
@ -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…
Reference in new issue