/* * Copyright (C) 2023 Riyyi * * SPDX-License-Identifier: MIT */ #include // size_t #include // uint64_t #include // std::strtoll #include // std::static_pointer_cast #include // std::move #include "error.h" #include "ruc/format/color.h" #include "ruc/meta/assert.h" #include "ast.h" #include "reader.h" #include "settings.h" #include "types.h" namespace blaze { Reader::Reader() { } Reader::Reader(std::vector&& tokens) noexcept : m_tokens(std::move(tokens)) { } Reader::~Reader() { } // ----------------------------------------- void Reader::read() { if (Error::the().hasAnyError() || m_node) { return; } m_node = readImpl(); if (Error::the().hasOtherError()) { return; } // Check for multiple expressions if (!isEOF()) { Error::the().add("more than one sexp in input"); } } ValuePtr Reader::readImpl() { if (m_tokens.size() == 0) { return nullptr; } switch (peek().type) { case Token::Type::Special: // ~@ return readSpliceUnquote(); break; case Token::Type::ParenOpen: // ( return readList(); break; case Token::Type::ParenClose: // ) Error::the().add("invalid read syntax: ')'"); return nullptr; break; case Token::Type::BracketOpen: // [ return readVector(); break; case Token::Type::BracketClose: // ] Error::the().add("invalid read syntax: ']'"); return nullptr; break; case Token::Type::BraceOpen: // { return readHashMap(); break; case Token::Type::BraceClose: // } Error::the().add("invalid read syntax: '}'"); return nullptr; break; case Token::Type::Quote: // ' return readQuote(); break; case Token::Type::Backtick: // ` return readQuasiQuote(); break; case Token::Type::Tilde: // ~ return readUnquote(); break; case Token::Type::Caret: // ^ return readWithMeta(); break; case Token::Type::At: // @ return readDeref(); break; case Token::Type::String: // "foobar" return readString(); break; case Token::Type::Keyword: // :keyword return readKeyword(); break; case Token::Type::Value: // true, false, nil return readValue(); break; default: break; }; // Unimplemented token VERIFY_NOT_REACHED(); return nullptr; } ValuePtr Reader::readSpliceUnquote() { ignore(); // ~@ if (isEOF()) { Error::the().add("expected form, got EOF"); return nullptr; } auto nodes = ValueVector(2); nodes.at(0) = makePtr("splice-unquote"); nodes.at(1) = readImpl(); return makePtr(nodes); } ValuePtr Reader::readList() { ignore(); // ( auto nodes = ValueVector(); while (!isEOF() && peek().type != Token::Type::ParenClose) { auto node = readImpl(); if (node == nullptr) { return nullptr; } nodes.push_back(node); } if (!consumeSpecific(Token { .type = Token::Type::ParenClose })) { // ) Error::the().add("expected ')', got EOF"); return nullptr; } return makePtr(nodes); } ValuePtr Reader::readVector() { ignore(); // [ auto nodes = ValueVector(); while (!isEOF() && peek().type != Token::Type::BracketClose) { auto node = readImpl(); if (node == nullptr) { return nullptr; } nodes.push_back(node); } if (!consumeSpecific(Token { .type = Token::Type::BracketClose })) { // ] Error::the().add("expected ']', got EOF"); } return makePtr(nodes); } ValuePtr Reader::readHashMap() { ignore(); // { Elements elements; while (!isEOF() && peek().type != Token::Type::BraceClose) { auto key = readImpl(); if (key == nullptr || isEOF()) { break; } if (peek().type == Token::Type::BraceClose) { Error::the().add("hash-map requires an even-sized list"); return nullptr; } if (!is(key.get()) && !is(key.get())) { Error::the().add(::format("wrong argument type: string or keyword, {}", key)); return nullptr; } auto value = readImpl(); elements.insert_or_assign(HashMap::getKeyString(key), value); } if (!consumeSpecific(Token { .type = Token::Type::BraceClose })) { // } Error::the().add("expected '}', got EOF"); return nullptr; } return makePtr(elements); } ValuePtr Reader::readQuote() { ignore(); // ' if (isEOF()) { Error::the().add("expected form, got EOF"); return nullptr; } auto nodes = ValueVector(2); nodes.at(0) = makePtr("quote"); nodes.at(1) = readImpl(); return makePtr(nodes); } ValuePtr Reader::readQuasiQuote() { ignore(); // ` if (isEOF()) { Error::the().add("expected form, got EOF"); return nullptr; } auto nodes = ValueVector(2); nodes.at(0) = makePtr("quasiquote"); nodes.at(1) = readImpl(); return makePtr(nodes); } ValuePtr Reader::readUnquote() { ignore(); // ~ if (isEOF()) { Error::the().add("expected form, got EOF"); return nullptr; } auto nodes = ValueVector(2); nodes.at(0) = makePtr("unquote"); nodes.at(1) = readImpl(); return makePtr(nodes); } ValuePtr Reader::readWithMeta() { ignore(); // ^ ignore(); // first token if (isEOF()) { // second token Error::the().add("expected form, got EOF"); return nullptr; } retreat(); auto nodes = ValueVector(3); nodes.at(0) = makePtr("with-meta"); nodes.at(2) = readImpl(); // Note: second Value is read first nodes.at(1) = readImpl(); return makePtr(nodes); } ValuePtr Reader::readDeref() { ignore(); // @ if (isEOF()) { Error::the().add("expected form, got EOF"); return nullptr; } auto nodes = ValueVector(2); nodes.at(0) = makePtr("deref"); nodes.at(1) = readImpl(); return makePtr(nodes); } ValuePtr Reader::readString() { std::string symbol = consume().symbol; return makePtr(symbol); } ValuePtr Reader::readKeyword() { return makePtr(consume().symbol); } ValuePtr Reader::readValue() { Token token = consume(); char* end_ptr = nullptr; int64_t result = std::strtoll(token.symbol.c_str(), &end_ptr, 10); if (end_ptr == token.symbol.c_str() + token.symbol.size()) { return makePtr(result); } if (token.symbol == "nil") { return makePtr(Constant::Nil); } else if (token.symbol == "true") { return makePtr(Constant::True); } else if (token.symbol == "false") { return makePtr(Constant::False); } return makePtr(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++]; } bool Reader::consumeSpecific(Token token) { if (isEOF() || peek().type != token.type) { return false; } ignore(); return true; } void Reader::ignore() { m_index++; } void Reader::retreat() { m_index--; } // ----------------------------------------- void Reader::dump(ValuePtr node) { m_indentation = 0; dumpImpl((node != nullptr) ? node : m_node); } void Reader::dumpImpl(ValuePtr node) { std::string indentation = std::string(m_indentation * INDENTATION_WIDTH, ' '); print("{}", indentation); bool pretty_print = Settings::the().get("pretty-print") == "1"; auto blue = fg(ruc::format::TerminalColor::BrightBlue); auto yellow = fg(ruc::format::TerminalColor::Yellow); Value* node_raw_ptr = node.get(); if (is(node_raw_ptr)) { auto container = (is(node_raw_ptr)) ? "List" : "Vector"; auto parens = (is(node_raw_ptr)) ? "()" : "[]"; pretty_print ? print(blue, "{}", container) : print("{}", container); print(" <"); pretty_print ? print(blue, "{}", parens) : print("{}", parens); print(">\n"); m_indentation++; auto nodes = std::static_pointer_cast(node)->nodesRead(); for (auto node : nodes) { dumpImpl(node); } m_indentation--; return; } else if (is(node_raw_ptr)) { auto hash_map = std::static_pointer_cast(node); auto elements = hash_map->elements(); pretty_print ? print(blue, "HashMap") : print("HashMap"); print(" <"); pretty_print ? print(blue, "{{}}") : print("{{}}"); print(">\n"); m_indentation++; ValuePtr key_node = nullptr; for (auto element : elements) { bool is_keyword = element.first.front() == 0x7f; // 127 is_keyword ? dumpImpl(makePtr(element.first.substr(1))) : dumpImpl(makePtr(element.first)); m_indentation++; dumpImpl(element.second); m_indentation--; } m_indentation--; return; } else if (is(node_raw_ptr)) { pretty_print ? print(yellow, "StringNode") : print("StringNode"); print(" <{}>", node); } else if (is(node_raw_ptr)) { pretty_print ? print(yellow, "KeywordNode") : print("KeywordNode"); print(" <{}>", node); } else if (is(node_raw_ptr)) { pretty_print ? print(yellow, "NumberNode") : print("NumberNode"); print(" <{}>", node); } else if (is(node_raw_ptr)) { pretty_print ? print(yellow, "ValueNode") : print("ValueNode"); print(" <{}>", node); } else if (is(node_raw_ptr)) { pretty_print ? print(yellow, "SymbolNode") : print("SymbolNode"); print(" <{}>", node); } else if (is(node_raw_ptr)) { auto function = std::static_pointer_cast(node); pretty_print ? print(blue, "Function") : print("Function"); print(" <"); pretty_print ? print(blue, "{}", function->name()) : print("{}", function->name()); print(">\n"); m_indentation++; indentation = std::string(m_indentation * INDENTATION_WIDTH, ' '); // bindings print("{}", indentation); pretty_print ? print(blue, "Bindings") : print("Bindings"); print(" <{}>\n", function->bindings()); m_indentation--; return; } else if (is(node_raw_ptr) || is(node_raw_ptr)) { auto container = (is(node_raw_ptr)) ? "Lambda" : "Macro"; auto lambda = std::static_pointer_cast(node); pretty_print ? print(blue, "{}", container) : print("{}", container); print(" <"); pretty_print ? print(blue, "{:p}", node_raw_ptr) : print("{:p}", node_raw_ptr); print(">\n"); m_indentation++; indentation = std::string(m_indentation * INDENTATION_WIDTH, ' '); // bindings print("{}", indentation); pretty_print ? print(blue, "Bindings") : print("Bindings"); print(" <"); const auto& bindings = lambda->bindings(); for (size_t i = 0; i < bindings.size(); ++i) { print("{}{}", (i > 0) ? " " : "", bindings[i]); } print(">\n"); // body dumpImpl(lambda->body()); m_indentation--; return; } else if (is(node_raw_ptr)) { pretty_print ? print(yellow, "AtomNode") : print("AtomNode"); print(" <{}>", std::static_pointer_cast(node)->deref()); } print("\n"); } } // namespace blaze