From d8812fa14e403415d717dcc965b245b218bb7776 Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Fri, 26 Dec 2025 13:28:47 +1100 Subject: [PATCH] Refactor type system, print all errors in file --- src/error.cpp | 16 +++-- src/error.h | 8 ++- src/lexer.cpp | 3 +- src/main.cpp | 2 + src/parser.cpp | 167 +++++++++++++++++++++++++++++++++++++++++-------- src/parser.h | 43 ++++++++----- 6 files changed, 189 insertions(+), 50 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index ab98ce9..2cd326a 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -11,7 +11,9 @@ namespace Solstice { const std::string RESET = "\033[0m"; const std::string BOLD = "\033[1m"; - [[noreturn]] void syntaxError(std::string what, int line, std::string lineContent) { + int amountOfErrors = 0; + + 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"; @@ -22,9 +24,9 @@ namespace Solstice { std::cout << "\n"; } std::cout << YELLOW << "-> " << what << RESET << "\n"; - exit(1); + amountOfErrors++; } - [[noreturn]] void typingError(std::string what, int line, std::string lineContent) { + 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"; @@ -35,7 +37,13 @@ namespace Solstice { std::cout << "\n"; } std::cout << YELLOW << "-> " << what << RESET << "\n"; - exit(1); + amountOfErrors++; + } + void summariseErrors() { + if (amountOfErrors > 0) { + std::cout << amountOfErrors << " errors generated.\n"; + exit(1); + } } } } diff --git a/src/error.h b/src/error.h index 18cba44..7ecd480 100644 --- a/src/error.h +++ b/src/error.h @@ -5,9 +5,11 @@ namespace Solstice { namespace Error { - [[noreturn]] void syntaxError(std::string what, int line = 0, std::string lineContent = ""); - [[noreturn]] void typingError(std::string what, int line = 0, std::string lineContent = ""); + void syntaxError(std::string what, int line = 0, std::string lineContent = ""); + void typingError(std::string what, int line = 0, std::string lineContent = ""); + void summariseErrors(); + extern int amountOfErrors; } } -#endif \ No newline at end of file +#endif diff --git a/src/lexer.cpp b/src/lexer.cpp index b30e4c7..ac13ec8 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -111,7 +111,8 @@ namespace Solstice { } case '(': case ')': - case '}': + case '}': + case ',': { if (!buf.empty()) { addToken(buf); diff --git a/src/main.cpp b/src/main.cpp index b45c466..99358e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include "lexer.h" #include "parser.h" #include "argparser.h" +#include "error.h" #define parseOneToken(token) Parser({token.value()}).parse().children[0] @@ -28,6 +29,7 @@ int main(int argc, char** argv) { auto lexed = Solstice::Lexer(ss.str()).lex(); auto parsed = Solstice::Parser::Parser(lexed).parse(); GroundProgram program = Solstice::Parser::assembleProgram(parsed); + Solstice::Error::summariseErrors(); if (args.output.has_value()) { std::FILE* originalStdout = stdout; std::FILE* tmp = std::tmpfile(); diff --git a/src/parser.cpp b/src/parser.cpp index 630648f..09b96dd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,7 +1,6 @@ #include "parser.h" #include "error.h" #include -#include #include #define parseOneToken(token) Parser({token}).parse().children[0] @@ -13,9 +12,9 @@ namespace Solstice { return id.rfind("tmp_", 0) == 0; } - std::map variables; + std::map variables; - SolDataType checkNodeReturnType(SolNode i) { + std::string checkNodeReturnType(SolNode i) { switch (i.nodeType) { case SolNodeType::Identifier: { if (variables.find(i.outputId) != variables.end()) { @@ -26,7 +25,7 @@ namespace Solstice { break; } case SolNodeType::Value: { - return i.data.type; + return i.data.getTypeString(); } case SolNodeType::Equal: case SolNodeType::Inequal: @@ -34,25 +33,25 @@ namespace Solstice { case SolNodeType::Lesser: case SolNodeType::EqGreater: case SolNodeType::EqLesser: - return SolDataType::Bool; + return "bool"; break; case SolNodeType::Add: { - if (checkNodeReturnType(i.children[0]) == SolDataType::String && checkNodeReturnType(i.children[1]) == SolDataType::String) { - return SolDataType::String; + if (checkNodeReturnType(i.children[0]) == "string" && checkNodeReturnType(i.children[1]) == "string") { + return "string"; } } case SolNodeType::Subtract: case SolNodeType::Multiply: case SolNodeType::Divide: { - if (checkNodeReturnType(i.children[0]) == SolDataType::Double) { - return SolDataType::Double; + if (checkNodeReturnType(i.children[0]) == "double") { + return "double"; } - if (checkNodeReturnType(i.children[1]) == SolDataType::Double) { - return SolDataType::Double; + if (checkNodeReturnType(i.children[1]) == "double") { + return "double"; } - return SolDataType::Int; + return "int"; break; } case SolNodeType::Puts: @@ -62,10 +61,11 @@ namespace Solstice { case SolNodeType::Set: case SolNodeType::Root: case SolNodeType::None: - return SolDataType::None; + case SolNodeType::FunctionDef: + return "none"; break; } - return SolDataType::None; + return "none"; } // SolData Implementation @@ -74,6 +74,7 @@ namespace Solstice { SolData::SolData(std::string in) : data(in), type(SolDataType::String) {} SolData::SolData(char in) : data(in), type(SolDataType::Char) {} SolData::SolData(bool in) : data(in), type(SolDataType::Bool) {} + SolData::SolData(SolFunction in) : data(in), type(SolDataType::Function) {} std::optional SolData::getInt() { if (type == SolDataType::Int) { @@ -110,6 +111,26 @@ namespace Solstice { return {}; } } + std::optional SolData::getFunction() { + if (type == SolDataType::Function) { + return std::get(data); + } else { + return {}; + } + } + + std::string SolData::getTypeString() { + switch (type) { + case SolDataType::Int: return "int"; + case SolDataType::Double: return "double"; + case SolDataType::String: return "string"; + case SolDataType::Char: return "char"; + case SolDataType::Bool: return "bool"; + case SolDataType::Function: return "function"; + case SolDataType::None: return "none"; + } + return "none"; + } // SolNode Implementation SolNode::SolNode(SolNodeType nodeType) : nodeType(nodeType) {} @@ -178,8 +199,8 @@ namespace Solstice { break; } case SolNodeType::Add: { - ensure3(children[0], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'"); - ensure3(children[1], SolDataType::Int, SolDataType::Double, SolDataType::String, "operator '+'"); + ensure3(children[0], "int", "double", "string", "operator '+'"); + ensure3(children[1], "int", "double", "string", "operator '+'"); ensuresame(children[0], children[1], "operator '+'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); @@ -197,8 +218,8 @@ namespace Solstice { break; } case SolNodeType::Subtract: { - ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '-'"); - ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '-'"); + ensure2(children[0], "int", "double", "operator '-'"); + ensure2(children[1], "int", "double", "operator '-'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(SUBTRACT); @@ -215,8 +236,8 @@ namespace Solstice { break; } case SolNodeType::Multiply: { - ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '*'"); - ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '*'"); + ensure2(children[0], "int", "double", "operator '*'"); + ensure2(children[1], "int", "double", "operator '*'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(MULTIPLY); @@ -233,8 +254,8 @@ namespace Solstice { break; } case SolNodeType::Divide: { - ensure2(children[0], SolDataType::Int, SolDataType::Double, "operator '/'"); - ensure2(children[1], SolDataType::Int, SolDataType::Double, "operator '/'"); + ensure2(children[0], "int", "double", "operator '/'"); + ensure2(children[1], "int", "double", "operator '/'"); SolGroundCodeBlock codeBlock; outputId = "tmp_" + std::to_string(tmpIdIterator++); GroundInstruction gi = groundCreateInstruction(DIVIDE); @@ -478,7 +499,7 @@ namespace Solstice { break; } case SolNodeType::If: { - ensure(children[0], SolDataType::Bool, "if condition"); + ensure(children[0], "bool", "if condition"); auto conditionCode = children[0].generateCode(); code.insert(code.end(), conditionCode.begin(), conditionCode.end()); outputId = "tmp_" + std::to_string(tmpIdIterator++); @@ -510,7 +531,7 @@ namespace Solstice { break; } case SolNodeType::While: { - ensure(children[0], SolDataType::Bool, "while condition"); + ensure(children[0], "bool", "while condition"); SolGroundCodeBlock startLabelBlock; std::string startLabelIdString = "whilestart_" + std::to_string(labelIterator++); std::string endLabelIdString = "whileend_" + std::to_string(labelIterator); @@ -574,13 +595,14 @@ namespace Solstice { if (isTemp(children[1].outputId)) codeBlock.toBeDropped.push_back(children[1].outputId); code.push_back(codeBlock); // Make sure we know what the variable type is - variables[children[0].outputId] = children[1].data.type; + variables[children[0].outputId] = children[1].data.getTypeString(); break; } case SolNodeType::FunctionCall: { // Take care of in built functions + // This will be removed when inline ground is added if (children[0].outputId == "input") { - ensure(children[1], SolDataType::String, "input function argument"); + ensure(children[1], "string", "input function argument"); SolGroundCodeBlock inputCodeBlock; if (children.size() > 1) { GroundInstruction printInstruction = groundCreateInstruction(PRINT); @@ -595,6 +617,7 @@ namespace Solstice { code.push_back(inputCodeBlock); break; } + std::string fnToCall = children[0].outputId; break; } @@ -728,6 +751,9 @@ namespace Solstice { if (in == "while") { return SolNodeType::While; } + if (in == "def") { + return SolNodeType::FunctionDef; + } if (in == "{") { return SolNodeType::CodeBlockStart; } @@ -1154,6 +1180,93 @@ namespace Solstice { rootNode.addNode(node); } } + case SolNodeType::FunctionDef: { + SolFunction fn; + auto nameTokenOpt = consume(); + if (!nameTokenOpt || getNodeType(nameTokenOpt.value().value) != SolNodeType::Identifier) { + Error::syntaxError("Expected function name", tokenObj.line, tokenObj.lineContent); + } + std::string fnName = nameTokenOpt.value().value; + + auto openParen = consume(); + if (!openParen || openParen.value().value != "(") { + Error::syntaxError("Expected '(' after function name", tokenObj.line, tokenObj.lineContent); + } + + std::string signature = "fun("; + + bool first = true; + while (auto next = peek()) { + if (next.value().value == ")") { + consume(); + break; + } + if (!first) { + auto comma = consume(); + if (comma.value().value != ",") { + Error::syntaxError("Expected ',' between arguments", tokenObj.line, tokenObj.lineContent); + } + } + + auto typeToken = consume(); + if (!typeToken) Error::syntaxError("Expected type", tokenObj.line, tokenObj.lineContent); + std::string argType = typeToken.value().value; + + auto argNameToken = consume(); + if (!argNameToken) Error::syntaxError("Expected argument name", tokenObj.line, tokenObj.lineContent); + std::string argName = argNameToken.value().value; + + fn.parameters.push_back({argName, argType}); + + if (!first) signature += ", "; + signature += argType; + first = false; + } + signature += ") "; + + while (peek() && peek().value().value == "\n") consume(); + + auto next = peek(); + if (next && next.value().value != "{") { + auto retTypeToken = consume(); + fn.returnType = retTypeToken.value().value; + } else { + fn.returnType = "void"; + } + signature += fn.returnType; + fn.signature = signature; + + while (peek() && peek().value().value == "\n") consume(); + + auto brace = peek(); + if (!brace || brace.value().value != "{") { + Error::syntaxError("Expected '{' starting function body", tokenObj.line, tokenObj.lineContent); + } + + consume(); + std::vector bodyTokens; + size_t brackets = 1; + while(auto t = consume()) { + if (t.value().value == "{") brackets++; + if (t.value().value == "}") brackets--; + if (brackets == 0) break; + bodyTokens.push_back(t.value()); + } + if (brackets != 0) Error::syntaxError("Unclosed function body", tokenObj.line, tokenObj.lineContent); + + auto parsedBody = Parser(bodyTokens).parse(); + auto bodyNode = std::make_shared(SolNodeType::CodeBlock); + bodyNode->children = parsedBody.children; + + fn.content = bodyNode; + + SolNode fnNode(SolNodeType::FunctionDef); + fnNode.data = SolData(fn); + variables[fnName] = signature; + + rootNode.addNode(fnNode); + break; + } } } return rootNode; @@ -1179,4 +1292,4 @@ namespace Solstice { } // namespace Parser -} \ No newline at end of file +} diff --git a/src/parser.h b/src/parser.h index 03dc2ae..55ee712 100644 --- a/src/parser.h +++ b/src/parser.h @@ -7,34 +7,35 @@ #include #include #include +#include #include "lexer.h" #include "error.h" #define ensure(node, datatype, op) \ if (checkNodeReturnType(node) != datatype) { \ - Error::typingError("Expected " #datatype " for " op, node.line, node.lineContent); \ + Error::typingError("Expected " + std::string(datatype) + " for " + op, node.line, node.lineContent); \ } #define ensure2(node, datatype1, datatype2, op) { \ - SolDataType dataType = checkNodeReturnType(node); \ + std::string dataType = checkNodeReturnType(node); \ if (!(dataType == datatype1 || dataType == datatype2)) { \ - Error::typingError("Expected either " #datatype1 " or " #datatype2 " for " op, node.line, node.lineContent); \ + Error::typingError("Expected either " + std::string(datatype1) + " or " + std::string(datatype2) + " for " + op, node.line, node.lineContent); \ } \ } -#define ensure3(node, datatype1, datatype2, datatype3, op) { \ - SolDataType dataType = checkNodeReturnType(node); \ +#define ensure3(node, datatype1, datatype2, datatype3, op) { \ + std::string dataType = checkNodeReturnType(node); \ if (!(dataType == datatype1 || dataType == datatype2 || dataType == datatype3)) { \ - Error::typingError("Expected either " #datatype1 ", " #datatype2 ", or " #datatype3 " for " op, node.line, node.lineContent); \ + Error::typingError("Expected either " + std::string(datatype1) + ", " + std::string(datatype2) + ", or " + std::string(datatype3) + " for " + op, node.line, node.lineContent); \ } \ } #define ensuresame(node1, node2, op) { \ - SolDataType n1t = checkNodeReturnType(node1); \ - SolDataType n2t = checkNodeReturnType(node2); \ + std::string n1t = checkNodeReturnType(node1); \ + std::string 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 for " op, node1.line, node1.lineContent); \ + if (!(n1t == "int" && n2t == "double" || n1t == "double" && n2t == "int")) { \ + Error::typingError("Expected types to be the same for " + std::string(op), node1.line, node1.lineContent); \ } \ } \ } @@ -56,14 +57,14 @@ namespace Solstice { enum class SolNodeType { - Add, Subtract, Multiply, Divide, Equal, Inequal, Greater, Lesser, EqGreater, EqLesser, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionCall, Expression, BracketStart, BracketEnd, Puts + Add, Subtract, Multiply, Divide, Equal, Inequal, Greater, Lesser, EqGreater, EqLesser, Set, While, If, Value, Identifier, None, Root, CodeBlock, CodeBlockStart, CodeBlockEnd, FunctionDef, FunctionCall, Expression, BracketStart, BracketEnd, Puts }; enum class SolDataType { - Int, String, Double, Bool, Char, None + Int, String, Double, Bool, Char, Function, None }; - extern std::map variables; + extern std::map variables; class SolNode; @@ -74,8 +75,17 @@ namespace Solstice { SolGroundCodeBlock() = default; }; + class SolFunction { + public: + std::string signature; + std::vector> parameters; // name, type + std::string returnType; + std::shared_ptr content; + SolFunction() = default; + }; + class SolData { - typedef std::variant varData; + typedef std::variant varData; varData data; public: SolDataType type = SolDataType::Int; @@ -85,11 +95,14 @@ namespace Solstice { SolData(std::string in); SolData(char in); SolData(bool in); + SolData(SolFunction in); std::optional getInt(); std::optional getDouble(); std::optional getString(); std::optional getChar(); std::optional getBool(); + std::optional getFunction(); + std::string getTypeString(); }; class SolNode { @@ -134,7 +147,7 @@ namespace Solstice { }; GroundProgram assembleProgram(SolNode& rootNode); - SolDataType checkNodeReturnType(SolNode in); + std::string checkNodeReturnType(SolNode in); } // namespace Parser }