Compare commits

..
6 Commits
13 changed files with 687 additions and 137 deletions
+5 -1
View File
@@ -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
View File
@@ -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());
}
}
+97 -33
View File
@@ -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;
};
+148
View File
@@ -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
+93
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+1 -1
View File
@@ -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);
+113
View File
@@ -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