2026-07-31 15:58:09 +10:00
|
|
|
#include "parser.hpp"
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <unordered_map>
|
2026-08-01 09:16:32 +10:00
|
|
|
#include <variant>
|
|
|
|
|
#include <ostream>
|
2026-07-31 15:58:09 +10:00
|
|
|
|
|
|
|
|
using TT = Solstice::TokenType;
|
|
|
|
|
|
|
|
|
|
namespace Solstice {
|
|
|
|
|
|
|
|
|
|
std::unordered_map<TokenType, NodeType> tokToNodeType = {
|
|
|
|
|
{TT::Math_Add, NodeType::Add},
|
|
|
|
|
{TT::Math_Subtract, NodeType::Subtract},
|
|
|
|
|
{TT::Math_Multiply, NodeType::Multiply},
|
|
|
|
|
{TT::Math_Divide, NodeType::Divide},
|
|
|
|
|
{TT::Comparison_Equal, NodeType::Equal},
|
|
|
|
|
{TT::Comparison_NotEqual, NodeType::NotEqual},
|
|
|
|
|
{TT::Comparison_GreaterThan, NodeType::GreaterThan},
|
|
|
|
|
{TT::Comparison_LesserThan, NodeType::LesserThan},
|
|
|
|
|
};
|
|
|
|
|
|
2026-08-01 09:16:32 +10:00
|
|
|
std::optional<Literal> Node::getLiteral() const {
|
|
|
|
|
if (std::holds_alternative<Literal>(data)) {
|
|
|
|
|
return std::get<Literal>(data);
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
std::optional<std::string> Node::getIdentifier() const {
|
|
|
|
|
if (std::holds_alternative<std::string>(data)) {
|
|
|
|
|
return std::get<std::string>(data);
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline std::string nodeTypeToString(NodeType type) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case NodeType::Root:
|
|
|
|
|
return "Root";
|
|
|
|
|
case NodeType::Literal:
|
|
|
|
|
return "Literal";
|
|
|
|
|
case NodeType::Identifier:
|
|
|
|
|
return "Identifier";
|
2026-08-01 19:26:41 +10:00
|
|
|
case NodeType::Expression:
|
|
|
|
|
return "Expression";
|
|
|
|
|
case NodeType::Tuple:
|
|
|
|
|
return "Tuple";
|
2026-08-01 09:16:32 +10:00
|
|
|
case NodeType::FunctionBind:
|
|
|
|
|
return "FunctionBind";
|
|
|
|
|
case NodeType::Bind:
|
|
|
|
|
return "Bind";
|
|
|
|
|
case NodeType::Set:
|
|
|
|
|
return "Set";
|
|
|
|
|
case NodeType::Lambda:
|
|
|
|
|
return "Lambda";
|
|
|
|
|
case NodeType::FunctionCall:
|
|
|
|
|
return "FunctionCall";
|
|
|
|
|
case NodeType::Add:
|
|
|
|
|
return "Add";
|
|
|
|
|
case NodeType::Subtract:
|
|
|
|
|
return "Subtract";
|
|
|
|
|
case NodeType::Multiply:
|
|
|
|
|
return "Multiply";
|
|
|
|
|
case NodeType::Divide:
|
|
|
|
|
return "Divide";
|
|
|
|
|
case NodeType::Equal:
|
|
|
|
|
return "Equal";
|
|
|
|
|
case NodeType::NotEqual:
|
|
|
|
|
return "NotEqual";
|
|
|
|
|
case NodeType::GreaterThan:
|
|
|
|
|
return "GreaterThan";
|
|
|
|
|
case NodeType::LesserThan:
|
|
|
|
|
return "LesserThan";
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& stream, const Node& node) {
|
|
|
|
|
stream << "Node( Type=" << nodeTypeToString(node.type);
|
|
|
|
|
if (node.type == NodeType::Literal) {
|
|
|
|
|
auto lit = node.getLiteral();
|
|
|
|
|
if (lit.has_value()) {
|
|
|
|
|
stream << ", Literal: " << *lit;
|
|
|
|
|
}
|
|
|
|
|
} else if (node.type == NodeType::Identifier) {
|
|
|
|
|
auto id = node.getIdentifier();
|
|
|
|
|
if (id.has_value()) {
|
|
|
|
|
stream << ", Identifier: " << *id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!node.children.empty()) {
|
|
|
|
|
stream << ", Children: { ";
|
|
|
|
|
bool first = true;
|
|
|
|
|
for (const auto& node : node.children) {
|
|
|
|
|
if (first) {
|
|
|
|
|
first = false;
|
|
|
|
|
} else {
|
|
|
|
|
stream << ", ";
|
|
|
|
|
}
|
|
|
|
|
stream << node;
|
|
|
|
|
}
|
|
|
|
|
stream << " }";
|
|
|
|
|
}
|
|
|
|
|
stream << " )";
|
|
|
|
|
return stream;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 15:58:09 +10:00
|
|
|
std::optional<Token> Parser::peek(int64_t ahead) {
|
|
|
|
|
if (current + ahead >= input.size() || current + ahead < 0) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
return input[current + ahead];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<Token> Parser::consume() {
|
|
|
|
|
if (current >= input.size()) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
return input[current++];
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-01 19:26:41 +10:00
|
|
|
std::optional<Node> Parser::getPreviousNode(Node& parent, bool popBack) {
|
2026-07-31 15:58:09 +10:00
|
|
|
if (parent.children.empty()) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
Node node = parent.children[parent.children.size() - 1];
|
2026-08-01 19:26:41 +10:00
|
|
|
if (popBack) {
|
|
|
|
|
parent.children.pop_back();
|
|
|
|
|
}
|
2026-07-31 15:58:09 +10:00
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Node Parser::parseExpr(TT type) {
|
2026-08-01 19:26:41 +10:00
|
|
|
auto left = getPreviousNode(*context);
|
2026-07-31 15:58:09 +10:00
|
|
|
if (!left.has_value()) {
|
2026-08-01 18:01:59 +10:00
|
|
|
throw std::runtime_error("Expecting expression on left of expression");
|
2026-07-31 15:58:09 +10:00
|
|
|
}
|
2026-08-01 18:01:59 +10:00
|
|
|
Precedence leftPrec = getNodePrecedence(*left);
|
2026-07-31 15:58:09 +10:00
|
|
|
Precedence precedence = getTokenPrecedence(Token(type));
|
|
|
|
|
auto right = parseOneNode(precedence);
|
|
|
|
|
if (!right.has_value()) {
|
2026-08-01 18:01:59 +10:00
|
|
|
throw std::runtime_error("Expecting expression on right of expression");
|
2026-07-31 15:58:09 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NodeType nodeType;
|
|
|
|
|
if (tokToNodeType.find(type) == tokToNodeType.end()) {
|
|
|
|
|
throw std::runtime_error("FIXME couldn't map token type to node type");
|
|
|
|
|
} else {
|
|
|
|
|
nodeType = tokToNodeType[type];
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-01 18:01:59 +10:00
|
|
|
if (leftPrec >= precedence) {
|
|
|
|
|
// enclose the left node inside this node
|
|
|
|
|
return Node(nodeType, {*left, *right});
|
|
|
|
|
} else {
|
|
|
|
|
// steal the right child of the left node and use it as our left value,
|
|
|
|
|
// then enclose ourself inside the left node
|
|
|
|
|
auto newLeft = left->children[1];
|
|
|
|
|
Node us{nodeType, {newLeft, *right}};
|
|
|
|
|
|
|
|
|
|
left->children[1] = us;
|
|
|
|
|
return *left;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 15:58:09 +10:00
|
|
|
}
|
|
|
|
|
|
2026-08-01 19:26:41 +10:00
|
|
|
Node Parser::parseOpenParen() {
|
|
|
|
|
Node node{NodeType::Expression};
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
auto next = peek();
|
|
|
|
|
if (!next.has_value()) {
|
|
|
|
|
throw std::runtime_error("unclosed parens");
|
|
|
|
|
}
|
|
|
|
|
if (next->type == TT::CloseParen) {
|
|
|
|
|
consume();
|
|
|
|
|
if (node.children.size() != 1) {
|
|
|
|
|
node.type = NodeType::Tuple;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Node elem{NodeType::Root};
|
|
|
|
|
Node* prevContext = context;
|
|
|
|
|
context = &elem;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
auto t = peek();
|
|
|
|
|
if (!t.has_value() || t->type == TT::CloseParen || t->type == TT::Comma) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
auto n = parseOneNode(Precedence::NewLine);
|
|
|
|
|
if (!n.has_value()) break;
|
|
|
|
|
elem.children.push_back(*n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
context = prevContext;
|
|
|
|
|
|
|
|
|
|
if (elem.children.empty()) {
|
|
|
|
|
throw std::runtime_error("expecting expression in parens");
|
|
|
|
|
}
|
|
|
|
|
node.children.push_back(elem.children.back());
|
|
|
|
|
|
|
|
|
|
auto sep = peek();
|
|
|
|
|
if (!sep.has_value()) {
|
|
|
|
|
throw std::runtime_error("unclosed parens");
|
|
|
|
|
}
|
|
|
|
|
if (sep->type == TT::Comma) {
|
|
|
|
|
consume();
|
|
|
|
|
} else if (sep->type != TT::CloseParen) {
|
|
|
|
|
throw std::runtime_error("expecting comma between expressions in parens");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 15:58:09 +10:00
|
|
|
Node Parser::parseLiteral() {
|
|
|
|
|
auto current = peek(-1);
|
|
|
|
|
if (!current.has_value()) {
|
|
|
|
|
throw std::runtime_error("FIXME couldn't get current token");
|
|
|
|
|
}
|
|
|
|
|
auto literal = current->getLiteral();
|
|
|
|
|
if (!literal.has_value()) {
|
|
|
|
|
throw std::runtime_error("FIXME token with type literal does not hold a literal");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Node(*literal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Node Parser::parseIdentifier() {
|
|
|
|
|
auto current = peek(-1);
|
|
|
|
|
if (!current.has_value()) {
|
|
|
|
|
throw std::runtime_error("FIXME couldn't get current token");
|
|
|
|
|
}
|
|
|
|
|
auto id = current->getIdentifier();
|
|
|
|
|
if (!id.has_value()) {
|
|
|
|
|
throw std::runtime_error("FIXME token with type identifier does not hold an identifier");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Node(*id);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-01 18:01:59 +10:00
|
|
|
Precedence Parser::getNodePrecedence(const Node& node) {
|
|
|
|
|
switch (node.type) {
|
|
|
|
|
case NodeType::Root:
|
|
|
|
|
return Precedence::Root;
|
|
|
|
|
case NodeType::Literal:
|
|
|
|
|
case NodeType::Identifier:
|
2026-08-01 19:26:41 +10:00
|
|
|
case NodeType::Lambda:
|
|
|
|
|
case NodeType::FunctionCall:
|
|
|
|
|
case NodeType::Expression:
|
|
|
|
|
case NodeType::Tuple:
|
2026-08-01 18:01:59 +10:00
|
|
|
return Precedence::Identifier;
|
|
|
|
|
case NodeType::FunctionBind:
|
|
|
|
|
case NodeType::Bind:
|
|
|
|
|
case NodeType::Set:
|
|
|
|
|
return Precedence::Set;
|
|
|
|
|
case NodeType::Add:
|
|
|
|
|
case NodeType::Subtract:
|
|
|
|
|
return Precedence::Add;
|
|
|
|
|
case NodeType::Multiply:
|
|
|
|
|
case NodeType::Divide:
|
|
|
|
|
return Precedence::Multiply;
|
|
|
|
|
case NodeType::Equal:
|
|
|
|
|
case NodeType::NotEqual:
|
|
|
|
|
case NodeType::GreaterThan:
|
|
|
|
|
case NodeType::LesserThan:
|
|
|
|
|
return Precedence::Compare;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 15:58:09 +10:00
|
|
|
Precedence Parser::getTokenPrecedence(const Token& token) {
|
|
|
|
|
switch (token.type) {
|
|
|
|
|
case TT::None:
|
|
|
|
|
case TT::Kw_Type:
|
|
|
|
|
case TT::Hash_CImport:
|
|
|
|
|
case TT::Hash_Effect:
|
|
|
|
|
case TT::Action_Function:
|
|
|
|
|
return Precedence::Other;
|
|
|
|
|
case TT::Math_Add:
|
|
|
|
|
case TT::Math_Subtract:
|
|
|
|
|
return Precedence::Add;
|
|
|
|
|
case TT::Math_Multiply:
|
|
|
|
|
case TT::Math_Divide:
|
|
|
|
|
return Precedence::Multiply;
|
|
|
|
|
case TT::Assign_Bind:
|
|
|
|
|
case TT::Assign_Set:
|
|
|
|
|
case TT::Assign_Type:
|
|
|
|
|
return Precedence::Set;
|
|
|
|
|
case TT::NewLine:
|
|
|
|
|
return Precedence::NewLine;
|
|
|
|
|
case TT::Identifier:
|
|
|
|
|
return Precedence::Identifier;
|
|
|
|
|
case TT::Literal:
|
|
|
|
|
return Precedence::Identifier;
|
|
|
|
|
default:
|
|
|
|
|
return Precedence::Other;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<Node> Parser::parseOneNode(Precedence precedence) {
|
|
|
|
|
auto next = peek();
|
|
|
|
|
if (!next.has_value()) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
2026-08-01 18:01:59 +10:00
|
|
|
if (getTokenPrecedence(*next) <= precedence) {
|
2026-07-31 15:58:09 +10:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
consume();
|
|
|
|
|
switch (next->type) {
|
|
|
|
|
case TT::None: break;
|
|
|
|
|
case TT::Identifier:
|
|
|
|
|
return parseIdentifier();
|
|
|
|
|
case TT::Literal:
|
|
|
|
|
return parseLiteral();
|
|
|
|
|
case TT::Math_Add:
|
|
|
|
|
case TT::Math_Subtract:
|
|
|
|
|
case TT::Math_Multiply:
|
|
|
|
|
case TT::Math_Divide:
|
|
|
|
|
return parseExpr(next->type);
|
2026-08-01 19:26:41 +10:00
|
|
|
case TT::OpenParen:
|
|
|
|
|
return parseOpenParen();
|
|
|
|
|
|
2026-08-01 19:30:49 +10:00
|
|
|
case TT::NewLine:
|
|
|
|
|
// ignore new line
|
|
|
|
|
return parseOneNode(precedence);
|
|
|
|
|
|
2026-08-01 19:26:41 +10:00
|
|
|
// stuff that causes errors
|
|
|
|
|
case TT::CloseParen:
|
|
|
|
|
throw std::runtime_error("Extra closing paren");
|
|
|
|
|
case TT::CloseSpiky:
|
|
|
|
|
throw std::runtime_error("Extra closing angle bracket");
|
|
|
|
|
case TT::CloseCurly:
|
|
|
|
|
throw std::runtime_error("Extra closing curly brace");
|
2026-07-31 15:58:09 +10:00
|
|
|
}
|
|
|
|
|
throw std::runtime_error("FIXME: unimplemented parsing case");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Node& Parser::parse() {
|
|
|
|
|
while (auto node = parseOneNode(Precedence::Root)) {
|
|
|
|
|
output.children.push_back(*node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
}
|