Lisp: Add source
This commit is contained in:
+48
@@ -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
|
||||||
+248
@@ -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
|
||||||
+110
@@ -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
|
||||||
+161
@@ -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
|
||||||
+28
@@ -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
|
||||||
Reference in New Issue
Block a user