Riyyi
2 years ago
11 changed files with 1012 additions and 0 deletions
@ -0,0 +1,48 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <cstdint> // int64_t |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
List::~List() |
||||||
|
{ |
||||||
|
for (auto node : m_nodes) { |
||||||
|
delete node; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void List::addNode(ASTNode* node) |
||||||
|
{ |
||||||
|
m_nodes.push_back(node); |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
String::String(const std::string& data) |
||||||
|
: m_data(data) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
Number::Number(int64_t number) |
||||||
|
: m_number(number) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
Symbol::Symbol(const std::string& symbol) |
||||||
|
: m_symbol(symbol) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,168 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cstdint> // int64_t |
||||||
|
#include <string> |
||||||
|
#include <string_view> |
||||||
|
#include <typeinfo> // typeid |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
class ASTNode { |
||||||
|
public: |
||||||
|
virtual ~ASTNode() = default; |
||||||
|
|
||||||
|
std::string className() const { return typeid(*this).name(); } |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
bool fastIs() const = delete; |
||||||
|
|
||||||
|
virtual bool isVector() const { return false; } |
||||||
|
virtual bool isHashMap() const { return false; } |
||||||
|
virtual bool isList() const { return false; } |
||||||
|
virtual bool isString() const { return false; } |
||||||
|
virtual bool isNumber() const { return false; } |
||||||
|
virtual bool isSpecialSymbol() const { return false; } |
||||||
|
virtual bool isSymbol() const { return false; } |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// []
|
||||||
|
class Vector final : public ASTNode { |
||||||
|
public: |
||||||
|
Vector(); |
||||||
|
virtual ~Vector(); |
||||||
|
|
||||||
|
virtual bool isVector() const override { return true; } |
||||||
|
|
||||||
|
private: |
||||||
|
std::vector<ASTNode*> m_nodes; |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// {}
|
||||||
|
class HashMap final : public ASTNode { |
||||||
|
public: |
||||||
|
HashMap(); |
||||||
|
virtual ~HashMap(); |
||||||
|
|
||||||
|
virtual bool isHashMap() const override { return true; } |
||||||
|
|
||||||
|
private: |
||||||
|
std::vector<ASTNode*> m_nodes; |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// ()
|
||||||
|
class List final : public ASTNode { |
||||||
|
public: |
||||||
|
List() = default; |
||||||
|
virtual ~List() override; |
||||||
|
|
||||||
|
virtual bool isList() const override { return true; } |
||||||
|
|
||||||
|
void addNode(ASTNode* node); |
||||||
|
|
||||||
|
const std::vector<ASTNode*>& nodes() const { return m_nodes; } |
||||||
|
|
||||||
|
private: |
||||||
|
std::vector<ASTNode*> m_nodes; |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// "string"
|
||||||
|
class String final : public ASTNode { |
||||||
|
public: |
||||||
|
String(const std::string& data); |
||||||
|
virtual ~String() = default; |
||||||
|
|
||||||
|
virtual bool isString() const override { return true; } |
||||||
|
|
||||||
|
const std::string& data() const { return m_data; } |
||||||
|
|
||||||
|
private: |
||||||
|
std::string m_data; |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// 123
|
||||||
|
class Number final : public ASTNode { |
||||||
|
public: |
||||||
|
Number(int64_t number); |
||||||
|
virtual ~Number() = default; |
||||||
|
|
||||||
|
virtual bool isNumber() const override { return true; } |
||||||
|
|
||||||
|
int64_t number() const { return m_number; } |
||||||
|
|
||||||
|
private: |
||||||
|
int64_t m_number { 0 }; |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// true, false, nil
|
||||||
|
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 { |
||||||
|
public: |
||||||
|
Symbol(const std::string& symbol); |
||||||
|
virtual ~Symbol() = default; |
||||||
|
|
||||||
|
virtual bool isSymbol() const override { return true; } |
||||||
|
|
||||||
|
std::string symbol() const { return m_symbol; } |
||||||
|
|
||||||
|
private: |
||||||
|
std::string m_symbol; |
||||||
|
}; |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
template<> |
||||||
|
inline bool ASTNode::fastIs<Vector>() const { return isVector(); } |
||||||
|
|
||||||
|
template<> |
||||||
|
inline bool ASTNode::fastIs<HashMap>() const { return isHashMap(); } |
||||||
|
|
||||||
|
template<> |
||||||
|
inline bool ASTNode::fastIs<List>() const { return isList(); } |
||||||
|
|
||||||
|
template<> |
||||||
|
inline bool ASTNode::fastIs<String>() const { return isString(); } |
||||||
|
|
||||||
|
template<> |
||||||
|
inline bool ASTNode::fastIs<Number>() const { return isNumber(); } |
||||||
|
|
||||||
|
template<> |
||||||
|
inline bool ASTNode::fastIs<SpecialSymbol>() const { return isSpecialSymbol(); } |
||||||
|
|
||||||
|
template<> |
||||||
|
inline bool ASTNode::fastIs<Symbol>() const { return isSymbol(); } |
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,248 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <string> |
||||||
|
#include <unordered_set> |
||||||
|
|
||||||
|
#include "ruc/format/print.h" |
||||||
|
#include "ruc/genericlexer.h" |
||||||
|
|
||||||
|
#include "lexer.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
Lexer::Lexer(std::string_view input) |
||||||
|
: ruc::GenericLexer(input) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Lexer::~Lexer() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void Lexer::tokenize() |
||||||
|
{ |
||||||
|
if (m_tokens.size() != 0) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
while (m_index < m_input.length()) { |
||||||
|
switch (peek()) { |
||||||
|
case '~': // ~@ or ~
|
||||||
|
consumeSpliceUnquoteOrUnquote(); |
||||||
|
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 '{': |
||||||
|
m_tokens.push_back({ Token::Type::BraceOpen, m_line, m_column, "{" }); |
||||||
|
break; |
||||||
|
case '}': |
||||||
|
m_tokens.push_back({ Token::Type::BraceClose, m_line, m_column, "}" }); |
||||||
|
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 '\'': |
||||||
|
m_tokens.push_back({ Token::Type::ParenClose, m_line, m_column, "'" }); |
||||||
|
break; |
||||||
|
case '`': |
||||||
|
m_tokens.push_back({ Token::Type::ParenClose, m_line, m_column, "`" }); |
||||||
|
break; |
||||||
|
case '^': |
||||||
|
m_tokens.push_back({ Token::Type::ParenClose, m_line, m_column, "^" }); |
||||||
|
break; |
||||||
|
case '@': |
||||||
|
m_tokens.push_back({ Token::Type::ParenClose, m_line, m_column, "@" }); |
||||||
|
break; |
||||||
|
case '"': |
||||||
|
if (!consumeString()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
break; |
||||||
|
case ';': |
||||||
|
consumeComment(); |
||||||
|
break; |
||||||
|
case ' ': |
||||||
|
case '\t': |
||||||
|
case ',': |
||||||
|
break; |
||||||
|
case '\r': |
||||||
|
if (peek(1) == '\n') { // CRLF \r\n
|
||||||
|
break; |
||||||
|
} |
||||||
|
m_column = -1; |
||||||
|
m_line++; |
||||||
|
break; |
||||||
|
case '\n': |
||||||
|
m_column = -1; |
||||||
|
m_line++; |
||||||
|
break; |
||||||
|
default: |
||||||
|
consumeValue(); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
ignore(); |
||||||
|
m_column++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool Lexer::consumeSpliceUnquoteOrUnquote() |
||||||
|
{ |
||||||
|
size_t column = m_column; |
||||||
|
|
||||||
|
ignore(); // ~
|
||||||
|
if (peek() == '@') { |
||||||
|
m_tokens.push_back({ Token::Type::Special, m_line, column, "~@" }); |
||||||
|
} |
||||||
|
else { |
||||||
|
m_tokens.push_back({ Token::Type::Tilde, m_line, column, "~" }); |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool Lexer::consumeString() |
||||||
|
{ |
||||||
|
size_t column = m_column; |
||||||
|
std::string text = ""; |
||||||
|
|
||||||
|
static std::unordered_set<char> exit = { |
||||||
|
'"', |
||||||
|
'\r', |
||||||
|
'\n', |
||||||
|
'\0', |
||||||
|
}; |
||||||
|
|
||||||
|
bool escape = false; |
||||||
|
char character = consume(); |
||||||
|
for (;;) { |
||||||
|
character = peek(); |
||||||
|
|
||||||
|
if (!escape && character == '\\') { |
||||||
|
text += '\\'; |
||||||
|
ignore(); |
||||||
|
escape = true; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (!escape && exit.find(character) != exit.end()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
text += character; |
||||||
|
ignore(); |
||||||
|
|
||||||
|
if (escape) { |
||||||
|
escape = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
m_tokens.push_back({ Token::Type::String, m_line, column, text }); |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
bool Lexer::consumeValue() |
||||||
|
{ |
||||||
|
size_t column = m_column; |
||||||
|
std::string value = ""; |
||||||
|
|
||||||
|
static std::unordered_set<char> exit = { |
||||||
|
'[', |
||||||
|
']', |
||||||
|
'{', |
||||||
|
'}', |
||||||
|
'(', |
||||||
|
')', |
||||||
|
'\'', |
||||||
|
'`', |
||||||
|
',', |
||||||
|
'"', |
||||||
|
';', |
||||||
|
' ', |
||||||
|
'\t', |
||||||
|
'\r', |
||||||
|
'\n', |
||||||
|
'\0', |
||||||
|
}; |
||||||
|
|
||||||
|
char character = 0; |
||||||
|
for (;;) { |
||||||
|
character = peek(); |
||||||
|
|
||||||
|
if (exit.find(character) != exit.end()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
value += character; |
||||||
|
ignore(); |
||||||
|
} |
||||||
|
|
||||||
|
m_tokens.push_back({ Token::Type::Value, m_line, column, value }); |
||||||
|
|
||||||
|
retreat(); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void Lexer::dump() const |
||||||
|
{ |
||||||
|
print("tokens: {}\n", m_tokens.size()); |
||||||
|
print("\""); |
||||||
|
for (auto& token : m_tokens) { |
||||||
|
print("{}", token.symbol); |
||||||
|
} |
||||||
|
print("\"\n"); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,110 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cstddef> // size_t |
||||||
|
#include <cstdint> // uint8_t |
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "ruc/format/print.h" |
||||||
|
#include "ruc/genericlexer.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
struct Token { |
||||||
|
enum class Type : uint8_t { |
||||||
|
None, |
||||||
|
Special, // ~@
|
||||||
|
BracketOpen, // [
|
||||||
|
BracketClose, // ]
|
||||||
|
BraceOpen, // {
|
||||||
|
BraceClose, // }
|
||||||
|
ParenOpen, // (
|
||||||
|
ParenClose, // )
|
||||||
|
Quote, // '
|
||||||
|
Backtick, // `
|
||||||
|
Tilde, // ~
|
||||||
|
Caret, // ^
|
||||||
|
At, // @
|
||||||
|
String, // "foobar"
|
||||||
|
Comment, // ;
|
||||||
|
Value, // symbols, numbers, "true", "false", and "nil"
|
||||||
|
}; |
||||||
|
|
||||||
|
Type type { Type::None }; |
||||||
|
size_t column { 0 }; |
||||||
|
size_t line { 0 }; |
||||||
|
std::string symbol; |
||||||
|
}; |
||||||
|
|
||||||
|
// Lexical analyzer -> tokenizes
|
||||||
|
class Lexer final : public ruc::GenericLexer { |
||||||
|
public: |
||||||
|
Lexer(std::string_view input); |
||||||
|
virtual ~Lexer(); |
||||||
|
|
||||||
|
void tokenize(); |
||||||
|
|
||||||
|
void dump() const; |
||||||
|
|
||||||
|
std::vector<Token>& tokens() { return m_tokens; } |
||||||
|
|
||||||
|
private: |
||||||
|
bool consumeSpliceUnquoteOrUnquote(); // ~@ or ~
|
||||||
|
bool consumeString(); |
||||||
|
bool consumeComment(); |
||||||
|
bool consumeValue(); |
||||||
|
|
||||||
|
size_t m_column { 0 }; |
||||||
|
size_t m_line { 0 }; |
||||||
|
|
||||||
|
std::vector<Token> m_tokens; |
||||||
|
}; |
||||||
|
|
||||||
|
} // 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());
|
||||||
|
// }
|
@ -0,0 +1,70 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "ruc/format/print.h" |
||||||
|
|
||||||
|
#include "printer.h" |
||||||
|
#include "types.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
Printer::Printer(ASTNode* node) |
||||||
|
: m_node(node) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Printer::~Printer() |
||||||
|
{ |
||||||
|
delete m_node; |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void Printer::dump() |
||||||
|
{ |
||||||
|
if (m_node == nullptr) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
dumpImpl(m_node); |
||||||
|
print("\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void Printer::dumpImpl(ASTNode* node) |
||||||
|
{ |
||||||
|
auto printSpacing = [this]() { |
||||||
|
if (!m_firstNode && !m_previousNodeIsList) { |
||||||
|
print(" "); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
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)) { |
||||||
|
printSpacing(); |
||||||
|
print("\"{}\"", static_cast<String*>(node)->data()); |
||||||
|
} |
||||||
|
else if (is<Number>(node)) { |
||||||
|
printSpacing(); |
||||||
|
print("{}", static_cast<Number*>(node)->number()); |
||||||
|
} |
||||||
|
else if (is<Symbol>(node)) { |
||||||
|
printSpacing(); |
||||||
|
print("{}", static_cast<Symbol*>(node)->symbol()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,29 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
// Serializer -> return to string
|
||||||
|
class Printer { |
||||||
|
public: |
||||||
|
Printer(ASTNode* node); |
||||||
|
virtual ~Printer(); |
||||||
|
|
||||||
|
void dump(); |
||||||
|
|
||||||
|
private: |
||||||
|
void dumpImpl(ASTNode* node); |
||||||
|
|
||||||
|
bool m_firstNode { true }; |
||||||
|
bool m_previousNodeIsList { false }; |
||||||
|
ASTNode* m_node { nullptr }; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,161 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <cstddef> // size_t |
||||||
|
#include <cstdint> // uint64_t |
||||||
|
#include <cstdlib> // std::strtoll |
||||||
|
#include <utility> // std::move |
||||||
|
|
||||||
|
#include "ruc/format/color.h" |
||||||
|
#include "ruc/meta/assert.h" |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "reader.h" |
||||||
|
#include "types.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
Reader::Reader(std::vector<Token>&& tokens) noexcept |
||||||
|
: m_tokens(std::move(tokens)) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
Reader::~Reader() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void Reader::read() |
||||||
|
{ |
||||||
|
if (m_node != nullptr) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
m_node = readImpl(); |
||||||
|
VERIFY(m_index > m_tokens.size() - 1, "more than one sexp in input"); |
||||||
|
} |
||||||
|
|
||||||
|
ASTNode* Reader::readImpl() |
||||||
|
{ |
||||||
|
switch (peek().type) { |
||||||
|
case Token::Type::ParenOpen: |
||||||
|
return readList(); |
||||||
|
break; |
||||||
|
case Token::Type::String: |
||||||
|
return readString(); |
||||||
|
break; |
||||||
|
case Token::Type::Value: |
||||||
|
return readValue(); |
||||||
|
default: |
||||||
|
// Unimplemented token
|
||||||
|
VERIFY_NOT_REACHED(); |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ASTNode* Reader::readList() |
||||||
|
{ |
||||||
|
ignore(); // (
|
||||||
|
|
||||||
|
List* list = new List(); |
||||||
|
while (m_index < m_tokens.size() && peek().type != Token::Type::ParenClose) { |
||||||
|
list->addNode(readImpl()); |
||||||
|
} |
||||||
|
|
||||||
|
VERIFY(m_index != m_tokens.size(), "missing closing ')'"); |
||||||
|
|
||||||
|
ignore(); // )
|
||||||
|
|
||||||
|
return list; |
||||||
|
} |
||||||
|
|
||||||
|
ASTNode* Reader::readString() |
||||||
|
{ |
||||||
|
Token token = consume(); |
||||||
|
return new String(token.symbol); |
||||||
|
} |
||||||
|
|
||||||
|
ASTNode* Reader::readValue() |
||||||
|
{ |
||||||
|
Token token = consume(); |
||||||
|
char* endPtr = nullptr; |
||||||
|
int64_t result = std::strtoll(token.symbol.c_str(), &endPtr, 10); |
||||||
|
if (endPtr == token.symbol.c_str() + token.symbol.size()) { |
||||||
|
return new Number(result); |
||||||
|
} |
||||||
|
|
||||||
|
return new Symbol(token.symbol); |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
bool Reader::isEOF() const |
||||||
|
{ |
||||||
|
return m_index >= m_tokens.size(); |
||||||
|
} |
||||||
|
|
||||||
|
Token Reader::peek() const |
||||||
|
{ |
||||||
|
VERIFY(!isEOF()); |
||||||
|
return m_tokens[m_index]; |
||||||
|
} |
||||||
|
|
||||||
|
Token Reader::consume() |
||||||
|
{ |
||||||
|
VERIFY(!isEOF()); |
||||||
|
return m_tokens[m_index++]; |
||||||
|
} |
||||||
|
|
||||||
|
void Reader::ignore() |
||||||
|
{ |
||||||
|
m_index++; |
||||||
|
} |
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
void Reader::dump() |
||||||
|
{ |
||||||
|
dumpImpl(m_node); |
||||||
|
} |
||||||
|
|
||||||
|
void Reader::dumpImpl(ASTNode* node) |
||||||
|
{ |
||||||
|
std::string indentation = std::string(m_indentation * 2, ' '); |
||||||
|
|
||||||
|
if (is<List>(node)) { |
||||||
|
List* list = static_cast<List*>(node); |
||||||
|
print("{}", indentation); |
||||||
|
print(fg(ruc::format::TerminalColor::Blue), "ListContainer"); |
||||||
|
print(" <"); |
||||||
|
print(fg(ruc::format::TerminalColor::Blue), "()"); |
||||||
|
print(">\n"); |
||||||
|
m_indentation++; |
||||||
|
for (size_t i = 0; i < list->nodes().size(); ++i) { |
||||||
|
dumpImpl(list->nodes()[i]); |
||||||
|
} |
||||||
|
m_indentation--; |
||||||
|
return; |
||||||
|
} |
||||||
|
else if (is<String>(node)) { |
||||||
|
print("{}", indentation); |
||||||
|
print(fg(ruc::format::TerminalColor::Yellow), "StringNode"); |
||||||
|
print(" <{}>", static_cast<String*>(node)->data()); |
||||||
|
} |
||||||
|
else if (is<Number>(node)) { |
||||||
|
print("{}", indentation); |
||||||
|
print(fg(ruc::format::TerminalColor::Yellow), "NumberNode"); |
||||||
|
print(" <{}>", static_cast<Number*>(node)->number()); |
||||||
|
} |
||||||
|
else if (is<Symbol>(node)) { |
||||||
|
print("{}", indentation); |
||||||
|
print(fg(ruc::format::TerminalColor::Yellow), "SymbolNode"); |
||||||
|
print(" <{}>", static_cast<Symbol*>(node)->symbol()); |
||||||
|
} |
||||||
|
print("\n"); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,49 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cstddef> // size_t |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "lexer.h" |
||||||
|
|
||||||
|
namespace blaze { |
||||||
|
|
||||||
|
// Parsing -> creates AST
|
||||||
|
class Reader { |
||||||
|
public: |
||||||
|
Reader(std::vector<Token>&& tokens) noexcept; |
||||||
|
virtual ~Reader(); |
||||||
|
|
||||||
|
void read(); |
||||||
|
|
||||||
|
void dump(); |
||||||
|
|
||||||
|
ASTNode* node() { return m_node; } |
||||||
|
|
||||||
|
private: |
||||||
|
bool isEOF() const; |
||||||
|
Token peek() const; |
||||||
|
Token consume(); |
||||||
|
void ignore(); |
||||||
|
|
||||||
|
ASTNode* readImpl(); |
||||||
|
ASTNode* readList(); |
||||||
|
ASTNode* readString(); |
||||||
|
ASTNode* readValue(); |
||||||
|
|
||||||
|
void dumpImpl(ASTNode* node); |
||||||
|
|
||||||
|
size_t m_index { 0 }; |
||||||
|
size_t m_indentation { 0 }; |
||||||
|
std::vector<Token> m_tokens; |
||||||
|
|
||||||
|
ASTNode* m_node { nullptr }; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace blaze
|
@ -0,0 +1,44 @@ |
|||||||
|
#include <cstdio> |
||||||
|
#include <iostream> // std::cin |
||||||
|
#include <string> // std::getline |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#if 0 |
||||||
|
auto read(std::string_view data) -> std::string_view |
||||||
|
{ |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(std::string_view data) -> std::string_view |
||||||
|
{ |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
auto print(std::string_view data) -> void |
||||||
|
{ |
||||||
|
printf("%s\n", data.data()); |
||||||
|
} |
||||||
|
|
||||||
|
auto rep(std::string_view data) -> void |
||||||
|
{ |
||||||
|
print(eval(read(data))); |
||||||
|
} |
||||||
|
|
||||||
|
auto main() -> int |
||||||
|
{ |
||||||
|
while (true) { |
||||||
|
printf("user> "); |
||||||
|
std::string line; |
||||||
|
std::getline(std::cin, line); |
||||||
|
|
||||||
|
// Exit with Ctrl-D
|
||||||
|
if (std::cin.eof() || std::cin.fail()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
rep(line); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,57 @@ |
|||||||
|
#include <cstdio> |
||||||
|
#include <iostream> // std::cin |
||||||
|
#include <string> // std::getline |
||||||
|
#include <string_view> |
||||||
|
|
||||||
|
#include "ast.h" |
||||||
|
#include "lexer.h" |
||||||
|
#include "printer.h" |
||||||
|
#include "reader.h" |
||||||
|
|
||||||
|
#if 1 |
||||||
|
auto read(std::string_view data) -> blaze::ASTNode* |
||||||
|
{ |
||||||
|
blaze::Lexer lexer(data); |
||||||
|
lexer.tokenize(); |
||||||
|
// lexer.dump();
|
||||||
|
blaze::Reader reader(std::move(lexer.tokens())); |
||||||
|
reader.read(); |
||||||
|
// reader.dump();
|
||||||
|
|
||||||
|
return reader.node(); |
||||||
|
} |
||||||
|
|
||||||
|
auto eval(blaze::ASTNode* node) -> blaze::ASTNode* |
||||||
|
{ |
||||||
|
return node; |
||||||
|
} |
||||||
|
|
||||||
|
auto print(blaze::ASTNode* node) -> void |
||||||
|
{ |
||||||
|
blaze::Printer printer(node); |
||||||
|
printer.dump(); |
||||||
|
} |
||||||
|
|
||||||
|
auto rep(std::string_view data) -> void |
||||||
|
{ |
||||||
|
print(eval(read(data))); |
||||||
|
} |
||||||
|
|
||||||
|
auto main() -> int |
||||||
|
{ |
||||||
|
while (true) { |
||||||
|
printf("user> "); |
||||||
|
std::string line; |
||||||
|
std::getline(std::cin, line); |
||||||
|
|
||||||
|
// Exit with Ctrl-D
|
||||||
|
if (std::cin.eof() || std::cin.fail()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
rep(line); |
||||||
|
} |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
#endif |
@ -0,0 +1,28 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Riyyi |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: MIT |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <typeinfo> |
||||||
|
|
||||||
|
template<typename T, typename U> |
||||||
|
inline bool is(U& input) |
||||||
|
{ |
||||||
|
if constexpr (requires { input.template fastIs<T>(); }) { |
||||||
|
return input.template fastIs<T>(); |
||||||
|
} |
||||||
|
|
||||||
|
return typeid(input) == typeid(T); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T, typename U> |
||||||
|
inline bool is(U* input) |
||||||
|
{ |
||||||
|
return input && is<T>(*input); |
||||||
|
} |
||||||
|
|
||||||
|
// serenity/AK/TypeCasts.h
|
||||||
|
// serenity/Userland/Libraries/LibJS/AST.h
|
Loading…
Reference in new issue