Compare commits
6
Commits
f6f8207e1a
...
c6ea42bc5d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6ea42bc5d | ||
|
|
5c5a766b7e | ||
|
|
da0b0a91a6 | ||
|
|
b51a3bf15b | ||
|
|
27e584ea84 | ||
|
|
0fea075953 |
+5
-1
@@ -74,7 +74,7 @@ target_link_libraries(${PROJECT} ruc)
|
|||||||
# Execute target
|
# Execute target
|
||||||
|
|
||||||
add_custom_target(run
|
add_custom_target(run
|
||||||
COMMAND ${PROJECT}
|
COMMAND ${PROJECT} -c
|
||||||
DEPENDS ${PROJECT})
|
DEPENDS ${PROJECT})
|
||||||
|
|
||||||
# ------------------------------------------
|
# ------------------------------------------
|
||||||
@@ -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})
|
||||||
|
|||||||
+52
@@ -5,8 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cstdint> // int64_t
|
#include <cstdint> // int64_t
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
@@ -24,6 +26,13 @@ void Collection::addNode(ASTNode* node)
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void HashMap::addElement(const std::string& key, ASTNode* value)
|
||||||
|
{
|
||||||
|
m_elements.emplace(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
String::String(const std::string& data)
|
String::String(const std::string& data)
|
||||||
: m_data(data)
|
: m_data(data)
|
||||||
{
|
{
|
||||||
@@ -31,6 +40,13 @@ String::String(const std::string& data)
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
|
Keyword::Keyword(const std::string& data)
|
||||||
|
: m_data(data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
Number::Number(int64_t number)
|
Number::Number(int64_t number)
|
||||||
: m_number(number)
|
: m_number(number)
|
||||||
{
|
{
|
||||||
@@ -43,4 +59,40 @@ Symbol::Symbol(const std::string& symbol)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
Value::Value(const std::string& value)
|
||||||
|
: m_value(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
Function::Function(Lambda lambda)
|
||||||
|
: m_lambda(lambda)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace blaze
|
} // namespace blaze
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void Formatter<blaze::ASTNode*>::format(Builder& builder, blaze::ASTNode* value) const
|
||||||
|
{
|
||||||
|
if (is<blaze::String>(value)) {
|
||||||
|
return Formatter<std::string>::format(builder, static_cast<blaze::String*>(value)->data());
|
||||||
|
}
|
||||||
|
if (is<blaze::Keyword>(value)) {
|
||||||
|
return Formatter<std::string>::format(builder, ":" + static_cast<blaze::Keyword*>(value)->keyword().substr(1));
|
||||||
|
}
|
||||||
|
else if (is<blaze::Number>(value)) {
|
||||||
|
Formatter<int64_t> formatter { .specifier = specifier };
|
||||||
|
return formatter.format(builder, static_cast<blaze::Number*>(value)->number());
|
||||||
|
}
|
||||||
|
else if (is<blaze::Value>(value)) {
|
||||||
|
return Formatter<std::string>::format(builder, static_cast<blaze::Value*>(value)->value());
|
||||||
|
}
|
||||||
|
else if (is<blaze::Symbol>(value)) {
|
||||||
|
return Formatter<std::string>::format(builder, static_cast<blaze::Symbol*>(value)->symbol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,12 +6,17 @@
|
|||||||
|
|
||||||
#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
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "ruc/format/formatter.h"
|
||||||
|
|
||||||
namespace blaze {
|
namespace blaze {
|
||||||
|
|
||||||
class ASTNode {
|
class ASTNode {
|
||||||
@@ -28,9 +33,11 @@ public:
|
|||||||
virtual bool isHashMap() const { return false; }
|
virtual bool isHashMap() const { return false; }
|
||||||
virtual bool isList() const { return false; }
|
virtual bool isList() const { return false; }
|
||||||
virtual bool isString() const { return false; }
|
virtual bool isString() const { return false; }
|
||||||
|
virtual bool isKeyword() const { return false; }
|
||||||
virtual bool isNumber() const { return false; }
|
virtual bool isNumber() const { return false; }
|
||||||
virtual bool isSpecialSymbol() const { return false; }
|
virtual bool isValue() const { return false; }
|
||||||
virtual bool isSymbol() const { return false; }
|
virtual bool isSymbol() const { return false; }
|
||||||
|
virtual bool isFunction() const { return false; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ASTNode() {}
|
ASTNode() {}
|
||||||
@@ -47,6 +54,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() {}
|
||||||
@@ -57,6 +66,18 @@ private:
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// ()
|
||||||
|
class List final : public Collection {
|
||||||
|
public:
|
||||||
|
List() = default;
|
||||||
|
virtual ~List() = default;
|
||||||
|
|
||||||
|
virtual bool isCollection() const override { return false; }
|
||||||
|
virtual bool isList() const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
// []
|
// []
|
||||||
class Vector final : public Collection {
|
class Vector final : public Collection {
|
||||||
public:
|
public:
|
||||||
@@ -70,25 +91,21 @@ public:
|
|||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
// {}
|
// {}
|
||||||
class HashMap final : public Collection {
|
class HashMap final : public ASTNode {
|
||||||
public:
|
public:
|
||||||
HashMap() = default;
|
HashMap() = default;
|
||||||
virtual ~HashMap() = default;
|
virtual ~HashMap() = default;
|
||||||
|
|
||||||
virtual bool isCollection() const override { return false; }
|
|
||||||
virtual bool isHashMap() const override { return true; }
|
virtual bool isHashMap() const override { return true; }
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------
|
void addElement(const std::string& key, ASTNode* value);
|
||||||
|
|
||||||
// ()
|
const std::unordered_map<std::string, ASTNode*>& elements() const { return m_elements; }
|
||||||
class List final : public Collection {
|
size_t size() const { return m_elements.size(); }
|
||||||
public:
|
bool empty() const { return m_elements.size() == 0; }
|
||||||
List() = default;
|
|
||||||
virtual ~List() = default;
|
|
||||||
|
|
||||||
virtual bool isCollection() const override { return false; }
|
private:
|
||||||
virtual bool isList() const override { return true; }
|
std::unordered_map<std::string, ASTNode*> m_elements;
|
||||||
};
|
};
|
||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
@@ -109,6 +126,21 @@ private:
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// :keyword
|
||||||
|
class Keyword final : public ASTNode {
|
||||||
|
public:
|
||||||
|
Keyword(const std::string& data);
|
||||||
|
virtual ~Keyword() = default;
|
||||||
|
|
||||||
|
virtual bool isKeyword() const override { return true; }
|
||||||
|
|
||||||
|
const std::string& keyword() const { return m_data; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
// 123
|
// 123
|
||||||
class Number final : public ASTNode {
|
class Number final : public ASTNode {
|
||||||
public:
|
public:
|
||||||
@@ -125,21 +157,7 @@ private:
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
// true, false, nil
|
// Symbols
|
||||||
class SpecialSymbol final : public ASTNode {
|
|
||||||
public:
|
|
||||||
SpecialSymbol();
|
|
||||||
virtual ~SpecialSymbol();
|
|
||||||
|
|
||||||
virtual bool isSpecialSymbol() const override { return true; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_symbol;
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
|
|
||||||
// Other symbols
|
|
||||||
class Symbol final : public ASTNode {
|
class Symbol final : public ASTNode {
|
||||||
public:
|
public:
|
||||||
Symbol(const std::string& symbol);
|
Symbol(const std::string& symbol);
|
||||||
@@ -147,7 +165,7 @@ public:
|
|||||||
|
|
||||||
virtual bool isSymbol() const override { return true; }
|
virtual bool isSymbol() const override { return true; }
|
||||||
|
|
||||||
std::string symbol() const { return m_symbol; }
|
const std::string& symbol() const { return m_symbol; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_symbol;
|
std::string m_symbol;
|
||||||
@@ -155,10 +173,46 @@ private:
|
|||||||
|
|
||||||
// -----------------------------------------
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// true, false, nil
|
||||||
|
class Value final : public ASTNode {
|
||||||
|
public:
|
||||||
|
Value(const std::string& value);
|
||||||
|
virtual ~Value() = default;
|
||||||
|
|
||||||
|
virtual bool isValue() const override { return true; }
|
||||||
|
|
||||||
|
const std::string& value() const { return m_value; }
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
template<>
|
template<>
|
||||||
inline bool ASTNode::fastIs<Collection>() const { return isCollection(); }
|
inline bool ASTNode::fastIs<Collection>() const { return isCollection(); }
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline bool ASTNode::fastIs<List>() const { return isList(); }
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ASTNode::fastIs<Vector>() const { return isVector(); }
|
inline bool ASTNode::fastIs<Vector>() const { return isVector(); }
|
||||||
|
|
||||||
@@ -166,19 +220,29 @@ template<>
|
|||||||
inline bool ASTNode::fastIs<HashMap>() const { return isHashMap(); }
|
inline bool ASTNode::fastIs<HashMap>() const { return isHashMap(); }
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ASTNode::fastIs<List>() const { return isList(); }
|
inline bool ASTNode::fastIs<String>() const { return isString(); }
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ASTNode::fastIs<String>() const { return isString(); }
|
inline bool ASTNode::fastIs<Keyword>() const { return isKeyword(); }
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ASTNode::fastIs<Number>() const { return isNumber(); }
|
inline bool ASTNode::fastIs<Number>() const { return isNumber(); }
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ASTNode::fastIs<SpecialSymbol>() const { return isSpecialSymbol(); }
|
inline bool ASTNode::fastIs<Symbol>() const { return isSymbol(); }
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ASTNode::fastIs<Symbol>() const { return isSymbol(); }
|
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
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct ruc::format::Formatter<blaze::ASTNode*> : public Formatter<std::string> {
|
||||||
|
void format(Builder& builder, blaze::ASTNode* value) const;
|
||||||
|
};
|
||||||
|
|||||||
@@ -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,93 @@
|
|||||||
|
/*
|
||||||
|
* 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)) {
|
||||||
|
auto result = new HashMap();
|
||||||
|
auto elements = static_cast<HashMap*>(ast)->elements();
|
||||||
|
for (auto& element : elements) {
|
||||||
|
result->addElement(element.first, evalImpl(element.second, env));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
+69
-18
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string> // std::to_string
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
#include "ruc/format/print.h"
|
#include "ruc/format/print.h"
|
||||||
@@ -38,6 +38,12 @@ void Lexer::tokenize()
|
|||||||
case '~': // ~@ or ~
|
case '~': // ~@ or ~
|
||||||
consumeSpliceUnquoteOrUnquote();
|
consumeSpliceUnquoteOrUnquote();
|
||||||
break;
|
break;
|
||||||
|
case '(':
|
||||||
|
m_tokens.push_back({ Token::Type::ParenOpen, m_line, m_column, "(" });
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
m_tokens.push_back({ Token::Type::ParenClose, m_line, m_column, ")" });
|
||||||
|
break;
|
||||||
case '[':
|
case '[':
|
||||||
m_tokens.push_back({ Token::Type::BracketOpen, m_line, m_column, "[" });
|
m_tokens.push_back({ Token::Type::BracketOpen, m_line, m_column, "[" });
|
||||||
break;
|
break;
|
||||||
@@ -50,12 +56,6 @@ void Lexer::tokenize()
|
|||||||
case '}':
|
case '}':
|
||||||
m_tokens.push_back({ Token::Type::BraceClose, m_line, m_column, "}" });
|
m_tokens.push_back({ Token::Type::BraceClose, m_line, m_column, "}" });
|
||||||
break;
|
break;
|
||||||
case '(':
|
|
||||||
m_tokens.push_back({ Token::Type::ParenOpen, m_line, m_column, "(" });
|
|
||||||
break;
|
|
||||||
case ')':
|
|
||||||
m_tokens.push_back({ Token::Type::ParenClose, m_line, m_column, ")" });
|
|
||||||
break;
|
|
||||||
case '\'':
|
case '\'':
|
||||||
m_tokens.push_back({ Token::Type::Quote, m_line, m_column, "'" });
|
m_tokens.push_back({ Token::Type::Quote, m_line, m_column, "'" });
|
||||||
break;
|
break;
|
||||||
@@ -73,6 +73,11 @@ void Lexer::tokenize()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ':':
|
||||||
|
if (!consumeKeyword()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ';':
|
case ';':
|
||||||
consumeComment();
|
consumeComment();
|
||||||
break;
|
break;
|
||||||
@@ -162,14 +167,28 @@ bool Lexer::consumeString()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Lexer::consumeComment()
|
bool Lexer::consumeKeyword()
|
||||||
{
|
{
|
||||||
size_t column = m_column;
|
size_t column = m_column;
|
||||||
std::string comment = "";
|
std::string keyword;
|
||||||
|
keyword += 0x7f; // 127
|
||||||
|
|
||||||
ignore(); // ;
|
ignore(); // :
|
||||||
|
|
||||||
static std::unordered_set<char> exit = {
|
static std::unordered_set<char> exit = {
|
||||||
|
'[',
|
||||||
|
']',
|
||||||
|
'{',
|
||||||
|
'}',
|
||||||
|
'(',
|
||||||
|
')',
|
||||||
|
'\'',
|
||||||
|
'`',
|
||||||
|
',',
|
||||||
|
'"',
|
||||||
|
';',
|
||||||
|
' ',
|
||||||
|
'\t',
|
||||||
'\r',
|
'\r',
|
||||||
'\n',
|
'\n',
|
||||||
'\0',
|
'\0',
|
||||||
@@ -183,17 +202,13 @@ bool Lexer::consumeComment()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
comment += character;
|
keyword += character;
|
||||||
ignore();
|
ignore();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim comment
|
m_tokens.push_back({ Token::Type::Keyword, m_line, column, keyword });
|
||||||
comment.erase(comment.begin(),
|
|
||||||
std::find_if(comment.begin(), comment.end(), [](char c) { return !std::isspace(c); }));
|
|
||||||
comment.erase(std::find_if(comment.rbegin(), comment.rend(), [](char c) { return !std::isspace(c); }).base(),
|
|
||||||
comment.end());
|
|
||||||
|
|
||||||
m_tokens.push_back({ Token::Type::Comment, m_line, column, comment });
|
retreat();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -201,7 +216,7 @@ bool Lexer::consumeComment()
|
|||||||
bool Lexer::consumeValue()
|
bool Lexer::consumeValue()
|
||||||
{
|
{
|
||||||
size_t column = m_column;
|
size_t column = m_column;
|
||||||
std::string value = "";
|
std::string value;
|
||||||
|
|
||||||
static std::unordered_set<char> exit = {
|
static std::unordered_set<char> exit = {
|
||||||
'[',
|
'[',
|
||||||
@@ -241,6 +256,42 @@ bool Lexer::consumeValue()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Lexer::consumeComment()
|
||||||
|
{
|
||||||
|
size_t column = m_column;
|
||||||
|
std::string comment;
|
||||||
|
|
||||||
|
ignore(); // ;
|
||||||
|
|
||||||
|
static std::unordered_set<char> exit = {
|
||||||
|
'\r',
|
||||||
|
'\n',
|
||||||
|
'\0',
|
||||||
|
};
|
||||||
|
|
||||||
|
char character = 0;
|
||||||
|
for (;;) {
|
||||||
|
character = peek();
|
||||||
|
|
||||||
|
if (exit.find(character) != exit.end()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
comment += character;
|
||||||
|
ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim comment
|
||||||
|
comment.erase(comment.begin(),
|
||||||
|
std::find_if(comment.begin(), comment.end(), [](char c) { return !std::isspace(c); }));
|
||||||
|
comment.erase(std::find_if(comment.rbegin(), comment.rend(), [](char c) { return !std::isspace(c); }).base(),
|
||||||
|
comment.end());
|
||||||
|
|
||||||
|
m_tokens.push_back({ Token::Type::Comment, m_line, column, comment });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Lexer::dump() const
|
void Lexer::dump() const
|
||||||
{
|
{
|
||||||
print("tokens: {}\n", m_tokens.size());
|
print("tokens: {}\n", m_tokens.size());
|
||||||
|
|||||||
+6
-45
@@ -20,20 +20,21 @@ struct Token {
|
|||||||
enum class Type : uint8_t {
|
enum class Type : uint8_t {
|
||||||
None,
|
None,
|
||||||
Special, // ~@
|
Special, // ~@
|
||||||
|
ParenOpen, // (
|
||||||
|
ParenClose, // )
|
||||||
BracketOpen, // [
|
BracketOpen, // [
|
||||||
BracketClose, // ]
|
BracketClose, // ]
|
||||||
BraceOpen, // {
|
BraceOpen, // {
|
||||||
BraceClose, // }
|
BraceClose, // }
|
||||||
ParenOpen, // (
|
|
||||||
ParenClose, // )
|
|
||||||
Quote, // '
|
Quote, // '
|
||||||
Backtick, // `
|
Backtick, // `
|
||||||
Tilde, // ~
|
Tilde, // ~
|
||||||
Caret, // ^
|
Caret, // ^
|
||||||
At, // @
|
At, // @
|
||||||
String, // "foobar"
|
String, // "foobar"
|
||||||
|
Keyword, // :keyword
|
||||||
|
Value, // numbers, "true", "false", and "nil", symbols
|
||||||
Comment, // ;
|
Comment, // ;
|
||||||
Value, // symbols, numbers, "true", "false", and "nil"
|
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -58,8 +59,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool consumeSpliceUnquoteOrUnquote(); // ~@ or ~
|
bool consumeSpliceUnquoteOrUnquote(); // ~@ or ~
|
||||||
bool consumeString();
|
bool consumeString();
|
||||||
bool consumeComment();
|
bool consumeKeyword();
|
||||||
bool consumeValue();
|
bool consumeValue();
|
||||||
|
bool consumeComment();
|
||||||
|
|
||||||
size_t m_column { 0 };
|
size_t m_column { 0 };
|
||||||
size_t m_line { 0 };
|
size_t m_line { 0 };
|
||||||
@@ -68,44 +70,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace blaze
|
} // namespace blaze
|
||||||
|
|
||||||
// ~^@
|
|
||||||
// (+ 2 (* 3 4))
|
|
||||||
|
|
||||||
// Lexing -> creates tokens
|
|
||||||
// Parsing -> creates AST
|
|
||||||
|
|
||||||
// class Thing1 {
|
|
||||||
// public:
|
|
||||||
// std::vector<int>& numbers() { return m_numbers; }
|
|
||||||
|
|
||||||
// private:
|
|
||||||
// std::vector<int> m_numbers;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// class Thing2 {
|
|
||||||
// public:
|
|
||||||
// std::vector<int>&& numbers() { return std::move(m_numbers); }
|
|
||||||
|
|
||||||
// private:
|
|
||||||
// std::vector<int> m_numbers;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// class OtherThing {
|
|
||||||
// public:
|
|
||||||
// OtherThing(std::vector<int>&& numbers) noexcept
|
|
||||||
// : m_numbers(std::move(numbers))
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private:
|
|
||||||
// std::vector<int> m_numbers;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// int main()
|
|
||||||
// {
|
|
||||||
// Thing1 thing1;
|
|
||||||
// Thing2 thing2;
|
|
||||||
// OtherThing other_thing(std::move(thing1.numbers()));
|
|
||||||
// OtherThing other_thing2(thing2.numbers());
|
|
||||||
// }
|
|
||||||
|
|||||||
+20
-15
@@ -48,7 +48,19 @@ void Printer::dumpImpl(ASTNode* node)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (is<Vector>(node)) {
|
if (is<List>(node)) {
|
||||||
|
printSpacing();
|
||||||
|
print("(");
|
||||||
|
m_firstNode = false;
|
||||||
|
m_previousNodeIsList = true;
|
||||||
|
List* list = static_cast<List*>(node);
|
||||||
|
for (size_t i = 0; i < list->nodes().size(); ++i) {
|
||||||
|
dumpImpl(list->nodes()[i]);
|
||||||
|
m_previousNodeIsList = false;
|
||||||
|
}
|
||||||
|
print(")");
|
||||||
|
}
|
||||||
|
else if (is<Vector>(node)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
print("[");
|
print("[");
|
||||||
m_firstNode = false;
|
m_firstNode = false;
|
||||||
@@ -66,28 +78,21 @@ void Printer::dumpImpl(ASTNode* node)
|
|||||||
m_firstNode = false;
|
m_firstNode = false;
|
||||||
m_previousNodeIsList = true;
|
m_previousNodeIsList = true;
|
||||||
HashMap* hash_map = static_cast<HashMap*>(node);
|
HashMap* hash_map = static_cast<HashMap*>(node);
|
||||||
for (size_t i = 0; i < hash_map->nodes().size(); ++i) {
|
for (auto element : hash_map->elements()) {
|
||||||
dumpImpl(hash_map->nodes()[i]);
|
print("{} ", element.first.front() == 0x7f ? ":" + element.first.substr(1) : element.first); // 127
|
||||||
|
dumpImpl(element.second);
|
||||||
m_previousNodeIsList = false;
|
m_previousNodeIsList = false;
|
||||||
}
|
}
|
||||||
print("}}");
|
print("}}");
|
||||||
}
|
}
|
||||||
else if (is<List>(node)) {
|
|
||||||
printSpacing();
|
|
||||||
print("(");
|
|
||||||
m_firstNode = false;
|
|
||||||
m_previousNodeIsList = true;
|
|
||||||
List* list = static_cast<List*>(node);
|
|
||||||
for (size_t i = 0; i < list->nodes().size(); ++i) {
|
|
||||||
dumpImpl(list->nodes()[i]);
|
|
||||||
m_previousNodeIsList = false;
|
|
||||||
}
|
|
||||||
print(")");
|
|
||||||
}
|
|
||||||
else if (is<String>(node)) {
|
else if (is<String>(node)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
print("{}", static_cast<String*>(node)->data());
|
print("{}", static_cast<String*>(node)->data());
|
||||||
}
|
}
|
||||||
|
else if (is<Keyword>(node)) {
|
||||||
|
printSpacing();
|
||||||
|
print(":{}", static_cast<Keyword*>(node)->keyword().substr(1));
|
||||||
|
}
|
||||||
else if (is<Number>(node)) {
|
else if (is<Number>(node)) {
|
||||||
printSpacing();
|
printSpacing();
|
||||||
print("{}", static_cast<Number*>(node)->number());
|
print("{}", static_cast<Number*>(node)->number());
|
||||||
|
|||||||
+48
-22
@@ -117,14 +117,17 @@ ASTNode* Reader::readImpl()
|
|||||||
case Token::Type::At: // @
|
case Token::Type::At: // @
|
||||||
return readDeref();
|
return readDeref();
|
||||||
break;
|
break;
|
||||||
case Token::Type::String:
|
case Token::Type::String: // "foobar"
|
||||||
return readString();
|
return readString();
|
||||||
break;
|
break;
|
||||||
|
case Token::Type::Keyword: // :keyword
|
||||||
|
return readKeyword();
|
||||||
|
break;
|
||||||
case Token::Type::Comment: // ;
|
case Token::Type::Comment: // ;
|
||||||
ignore();
|
ignore();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
break;
|
break;
|
||||||
case Token::Type::Value:
|
case Token::Type::Value: // true, false, nil
|
||||||
return readValue();
|
return readValue();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -152,6 +155,23 @@ ASTNode* Reader::readSpliceUnquote()
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASTNode* Reader::readList()
|
||||||
|
{
|
||||||
|
ignore(); // (
|
||||||
|
|
||||||
|
List* list = new List();
|
||||||
|
while (!isEOF() && peek().type != Token::Type::ParenClose) {
|
||||||
|
list->addNode(readImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!consumeSpecific(Token { .type = Token::Type::ParenClose })) { // )
|
||||||
|
m_error_character = ')';
|
||||||
|
m_is_unbalanced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
ASTNode* Reader::readVector()
|
ASTNode* Reader::readVector()
|
||||||
{
|
{
|
||||||
ignore(); // [
|
ignore(); // [
|
||||||
@@ -173,9 +193,27 @@ ASTNode* Reader::readHashMap()
|
|||||||
{
|
{
|
||||||
ignore(); // {
|
ignore(); // {
|
||||||
|
|
||||||
HashMap* vector = new HashMap();
|
HashMap* hash_map = new HashMap();
|
||||||
while (!isEOF() && peek().type != Token::Type::BraceClose) {
|
while (!isEOF() && peek().type != Token::Type::BraceClose) {
|
||||||
vector->addNode(readImpl());
|
ASTNode* key = readImpl();
|
||||||
|
ASTNode* value = readImpl();
|
||||||
|
|
||||||
|
if (!key && !value) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key || !value) {
|
||||||
|
Error::the().addError("hash-map requires an even-sized list");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is<String>(key) && !is<Keyword>(key)) {
|
||||||
|
Error::the().addError(format("{} is not a string or keyword", key));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string keyString = is<String>(key) ? static_cast<String*>(key)->data() : static_cast<Keyword*>(key)->keyword();
|
||||||
|
hash_map->addElement(keyString, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!consumeSpecific(Token { .type = Token::Type::BraceClose })) { // }
|
if (!consumeSpecific(Token { .type = Token::Type::BraceClose })) { // }
|
||||||
@@ -183,24 +221,7 @@ ASTNode* Reader::readHashMap()
|
|||||||
m_is_unbalanced = true;
|
m_is_unbalanced = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return vector;
|
return hash_map;
|
||||||
}
|
|
||||||
|
|
||||||
ASTNode* Reader::readList()
|
|
||||||
{
|
|
||||||
ignore(); // (
|
|
||||||
|
|
||||||
List* list = new List();
|
|
||||||
while (!isEOF() && peek().type != Token::Type::ParenClose) {
|
|
||||||
list->addNode(readImpl());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!consumeSpecific(Token { .type = Token::Type::ParenClose })) { // )
|
|
||||||
m_error_character = ')';
|
|
||||||
m_is_unbalanced = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode* Reader::readQuote()
|
ASTNode* Reader::readQuote()
|
||||||
@@ -301,6 +322,11 @@ ASTNode* Reader::readString()
|
|||||||
return new String(symbol);
|
return new String(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASTNode* Reader::readKeyword()
|
||||||
|
{
|
||||||
|
return new Keyword(consume().symbol);
|
||||||
|
}
|
||||||
|
|
||||||
ASTNode* Reader::readValue()
|
ASTNode* Reader::readValue()
|
||||||
{
|
{
|
||||||
Token token = consume();
|
Token token = consume();
|
||||||
|
|||||||
+3
-2
@@ -36,16 +36,17 @@ private:
|
|||||||
|
|
||||||
ASTNode* readImpl();
|
ASTNode* readImpl();
|
||||||
ASTNode* readSpliceUnquote(); // ~@
|
ASTNode* readSpliceUnquote(); // ~@
|
||||||
|
ASTNode* readList(); // ()
|
||||||
ASTNode* readVector(); // []
|
ASTNode* readVector(); // []
|
||||||
ASTNode* readHashMap(); // {}
|
ASTNode* readHashMap(); // {}
|
||||||
ASTNode* readList(); // ()
|
|
||||||
ASTNode* readQuote(); // '
|
ASTNode* readQuote(); // '
|
||||||
ASTNode* readQuasiQuote(); // `
|
ASTNode* readQuasiQuote(); // `
|
||||||
ASTNode* readUnquote(); // ~
|
ASTNode* readUnquote(); // ~
|
||||||
ASTNode* readWithMeta(); // ^
|
ASTNode* readWithMeta(); // ^
|
||||||
ASTNode* readDeref(); // @
|
ASTNode* readDeref(); // @
|
||||||
ASTNode* readString(); // "foobar"
|
ASTNode* readString(); // "foobar"
|
||||||
ASTNode* readValue();
|
ASTNode* readKeyword(); // :keyword
|
||||||
|
ASTNode* readValue(); // true, false, nil
|
||||||
|
|
||||||
void dumpImpl(ASTNode* node);
|
void dumpImpl(ASTNode* node);
|
||||||
|
|
||||||
|
|||||||
@@ -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