From 9a311c3cb85d4836733644fda42ed1730907177c Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Thu, 25 Dec 2025 22:37:40 +1100 Subject: [PATCH] Better errors, comments --- src/error.cpp | 35 ++++++-- src/error.h | 9 +- src/lexer.cpp | 95 +++++++++++++++++---- src/lexer.h | 14 ++- src/parser.cpp | 225 ++++++++++++++++++++++++++++++------------------- src/parser.h | 33 +++++--- 6 files changed, 283 insertions(+), 128 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index 0ce3d05..ab98ce9 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -4,14 +4,37 @@ namespace Solstice { namespace Error { - [[noreturn]] void syntaxError(std::string what) { - std::cout << "\033[31mSyntax error\n"; - std::cout << "What: " << what << "\n"; + // ANSI Color Codes + const std::string RED = "\033[31m"; + const std::string YELLOW = "\033[33m"; + const std::string CYAN = "\033[36m"; + const std::string RESET = "\033[0m"; + const std::string BOLD = "\033[1m"; + + [[noreturn]] void syntaxError(std::string what, int line, std::string lineContent) { + std::cout << BOLD << RED << "Syntax Error" << RESET; + if (line > 0) std::cout << " on line " << BOLD << line << RESET; + std::cout << ":\n"; + + if (!lineContent.empty()) { + std::cout << "\n " << CYAN << lineContent << RESET << "\n\n"; + } else { + std::cout << "\n"; + } + std::cout << YELLOW << "-> " << what << RESET << "\n"; exit(1); } - [[noreturn]] void typingError(std::string what) { - std::cout << "\033[31mTyping error\n"; - std::cout << "What: " << what << "\n"; + [[noreturn]] void typingError(std::string what, int line, std::string lineContent) { + std::cout << BOLD << RED << "Typing Error" << RESET; + if (line > 0) std::cout << " on line " << BOLD << line << RESET; + std::cout << ":\n"; + + if (!lineContent.empty()) { + std::cout << "\n " << CYAN << lineContent << RESET << "\n\n"; + } else { + std::cout << "\n"; + } + std::cout << YELLOW << "-> " << what << RESET << "\n"; exit(1); } } diff --git a/src/error.h b/src/error.h index 726fe51..18cba44 100644 --- a/src/error.h +++ b/src/error.h @@ -1,8 +1,13 @@ +#ifndef ERROR_H +#define ERROR_H + #include namespace Solstice { namespace Error { - [[noreturn]] void syntaxError(std::string what); - [[noreturn]] void typingError(std::string what); + [[noreturn]] void syntaxError(std::string what, int line = 0, std::string lineContent = ""); + [[noreturn]] void typingError(std::string what, int line = 0, std::string lineContent = ""); } } + +#endif \ No newline at end of file diff --git a/src/lexer.cpp b/src/lexer.cpp index c2aeebc..b30e4c7 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "lexer.h" namespace Solstice { @@ -21,45 +22,102 @@ namespace Solstice { } } - Lexer::Lexer(std::string in) : input(in), size(in.size()) {} - std::vector Lexer::lex() { + Lexer::Lexer(std::string in) : input(in), size(in.size()) { + std::stringstream ss(in); + std::string line; + while (std::getline(ss, line)) { + lines.push_back(line); + } + if (lines.empty()) lines.push_back(""); + } + + std::vector Lexer::lex() { current = 0; - std::vector tokens; + std::vector tokens; std::string buf; bool inString = false; + size_t currentLine = 1; + + auto addToken = [&](std::string val) { + std::string content = ""; + if (currentLine <= lines.size()) { + content = lines[currentLine - 1]; + } + tokens.push_back({val, currentLine, content}); + }; + while (auto copt = consume()) { char c = copt.value(); + if (inString) { buf.push_back(c); if (c == '"') { - tokens.push_back(buf); + addToken(buf); buf.clear(); inString = false; } + if (c == '\n') currentLine++; continue; } + switch (c) { // double quotes are special case '"': { if (!buf.empty()) { - tokens.push_back(buf); + addToken(buf); buf.clear(); } buf.push_back(c); inString = true; break; } + // slash is special + case '/': { + if (peek(0).has_value() && peek(0).value() == '/') { + while (auto copt = consume()) { + char c = copt.value(); + if (c == '\n') { + break; + } + } + continue; + } + std::string newToken(1, c); + auto tokenopt = peek(0); + if (tokenopt) { + char token = tokenopt.value(); + if (token == '=') { + newToken += token; + consume(); + } + } + if (!buf.empty()) { + addToken(buf); + buf.clear(); + } + addToken(newToken); + break; + } // tokens which are not followed by anything case '\n': - case '(': + { + if (!buf.empty()) { + addToken(buf); + buf.clear(); + } + addToken(std::string(1, c)); + currentLine++; + break; + } + case '(': case ')': case '}': { if (!buf.empty()) { - tokens.push_back(buf); + addToken(buf); buf.clear(); } - tokens.push_back(std::string(1, c)); + addToken(std::string(1, c)); break; } // tokens which may be followed by either themselves @@ -77,16 +135,15 @@ namespace Solstice { } } if (!buf.empty()) { - tokens.push_back(buf); + addToken(buf); buf.clear(); } - tokens.push_back(newToken); + addToken(newToken); break; } // tokens which may be followed by an equals sign case '!': case '*': - case '/': case '=': case '>': case '<': @@ -101,27 +158,29 @@ namespace Solstice { } } if (!buf.empty()) { - tokens.push_back(buf); + addToken(buf); buf.clear(); } - tokens.push_back(newToken); + addToken(newToken); break; } // tokens which need a newline inserted for them case '{': { if (!buf.empty()) { - tokens.push_back(buf); + addToken(buf); buf.clear(); } - tokens.push_back("\n"); - tokens.push_back(std::string(1, c)); + addToken("\n"); + addToken(std::string(1, c)); + break; } // tokens which do not need to be included case ' ': + case '\r': { if (!buf.empty()) { - tokens.push_back(buf); + addToken(buf); buf.clear(); } break; @@ -134,7 +193,7 @@ namespace Solstice { } if (!buf.empty()) { - tokens.push_back(buf); + addToken(buf); } return tokens; } diff --git a/src/lexer.h b/src/lexer.h index c1f9fcf..1efc761 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -1,17 +1,29 @@ +#ifndef LEXER_H +#define LEXER_H + #include #include #include namespace Solstice { + struct Token { + std::string value; + int line; + std::string lineContent; + }; + class Lexer { std::string input; + std::vector lines; size_t size; size_t current; std::optional peek(int ahead = 1); std::optional consume(); public: Lexer(std::string in); - std::vector lex(); + std::vector lex(); }; } + +#endif \ No newline at end of file diff --git a/src/parser.cpp b/src/parser.cpp index fe3c0aa..630648f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4,7 +4,7 @@ #include #include -#define parseOneToken(token) Parser({token.value()}).parse().children[0] +#define parseOneToken(token) Parser({token}).parse().children[0] namespace Solstice { namespace Parser { @@ -21,7 +21,7 @@ namespace Solstice { if (variables.find(i.outputId) != variables.end()) { return variables[i.outputId]; } else { - Error::syntaxError("Unknown variable"); + Error::syntaxError("Unknown variable", i.line, i.lineContent); } break; } @@ -178,14 +178,14 @@ namespace Solstice { break; } case SolNodeType::Add: { - ensure3(children[0], SolDataType::Int, SolDataType::Double, SolDataType::String); - ensure3(children[1], SolDataType::Int, SolDataType::Double, SolDataType::String); - ensuresame(children[0], children[1]); + ensure3(children[0], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'"); + ensure3(children[1], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'"); + ensuresame(children[0], children[1], "operator '+'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(ADD); if (children.size() < 2) { - std::cout << "Need more stuff to add\n"; + Error::typingError("Need more stuff to add", children[0].line, children[0].lineContent); } groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); @@ -197,13 +197,13 @@ namespace Solstice { break; } case SolNodeType::Subtract: { - ensure2(children[0], SolDataType::Int, SolDataType::Double); - ensure2(children[1], SolDataType::Int, SolDataType::Double); + ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '-'"); + ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '-'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(SUBTRACT); if (children.size() < 2) { - std::cout << "Need more stuff to add\n"; + Error::typingError("Need more stuff to subtract", children[0].line, children[0].lineContent); } groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); @@ -215,13 +215,13 @@ namespace Solstice { break; } case SolNodeType::Multiply: { - ensure2(children[0], SolDataType::Int, SolDataType::Double); - ensure2(children[1], SolDataType::Int, SolDataType::Double); + ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '*'"); + ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '*'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(MULTIPLY); if (children.size() < 2) { - std::cout << "Need more stuff to add\n"; + Error::typingError("Need more stuff to multiply", children[0].line, children[0].lineContent); } groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); @@ -233,13 +233,13 @@ namespace Solstice { break; } case SolNodeType::Divide: { - ensure2(children[0], SolDataType::Int, SolDataType::Double); - ensure2(children[1], SolDataType::Int, SolDataType::Double); + ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '/'"); + ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '/'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(DIVIDE); if (children.size() < 2) { - std::cout << "Need more stuff to add\n"; + Error::typingError("Need more stuff to divide", children[0].line, children[0].lineContent); } groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); @@ -251,12 +251,12 @@ namespace Solstice { break; } case SolNodeType::Equal: { - ensuresame(children[0], children[1]); + ensuresame(children[0], children[1], "operator '=='"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(EQUAL); if (children.size() < 2) { - std::cout << "Need more stuff to equal\n"; + Error::typingError("Need more stuff to equal", children[0].line, children[0].lineContent); } groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); @@ -268,12 +268,12 @@ namespace Solstice { break; } case SolNodeType::Inequal: { - ensuresame(children[0], children[1]); + ensuresame(children[0], children[1], "operator '!='"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(INEQUAL); if (children.size() < 2) { - std::cout << "Need more stuff to inequal\n"; + Error::typingError("Need more stuff to inequal", children[0].line, children[0].lineContent); } groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); @@ -285,12 +285,12 @@ namespace Solstice { break; } case SolNodeType::Greater: { - ensuresame(children[0], children[1]); + ensuresame(children[0], children[1], "operator '>'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(GREATER); if (children.size() < 2) { - std::cout << "Need more stuff to greater\n"; + Error::typingError("Need more stuff to greater", children[0].line, children[0].lineContent); } groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); @@ -302,12 +302,12 @@ namespace Solstice { break; } case SolNodeType::Lesser: { - ensuresame(children[0], children[1]); + ensuresame(children[0], children[1], "operator '<'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(LESSER); if (children.size() < 2) { - std::cout << "Need more stuff to lesser\n"; + Error::typingError("Need more stuff to lesser", children[0].line, children[0].lineContent); } groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[1].outputId.data())); @@ -319,10 +319,10 @@ namespace Solstice { break; } case SolNodeType::EqGreater: { - ensuresame(children[0], children[1]); + ensuresame(children[0], children[1], "operator '>='"); SolGroundCodeBlock codeBlock; if (children.size() < 2) { - std::cout << "Need more stuff to inequal\n"; + Error::typingError("Need more stuff to inequal", children[0].line, children[0].lineContent); } outputId = "tmp_" + std::to_string(tmpIdIterator++); std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++); @@ -392,10 +392,10 @@ namespace Solstice { break; } case SolNodeType::EqLesser: { - ensuresame(children[0], children[1]); + ensuresame(children[0], children[1], "operator '<='"); SolGroundCodeBlock codeBlock; if (children.size() < 2) { - std::cout << "Need more stuff to inequal\n"; + Error::typingError("Need more stuff to inequal", children[0].line, children[0].lineContent); } outputId = "tmp_" + std::to_string(tmpIdIterator++); std::string trueLabelIdString = "internal_true" + std::to_string(labelIterator++); @@ -469,7 +469,7 @@ namespace Solstice { exists(children[0]); GroundInstruction gi = groundCreateInstruction(PRINTLN); if (children.size() < 1) { - std::cout << "Need more stuff to puts\n"; + Error::typingError("Need more stuff to puts", children[0].line, children[0].lineContent); } groundAddReferenceToInstruction(&gi, groundCreateReference(VALREF, children[0].outputId.data())); codeBlock.code.push_back(gi); @@ -478,7 +478,7 @@ namespace Solstice { break; } case SolNodeType::If: { - ensure(children[0], SolDataType::Bool); + ensure(children[0], SolDataType::Bool, "if condition"); auto conditionCode = children[0].generateCode(); code.insert(code.end(), conditionCode.begin(), conditionCode.end()); outputId = "tmp_" + std::to_string(tmpIdIterator++); @@ -510,7 +510,7 @@ namespace Solstice { break; } case SolNodeType::While: { - ensure(children[0], SolDataType::Bool); + ensure(children[0], SolDataType::Bool, "while condition"); SolGroundCodeBlock startLabelBlock; std::string startLabelIdString = "whilestart_" + std::to_string(labelIterator++); std::string endLabelIdString = "whileend_" + std::to_string(labelIterator); @@ -562,7 +562,7 @@ namespace Solstice { case SolNodeType::Set: { if (variables.find(children[0].outputId) != variables.end()) { if (variables[children[0].outputId] != checkNodeReturnType(children[1])) { - Error::typingError("Cannot change type of this variable"); + Error::typingError("Cannot change type of this variable", children[0].line, children[0].lineContent); } } exists(children[1]); @@ -580,7 +580,7 @@ namespace Solstice { case SolNodeType::FunctionCall: { // Take care of in built functions if (children[0].outputId == "input") { - ensure(children[1], SolDataType::String); + ensure(children[1], SolDataType::String, "input function argument"); SolGroundCodeBlock inputCodeBlock; if (children.size() > 1) { GroundInstruction printInstruction = groundCreateInstruction(PRINT); @@ -604,9 +604,9 @@ namespace Solstice { } // Parser Implementation - Parser::Parser(std::vector in) : tokensToParse(in) {} + Parser::Parser(std::vector in) : tokensToParse(in) {} - std::optional Parser::peek(int ahead) { + std::optional Parser::peek(int ahead) { if (current + ahead < size) { return tokensToParse[current + ahead]; } else { @@ -614,7 +614,7 @@ namespace Solstice { } } - std::optional Parser::consume() { + std::optional Parser::consume() { if (current < size) { return tokensToParse[current++]; } else { @@ -752,14 +752,16 @@ namespace Solstice { SolNode Parser::parsePrimary() { auto tokenopt = consume(); if (!tokenopt) { - std::cout << "Unexpected end of input\n"; - exit(1); + Error::syntaxError("Unexpected end of input"); } - std::string token = tokenopt.value(); + Token tokenObj = tokenopt.value(); + std::string token = tokenObj.value; SolNodeType type = getNodeType(token); if (type == SolNodeType::Value) { SolNode node(SolNodeType::Value); + node.line = tokenObj.line; + node.lineContent = tokenObj.lineContent; switch (getDataType(token)) { case SolDataType::Int: node.setValue((int64_t) std::stoll(token)); break; case SolDataType::Double: node.setValue(std::stod(token)); break; @@ -774,19 +776,28 @@ namespace Solstice { if (type == SolNodeType::Identifier) { SolNode idNode(SolNodeType::Identifier); idNode.outputId = token; - if (peek().value_or("") == "(") { + idNode.line = tokenObj.line; + idNode.lineContent = tokenObj.lineContent; + + auto peekOpt = peek(); + if (peekOpt.has_value() && peekOpt.value().value == "(") { consume(); // ( SolNode fnCallNode(SolNodeType::FunctionCall); + fnCallNode.line = tokenObj.line; + fnCallNode.lineContent = tokenObj.lineContent; fnCallNode.addNode(idNode); - std::vector tokens; + std::vector tokens; size_t brackets = 1; while (auto t = consume()) { - if (t.value() == "(") brackets++; - if (t.value() == ")") brackets--; + if (t.value().value == "(") brackets++; + if (t.value().value == ")") brackets--; if (brackets == 0) break; tokens.push_back(t.value()); } + if (brackets != 0) { + Error::syntaxError("Unclosed function call bracket", tokenObj.line, tokenObj.lineContent); + } auto args = Parser(tokens).parse(); for(auto& child : args.children) fnCallNode.addNode(child); return fnCallNode; @@ -795,21 +806,23 @@ namespace Solstice { } if (type == SolNodeType::BracketStart) { - std::vector tokens; + std::vector tokens; size_t brackets = 1; while (auto t = consume()) { - if (t.value() == "(") brackets++; - if (t.value() == ")") brackets--; + if (t.value().value == "(") brackets++; + if (t.value().value == ")") brackets--; if (brackets == 0) break; tokens.push_back(t.value()); } + if (brackets != 0) { + Error::syntaxError("Unclosed bracket", tokenObj.line, tokenObj.lineContent); + } auto inner = Parser(tokens).parse(); if (inner.children.size() > 0) return inner.children[0]; return SolNode(SolNodeType::None); } - std::cout << "Unexpected token in expression: " << token << "\n"; - exit(1); + Error::syntaxError("Unexpected token in expression: " + token, tokenObj.line, tokenObj.lineContent); } SolNode Parser::parseExpression(int minPrec) { @@ -817,13 +830,16 @@ namespace Solstice { while(true) { auto opOpt = peek(); if (!opOpt) break; - std::string op = opOpt.value(); + Token opToken = opOpt.value(); + std::string op = opToken.value; int prec = getPrecedence(op); if (prec < minPrec) break; consume(); SolNode rhs = parseExpression(prec + 1); SolNode newNode(getNodeType(op)); + newNode.line = opToken.line; + newNode.lineContent = opToken.lineContent; newNode.addNode(lhs); newNode.addNode(rhs); lhs = newNode; @@ -836,37 +852,48 @@ namespace Solstice { size = tokensToParse.size(); SolNode rootNode(SolNodeType::Root); while (auto tokenopt = consume()) { - std::string token = tokenopt.value(); + Token tokenObj = tokenopt.value(); + std::string token = tokenObj.value; switch (getNodeType(token)) { case SolNodeType::Value: { switch (getDataType(token)) { case SolDataType::Int: { SolNode intNode(SolNodeType::Value); intNode.setValue((int64_t) std::stoll(token)); + intNode.line = tokenObj.line; + intNode.lineContent = tokenObj.lineContent; rootNode.addNode(intNode); break; } case SolDataType::Double: { SolNode doubleNode(SolNodeType::Value); doubleNode.setValue(std::stod(token)); + doubleNode.line = tokenObj.line; + doubleNode.lineContent = tokenObj.lineContent; rootNode.addNode(doubleNode); break; } case SolDataType::String: { SolNode stringNode(SolNodeType::Value); stringNode.setValue(token.substr(1, token.size() - 2)); + stringNode.line = tokenObj.line; + stringNode.lineContent = tokenObj.lineContent; rootNode.addNode(stringNode); break; } case SolDataType::Char: { SolNode charNode(SolNodeType::Value); charNode.setValue(token[1]); + charNode.line = tokenObj.line; + charNode.lineContent = tokenObj.lineContent; rootNode.addNode(charNode); break; } case SolDataType::Bool: { SolNode boolNode(SolNodeType::Value); boolNode.setValue(token == "true"); + boolNode.line = tokenObj.line; + boolNode.lineContent = tokenObj.lineContent; rootNode.addNode(boolNode); break; } @@ -879,9 +906,11 @@ namespace Solstice { case SolNodeType::Divide: { SolNode opNode(getNodeType(token)); + opNode.line = tokenObj.line; + opNode.lineContent = tokenObj.lineContent; + if (rootNode.children.empty()) { - std::cout << "Expected operand before operator\n"; - exit(1); + Error::syntaxError("Expected operand before operator", tokenObj.line, tokenObj.lineContent); } opNode.addNode(rootNode.children.back()); rootNode.children.pop_back(); @@ -901,15 +930,17 @@ namespace Solstice { case SolNodeType::EqGreater: { SolNode equalNode(getNodeType(token)); + equalNode.line = tokenObj.line; + equalNode.lineContent = tokenObj.lineContent; + if (rootNode.children.empty()) { - std::cout << "ah dingus\n"; - exit(1); + Error::syntaxError("Expected operand before comparison", tokenObj.line, tokenObj.lineContent); } equalNode.addNode(rootNode.children.back()); rootNode.children.pop_back(); - std::vector tokens; + std::vector tokens; while (auto tokenopt = consume()) { - if (tokenopt.value() == "\n") { + if (tokenopt.value().value == "\n") { break; } tokens.push_back(tokenopt.value()); @@ -921,9 +952,11 @@ namespace Solstice { } case SolNodeType::Puts: { SolNode putsNode(SolNodeType::Puts); - std::vector tokens; + putsNode.line = tokenObj.line; + putsNode.lineContent = tokenObj.lineContent; + std::vector tokens; while (auto tokenopt = consume()) { - if (tokenopt.value() == "\n") { + if (tokenopt.value().value == "\n") { break; } tokens.push_back(tokenopt.value()); @@ -937,9 +970,11 @@ namespace Solstice { } case SolNodeType::If: { SolNode ifNode(SolNodeType::If); - std::vector tokens; + ifNode.line = tokenObj.line; + ifNode.lineContent = tokenObj.lineContent; + std::vector tokens; while (auto tokenopt = consume()) { - if (tokenopt.value() == "\n") { + if (tokenopt.value().value == "\n") { break; } tokens.push_back(tokenopt.value()); @@ -950,27 +985,28 @@ namespace Solstice { size_t brackets = 1; auto tokenopt = consume(); if (tokenopt) { - if (tokenopt.value() == "{") { + if (tokenopt.value().value == "{") { tokens.push_back(tokenopt.value()); while (auto tokenopt = consume()) { tokens.push_back(tokenopt.value()); - if (tokenopt.value() == "{") { + if (tokenopt.value().value == "{") { brackets++; } - if (tokenopt.value() == "}") { + if (tokenopt.value().value == "}") { brackets--; } if (brackets == 0) { break; } } + if (brackets != 0) { + Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent); + } } else { - std::cout << "I want a code block instead of a " + tokenopt.value() + "\n"; - exit(1); + Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent); } } else { - std::cout << "FEED ME MORE TOKENSSSSS\n"; - exit(1); + Error::syntaxError("Expected code block after if condition", ifNode.line, ifNode.lineContent); } auto childCodeBlock = Parser(tokens).parse(); ifNode.addNode(childCodeBlock.children[0]); @@ -979,9 +1015,11 @@ namespace Solstice { } case SolNodeType::While: { SolNode whileNode(SolNodeType::While); - std::vector tokens; + whileNode.line = tokenObj.line; + whileNode.lineContent = tokenObj.lineContent; + std::vector tokens; while (auto tokenopt = consume()) { - if (tokenopt.value() == "\n") { + if (tokenopt.value().value == "\n") { break; } tokens.push_back(tokenopt.value()); @@ -992,27 +1030,28 @@ namespace Solstice { size_t brackets = 1; auto tokenopt = consume(); if (tokenopt) { - if (tokenopt.value() == "{") { + if (tokenopt.value().value == "{") { tokens.push_back(tokenopt.value()); while (auto tokenopt = consume()) { tokens.push_back(tokenopt.value()); - if (tokenopt.value() == "{") { + if (tokenopt.value().value == "{") { brackets++; } - if (tokenopt.value() == "}") { + if (tokenopt.value().value == "}") { brackets--; } if (brackets == 0) { break; } } + if (brackets != 0) { + Error::syntaxError("Unclosed code block", tokenopt.value().line, tokenopt.value().lineContent); + } } else { - std::cout << "I want a code block instead of a " + tokenopt.value() + "\n"; - exit(1); + Error::syntaxError("I want a code block instead of a " + tokenopt.value().value, tokenopt.value().line, tokenopt.value().lineContent); } } else { - std::cout << "FEED ME MORE TOKENSSSSS\n"; - exit(1); + Error::syntaxError("Expected code block after while condition", whileNode.line, whileNode.lineContent); } auto childCodeBlock = Parser(tokens).parse(); whileNode.addNode(childCodeBlock.children[0]); @@ -1021,13 +1060,15 @@ namespace Solstice { } case SolNodeType::CodeBlockStart: { SolNode codeBlockNode(SolNodeType::CodeBlock); + codeBlockNode.line = tokenObj.line; + codeBlockNode.lineContent = tokenObj.lineContent; size_t brackets = 1; - std::vector tokens; + std::vector tokens; while (auto tokenopt = consume()) { - if (tokenopt.value() == "{") { + if (tokenopt.value().value == "{") { brackets++; } - if (tokenopt.value() == "}") { + if (tokenopt.value().value == "}") { brackets--; } if (brackets == 0) { @@ -1035,6 +1076,9 @@ namespace Solstice { } tokens.push_back(tokenopt.value()); } + if (brackets != 0) { + Error::syntaxError("Unclosed code block", tokenObj.line, tokenObj.lineContent); + } codeBlockNode.children = Parser(tokens).parse().children; rootNode.addNode(codeBlockNode); break; @@ -1042,16 +1086,21 @@ namespace Solstice { case SolNodeType::Identifier: { SolNode idNode(SolNodeType::Identifier); idNode.outputId = token; + idNode.line = tokenObj.line; + idNode.lineContent = tokenObj.lineContent; rootNode.addNode(idNode); break; } case SolNodeType::Set: { SolNode setNode(SolNodeType::Set); + setNode.line = tokenObj.line; + setNode.lineContent = tokenObj.lineContent; + setNode.addNode(rootNode.children.back()); rootNode.children.pop_back(); - std::vector tokens; + std::vector tokens; while (auto tokenopt = consume()) { - if (tokenopt.value() == "\n") { + if (tokenopt.value().value == "\n") { break; } tokens.push_back(tokenopt.value()); @@ -1064,15 +1113,17 @@ namespace Solstice { case SolNodeType::BracketStart: { if (rootNode.children.back().nodeType == SolNodeType::Identifier) { SolNode fnCallNode(SolNodeType::FunctionCall); + fnCallNode.line = tokenObj.line; + fnCallNode.lineContent = tokenObj.lineContent; fnCallNode.addNode(rootNode.children.back()); rootNode.children.pop_back(); - std::vector tokens; + std::vector tokens; size_t brackets = 1; while (auto tokenopt = consume()) { - if (tokenopt.value() == "(") { + if (tokenopt.value().value == "(") { brackets++; } - if (tokenopt.value() == ")") { + if (tokenopt.value().value == ")") { brackets--; } if (brackets < 1) { @@ -1084,13 +1135,13 @@ namespace Solstice { fnCallNode.children.insert(fnCallNode.children.end(), node.children.begin(), node.children.end()); rootNode.addNode(fnCallNode); } else { - std::vector tokens; + std::vector tokens; size_t brackets = 1; while (auto tokenopt = consume()) { - if (tokenopt.value() == "(") { + if (tokenopt.value().value == "(") { brackets++; } - if (tokenopt.value() == ")") { + if (tokenopt.value().value == ")") { brackets--; } if (brackets < 1) { @@ -1128,4 +1179,4 @@ namespace Solstice { } // namespace Parser -} +} \ No newline at end of file diff --git a/src/parser.h b/src/parser.h index 41f91e7..03dc2ae 100644 --- a/src/parser.h +++ b/src/parser.h @@ -7,32 +7,34 @@ #include #include #include +#include "lexer.h" +#include "error.h" -#define ensure(node, datatype) \ +#define ensure(node, datatype, op) \ if (checkNodeReturnType(node) != datatype) { \ - Error::typingError("Expected " #datatype); \ + Error::typingError("Expected " #datatype " for " op, node.line, node.lineContent); \ } -#define ensure2(node, datatype1, datatype2) { \ +#define ensure2(node, datatype1, datatype2, op) { \ SolDataType dataType = checkNodeReturnType(node); \ if (!(dataType == datatype1 || dataType == datatype2)) { \ - Error::typingError("Expected either " #datatype1 " or " #datatype2); \ + Error::typingError("Expected either " #datatype1 " or " #datatype2 " for " op, node.line, node.lineContent); \ } \ } -#define ensure3(node, datatype1, datatype2, datatype3) { \ +#define ensure3(node, datatype1, datatype2, datatype3, op) { \ SolDataType dataType = checkNodeReturnType(node); \ if (!(dataType == datatype1 || dataType == datatype2 || dataType == datatype3)) { \ - Error::typingError("Expected either " #datatype1 ", " #datatype2 ", or " #datatype3); \ + Error::typingError("Expected either " #datatype1 ", " #datatype2 ", or " #datatype3 " for " op, node.line, node.lineContent); \ } \ } -#define ensuresame(node1, node2) { \ +#define ensuresame(node1, node2, op) { \ SolDataType n1t = checkNodeReturnType(node1); \ SolDataType n2t = checkNodeReturnType(node2); \ if (n1t != n2t) { \ if (!(n1t == SolDataType::Int && n2t == SolDataType::Double || n1t == SolDataType::Double && n2t == SolDataType::Int)) { \ - Error::typingError("Expected types to be the same"); \ + Error::typingError("Expected types to be the same for " op, node1.line, node1.lineContent); \ } \ } \ } @@ -40,11 +42,11 @@ #define exists(node) \ if (node.nodeType == SolNodeType::Identifier) { \ if (variables.find(node.outputId) == variables.end()) { \ - Error::syntaxError("Variable does not exist"); \ + Error::syntaxError("Variable does not exist", node.line, node.lineContent); \ } \ } -namespace Solstice { +namespace Solstice { // External variables referenced in parser logic extern int tmpIdIterator; @@ -96,6 +98,9 @@ namespace Solstice { SolNodeType nodeType = SolNodeType::None; std::vector children; std::string outputId; + int line = 0; + std::string lineContent = ""; + SolNode(SolNodeType nodeType); SolNode(SolNodeType nodeType, SolData data); SolNode() = default; @@ -105,12 +110,12 @@ namespace Solstice { }; class Parser { - std::vector tokensToParse; + std::vector tokensToParse; size_t current; size_t size; - std::optional peek(int ahead = 0); - std::optional consume(); + std::optional peek(int ahead = 0); + std::optional consume(); bool isInt(std::string in); bool isDouble(std::string in); bool isString(std::string in); @@ -124,7 +129,7 @@ namespace Solstice { int getPrecedence(std::string token); public: - Parser(std::vector in); + Parser(std::vector in); SolNode parse(); };