Compare commits
3
Commits
aba70beeb3
...
f132397e15
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f132397e15 | ||
|
|
58584f5bba | ||
|
|
f4e388716d |
@@ -91,3 +91,7 @@ add_dependencies(test1 ${PROJECT})
|
|||||||
add_custom_target(test2
|
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})
|
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_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})
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ Function::Function(Lambda lambda)
|
|||||||
|
|
||||||
void Formatter<blaze::ASTNodePtr>::format(Builder& builder, blaze::ASTNodePtr value) const
|
void Formatter<blaze::ASTNodePtr>::format(Builder& builder, blaze::ASTNodePtr value) const
|
||||||
{
|
{
|
||||||
|
// printf("ASDJASJKDASJKDNAJK\n");
|
||||||
blaze::Printer printer;
|
blaze::Printer printer;
|
||||||
return Formatter<std::string>::format(builder, printer.printNoErrorCheck(value));
|
return Formatter<std::string>::format(builder, printer.printNoErrorCheck(value));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
class ASTNode;
|
class ASTNode;
|
||||||
typedef std::shared_ptr<ASTNode> ASTNodePtr;
|
typedef std::shared_ptr<ASTNode> ASTNodePtr;
|
||||||
|
|
||||||
class ASTNode {
|
class ASTNode {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -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
|
||||||
+17
-120
@@ -6,146 +6,43 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint> // int64_t
|
#include <string>
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <span>
|
|
||||||
#include <string_view>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "error.h"
|
|
||||||
#include "ruc/format/color.h"
|
|
||||||
#include "ruc/singleton.h"
|
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
|
class Environment;
|
||||||
|
typedef std::shared_ptr<Environment> EnvironmentPtr;
|
||||||
|
|
||||||
class Environment {
|
class Environment {
|
||||||
public:
|
public:
|
||||||
Environment() = default;
|
Environment() = default;
|
||||||
|
Environment(EnvironmentPtr outer);
|
||||||
virtual ~Environment() = default;
|
virtual ~Environment() = default;
|
||||||
|
|
||||||
ASTNodePtr lookup(const std::string& symbol)
|
bool exists(const std::string& symbol);
|
||||||
{
|
ASTNodePtr set(const std::string& symbol, ASTNodePtr value);
|
||||||
m_current_key = symbol;
|
ASTNodePtr get(const std::string& symbol);
|
||||||
return m_values.find(symbol) != m_values.end() ? m_values[symbol] : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string m_current_key;
|
std::string m_current_key;
|
||||||
std::unordered_map<std::string, ASTNodePtr> m_values;
|
std::unordered_map<std::string, ASTNodePtr> m_values;
|
||||||
|
EnvironmentPtr m_outer { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
class GlobalEnvironment final : public Environment {
|
class GlobalEnvironment final : public Environment {
|
||||||
public:
|
public:
|
||||||
GlobalEnvironment()
|
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));
|
|
||||||
}
|
|
||||||
virtual ~GlobalEnvironment() = default;
|
virtual ~GlobalEnvironment() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// TODO: Add more native functions
|
||||||
|
void add();
|
||||||
|
void sub();
|
||||||
|
void mul();
|
||||||
|
void div();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace blaze
|
} // 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
|
|
||||||
|
|||||||
+136
-21
@@ -4,17 +4,21 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <span> // std::span
|
#include <memory> // std::static_pointer_cast
|
||||||
|
#include <span> // std::span
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
|
#include "error.h"
|
||||||
#include "eval.h"
|
#include "eval.h"
|
||||||
#include "ruc/meta/assert.h"
|
#include "ruc/meta/assert.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
Eval::Eval(ASTNodePtr ast, Environment* env)
|
Eval::Eval(ASTNodePtr ast, EnvironmentPtr env)
|
||||||
: m_ast(ast)
|
: m_ast(ast)
|
||||||
, m_env(env)
|
, m_env(env)
|
||||||
{
|
{
|
||||||
@@ -25,55 +29,86 @@ void Eval::eval()
|
|||||||
m_ast = evalImpl(m_ast, m_env);
|
m_ast = evalImpl(m_ast, m_env);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNodePtr Eval::evalImpl(ASTNodePtr ast, Environment* env)
|
ASTNodePtr Eval::evalImpl(ASTNodePtr ast, EnvironmentPtr env)
|
||||||
{
|
{
|
||||||
|
if (ast == nullptr || env == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (!is<List>(ast.get())) {
|
if (!is<List>(ast.get())) {
|
||||||
return evalAst(ast, env);
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function call
|
||||||
|
return apply(std::static_pointer_cast<List>(evalAst(ast, env)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNodePtr Eval::evalAst(ASTNodePtr ast, Environment* env)
|
ASTNodePtr Eval::evalAst(ASTNodePtr ast, EnvironmentPtr env)
|
||||||
{
|
{
|
||||||
|
if (ast == nullptr || env == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
ASTNode* ast_raw_ptr = ast.get();
|
ASTNode* ast_raw_ptr = ast.get();
|
||||||
if (is<Symbol>(ast_raw_ptr)) {
|
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) {
|
if (!result) {
|
||||||
// TODO: Maybe add backlink to parent nodes?
|
Error::the().addError(format("'{}' not found", ast));
|
||||||
if (is<List>(m_ast)) {
|
return nullptr;
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else if (is<List>(ast_raw_ptr)) {
|
else if (is<List>(ast_raw_ptr)) {
|
||||||
auto result = makePtr<List>();
|
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) {
|
for (auto node : nodes) {
|
||||||
result->addNode(evalImpl(node, env));
|
ASTNodePtr eval_node = evalImpl(node, env);
|
||||||
|
if (eval_node == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
result->addNode(eval_node);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else if (is<Vector>(ast_raw_ptr)) {
|
else if (is<Vector>(ast_raw_ptr)) {
|
||||||
auto result = makePtr<Vector>();
|
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) {
|
for (auto node : nodes) {
|
||||||
result->addNode(evalImpl(node, env));
|
ASTNodePtr eval_node = evalImpl(node, env);
|
||||||
|
if (eval_node == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
result->addNode(eval_node);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else if (is<HashMap>(ast_raw_ptr)) {
|
else if (is<HashMap>(ast_raw_ptr)) {
|
||||||
auto result = makePtr<HashMap>();
|
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) {
|
for (auto& element : elements) {
|
||||||
result->addElement(element.first, evalImpl(element.second, env));
|
ASTNodePtr element_node = evalImpl(element.second, env);
|
||||||
|
if (element_node == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
result->addElement(element.first, element_node);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -81,8 +116,88 @@ ASTNodePtr Eval::evalAst(ASTNodePtr ast, Environment* env)
|
|||||||
return ast;
|
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();
|
||||||
|
ASTNodePtr value = evalImpl(nodes[2], env);
|
||||||
|
|
||||||
|
// Dont overwrite symbols after an error
|
||||||
|
if (Error::the().hasAnyError()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify existing environment
|
||||||
|
return env->set(symbol, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First argument needs to be a List or Vector
|
||||||
|
if (!is<List>(nodes[1].get()) && !is<Vector>(nodes[1].get())) {
|
||||||
|
Error::the().addError(format("wrong argument type: list, '{}'", nodes[1]));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the nodes out of the List or Vector
|
||||||
|
std::vector<ASTNodePtr> binding_nodes;
|
||||||
|
if (is<List>(nodes[1].get())) {
|
||||||
|
auto bindings = std::static_pointer_cast<List>(nodes[1]);
|
||||||
|
binding_nodes = bindings->nodes();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto bindings = std::static_pointer_cast<Vector>(nodes[1]);
|
||||||
|
binding_nodes = bindings->nodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
// List or Vector needs to have an even number of elements
|
||||||
|
size_t count = binding_nodes.size();
|
||||||
|
if (count % 2 != 0) {
|
||||||
|
Error::the().addError(format("wrong number of arguments: {}, {}", "let* bindings", count));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new environment
|
||||||
|
auto let_env = makePtr<Environment>(env);
|
||||||
|
|
||||||
|
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 argument type: symbol, '{}'", binding_nodes[i]));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key = std::static_pointer_cast<Symbol>(binding_nodes[i])->symbol();
|
||||||
|
ASTNodePtr value = evalImpl(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)
|
ASTNodePtr Eval::apply(std::shared_ptr<List> evaluated_list)
|
||||||
{
|
{
|
||||||
|
if (evaluated_list == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto nodes = evaluated_list->nodes();
|
auto nodes = evaluated_list->nodes();
|
||||||
|
|
||||||
if (!is<Function>(nodes[0].get())) {
|
if (!is<Function>(nodes[0].get())) {
|
||||||
@@ -91,7 +206,7 @@ ASTNodePtr Eval::apply(std::shared_ptr<List> evaluated_list)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// car
|
// car
|
||||||
auto lambda = static_pointer_cast<Function>(nodes[0])->lambda();
|
auto lambda = std::static_pointer_cast<Function>(nodes[0])->lambda();
|
||||||
// cdr
|
// cdr
|
||||||
std::span<ASTNodePtr> span { nodes.data() + 1, nodes.size() - 1 };
|
std::span<ASTNodePtr> span { nodes.data() + 1, nodes.size() - 1 };
|
||||||
|
|
||||||
|
|||||||
+9
-5
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
|
|
||||||
@@ -13,7 +15,7 @@ namespace blaze {
|
|||||||
|
|
||||||
class Eval {
|
class Eval {
|
||||||
public:
|
public:
|
||||||
Eval(ASTNodePtr ast, Environment* env);
|
Eval(ASTNodePtr ast, EnvironmentPtr env);
|
||||||
virtual ~Eval() = default;
|
virtual ~Eval() = default;
|
||||||
|
|
||||||
void eval();
|
void eval();
|
||||||
@@ -21,12 +23,14 @@ public:
|
|||||||
ASTNodePtr ast() const { return m_ast; }
|
ASTNodePtr ast() const { return m_ast; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ASTNodePtr evalImpl(ASTNodePtr ast, Environment* env);
|
ASTNodePtr evalImpl(ASTNodePtr ast, EnvironmentPtr env);
|
||||||
ASTNodePtr evalAst(ASTNodePtr ast, Environment* 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 apply(std::shared_ptr<List> evaluated_list);
|
||||||
|
|
||||||
ASTNodePtr m_ast { nullptr };
|
ASTNodePtr m_ast;
|
||||||
Environment* m_env { nullptr };
|
EnvironmentPtr m_env;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace blaze
|
} // namespace blaze
|
||||||
|
|||||||
@@ -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 argument type: number, '{}'", 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 argument type: number, '{}'", 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 argument type: number, '{}'", 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
|
||||||
+8
-7
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <iterator> // std::next
|
#include <iterator> // std::next
|
||||||
|
#include <memory> // std::static_pointer_cast
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ruc/format/format.h"
|
#include "ruc/format/format.h"
|
||||||
@@ -74,7 +75,7 @@ void Printer::printImpl(ASTNodePtr node)
|
|||||||
m_print += '(';
|
m_print += '(';
|
||||||
m_first_node = false;
|
m_first_node = false;
|
||||||
m_previous_node_is_list = true;
|
m_previous_node_is_list = true;
|
||||||
auto nodes = static_pointer_cast<List>(node)->nodes();
|
auto nodes = std::static_pointer_cast<List>(node)->nodes();
|
||||||
for (size_t i = 0; i < nodes.size(); ++i) {
|
for (size_t i = 0; i < nodes.size(); ++i) {
|
||||||
printImpl(nodes[i]);
|
printImpl(nodes[i]);
|
||||||
m_previous_node_is_list = false;
|
m_previous_node_is_list = false;
|
||||||
@@ -86,7 +87,7 @@ void Printer::printImpl(ASTNodePtr node)
|
|||||||
m_print += '[';
|
m_print += '[';
|
||||||
m_first_node = false;
|
m_first_node = false;
|
||||||
m_previous_node_is_list = true;
|
m_previous_node_is_list = true;
|
||||||
auto nodes = static_pointer_cast<Vector>(node)->nodes();
|
auto nodes = std::static_pointer_cast<Vector>(node)->nodes();
|
||||||
for (size_t i = 0; i < nodes.size(); ++i) {
|
for (size_t i = 0; i < nodes.size(); ++i) {
|
||||||
printImpl(nodes[i]);
|
printImpl(nodes[i]);
|
||||||
m_previous_node_is_list = false;
|
m_previous_node_is_list = false;
|
||||||
@@ -98,7 +99,7 @@ void Printer::printImpl(ASTNodePtr node)
|
|||||||
m_print += "{";
|
m_print += "{";
|
||||||
m_first_node = false;
|
m_first_node = false;
|
||||||
m_previous_node_is_list = true;
|
m_previous_node_is_list = true;
|
||||||
auto elements = static_pointer_cast<HashMap>(node)->elements();
|
auto elements = std::static_pointer_cast<HashMap>(node)->elements();
|
||||||
for (auto it = elements.begin(); it != elements.end(); ++it) {
|
for (auto it = elements.begin(); it != elements.end(); ++it) {
|
||||||
m_print += format("{} ", it->first.front() == 0x7f ? ":" + it->first.substr(1) : it->first); // 127
|
m_print += format("{} ", it->first.front() == 0x7f ? ":" + it->first.substr(1) : it->first); // 127
|
||||||
printImpl(it->second);
|
printImpl(it->second);
|
||||||
@@ -113,19 +114,19 @@ void Printer::printImpl(ASTNodePtr node)
|
|||||||
else if (is<String>(node_raw_ptr)) {
|
else if (is<String>(node_raw_ptr)) {
|
||||||
// TODO: Implement string readably printing
|
// TODO: Implement string readably printing
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += format("{}", static_pointer_cast<String>(node)->data());
|
m_print += format("{}", std::static_pointer_cast<String>(node)->data());
|
||||||
}
|
}
|
||||||
else if (is<Keyword>(node_raw_ptr)) {
|
else if (is<Keyword>(node_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += format(":{}", static_pointer_cast<Keyword>(node)->keyword().substr(1));
|
m_print += format(":{}", std::static_pointer_cast<Keyword>(node)->keyword().substr(1));
|
||||||
}
|
}
|
||||||
else if (is<Number>(node_raw_ptr)) {
|
else if (is<Number>(node_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += format("{}", static_pointer_cast<Number>(node)->number());
|
m_print += format("{}", std::static_pointer_cast<Number>(node)->number());
|
||||||
}
|
}
|
||||||
else if (is<Symbol>(node_raw_ptr)) {
|
else if (is<Symbol>(node_raw_ptr)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
m_print += format("{}", static_pointer_cast<Symbol>(node)->symbol());
|
m_print += format("{}", std::static_pointer_cast<Symbol>(node)->symbol());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -7,7 +7,7 @@
|
|||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint64_t
|
#include <cstdint> // uint64_t
|
||||||
#include <cstdlib> // std::strtoll
|
#include <cstdlib> // std::strtoll
|
||||||
#include <memory> // makePtr, std::shared_ptr
|
#include <memory> // std::static_pointer_cast
|
||||||
#include <utility> // std::move
|
#include <utility> // std::move
|
||||||
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
@@ -196,7 +196,7 @@ ASTNodePtr Reader::readHashMap()
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string keyString = is<String>(key.get()) ? static_pointer_cast<String>(key)->data() : static_pointer_cast<Keyword>(key)->keyword();
|
std::string keyString = is<String>(key.get()) ? std::static_pointer_cast<String>(key)->data() : std::static_pointer_cast<Keyword>(key)->keyword();
|
||||||
hash_map->addElement(keyString, value);
|
hash_map->addElement(keyString, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
#include "readline.h"
|
#include "readline.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#if 1
|
#if 0
|
||||||
auto read(std::string_view input) -> blaze::ASTNodePtr
|
auto read(std::string_view input) -> blaze::ASTNodePtr
|
||||||
{
|
{
|
||||||
blaze::Lexer lexer(input);
|
blaze::Lexer lexer(input);
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user