diff --git a/.run/Spice_run.run.xml b/.run/Spice_run.run.xml index 67ebee1e4..dfadeebbf 100644 --- a/.run/Spice_run.run.xml +++ b/.run/Spice_run.run.xml @@ -1,5 +1,5 @@ - + diff --git a/media/test-project/os-test.spice b/media/test-project/os-test.spice index 8457702bd..34e41f8b9 100644 --- a/media/test-project/os-test.spice +++ b/media/test-project/os-test.spice @@ -10,21 +10,9 @@ f main() { printf("Hello %s!", p1.getSecond()); }*/ -/*import "std/net/http" as http; +import "std/net/http" as http; f main() { http::HttpServer server = http::HttpServer(); server.serve("/test", "Hello World!"); -}*/ - -type TokenType enum { - IDENTIFIER, - DOT = 12, - COMMA, - SIZEOF = 0, - WS -} - -f main() { - printf("%d\n", TokenType.DOT); } \ No newline at end of file diff --git a/src-bootstrap/CliInterface.spice b/src-bootstrap/CliInterface.spice new file mode 100644 index 000000000..fe3285904 --- /dev/null +++ b/src-bootstrap/CliInterface.spice @@ -0,0 +1,153 @@ +// Imports +import "std/text/print" as print; +import "std/os/cmd" as cmd; + +/** + * Representation of the various cli options + */ +public type CliOptions struct { + string mainSourceFile // e.g. main.spice + string targetTriple // In format: --- + string targetArch + string targetVendor + string targetOs + string outputDir // Where the object files go. Should always be a temp directory + string outputPath // Where the output binary goes. + bool printDebugInfo + bool dumpCST + bool dumpAST + bool dumpIR + bool dumpAssembly + bool dumpSymbolTables + short optLevel // -O0 = 0, -O1 = 1, -O2 = 2, -O3 = 3, -Os = 4, -Oz = 5 + bool generateDebugInfo + bool disableVerifier + bool testMode +} + +/** + * Helper class to setup the cli interface and command line parser + */ +public type CliInterface struct { + //CliApp cliApp + CliOptions cliOptions + bool compile + bool install + bool run +} + +public p CliInterface.ctor() { + // Initialize cli object + this.cliOptions = CliOptions{/* mainSourceFile */ "", /* targetTriple */ "", /* targetArch */ "", /* targetVendor */ "", + /* targetOs */ "", /* outputDir */ "", /* outputPath */ "", /* printDebugInfo */ false, /* dumpCST */ false, + /* dumpAST */ false, /* dumpIR */ false, /* dumpAssembly */ false, /* dumpSymbolTables */ false, /* optLevel */ 2s, + /* generateDebugInfo */ false, /* disableVerifier */ false, /* testMode */ false }; + this.compile = false; + this.install = false; + this.run = false; +} + +p CliInterface.createInterface() { + // ToDo: Extend +} + +/** + * Validates if all necessary cli options were provided. + * + * @throws InvalidCliOptionsException if there were an invalid combination of cli options provided + */ +p CliInterface.validate() { + // ToDo: Extend +} + +/** + * Initialize the cli options based on the input of the user + */ +p CliInterface.enrich() { + // Propagate target information + if this.cliOptions.targetTriple.empty() { + // ToDo: Extend + } + + // Dump AST, IR and symbol table if all debug output is enabled + if this.cliOptions.printDebugInfo { + this.cliOptions.dumpAST = true; + this.cliOptions.dumpIR = true; + this.cliOptions.dumpSymbolTables = true; + } +} + +/** + * Add build subcommand to cli interface + */ +p CliInterface.addBuildSubcommand() { + // Create sub-command itself + // ToDo: Extend +} + +/** + * Add run subcommand to cli interface + */ +p CliInterface.addRunSubcommand() { + // Create sub-command itself + // ToDo: Extend +} + +/** + * Add install subcommand to cli interface + */ +p CliInterface.addInstallSubcommand() { + // Create sub-command itself + // ToDo: Extend +} + +/** + * Add uninstall subcommand to cli interface + */ +p CliInterface.addUninstallSubcommand() { + // Create sub-command itself + // ToDo: Extend +} + +/** + * Checks if compiling is necessary + * + * @return Compile or not + */ +f CliInterface.shouldCompile() { + return this.compile; +} + +/** + * Checks if running is necessary + * + * @return Run or not + */ +f CliInterface.shoudRun() { + return this.run; +} + +/** + * Start the parsing process + * + * @param argc Argument count + * @param argv Argument vector + * @return Return code + */ +f CliInterface.parse(int argc, string[] argv) { + // ToDo: Extend + return 0; +} + +/** + * Executes the built executable + */ +p CliInterface.runBinary() { + // Print status message + if this.cliOptions.printDebugOutput { + print.println("Running executable ...\n"); + } + + // Run executable + cmd.execCmd(this.cliOptions.outputPath); +} \ No newline at end of file diff --git a/src-bootstrap/SourceFile.spice b/src-bootstrap/SourceFile.spice new file mode 100644 index 000000000..55c2ca080 --- /dev/null +++ b/src-bootstrap/SourceFile.spice @@ -0,0 +1,156 @@ +// Imports +import "std/text/print" as print; +import "CliInterface" as cli; +import "ast/AstNodes" as ast; +import "symbol/SymbolTable" as st; +import "analyzer/Analyzer" as alzr; +import "generator/Generator" as gen; + +/** + * Collects the output of the compiler for debugging + */ +type CompilerOutput struct { + string cstString + string astString + string symbolTableString + string irString + string irOptString +} + +/** + * Represents a single source file + */ +public type SourceFile struct { + string name + string fileName + string filePath + string fileDir + string objectFilePath + bool stdFile + CompilerOutput compilerOutput + SourceFile* parent + cli::CliOptions* cliOptions + ast::EntryNode* ast + st::SymbolTable* symbolTable + alzr::Analyzer* analyzer + gen::Generator* generator + map::Map> dependencies +} + +public p SourceFile.ctor(CliOptions* cliOptions, SourceFile* parent, string name, string filePath, bool stdFile) { + // Copy data + this.cliOptions = cliOptions; + this.parent = parent; + this.name = name; + this.filePath = filePath; + this.stdFile = stdFile; + + // Deduce fileName and fileDir + /*this.fileName = ; + this.fileDir = ;*/ +} + +p SourceFile.runLexer() { + // Lex this source file +} + +p SourceFile.runParser() { + // Parse this source file +} + +p SourceFile.visualizeCST(string* output) { + // Only execute if enabled + if !cliOptions.dumpCST && !cliOptions.testMode { return; } + + // ToDo: Extend +} + +p SourceFile.buildAST() { + // Transform the imported source files + // ToDo: Extend +} + +p SourceFile.visualizeAST(string* output) { + // Only execute if enabled + if !cliOptions.dumpAST && !cliOptions.testMode { return; } + + // ToDo: Extend +} + +p SourceFile.preAnalyze() { + // Pre-analyze this source file + // ToDo: Extend + + // Pre-analyze this source file + foreach dyn dependencyPair : this.dependencies { + SourceFile dependencyFile = dependencyPair.getSecond(); + dependencyFile.runLexer(); + dependencyFile.runParser(); + dependencyFile.buildAST(); + dependencyFile.preAnalyze(); + } +} + +p SourceFile.analyze() { + // Analyze the imported source files first + // ToDo: Extend + + // Analyze this source file +} + +p SourceFile.reAnalyze() { + // Re-Analyze this source file + + // Analyze the imported source files first + // ToDo: Extend + + // Save the JSON version in the compiler output + + // Dump symbolTable + if cliOptions.dumpSymbolTable { + printf("\nSymbol table of file %s:\n\n", filePath); + printf("%s\n", compilerOutput.symbolTableString); + } +} + +p SourceFile.generate() { + // Generate the imported source files + + // Generate this source file + + // Save the JSON version in the compiler output + + // Dump unoptimized IR code + + // Optimize IR code + + // Dump assembly code + + // Emit object file + + // Add object file to the linker interface + + // Print warning if verifier is disabled + if parent == nil && cliOptions.disableVerifier { + print.emptyLine(); + // ToDo + print.emptyLine(); + } +} + +p SourceFile.addDependency(const CodeLoc codeLoc, const string name, const string stringFilePath, bool stdFile) { + // Check if this would cause a circular dependency + if isAlreadyImported(filePath) { + // ToDo: Error out + } + + // Add the dependency + // ToDo +} + +f SourceFile.isAlreadyImported(const string filePathSearch) { + // Check if the current source file corresponds to the path to search + if filePath == filePathSearch { return true; } + // Check parent recursively + return parent != nil && parent.isAlreadyImported(filePathSearch); +} \ No newline at end of file diff --git a/src-bootstrap/analyzer/analyzer.spice b/src-bootstrap/analyzer/analyzer.spice new file mode 100644 index 000000000..c1496b6e8 --- /dev/null +++ b/src-bootstrap/analyzer/analyzer.spice @@ -0,0 +1,5 @@ +// Imports + +public type Analyzer struct { + +} \ No newline at end of file diff --git a/src-bootstrap/ast/AstNodes.spice b/src-bootstrap/ast/AstNodes.spice new file mode 100644 index 000000000..af15f1b04 --- /dev/null +++ b/src-bootstrap/ast/AstNodes.spice @@ -0,0 +1,5 @@ +// Imports + +public type EntryNode struct { + +} \ No newline at end of file diff --git a/src-bootstrap/exception/SemanticError.spice b/src-bootstrap/exception/SemanticError.spice new file mode 100644 index 000000000..ca8e1cf00 --- /dev/null +++ b/src-bootstrap/exception/SemanticError.spice @@ -0,0 +1,71 @@ +// Imports +import "../util/CodeLoc" as cl; + +public type ErrorType enum { + REFERENCED_UNDEFINED_FUNCTION, + REFERENCED_UNDEFINED_VARIABLE, + REFERENCED_UNDEFINED_STRUCT, + FUNCTION_AMBIGUITY, + STRUCT_AMBIGUITY, + VARIABLE_DECLARED_TWICE, + FUNCTION_DECLARED_TWICE, + GENERIC_TYPE_DECLARED_TWICE, + STRUCT_DECLARED_TWICE, + ENUM_DECLARED_TWICE, + DUPLICATE_ENUM_ITEM_NAME, + DUPLICATE_ENUM_ITEM_VALUE, + GLOBAL_OF_TYPE_DYN, + GLOBAL_OF_INVALID_TYPE, + GLOBAL_CONST_WITHOUT_VALUE, + FUNCTION_WITHOUT_RETURN_STMT, + INVALID_PARAM_ORDER, + DTOR_MUST_BE_PROCEDURE, + DTOR_WITH_PARAMS, + OPERATOR_WRONG_DATA_TYPE, + UNEXPECTED_DYN_TYPE_SA, + REASSIGN_CONST_VARIABLE, + CONDITION_MUST_BE_BOOL, + MISSING_MAIN_FUNCTION, + FCT_PARAM_IS_TYPE_DYN, + INVALID_BREAK_NUMBER, + INVALID_CONTINUE_NUMBER, + PRINTF_TYPE_ERROR, + PRINTF_ARG_COUNT_ERROR, + STD_NOT_FOUND, + DUPLICATE_IMPORT_NAME, + IMPORTED_FILE_NOT_EXISTING, + CIRCULAR_DEPENDENCY, + MEMBER_ACCESS_ONLY_STRUCTS, + SCOPE_ACCESS_ONLY_IMPORTS, + UNKNOWN_DATATYPE, + NUMBER_OF_FIELDS_NOT_MATCHING, + FIELD_TYPE_NOT_MATCHING, + ARRAY_SIZE_INVALID, + ARRAY_INDEX_NO_INTEGER, + ARRAY_ITEM_TYPE_NOT_MATCHING, + EXPECTED_ARRAY_TYPE, + SIZEOF_DYNAMIC_SIZED_ARRAY, + RETURN_WITHOUT_VALUE_RESULT, + RETURN_WITH_VALUE_IN_PROCEDURE, + DYN_POINTERS_NOT_ALLOWED, + DYN_ARRAYS_NOT_ALLOWED, + GENERIC_TYPE_NOT_IN_TEMPLATE, + SPECIFIER_AT_ILLEGAL_CONTEXT, + INSUFFICIENT_VISIBILITY, + TID_INVALID, + JOIN_ARG_MUST_BE_TID, + EXPECTED_GENERIC_TYPE, + EXPECTED_VALUE, + EXPECTED_TYPE, + UNSAFE_OPERATION_IN_SAFE_CONTEXT, + ASSERTION_CONDITION_BOOL, + ARRAY_INDEX_OUT_OF_BOUNDS, + RESERVED_KEYWORD, + COMING_SOON_SA +} + +public type SemanticError struct { + cl::CodeLoc codeLoc + ErrorType errorType + string message +} \ No newline at end of file diff --git a/src-bootstrap/generator/generator.spice b/src-bootstrap/generator/generator.spice new file mode 100644 index 000000000..bbe2c37b5 --- /dev/null +++ b/src-bootstrap/generator/generator.spice @@ -0,0 +1,5 @@ +// Imports + +public type Generator struct { + +} \ No newline at end of file diff --git a/src-bootstrap/lexer/Lexer.spice b/src-bootstrap/lexer/Lexer.spice new file mode 100644 index 000000000..6f47bcf28 --- /dev/null +++ b/src-bootstrap/lexer/Lexer.spice @@ -0,0 +1,2 @@ +// Imports +import "Token" as tk; \ No newline at end of file diff --git a/src-bootstrap/lexer/Reader.spice b/src-bootstrap/lexer/Reader.spice new file mode 100644 index 000000000..38280a8e6 --- /dev/null +++ b/src-bootstrap/lexer/Reader.spice @@ -0,0 +1,40 @@ +// Imports +import "std/io/file" as file; + +public type Reader struct { + File inputFile + char curChar + char nextChar + bool metEof + unsigned long line + unsigned long col +} + +public p Reader.ctor(const string inputFileName) { + this.inputFile = file.openFile(inputFileName, file::MODE_READ); + this.cursorPos = 0; + this.metEof = false; + this.line = 1; + this.col = 0; + // Fill +} + +public f Reader.getCurChar() { + return this.curChar; +} + +public f Reader.expectChar(char expected) { + bool gotExpected = expected == this.nextChar; + this.advance(); + return gotExpected; +} + +public p Reader.advance() { + int readChar = this.inputFile.readChar(); + if readChar == -1 { + metEof = true; + return; + } + this.curChar = this.nextChar; + this.nextChar = (char) readChar; +} \ No newline at end of file diff --git a/src-bootstrap/lexer/Token.spice b/src-bootstrap/lexer/Token.spice new file mode 100644 index 000000000..47da5ba66 --- /dev/null +++ b/src-bootstrap/lexer/Token.spice @@ -0,0 +1,116 @@ +// Imports + +// Enums +public type TokenType enum { + // Keyword tokens + TYPE_DOUBLE, + TYPE_INT, + TYPE_SHORT, + TYPE_LONG, + TYPE_BYTE, + TYPE_CHAR, + TYPE_STRING, + TYPE_BOOL, + TYPE_DYN, + CONST, + SIGNED, + UNSIGNED, + INLINE, + PUBLIC, + F, + P, + IF, + ELSE, + ASSERT, + FOR, + FOREACH, + WHILE, + IMPORT, + BREAK, + CONTINUE, + RETURN, + AS, + STRUCT, + TYPE, + ENUM, + THREAD, + UNSAFE, + NIL, + MAIN, + PRINTF, + SIZEOF, + LEN, + TID, + JOIN, + EXT, + DLL, + TRUE, + FALSE, + // Operator tokens + LBRACE, + RBRACE, + LPAREN, + RPAREN, + LBRACKET, + RBRACKET, + LOGICAL_OR, + LOGICAL_AND, + BITWISE_OR, + BITWISE_XOR, + BITWISE_AND, + PLUS_PLUS, + MINUS_MINUS, + PLUS_EQUAL, + MINUS_EQUAL, + MUL_EQUAL, + DIV_EQUAL, + REM_EQUAL, + SHL_EQUAL, + SHR_EQUAL, + AND_EQUAL, + OR_EQUAL, + XOR_EQUAL, + PLUS, + MINUS, + MUL, + DIV, + REM, + NOT, + BITWISE_NOT, + GREATER, + LESS, + GREATER_EQUAL, + LESS_EQUAL, + EQUAL, + NOT_EQUAL, + ASSIGN, + QUESTION_MARK, + SEMICOLON, + COLON, + COMMA, + DOT, + SCOPE_ACCESS, + ELLIPSIS, + // Regex tokens + DOUBLE_LIT, + INT_LIT, + SHORT_LIT, + LONG_LIT, + CHAR_LIT, + STRING_LIT, + IDENTIFIER, + // Skipped tokens + BLOCK_COMMENT, + LINE_COMMENT, + WS +} + +public type Token struct { + public TokenType tokenType + public string value + public CodeLoc codeLoc +} + +public f getText() { + return this.value; +} \ No newline at end of file diff --git a/src-bootstrap/main.spice b/src-bootstrap/main.spice new file mode 100644 index 000000000..86eee6929 --- /dev/null +++ b/src-bootstrap/main.spice @@ -0,0 +1,34 @@ +// Imports +import "util/ThreadFactory" as tf; +import "SourceFile" as sf; +import "CliInterface" as cli; + +/** + * Compile main source file. All files, that are included by the main source file will be resolved recursively. + * + * @param options Command line options + */ +p compileProject(const CliOptions* options) { + // ToDo: Initialize LLVM resources + + // Prepare instance of thread factory, which has to exist exactly once per executable + tf::ThreadFactory threadFactory = tf::ThreadFactory(); + + // Prepare linker interface + // ToDo + + // Create source file instance for main source file + sf::SourceFile mainSourceFile = sf::SourceFile(options, nil, "root", options.mainSourceFile, false); +} + +/** + * Entry point to the Spice compiler + * + * @param argc Argument count + * @param argv Argument vector + * @return Return code + */ +f main(int argc, string[] argv) { + // Initialize command line parser + +} \ No newline at end of file diff --git a/src-bootstrap/parser/Parser.spice b/src-bootstrap/parser/Parser.spice new file mode 100644 index 000000000..cf6a92ac4 --- /dev/null +++ b/src-bootstrap/parser/Parser.spice @@ -0,0 +1,14 @@ +// Imports +import "../lexer/Token" as tk; + +public type Parser struct { + +} + +public p Parser.ctor(const tk::Token[] tokens, unsigned long tokenCount) { + +} + +public p Parser.parse() { + +} \ No newline at end of file diff --git a/src-bootstrap/symbol/SymbolTable.spice b/src-bootstrap/symbol/SymbolTable.spice new file mode 100644 index 000000000..f68db22fe --- /dev/null +++ b/src-bootstrap/symbol/SymbolTable.spice @@ -0,0 +1,51 @@ +// Imports +import "std/data/map" as map; + +import "SymbolTableEntry" as ste; +import "Capture" as cpt; +import "GenericType" as gt; + +public type ScopeType enum { + SCOPE_GLOBAL, + SCOPE_FUNC_PROC_BODY, + SCOPE_STRUCT, + SCOPE_ENUM, + SCOPE_IF_BODY, + SCOPE_WHILE_BODY, + SCOPE_FOR_BODY, + SCOPE_FOREACH_BODY, + SCOPE_THREAD_BODY, + SCOPE_UNSAFE_BODY +} + +/** + * Class for storing information about symbols of the AST. Symbol tables are meant to be arranged in a tree structure, + * so that you can navigate with the getParent() and getChild() methods up and down the tree. + */ +public type SymbolTable struct { + SymbolTable* parent + ScopeType scopeType + map::Map children + map::Map symbols + map::Map captures + map::Map genericTypes + + bool inMainSourceFile + bool isSourceFileRootScope + bool compilerWarningsEnabled + bool requiresCapturing +} + +p SymbolTable.ctor(SymbolTable* parent, ScopeType scopeType, bool inMainSourceFile = false, bool isSourceFileRoot = false) { + this.parent = parent; + this.scopeType = scopeType; + + this.inMainSourceFile = inMainSourceFile; + this.isSourceFileRootScope = isSourceFileRoot; + this.compilerWarningsEnabled = true; + this.requiresCapturing = false; +} + +p SymbolTable.insert() { + // ToDo +} \ No newline at end of file diff --git a/src-bootstrap/symbol/SymbolTableEntry.spice b/src-bootstrap/symbol/SymbolTableEntry.spice new file mode 100644 index 000000000..d11d0aa59 --- /dev/null +++ b/src-bootstrap/symbol/SymbolTableEntry.spice @@ -0,0 +1,9 @@ +// Imports + +public type SymbolTableEntry struct { + +} + +public p SymbolTableEntry.ctor() { + +} \ No newline at end of file diff --git a/src-bootstrap/util/CodeLoc.spice b/src-bootstrap/util/CodeLoc.spice new file mode 100644 index 000000000..368772072 --- /dev/null +++ b/src-bootstrap/util/CodeLoc.spice @@ -0,0 +1,44 @@ +// Imports +import "std/type/int" as intTy; + +public type CodeLoc struct { + unsigned long line + unsigned long col + string sourceFilePath +} + +public p CodeLoc.ctor(unsigned long line, unsigned long col, string sourceFilePath = "") { + this.line = line; + this.col = col; + this.sourceFilePath = sourceFilePath; +} + +/** + * Returns the code location as a string for using it as a map key or similar + * + * @return Code location string + */ +public f toString() { + return "L" + intTy.toString(line) + "C" + intTy.toString(col); +} + +/** + * Returns the code location in a pretty form + * + * @return Pretty code location + */ +public f toPrettyString() { + if sourceFilePath.empty() { + return intTy.toString(line) + ":" + intTy.toString(col); + } + return sourceFilePath + ":" + intTy.toString(line) + ":" + intTy.toString(col); +} + +/** + * Returns the line number in a pretty form + * + * @return Pretty line number + */ +public f toPrettyLine() { + return "l" + intTy.toString(line); +} \ No newline at end of file diff --git a/src-bootstrap/util/ThreadFactory.spice b/src-bootstrap/util/ThreadFactory.spice new file mode 100644 index 000000000..e0fffa8ff --- /dev/null +++ b/src-bootstrap/util/ThreadFactory.spice @@ -0,0 +1,15 @@ +public type ThreadFactory struct { + unsigned int nextSuffix +} + +public p ThreadFactory.ctor() { + this.nextSuffix = 0; +} + +public f ThreadFactory.getNextFunctionSuffix() { + return this.nextSuffix++; +} + +public f ThreadFactory.isUsingThreads() { + return this.nextSuffix > 0; +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 88c239d9a..f532bb6ba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,8 +45,6 @@ set(SOURCES ast/AstNodes.h dependency/SourceFile.cpp dependency/SourceFile.h - exception/ErrorFactory.cpp - exception/ErrorFactory.h exception/CliError.cpp exception/CliError.h exception/SemanticError.cpp diff --git a/src/analyzer/AnalyzerVisitor.cpp b/src/analyzer/AnalyzerVisitor.cpp index 8dcfbe256..b56078074 100644 --- a/src/analyzer/AnalyzerVisitor.cpp +++ b/src/analyzer/AnalyzerVisitor.cpp @@ -31,11 +31,8 @@ AnalyzerVisitor::AnalyzerVisitor(std::shared_ptr context, std options.targetOs = targetTriple.getOSName(); } - // Create error factory for this specific file - this->err = std::make_unique(); - // Create OpRuleManager - opRuleManager = std::make_unique(err.get(), allowUnsafeOperations); + opRuleManager = std::make_unique(allowUnsafeOperations); } std::any AnalyzerVisitor::visitEntry(EntryNode *node) { @@ -57,7 +54,7 @@ std::any AnalyzerVisitor::visitEntry(EntryNode *node) { // Check if the visitor got a main function if (requiresMainFct && !hasMainFunction) - throw err->get(node->codeLoc, MISSING_MAIN_FUNCTION, "No main function found"); + throw SemanticError(node->codeLoc, MISSING_MAIN_FUNCTION, "No main function found"); // Print compiler warnings once the whole ast is present, but not for std files if (requiresMainFct && !isStdFile && !reAnalyze) @@ -76,7 +73,7 @@ std::any AnalyzerVisitor::visitMainFctDef(MainFctDefNode *node) { if (runNumber == 1) { // First run // Check if the function is already defined if (currentScope->lookup(mainSignature)) - throw err->get(node->codeLoc, FUNCTION_DECLARED_TWICE, "Main function is declared twice"); + throw SemanticError(node->codeLoc, FUNCTION_DECLARED_TWICE, "Main function is declared twice"); // Insert function name into the root symbol table SymbolType symbolType = SymbolType(TY_FUNCTION); @@ -124,13 +121,13 @@ std::any AnalyzerVisitor::visitFctDef(FctDefNode *node) { // Change to the struct scope currentScope = currentScope->lookupTable(STRUCT_SCOPE_PREFIX + node->structName); if (!currentScope) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, "Struct '" + node->structName + "' could not be found"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, "Struct '" + node->structName + "' could not be found"); } if (runNumber == 1) { // First run // Check if name is dtor if (node->functionName == "dtor") - throw err->get(node->codeLoc, DTOR_MUST_BE_PROCEDURE, "Destructors are not allowed to be of type function"); + throw SemanticError(node->codeLoc, DTOR_MUST_BE_PROCEDURE, "Destructors are not allowed to be of type function"); // Create a new scope node->fctScope = currentScope = currentScope->createChildBlock(node->getScopeId(), SCOPE_FUNC_PROC_BODY); @@ -143,7 +140,7 @@ std::any AnalyzerVisitor::visitFctDef(FctDefNode *node) { SymbolTableEntry *structEntry = currentScope->lookup(node->structName); assert(structEntry != nullptr); thisType = structEntry->getType(); - thisPtrType = thisType.toPointer(err.get(), node->codeLoc); + thisPtrType = thisType.toPointer(node->codeLoc); for (const auto &templateType : thisType.getTemplateTypes()) templateTypes.emplace_back(templateType); } @@ -153,7 +150,7 @@ std::any AnalyzerVisitor::visitFctDef(FctDefNode *node) { for (const auto &dataType : node->templateTypeLst()->dataTypes()) { auto templateType = any_cast(visit(dataType)); if (!templateType.is(TY_GENERIC)) - throw err->get(dataType->codeLoc, EXPECTED_GENERIC_TYPE, "A template list can only contain generic types"); + throw SemanticError(dataType->codeLoc, EXPECTED_GENERIC_TYPE, "A template list can only contain generic types"); GenericType *genericType = currentScope->lookupGenericType(templateType.getSubType()); assert(genericType != nullptr); templateTypes.push_back(*genericType); @@ -173,8 +170,8 @@ std::any AnalyzerVisitor::visitFctDef(FctDefNode *node) { // Check if the type is present in the template for generic types if (argType.is(TY_GENERIC)) { if (std::none_of(templateTypes.begin(), templateTypes.end(), [&](const GenericType &t) { return t == argType; })) - throw err->get(node->paramLst()->codeLoc, GENERIC_TYPE_NOT_IN_TEMPLATE, - "Generic arg type not included in function template"); + throw SemanticError(node->paramLst()->codeLoc, GENERIC_TYPE_NOT_IN_TEMPLATE, + "Generic arg type not included in function template"); } argNames.push_back(argName); @@ -192,10 +189,10 @@ std::any AnalyzerVisitor::visitFctDef(FctDefNode *node) { // Declare variable for the return value in the function scope auto returnType = any_cast(visit(node->returnType())); if (returnType.is(TY_DYN)) - throw err->get(node->codeLoc, UNEXPECTED_DYN_TYPE_SA, "Dyn return types are not allowed"); + throw SemanticError(node->codeLoc, UNEXPECTED_DYN_TYPE_SA, "Dyn return types are not allowed"); if (returnType.isPointer()) - throw err->get(node->codeLoc, COMING_SOON_SA, - "Spice currently not supports pointer return types due to not supporting heap allocations."); + throw SemanticError(node->codeLoc, COMING_SOON_SA, + "Spice currently not supports pointer return types due to not supporting heap allocations."); currentScope->insert(RETURN_VARIABLE_NAME, returnType, SymbolSpecifiers(returnType), DECLARED, node); // Return to old scope @@ -210,13 +207,14 @@ std::any AnalyzerVisitor::visitFctDef(FctDefNode *node) { else if (specifier->type == SpecifierNode::TY_PUBLIC) fctSymbolSpecifiers.setPublic(true); else - throw err->get(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this specifier on a function definition"); + throw SemanticError(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, + "Cannot use this specifier on a function definition"); } } // Insert function into the symbol table Function spiceFunc(node->functionName, fctSymbolSpecifiers, thisType, returnType, argTypes, templateTypes, node); - currentScope->insertFunction(spiceFunc, err.get()); + currentScope->insertFunction(spiceFunc); // Rename / duplicate the original child block to reflect the substantiated versions of the function std::vector substantiatedFunctions = spiceFunc.substantiateOptionalArgs(); @@ -274,8 +272,8 @@ std::any AnalyzerVisitor::visitFctDef(FctDefNode *node) { if (returnVarEntry->getType().is(TY_GENERIC)) { SymbolType returnType = spiceFunc.getReturnType(); if (returnType.isPointer()) - throw err->get(node->codeLoc, COMING_SOON_SA, - "Spice currently not supports pointer return types due to not supporting heap allocations."); + throw SemanticError(node->codeLoc, COMING_SOON_SA, + "Spice currently not supports pointer return types due to not supporting heap allocations."); returnVarEntry->updateType(returnType, true); } @@ -313,7 +311,7 @@ std::any AnalyzerVisitor::visitFctDef(FctDefNode *node) { // Check if return variable is now initialized if (currentScope->lookup(RETURN_VARIABLE_NAME)->getState() == DECLARED) - throw err->get(node->codeLoc, FUNCTION_WITHOUT_RETURN_STMT, "Function without return statement"); + throw SemanticError(node->codeLoc, FUNCTION_WITHOUT_RETURN_STMT, "Function without return statement"); // Leave the function scope currentScope = currentScope->getParent(); @@ -339,7 +337,7 @@ std::any AnalyzerVisitor::visitProcDef(ProcDefNode *node) { if (node->isMethod) { currentScope = currentScope->lookupTable(STRUCT_SCOPE_PREFIX + node->structName); if (!currentScope) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, "Struct '" + node->structName + "' could not be found"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, "Struct '" + node->structName + "' could not be found"); } // Create a new scope @@ -352,7 +350,7 @@ std::any AnalyzerVisitor::visitProcDef(ProcDefNode *node) { if (node->isMethod) { SymbolTableEntry *structEntry = currentScope->lookup(node->structName); thisType = structEntry->getType(); - thisPtrType = thisType.toPointer(err.get(), node->codeLoc); + thisPtrType = thisType.toPointer(node->codeLoc); for (const auto &templateType : thisType.getTemplateTypes()) templateTypes.emplace_back(templateType); } @@ -362,7 +360,7 @@ std::any AnalyzerVisitor::visitProcDef(ProcDefNode *node) { for (const auto &dataType : node->templateTypeLst()->dataTypes()) { auto templateType = any_cast(visit(dataType)); if (!templateType.is(TY_GENERIC)) - throw err->get(dataType->codeLoc, EXPECTED_GENERIC_TYPE, "A template list can only contain generic types"); + throw SemanticError(dataType->codeLoc, EXPECTED_GENERIC_TYPE, "A template list can only contain generic types"); GenericType *genericType = currentScope->lookupGenericType(templateType.getSubType()); assert(genericType != nullptr); templateTypes.push_back(*genericType); @@ -370,7 +368,7 @@ std::any AnalyzerVisitor::visitProcDef(ProcDefNode *node) { } if (node->hasParams && node->procedureName == "dtor") - throw err->get(node->codeLoc, DTOR_WITH_PARAMS, "It is not allowed to specify parameters for destructors"); + throw SemanticError(node->codeLoc, DTOR_WITH_PARAMS, "It is not allowed to specify parameters for destructors"); // Visit arguments in new scope std::vector argNames; @@ -385,8 +383,8 @@ std::any AnalyzerVisitor::visitProcDef(ProcDefNode *node) { // Check if the type is present in the template for generic types if (argType.is(TY_GENERIC)) { if (std::none_of(templateTypes.begin(), templateTypes.end(), [&](const GenericType &t) { return t == argType; })) - throw err->get(node->paramLst()->codeLoc, GENERIC_TYPE_NOT_IN_TEMPLATE, - "Generic arg type not included in procedure template"); + throw SemanticError(node->paramLst()->codeLoc, GENERIC_TYPE_NOT_IN_TEMPLATE, + "Generic arg type not included in procedure template"); } argNames.push_back(argName); @@ -413,13 +411,14 @@ std::any AnalyzerVisitor::visitProcDef(ProcDefNode *node) { else if (specifier->type == SpecifierNode::TY_PUBLIC) procSymbolSpecifiers.setPublic(true); else - throw err->get(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this specifier on a function definition"); + throw SemanticError(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, + "Cannot use this specifier on a function definition"); } } // Insert function into the symbol table Function spiceProc(node->procedureName, procSymbolSpecifiers, thisType, SymbolType(TY_DYN), argTypes, templateTypes, node); - currentScope->insertFunction(spiceProc, err.get()); + currentScope->insertFunction(spiceProc); // Rename / duplicate the original child block to reflect the substantiated versions of the function std::vector substantiatedProcedures = spiceProc.substantiateOptionalArgs(); @@ -528,7 +527,7 @@ std::any AnalyzerVisitor::visitStructDef(StructDefNode *node) { // Check if struct already exists in this scope if (currentScope->lookup(node->structName)) - throw err->get(node->codeLoc, STRUCT_DECLARED_TWICE, "Duplicate struct '" + node->structName + "'"); + throw SemanticError(node->codeLoc, STRUCT_DECLARED_TWICE, "Duplicate struct '" + node->structName + "'"); // Get template types std::vector genericTemplateTypes; @@ -537,7 +536,7 @@ std::any AnalyzerVisitor::visitStructDef(StructDefNode *node) { for (const auto &dataType : node->templateTypeLst()->dataTypes()) { auto templateType = any_cast(visit(dataType)); if (!templateType.is(TY_GENERIC)) - throw err->get(dataType->codeLoc, EXPECTED_GENERIC_TYPE, "A template list can only contain generic types"); + throw SemanticError(dataType->codeLoc, EXPECTED_GENERIC_TYPE, "A template list can only contain generic types"); GenericType *genericType = currentScope->lookupGenericType(templateType.getSubType()); assert(genericType != nullptr); genericTemplateTypes.push_back(*genericType); @@ -553,7 +552,8 @@ std::any AnalyzerVisitor::visitStructDef(StructDefNode *node) { if (specifier->type == SpecifierNode::TY_PUBLIC) structSymbolSpecifiers.setPublic(true); else - throw err->get(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this specifier on a function definition"); + throw SemanticError(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, + "Cannot use this specifier on a function definition"); } } @@ -571,16 +571,16 @@ std::any AnalyzerVisitor::visitStructDef(StructDefNode *node) { if (fieldType.isBaseType(TY_GENERIC)) { // Check if the type is present in the template for generic types if (std::none_of(genericTemplateTypes.begin(), genericTemplateTypes.end(), [&](const GenericType &t) { return t == fieldType.getBaseType(); })) - throw err->get(field->dataType()->codeLoc, GENERIC_TYPE_NOT_IN_TEMPLATE, - "Generic field type not included in struct template"); + throw SemanticError(field->dataType()->codeLoc, GENERIC_TYPE_NOT_IN_TEMPLATE, + "Generic field type not included in struct template"); } auto fieldSymbolSpecifiers = SymbolSpecifiers(symbolType); if (SpecifierLstNode *specifierLst = field->specifierLst(); specifierLst) { for (const auto &specifier : specifierLst->specifiers()) { if (specifier->type == SpecifierNode::TY_CONST) - throw err->get(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, - "Struct fields cannot have the const specifier attached"); + throw SemanticError(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, + "Struct fields cannot have the const specifier attached"); else if (specifier->type == SpecifierNode::TY_SIGNED) fieldSymbolSpecifiers.setSigned(true); else if (specifier->type == SpecifierNode::TY_UNSIGNED) @@ -588,7 +588,8 @@ std::any AnalyzerVisitor::visitStructDef(StructDefNode *node) { else if (specifier->type == SpecifierNode::TY_PUBLIC) fieldSymbolSpecifiers.setPublic(true); else - throw err->get(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this specifier on a function definition"); + throw SemanticError(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, + "Cannot use this specifier on a function definition"); } } @@ -603,7 +604,7 @@ std::any AnalyzerVisitor::visitStructDef(StructDefNode *node) { // Add struct Struct s(node->structName, structSymbolSpecifiers, fieldTypes, genericTemplateTypes, node); - currentScope->insertStruct(s, err.get()); + currentScope->insertStruct(s); s.setSymbolTable(structScope); return nullptr; @@ -615,7 +616,7 @@ std::any AnalyzerVisitor::visitEnumDef(EnumDefNode *node) { // Check if enum already exists in this scope if (currentScope->lookup(node->enumName)) - throw err->get(node->codeLoc, ENUM_DECLARED_TWICE, "Duplicate symbol name '" + node->enumName + "'"); + throw SemanticError(node->codeLoc, ENUM_DECLARED_TWICE, "Duplicate symbol name '" + node->enumName + "'"); // Build symbol specifiers auto enumSymbolSpecifiers = SymbolSpecifiers(SymbolType(TY_ENUM, node->enumName)); @@ -624,7 +625,7 @@ std::any AnalyzerVisitor::visitEnumDef(EnumDefNode *node) { if (specifier->type == SpecifierNode::TY_PUBLIC) enumSymbolSpecifiers.setPublic(true); else - throw err->get(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this specifier on an enum definition"); + throw SemanticError(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this specifier on an enum definition"); } } @@ -638,12 +639,12 @@ std::any AnalyzerVisitor::visitEnumDef(EnumDefNode *node) { for (auto enumItem : node->itemLst()->items()) { // Check if the name does exist already if (std::find(names.begin(), names.end(), enumItem->itemName) != names.end()) - throw err->get(enumItem->codeLoc, DUPLICATE_ENUM_ITEM_NAME, "Duplicate enum item name, please use another"); + throw SemanticError(enumItem->codeLoc, DUPLICATE_ENUM_ITEM_NAME, "Duplicate enum item name, please use another"); names.push_back(enumItem->itemName); if (enumItem->hasValue) { if (std::find(values.begin(), values.end(), enumItem->itemValue) != values.end()) - throw err->get(enumItem->codeLoc, DUPLICATE_ENUM_ITEM_VALUE, "Duplicate enum item value, please use another"); + throw SemanticError(enumItem->codeLoc, DUPLICATE_ENUM_ITEM_VALUE, "Duplicate enum item value, please use another"); values.push_back(enumItem->itemValue); } } @@ -671,7 +672,7 @@ std::any AnalyzerVisitor::visitGenericTypeDef(GenericTypeDefNode *node) { // Check if type already exists in this scope if (currentScope->lookup(node->typeName)) - throw err->get(node->codeLoc, GENERIC_TYPE_DECLARED_TWICE, "Duplicate symbol name '" + node->typeName + "'"); + throw SemanticError(node->codeLoc, GENERIC_TYPE_DECLARED_TWICE, "Duplicate symbol name '" + node->typeName + "'"); // Get type conditions std::vector typeConditions; @@ -688,7 +689,7 @@ std::any AnalyzerVisitor::visitGenericTypeDef(GenericTypeDefNode *node) { if (specifier->type == SpecifierNode::TY_PUBLIC) structSymbolSpecifiers.setPublic(true); else - throw err->get(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this specifier on a struct definition"); + throw SemanticError(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, "Cannot use this specifier on a struct definition"); } } @@ -704,14 +705,14 @@ std::any AnalyzerVisitor::visitGlobalVarDef(GlobalVarDefNode *node) { // Check if symbol already exists in the symbol table if (currentScope->lookup(node->varName)) - throw err->get(node->codeLoc, VARIABLE_DECLARED_TWICE, - "The global variable '" + node->varName + "' was declared more than once"); + throw SemanticError(node->codeLoc, VARIABLE_DECLARED_TWICE, + "The global variable '" + node->varName + "' was declared more than once"); // Check if symbol already exists in any imported module scope if (currentScope->lookupGlobal(node->varName, true)) - throw err->get(node->codeLoc, VARIABLE_DECLARED_TWICE, - "A global variable named '" + node->varName + - "' is already declared in another module. Please use a different name."); + throw SemanticError(node->codeLoc, VARIABLE_DECLARED_TWICE, + "A global variable named '" + node->varName + + "' is already declared in another module. Please use a different name."); // Insert variable name to symbol table auto symbolType = any_cast(visit(node->dataType())); @@ -723,20 +724,20 @@ std::any AnalyzerVisitor::visitGlobalVarDef(GlobalVarDefNode *node) { if (symbolType.is(TY_DYN)) { symbolType = valueType; } else if (symbolType != valueType) { - throw err->get(node->value()->codeLoc, OPERATOR_WRONG_DATA_TYPE, - "Cannot apply the assign operator on different data types. You provided " + symbolType.getName(false) + - " and " + valueType.getName(false)); + throw SemanticError(node->value()->codeLoc, OPERATOR_WRONG_DATA_TYPE, + "Cannot apply the assign operator on different data types. You provided " + symbolType.getName(false) + + " and " + valueType.getName(false)); } state = INITIALIZED; } // Check if the type is missing if (symbolType.is(TY_DYN)) - throw err->get(node->dataType()->codeLoc, GLOBAL_OF_TYPE_DYN, "Global variables must have an explicit data type"); + throw SemanticError(node->dataType()->codeLoc, GLOBAL_OF_TYPE_DYN, "Global variables must have an explicit data type"); // Check if we would need to insert instructions in the global scope if (!symbolType.isPrimitive()) - throw err->get(node->dataType()->codeLoc, GLOBAL_OF_INVALID_TYPE, "Spice does not allow global variables of this type"); + throw SemanticError(node->dataType()->codeLoc, GLOBAL_OF_INVALID_TYPE, "Spice does not allow global variables of this type"); // Create symbol specifiers auto symbolTypeSpecifiers = SymbolSpecifiers(symbolType); @@ -745,7 +746,8 @@ std::any AnalyzerVisitor::visitGlobalVarDef(GlobalVarDefNode *node) { if (specifier->type == SpecifierNode::TY_CONST) { // Check if a value is attached if (!node->value()) - throw err->get(node->codeLoc, GLOBAL_CONST_WITHOUT_VALUE, "You must specify a value for constant global variables"); + throw SemanticError(node->codeLoc, GLOBAL_CONST_WITHOUT_VALUE, + "You must specify a value for constant global variables"); symbolTypeSpecifiers.setConst(true); } else if (specifier->type == SpecifierNode::TY_SIGNED) { symbolTypeSpecifiers.setSigned(true); @@ -754,8 +756,8 @@ std::any AnalyzerVisitor::visitGlobalVarDef(GlobalVarDefNode *node) { } else if (specifier->type == SpecifierNode::TY_PUBLIC) { symbolTypeSpecifiers.setPublic(true); } else { - throw err->get(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, - "Cannot use this specifier on a global variable definition"); + throw SemanticError(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, + "Cannot use this specifier on a global variable definition"); } } } @@ -776,7 +778,8 @@ std::any AnalyzerVisitor::visitExtDecl(ExtDeclNode *node) { for (const auto &arg : node->argTypeLst()->dataTypes()) { auto argType = any_cast(visit(arg)); if (argType.is(TY_DYN)) - throw err->get(arg->codeLoc, UNEXPECTED_DYN_TYPE_SA, "Dyn data type is not allowed as arg type for external functions"); + throw SemanticError(arg->codeLoc, UNEXPECTED_DYN_TYPE_SA, + "Dyn data type is not allowed as arg type for external functions"); argTypes.emplace_back(argType, false); } } @@ -785,13 +788,13 @@ std::any AnalyzerVisitor::visitExtDecl(ExtDeclNode *node) { // Check if return type is dyn auto returnType = any_cast(visit(node->returnType())); if (returnType.is(TY_DYN)) - throw err->get(node->returnType()->codeLoc, UNEXPECTED_DYN_TYPE_SA, - "Dyn data type is not allowed as return type for external functions"); + throw SemanticError(node->returnType()->codeLoc, UNEXPECTED_DYN_TYPE_SA, + "Dyn data type is not allowed as return type for external functions"); // Insert function into symbol table SymbolSpecifiers symbolSpecifiers = SymbolSpecifiers(SymbolType(TY_FUNCTION)); Function spiceFunc(node->extFunctionName, symbolSpecifiers, SymbolType(TY_DYN), returnType, argTypes, {}, node); - currentScope->insertFunction(spiceFunc, err.get()); + currentScope->insertFunction(spiceFunc); // Add return symbol for function SymbolTable *functionTable = currentScope->createChildBlock(spiceFunc.getSignature(), SCOPE_FUNC_PROC_BODY); @@ -801,7 +804,7 @@ std::any AnalyzerVisitor::visitExtDecl(ExtDeclNode *node) { // Insert procedure into symbol table SymbolSpecifiers symbolSpecifiers = SymbolSpecifiers(SymbolType(TY_PROCEDURE)); Function spiceProc(node->extFunctionName, symbolSpecifiers, SymbolType(TY_DYN), SymbolType(TY_DYN), argTypes, {}, node); - currentScope->insertFunction(spiceProc, err.get()); + currentScope->insertFunction(spiceProc); // Add empty scope for function body currentScope->createChildBlock(spiceProc.getSignature(), SCOPE_FUNC_PROC_BODY); @@ -821,7 +824,7 @@ std::any AnalyzerVisitor::visitThreadDef(ThreadDefNode *node) { // Return to old scope currentScope = currentScope->getParent(); - return node->setEvaluatedSymbolType(SymbolType(TY_BYTE).toPointer(err.get(), node->codeLoc)); + return node->setEvaluatedSymbolType(SymbolType(TY_BYTE).toPointer(node->codeLoc)); } std::any AnalyzerVisitor::visitUnsafeBlockDef(UnsafeBlockDefNode *node) { @@ -853,7 +856,7 @@ std::any AnalyzerVisitor::visitForLoop(ForLoopNode *node) { // Visit condition in new scope auto conditionType = any_cast(visit(node->condAssign())); if (!conditionType.is(TY_BOOL)) - throw err->get(node->condAssign()->codeLoc, CONDITION_MUST_BE_BOOL, "For loop condition must be of type bool"); + throw SemanticError(node->condAssign()->codeLoc, CONDITION_MUST_BE_BOOL, "For loop condition must be of type bool"); // Visit incrementer in new scope visit(node->incAssign()); @@ -876,12 +879,12 @@ std::any AnalyzerVisitor::visitForeachLoop(ForeachLoopNode *node) { expectedType = SymbolType(TY_DYN); auto arrayType = any_cast(visit(node->arrayAssign())); if (!arrayType.isArray() && !arrayType.is(TY_STRING)) - throw err->get(node->arrayAssign()->codeLoc, OPERATOR_WRONG_DATA_TYPE, - "Can only apply foreach loop on an array type. You provided " + arrayType.getName(false)); + throw SemanticError(node->arrayAssign()->codeLoc, OPERATOR_WRONG_DATA_TYPE, + "Can only apply foreach loop on an array type. You provided " + arrayType.getName(false)); if (arrayType.getArraySize() == 0) - throw err->get(node->arrayAssign()->codeLoc, OPERATOR_WRONG_DATA_TYPE, - "Can only apply foreach loop on an array type of which the size is known at compile time"); + throw SemanticError(node->arrayAssign()->codeLoc, OPERATOR_WRONG_DATA_TYPE, + "Can only apply foreach loop on an array type of which the size is known at compile time"); // Check index assignment or declaration SymbolType indexType; @@ -898,8 +901,8 @@ std::any AnalyzerVisitor::visitForeachLoop(ForeachLoopNode *node) { // Check if index type is int if (!indexType.is(TY_INT)) - throw err->get(node->idxVarDecl()->codeLoc, ARRAY_INDEX_NO_INTEGER, - "Index in foreach loop must be of type int. You provided " + indexType.getName(false)); + throw SemanticError(node->idxVarDecl()->codeLoc, ARRAY_INDEX_NO_INTEGER, + "Index in foreach loop must be of type int. You provided " + indexType.getName(false)); } else { // Declare the variable with the default index variable name SymbolType symbolType = SymbolType(TY_INT); @@ -920,9 +923,9 @@ std::any AnalyzerVisitor::visitForeachLoop(ForeachLoopNode *node) { node->itemDecl()->dataType()->setEvaluatedSymbolType(itemType); } else { if (itemType != arrayType.getContainedTy()) - throw err->get(node->itemDecl()->codeLoc, OPERATOR_WRONG_DATA_TYPE, - "Foreach loop item type does not match array type. Expected " + arrayType.getName(false) + ", provided " + - itemType.getName(false)); + throw SemanticError(node->itemDecl()->codeLoc, OPERATOR_WRONG_DATA_TYPE, + "Foreach loop item type does not match array type. Expected " + arrayType.getName(false) + + ", provided " + itemType.getName(false)); } itemVarSymbol->updateState(INITIALIZED, node->itemDecl()->codeLoc); @@ -944,7 +947,7 @@ std::any AnalyzerVisitor::visitWhileLoop(WhileLoopNode *node) { // Visit condition auto conditionType = any_cast(visit(node->condition())); if (!conditionType.is(TY_BOOL)) - throw err->get(node->condition()->codeLoc, CONDITION_MUST_BE_BOOL, "While loop condition must be of type bool"); + throw SemanticError(node->condition()->codeLoc, CONDITION_MUST_BE_BOOL, "While loop condition must be of type bool"); // Visit statement list in new scope nestedLoopCounter++; @@ -964,7 +967,7 @@ std::any AnalyzerVisitor::visitIfStmt(IfStmtNode *node) { // Visit condition auto conditionType = any_cast(visit(node->condition())); if (!conditionType.is(TY_BOOL)) - throw err->get(node->condition()->codeLoc, CONDITION_MUST_BE_BOOL, "If condition must be of type bool"); + throw SemanticError(node->condition()->codeLoc, CONDITION_MUST_BE_BOOL, "If condition must be of type bool"); // Visit statement list in new scope visit(node->stmtLst()); @@ -1000,7 +1003,7 @@ std::any AnalyzerVisitor::visitAssertStmt(AssertStmtNode *node) { // Check if assertStmt evaluates to bool if (!assertConditionType.is(TY_BOOL)) - throw err->get(node->assignExpr()->codeLoc, ASSERTION_CONDITION_BOOL, "The asserted condition must be of type bool"); + throw SemanticError(node->assignExpr()->codeLoc, ASSERTION_CONDITION_BOOL, "The asserted condition must be of type bool"); return nullptr; } @@ -1014,13 +1017,13 @@ std::any AnalyzerVisitor::visitParamLst(ParamLstNode *node) { // Check if the type could be inferred. Dyn without a default value is forbidden if (paramType.is(TY_DYN)) - throw err->get(node->codeLoc, FCT_PARAM_IS_TYPE_DYN, "Type of parameter '" + param->varName + "' is invalid"); + throw SemanticError(node->codeLoc, FCT_PARAM_IS_TYPE_DYN, "Type of parameter '" + param->varName + "' is invalid"); // Ensure that no optional param comes after a mandatory param if (param->hasAssignment) { metOptional = true; } else if (metOptional) { - throw err->get(param->codeLoc, INVALID_PARAM_ORDER, "Mandatory parameters must go before any optional parameters"); + throw SemanticError(param->codeLoc, INVALID_PARAM_ORDER, "Mandatory parameters must go before any optional parameters"); } namedParamList.emplace_back(param->varName, paramType, metOptional); @@ -1031,7 +1034,8 @@ std::any AnalyzerVisitor::visitParamLst(ParamLstNode *node) { std::any AnalyzerVisitor::visitDeclStmt(DeclStmtNode *node) { // Check if symbol already exists in the symbol table if (currentScope->lookupStrict(node->varName)) - throw err->get(node->codeLoc, VARIABLE_DECLARED_TWICE, "The variable '" + node->varName + "' was declared more than once"); + throw SemanticError(node->codeLoc, VARIABLE_DECLARED_TWICE, + "The variable '" + node->varName + "' was declared more than once"); // Get the type of the symbol SymbolType symbolType = expectedType = any_cast(visit(node->dataType())); @@ -1049,7 +1053,8 @@ std::any AnalyzerVisitor::visitDeclStmt(DeclStmtNode *node) { // If the rhs is of type array and was the array initialization, there must be a size attached if (symbolType.isArray() && symbolType.getArraySize() == 0 && currentVarName.empty()) - throw err->get(node->dataType()->codeLoc, ARRAY_SIZE_INVALID, "The declaration of an array type must have a size attached"); + throw SemanticError(node->dataType()->codeLoc, ARRAY_SIZE_INVALID, + "The declaration of an array type must have a size attached"); } // Build symbol specifiers @@ -1065,8 +1070,8 @@ std::any AnalyzerVisitor::visitDeclStmt(DeclStmtNode *node) { symbolTypeSpecifiers.setSigned(false); symbolType.setSigned(false); } else { - throw err->get(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, - "Cannot use this specifier on a local variable declaration"); + throw SemanticError(specifier->codeLoc, SPECIFIER_AT_ILLEGAL_CONTEXT, + "Cannot use this specifier on a local variable declaration"); } } } @@ -1100,9 +1105,9 @@ std::any AnalyzerVisitor::visitReturnStmt(ReturnStmtNode *node) { } else { // Check if return type matches with function definition if (returnType != returnVariable->getType()) - throw err->get(node->assignExpr()->codeLoc, OPERATOR_WRONG_DATA_TYPE, - "Passed wrong data type to return statement. Expected " + returnVariable->getType().getName(false) + - " but got " + returnType.getName(false)); + throw SemanticError(node->assignExpr()->codeLoc, OPERATOR_WRONG_DATA_TYPE, + "Passed wrong data type to return statement. Expected " + returnVariable->getType().getName(false) + + " but got " + returnType.getName(false)); } // Set the return variable to initialized @@ -1113,14 +1118,14 @@ std::any AnalyzerVisitor::visitReturnStmt(ReturnStmtNode *node) { // Check if result variable is initialized if (returnVariable->getState() != INITIALIZED) - throw err->get(node->codeLoc, RETURN_WITHOUT_VALUE_RESULT, - "Return without value, but result variable is not initialized yet"); + throw SemanticError(node->codeLoc, RETURN_WITHOUT_VALUE_RESULT, + "Return without value, but result variable is not initialized yet"); returnVariable->setUsed(); } else { // No return variable => procedure if (node->assignExpr()) - throw err->get(node->assignExpr()->codeLoc, RETURN_WITH_VALUE_IN_PROCEDURE, - "Return statements in procedures may not have a value attached"); + throw SemanticError(node->assignExpr()->codeLoc, RETURN_WITH_VALUE_IN_PROCEDURE, + "Return statements in procedures may not have a value attached"); } // Call destructors for variables, that are going out of scope @@ -1135,13 +1140,13 @@ std::any AnalyzerVisitor::visitBreakStmt(BreakStmtNode *node) { if (node->breakTimes != 1) { // Check if the stated number is valid if (node->breakTimes < 1) - throw err->get(node->codeLoc, INVALID_BREAK_NUMBER, - "Break count must be >= 1, you provided " + std::to_string(node->breakTimes)); + throw SemanticError(node->codeLoc, INVALID_BREAK_NUMBER, + "Break count must be >= 1, you provided " + std::to_string(node->breakTimes)); } // Check if we can break this often if (node->breakTimes > nestedLoopCounter) - throw err->get(node->codeLoc, INVALID_BREAK_NUMBER, - "We can only break " + std::to_string(nestedLoopCounter) + " time(s) here"); + throw SemanticError(node->codeLoc, INVALID_BREAK_NUMBER, + "We can only break " + std::to_string(nestedLoopCounter) + " time(s) here"); return nullptr; } @@ -1149,13 +1154,13 @@ std::any AnalyzerVisitor::visitContinueStmt(ContinueStmtNode *node) { if (node->continueTimes != 1) { // Check if the stated number is valid if (node->continueTimes < 1) - throw err->get(node->codeLoc, INVALID_CONTINUE_NUMBER, - "Continue count must be >= 1, you provided " + std::to_string(node->continueTimes)); + throw SemanticError(node->codeLoc, INVALID_CONTINUE_NUMBER, + "Continue count must be >= 1, you provided " + std::to_string(node->continueTimes)); } // Check if we can continue this often if (node->continueTimes > nestedLoopCounter) - throw err->get(node->codeLoc, INVALID_CONTINUE_NUMBER, - "We can only continue " + std::to_string(nestedLoopCounter) + " time(s) here"); + throw SemanticError(node->codeLoc, INVALID_CONTINUE_NUMBER, + "We can only continue " + std::to_string(nestedLoopCounter) + " time(s) here"); return nullptr; } @@ -1166,16 +1171,16 @@ std::any AnalyzerVisitor::visitPrintfCall(PrintfCallNode *node) { while (index != std::string::npos) { // Check if there is another assignExpr if (node->assignExpr().size() <= placeholderCount) - throw err->get(node->codeLoc, PRINTF_ARG_COUNT_ERROR, - "The placeholder string contains more placeholders that arguments were passed"); + throw SemanticError(node->codeLoc, PRINTF_ARG_COUNT_ERROR, + "The placeholder string contains more placeholders that arguments were passed"); auto assignment = node->assignExpr()[placeholderCount]; auto assignmentType = any_cast(visit(assignment)); switch (node->templatedString[index + 1]) { case 'c': { if (!assignmentType.is(TY_CHAR)) - throw err->get(assignment->codeLoc, PRINTF_TYPE_ERROR, - "Template string expects char, but got " + assignmentType.getName(false)); + throw SemanticError(assignment->codeLoc, PRINTF_TYPE_ERROR, + "Template string expects char, but got " + assignmentType.getName(false)); placeholderCount++; break; } @@ -1187,8 +1192,8 @@ std::any AnalyzerVisitor::visitPrintfCall(PrintfCallNode *node) { case 'x': case 'X': { if (!assignmentType.isOneOf({TY_INT, TY_SHORT, TY_LONG, TY_BYTE, TY_BOOL})) - throw err->get(assignment->codeLoc, PRINTF_TYPE_ERROR, - "Template string expects int, byte or bool, but got " + assignmentType.getName(false)); + throw SemanticError(assignment->codeLoc, PRINTF_TYPE_ERROR, + "Template string expects int, byte or bool, but got " + assignmentType.getName(false)); placeholderCount++; break; } @@ -1201,22 +1206,22 @@ std::any AnalyzerVisitor::visitPrintfCall(PrintfCallNode *node) { case 'g': case 'G': { if (!assignmentType.is(TY_DOUBLE)) - throw err->get(assignment->codeLoc, PRINTF_TYPE_ERROR, - "Template string expects double, but got " + assignmentType.getName(false)); + throw SemanticError(assignment->codeLoc, PRINTF_TYPE_ERROR, + "Template string expects double, but got " + assignmentType.getName(false)); placeholderCount++; break; } case 's': { if (!assignmentType.is(TY_STRING) && !assignmentType.isPointerOf(TY_CHAR) && !assignmentType.isArrayOf(TY_CHAR)) - throw err->get(assignment->codeLoc, PRINTF_TYPE_ERROR, - "Template string expects string, but got " + assignmentType.getName(false)); + throw SemanticError(assignment->codeLoc, PRINTF_TYPE_ERROR, + "Template string expects string, but got " + assignmentType.getName(false)); placeholderCount++; break; } case 'p': { if (!assignmentType.isPointer() && !assignmentType.isArray()) - throw err->get(assignment->codeLoc, PRINTF_TYPE_ERROR, - "Template string expects pointer, but got " + assignmentType.getName(false)); + throw SemanticError(assignment->codeLoc, PRINTF_TYPE_ERROR, + "Template string expects pointer, but got " + assignmentType.getName(false)); placeholderCount++; break; } @@ -1226,8 +1231,8 @@ std::any AnalyzerVisitor::visitPrintfCall(PrintfCallNode *node) { // Check if the number of placeholders matches the number of args if (placeholderCount < node->assignExpr().size()) - throw err->get(node->codeLoc, PRINTF_ARG_COUNT_ERROR, - "The placeholder string contains less placeholders that arguments were passed"); + throw SemanticError(node->codeLoc, PRINTF_ARG_COUNT_ERROR, + "The placeholder string contains less placeholders that arguments were passed"); return node->setEvaluatedSymbolType(SymbolType(TY_BOOL)); } @@ -1242,7 +1247,7 @@ std::any AnalyzerVisitor::visitSizeofCall(SizeofCallNode *node) { // Check if symbol type is dynamically sized array if (symbolType.is(TY_ARRAY) && symbolType.getArraySize() == -1) - throw err->get(node->codeLoc, SIZEOF_DYNAMIC_SIZED_ARRAY, "Cannot get sizeof dynamically sized array at compile time"); + throw SemanticError(node->codeLoc, SIZEOF_DYNAMIC_SIZED_ARRAY, "Cannot get sizeof dynamically sized array at compile time"); return node->setEvaluatedSymbolType(SymbolType(TY_INT)); } @@ -1252,7 +1257,7 @@ std::any AnalyzerVisitor::visitLenCall(LenCallNode *node) { // Check if arg is of type array if (!argType.isArray()) - throw err->get(node->assignExpr()->codeLoc, EXPECTED_ARRAY_TYPE, "The len builtin can only work on arrays"); + throw SemanticError(node->assignExpr()->codeLoc, EXPECTED_ARRAY_TYPE, "The len builtin can only work on arrays"); return node->setEvaluatedSymbolType(SymbolType(TY_INT)); } @@ -1263,12 +1268,12 @@ std::any AnalyzerVisitor::visitTidCall(TidCallNode *node) { } std::any AnalyzerVisitor::visitJoinCall(JoinCallNode *node) { - SymbolType bytePtr = SymbolType(TY_BYTE).toPointer(err.get(), node->codeLoc); + SymbolType bytePtr = SymbolType(TY_BYTE).toPointer(node->codeLoc); for (const auto &assignExpr : node->assignExpressions()) { auto argSymbolType = any_cast(visit(assignExpr)); if (argSymbolType == bytePtr && argSymbolType.isArrayOf(bytePtr)) - throw err->get(assignExpr->codeLoc, JOIN_ARG_MUST_BE_TID, - "You have to pass a thread id (byte*) or a array of thread ids (byte*[]) to to join builtin"); + throw SemanticError(assignExpr->codeLoc, JOIN_ARG_MUST_BE_TID, + "You have to pass a thread id (byte*) or a array of thread ids (byte*[]) to to join builtin"); } // Return the number of threads that were joined @@ -1315,8 +1320,8 @@ std::any AnalyzerVisitor::visitAssignExpr(AssignExprNode *node) { if (!variableName.empty()) { // Variable is involved on the left side // Check if the symbol exists if (!currentEntry) - throw err->get(node->lhs()->codeLoc, REFERENCED_UNDEFINED_VARIABLE, - "The variable '" + variableName + "' was referenced before defined"); + throw SemanticError(node->lhs()->codeLoc, REFERENCED_UNDEFINED_VARIABLE, + "The variable '" + variableName + "' was referenced before defined"); // Perform type inference if (lhsTy.is(TY_DYN)) @@ -1366,10 +1371,11 @@ std::any AnalyzerVisitor::visitTernaryExpr(TernaryExprNode *node) { } // Check if the condition evaluates to boolean if (!conditionType.is(TY_BOOL)) - throw err->get(condition->codeLoc, OPERATOR_WRONG_DATA_TYPE, "Condition operand in ternary must be a bool"); + throw SemanticError(condition->codeLoc, OPERATOR_WRONG_DATA_TYPE, "Condition operand in ternary must be a bool"); // Check if trueType and falseType are matching if (trueType != falseType) - throw err->get(node->codeLoc, OPERATOR_WRONG_DATA_TYPE, "True and false operands in ternary must be of same data type"); + throw SemanticError(node->codeLoc, OPERATOR_WRONG_DATA_TYPE, + "True and false operands in ternary must be of same data type"); return node->setEvaluatedSymbolType(trueType); } return visit(node->operands().front()); @@ -1633,8 +1639,8 @@ std::any AnalyzerVisitor::visitPrefixUnaryExpr(PrefixUnaryExprNode *node) { std::any AnalyzerVisitor::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) { auto lhs = any_cast(visit(node->atomicExpr())); if (lhs.is(TY_INVALID)) - throw ErrorFactory::get(node->codeLoc, REFERENCED_UNDEFINED_VARIABLE, - "Variable '" + node->atomicExpr()->identifier + "' was referenced before declared"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_VARIABLE, + "Variable '" + node->atomicExpr()->identifier + "' was referenced before declared"); size_t subscriptCounter = 0; size_t memberAccessCounter = 0; @@ -1652,22 +1658,22 @@ std::any AnalyzerVisitor::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) { auto indexType = any_cast(visit(indexExpr)); if (!indexType.is(TY_INT)) - throw err->get(node->codeLoc, ARRAY_INDEX_NO_INTEGER, "Array index must be of type int"); + throw SemanticError(node->codeLoc, ARRAY_INDEX_NO_INTEGER, "Array index must be of type int"); if (!lhs.isOneOf({TY_ARRAY, TY_STRING, TY_PTR})) - throw err->get(node->codeLoc, OPERATOR_WRONG_DATA_TYPE, - "Can only apply subscript operator on array type, got " + lhs.getName(true)); + throw SemanticError(node->codeLoc, OPERATOR_WRONG_DATA_TYPE, + "Can only apply subscript operator on array type, got " + lhs.getName(true)); if (lhs.is(TY_PTR) && !allowUnsafeOperations) { - throw err->get( + throw SemanticError( node->codeLoc, UNSAFE_OPERATION_IN_SAFE_CONTEXT, "The subscript operator on pointers is an unsafe operation. Use unsafe blocks if you know what you are doing."); } else if (lhs.is(TY_ARRAY) && lhs.getArraySize() > 0 && indexExpr->hasCompileTimeValue()) { std::int32_t constIndex = indexExpr->getCompileTimeValue().intValue; size_t constSize = lhs.getArraySize(); if (constIndex >= constSize) - throw err->get(node->codeLoc, ARRAY_INDEX_OUT_OF_BOUNDS, - "You are trying to access element with index " + std::to_string(constIndex) + " of an array with size " + - std::to_string(constSize)); + throw SemanticError(node->codeLoc, ARRAY_INDEX_OUT_OF_BOUNDS, + "You are trying to access element with index " + std::to_string(constIndex) + + " of an array with size " + std::to_string(constSize)); } // Get array item type @@ -1694,7 +1700,7 @@ std::any AnalyzerVisitor::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) { case PostfixUnaryExprNode::OP_MEMBER_ACCESS: { // Check if lhs is struct if (!lhs.isBaseType(TY_STRUCT) && !lhs.isBaseType(TY_ENUM)) - throw err->get(node->codeLoc, MEMBER_ACCESS_ONLY_STRUCTS, "Cannot apply member access operator on " + lhs.getName()); + throw SemanticError(node->codeLoc, MEMBER_ACCESS_ONLY_STRUCTS, "Cannot apply member access operator on " + lhs.getName()); PostfixUnaryExprNode *rhs = node->postfixUnaryExpr()[memberAccessCounter++]; lhs = any_cast(visit(rhs)); // Visit rhs @@ -1703,7 +1709,7 @@ std::any AnalyzerVisitor::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) { case PostfixUnaryExprNode::OP_SCOPE_ACCESS: { // Check if lhs is import if (!lhs.is(TY_IMPORT)) - throw err->get(node->codeLoc, SCOPE_ACCESS_ONLY_IMPORTS, "Cannot apply scope access operator on " + lhs.getName()); + throw SemanticError(node->codeLoc, SCOPE_ACCESS_ONLY_IMPORTS, "Cannot apply scope access operator on " + lhs.getName()); PostfixUnaryExprNode *rhs = node->postfixUnaryExpr()[memberAccessCounter++]; lhs = any_cast(visit(rhs)); // Visit rhs @@ -1743,8 +1749,8 @@ std::any AnalyzerVisitor::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) { node->opQueue = newOpQueue; if (lhs.is(TY_INVALID)) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_VARIABLE, - "Variable '" + currentVarName + "' was referenced before declared"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_VARIABLE, + "Variable '" + currentVarName + "' was referenced before declared"); return node->setEvaluatedSymbolType(lhs); } @@ -1758,9 +1764,10 @@ std::any AnalyzerVisitor::visitAtomicExpr(AtomicExprNode *node) { // Check if this is a reserved keyword if (std::find(RESERVED_KEYWORDS.begin(), RESERVED_KEYWORDS.end(), currentVarName) != RESERVED_KEYWORDS.end()) - throw err->get(node->codeLoc, RESERVED_KEYWORD, - "'" + currentVarName + - "' is a reserved keyword for future development of the language. Please use another identifier instead"); + throw SemanticError( + node->codeLoc, RESERVED_KEYWORD, + "'" + currentVarName + + "' is a reserved keyword for future development of the language. Please use another identifier instead"); // Load symbol table entry SymbolTable *accessScope = scopePath.getCurrentScope() ? scopePath.getCurrentScope() : currentScope; @@ -1777,8 +1784,8 @@ std::any AnalyzerVisitor::visitAtomicExpr(AtomicExprNode *node) { if (accessScope->isImported(currentScope)) { // Check if the entry is public if it is imported if (!entry->getSpecifiers().isPublic()) - throw err->get(node->codeLoc, INSUFFICIENT_VISIBILITY, - "Cannot access '" + currentVarName + "' due to its private visibility"); + throw SemanticError(node->codeLoc, INSUFFICIENT_VISIBILITY, + "Cannot access '" + currentVarName + "' due to its private visibility"); // Check if the entry is an external global variable and needs to be imported if (entry->isGlobal() && !entry->getType().isOneOf({TY_FUNCTION, TY_PROCEDURE, TY_IMPORT})) @@ -1806,8 +1813,8 @@ std::any AnalyzerVisitor::visitAtomicExpr(AtomicExprNode *node) { // Check if the entry is public if it is imported if (structCapture && !structCapture->getEntry()->getSpecifiers().isPublic() && accessScope->getParent()->isImported(currentScope)) - throw err->get(node->codeLoc, INSUFFICIENT_VISIBILITY, - "Cannot access '" + structSignature + "' due to its private visibility"); + throw SemanticError(node->codeLoc, INSUFFICIENT_VISIBILITY, + "Cannot access '" + structSignature + "' due to its private visibility"); // If the return type is an external struct, initialize it if (!scopePath.isEmpty() && scopePath.getCurrentScope()->isImported(currentScope)) { @@ -1824,8 +1831,8 @@ std::any AnalyzerVisitor::visitAtomicExpr(AtomicExprNode *node) { } else { // Check if we have seen a 'this.' prefix, because the generator needs that if (entry->getScope()->getScopeType() == SCOPE_STRUCT && currentThisType.is(TY_DYN)) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_VARIABLE, - "The symbol '" + currentVarName + "' could not be found. Missing 'this.' prefix?"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_VARIABLE, + "The symbol '" + currentVarName + "' could not be found. Missing 'this.' prefix?"); } assert(accessScope != nullptr); @@ -1874,7 +1881,7 @@ std::any AnalyzerVisitor::visitValue(ValueNode *node) { if (node->isNil) { auto nilType = any_cast(visit(node->nilType())); if (nilType.is(TY_DYN)) - throw err->get(node->nilType()->codeLoc, UNEXPECTED_DYN_TYPE_SA, "Nil must have an explicit type"); + throw SemanticError(node->nilType()->codeLoc, UNEXPECTED_DYN_TYPE_SA, "Nil must have an explicit type"); return node->setEvaluatedSymbolType(nilType); } @@ -1916,8 +1923,8 @@ std::any AnalyzerVisitor::visitFunctionCall(FunctionCallNode *node) { if (i < node->functionNameFragments.size() - 1) { if (!symbolEntry) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_FUNCTION, - "Symbol '" + scopePath.getScopePrefix() + identifier + "' was used before defined"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_FUNCTION, + "Symbol '" + scopePath.getScopePrefix() + identifier + "' was used before defined"); thisType = symbolEntry->getType().getBaseType(); } else if (symbolEntry != nullptr && symbolEntry->getType().getBaseType().is(TY_STRUCT)) { // Get the concrete template types @@ -1929,9 +1936,9 @@ std::any AnalyzerVisitor::visitFunctionCall(FunctionCallNode *node) { std::string structSignature = Struct::getSignature(identifier, concreteTemplateTypes); // Get the struct instance - Struct *spiceStruct = accessScope->matchStruct(currentScope, identifier, concreteTemplateTypes, err.get(), node->codeLoc); + Struct *spiceStruct = accessScope->matchStruct(currentScope, identifier, concreteTemplateTypes, node->codeLoc); if (!spiceStruct) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, "Struct '" + structSignature + "' could not be found"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, "Struct '" + structSignature + "' could not be found"); spiceStruct->setUsed(); symbolEntry = accessScope->lookup(structSignature); @@ -1954,7 +1961,7 @@ std::any AnalyzerVisitor::visitFunctionCall(FunctionCallNode *node) { accessScope = accessScope->lookupTable(tableName); if (!accessScope) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_FUNCTION, "Cannot call a function on '" + identifier + "'"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_FUNCTION, "Cannot call a function on '" + identifier + "'"); scopePath.pushFragment(identifier, accessScope); } @@ -1979,7 +1986,7 @@ std::any AnalyzerVisitor::visitFunctionCall(FunctionCallNode *node) { // Get the function/procedure instance SymbolType origThisType = thisType.replaceBaseSubType(CommonUtil::getLastFragment(thisType.getBaseType().getSubType(), ".")); - Function *spiceFunc = accessScope->matchFunction(currentScope, functionName, origThisType, argTypes, err.get(), node->codeLoc); + Function *spiceFunc = accessScope->matchFunction(currentScope, functionName, origThisType, argTypes, node->codeLoc); if (!spiceFunc) { // Build dummy function to get a better error message SymbolSpecifiers specifiers = SymbolSpecifiers(SymbolType(TY_FUNCTION)); @@ -1990,8 +1997,8 @@ std::any AnalyzerVisitor::visitFunctionCall(FunctionCallNode *node) { Function f(functionName, specifiers, thisType, SymbolType(TY_DYN), errArgTypes, {}, node); - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_FUNCTION, - "Function/Procedure '" + f.getSignature() + "' could not be found"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_FUNCTION, + "Function/Procedure '" + f.getSignature() + "' could not be found"); } spiceFunc->setUsed(); @@ -2002,8 +2009,8 @@ std::any AnalyzerVisitor::visitFunctionCall(FunctionCallNode *node) { // Check if the function entry has sufficient visibility if (accessScope->isImported(currentScope) && !functionEntry->getSpecifiers().isPublic()) - throw err->get(node->codeLoc, INSUFFICIENT_VISIBILITY, - "Cannot access function/procedure '" + spiceFunc->getSignature() + "' due to its private visibility"); + throw SemanticError(node->codeLoc, INSUFFICIENT_VISIBILITY, + "Cannot access function/procedure '" + spiceFunc->getSignature() + "' due to its private visibility"); // Analyze the function if not done yet. This is only necessary if we call a function in the same source file, which was // declared above. @@ -2045,9 +2052,9 @@ std::any AnalyzerVisitor::visitArrayInitialization(ArrayInitializationNode *node if (actualItemType.is(TY_DYN)) { actualItemType = itemType; } else if (itemType != actualItemType) { - throw err->get(arg->codeLoc, ARRAY_ITEM_TYPE_NOT_MATCHING, - "All provided values have to be of the same data type. You provided " + actualItemType.getName(false) + - " and " + itemType.getName(false)); + throw SemanticError(arg->codeLoc, ARRAY_ITEM_TYPE_NOT_MATCHING, + "All provided values have to be of the same data type. You provided " + + actualItemType.getName(false) + " and " + itemType.getName(false)); } actualSize++; } @@ -2062,13 +2069,13 @@ std::any AnalyzerVisitor::visitArrayInitialization(ArrayInitializationNode *node // Check if actual item type is known now if (actualItemType.is(TY_DYN)) { // Not enough info to perform type inference, because of empty array {} if (expectedType.is(TY_DYN)) - throw err->get(node->codeLoc, UNEXPECTED_DYN_TYPE_SA, "Not enough information to perform type inference"); + throw SemanticError(node->codeLoc, UNEXPECTED_DYN_TYPE_SA, "Not enough information to perform type inference"); if (expectedType.is(TY_DYN)) - throw err->get(node->codeLoc, ARRAY_ITEM_TYPE_NOT_MATCHING, "Cannot assign an array to a primitive data type"); + throw SemanticError(node->codeLoc, ARRAY_ITEM_TYPE_NOT_MATCHING, "Cannot assign an array to a primitive data type"); actualItemType = expectedType.getContainedTy(); } - return node->setEvaluatedSymbolType(actualItemType.toArray(err.get(), node->codeLoc, actualSize)); + return node->setEvaluatedSymbolType(actualItemType.toArray(node->codeLoc, actualSize)); } std::any AnalyzerVisitor::visitStructInstantiation(StructInstantiationNode *node) { @@ -2084,8 +2091,8 @@ std::any AnalyzerVisitor::visitStructInstantiation(StructInstantiationNode *node if (i < node->structNameFragments.size() - 1) { SymbolTableEntry *symbolEntry = accessScope->lookup(structName); if (!symbolEntry) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, - "Symbol '" + accessScopePrefix + structName + "' was used before defined"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, + "Symbol '" + accessScopePrefix + structName + "' was used before defined"); accessScopePrefix += structName + "."; std::string tableName = symbolEntry->getType().is(TY_IMPORT) ? structName : STRUCT_SCOPE_PREFIX + structName; accessScope = accessScope->lookupTable(tableName); @@ -2103,10 +2110,10 @@ std::any AnalyzerVisitor::visitStructInstantiation(StructInstantiationNode *node } // Get the struct instance - Struct *spiceStruct = accessScope->matchStruct(currentScope, structName, concreteTemplateTypes, err.get(), node->codeLoc); + Struct *spiceStruct = accessScope->matchStruct(currentScope, structName, concreteTemplateTypes, node->codeLoc); if (!spiceStruct) { std::string structSignature = Struct::getSignature(structName, concreteTemplateTypes); - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, "Struct '" + structSignature + "' could not be found"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, "Struct '" + structSignature + "' could not be found"); } spiceStruct->setUsed(); @@ -2118,8 +2125,8 @@ std::any AnalyzerVisitor::visitStructInstantiation(StructInstantiationNode *node SymbolTableEntry *structSymbol = currentScope->lookup(accessScopePrefix + Struct::getSignature(structName, concreteTemplateTypes)); if (!structSymbol) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, - "Could not find struct '" + accessScopePrefix + structName + "'"); + throw SemanticError(node->codeLoc, REFERENCED_UNDEFINED_STRUCT, + "Could not find struct '" + accessScopePrefix + structName + "'"); structType = structSymbol->getType(); } @@ -2135,8 +2142,8 @@ std::any AnalyzerVisitor::visitStructInstantiation(StructInstantiationNode *node std::vector fieldTypes; if (node->fieldLst()) { // Check if any fields are passed. Empty braces are also allowed if (spiceStruct->getFieldTypes().size() != node->fieldLst()->args().size()) - throw err->get(node->fieldLst()->codeLoc, NUMBER_OF_FIELDS_NOT_MATCHING, - "You've passed too less/many field values. Pass either none or all of them"); + throw SemanticError(node->fieldLst()->codeLoc, NUMBER_OF_FIELDS_NOT_MATCHING, + "You've passed too less/many field values. Pass either none or all of them"); // Check if the field types are matching for (int i = 0; i < node->fieldLst()->args().size(); i++) { @@ -2153,9 +2160,9 @@ std::any AnalyzerVisitor::visitStructInstantiation(StructInstantiationNode *node expectedSymbolType.replaceBaseSubType(accessScopePrefix + expectedSymbolType.getBaseType().getSubType()); // Check if type matches declaration if (actualType != expectedSymbolType) - throw err->get(assignExpr->codeLoc, FIELD_TYPE_NOT_MATCHING, - "Expected type " + expectedSymbolType.getName(false) + " for the field '" + expectedField->getName() + - "', but got " + actualType.getName(false)); + throw SemanticError(assignExpr->codeLoc, FIELD_TYPE_NOT_MATCHING, + "Expected type " + expectedSymbolType.getName(false) + " for the field '" + expectedField->getName() + + "', but got " + actualType.getName(false)); } } @@ -2172,21 +2179,21 @@ std::any AnalyzerVisitor::visitDataType(DataTypeNode *node) { DataTypeNode::TypeModifier typeModifier = tmQueue.front(); switch (typeModifier.modifierType) { case DataTypeNode::TYPE_PTR: { - type = type.toPointer(err.get(), node->codeLoc); + type = type.toPointer(node->codeLoc); break; } case DataTypeNode::TYPE_ARRAY: { if (typeModifier.hasSize) { if (typeModifier.isSizeHardcoded) { if (typeModifier.hardcodedSize <= 1) - throw err->get(node->codeLoc, ARRAY_SIZE_INVALID, "The size of an array must be > 1 and explicitly stated"); + throw SemanticError(node->codeLoc, ARRAY_SIZE_INVALID, "The size of an array must be > 1 and explicitly stated"); } else { auto sizeType = any_cast(visit(arraySizeExpr[assignExprCounter++])); if (!sizeType.isOneOf({TY_INT, TY_LONG, TY_SHORT})) - throw err->get(node->codeLoc, ARRAY_SIZE_INVALID, "The array size must be of type int, long or short"); + throw SemanticError(node->codeLoc, ARRAY_SIZE_INVALID, "The array size must be of type int, long or short"); } } - type = type.toArray(err.get(), node->codeLoc, typeModifier.hardcodedSize); + type = type.toArray(node->codeLoc, typeModifier.hardcodedSize); break; } default: @@ -2245,9 +2252,9 @@ std::any AnalyzerVisitor::visitCustomDataType(CustomDataTypeNode *node) { accessScopePrefix += identifier + "."; entry = accessScope->lookup(identifier); if (!entry) - throw err->get(node->codeLoc, UNKNOWN_DATATYPE, "Unknown symbol '" + identifier + "'"); + throw SemanticError(node->codeLoc, UNKNOWN_DATATYPE, "Unknown symbol '" + identifier + "'"); if (!entry->getType().isOneOf({TY_STRUCT, TY_ENUM, TY_IMPORT})) - throw err->get(node->codeLoc, EXPECTED_TYPE, "Expected type, but got " + entry->getType().getName()); + throw SemanticError(node->codeLoc, EXPECTED_TYPE, "Expected type, but got " + entry->getType().getName()); std::string tableName = identifier; if (entry->getType().is(TY_STRUCT)) { @@ -2273,7 +2280,7 @@ std::any AnalyzerVisitor::visitCustomDataType(CustomDataTypeNode *node) { } // Set the struct instance to used - Struct *spiceStruct = accessScope->matchStruct(nullptr, identifier, concreteTemplateTypes, err.get(), node->codeLoc); + Struct *spiceStruct = accessScope->matchStruct(nullptr, identifier, concreteTemplateTypes, node->codeLoc); if (spiceStruct) spiceStruct->setUsed(); @@ -2285,7 +2292,7 @@ std::any AnalyzerVisitor::visitCustomDataType(CustomDataTypeNode *node) { // Check if struct was declared SymbolTableEntry *structSymbol = accessScope->lookup(identifier); if (!structSymbol) - throw err->get(node->codeLoc, UNKNOWN_DATATYPE, "Unknown datatype '" + identifier + "'"); + throw SemanticError(node->codeLoc, UNKNOWN_DATATYPE, "Unknown datatype '" + identifier + "'"); structSymbol->setUsed(); return node->setEvaluatedSymbolType(SymbolType(TY_STRUCT, identifier, concreteTemplateTypes)); @@ -2301,7 +2308,7 @@ void AnalyzerVisitor::insertDestructorCall(const CodeLoc &codeLoc, SymbolTableEn accessScope = accessScope->getChild(STRUCT_SCOPE_PREFIX + structEntry->getName()); assert(accessScope != nullptr); SymbolType thisType = varEntry->getType(); - accessScope->matchFunction(currentScope, DTOR_VARIABLE_NAME, thisType, {}, err.get(), codeLoc); + accessScope->matchFunction(currentScope, DTOR_VARIABLE_NAME, thisType, {}, codeLoc); } SymbolType AnalyzerVisitor::initExtStruct(SymbolTable *sourceScope, const std::string &structScopePrefix, @@ -2324,7 +2331,7 @@ SymbolType AnalyzerVisitor::initExtStruct(SymbolTable *sourceScope, const std::s std::string structSignature = Struct::getSignature(structName, templateTypes); SymbolTableEntry *externalStructSymbol = sourceScope->lookup(structSignature); if (!externalStructSymbol) - throw err->get(codeLoc, REFERENCED_UNDEFINED_STRUCT, "Could not find struct '" + newStructName + "'"); + throw SemanticError(codeLoc, REFERENCED_UNDEFINED_STRUCT, "Could not find struct '" + newStructName + "'"); // Get the associated symbolTable of the external struct symbol SymbolTable *externalStructTable = sourceScope->lookupTable(STRUCT_SCOPE_PREFIX + structSignature); @@ -2345,7 +2352,7 @@ SymbolType AnalyzerVisitor::initExtStruct(SymbolTable *sourceScope, const std::s externalStructSymbol->setUsed(); // Set the struct instance to used - Struct *externalSpiceStruct = sourceScope->matchStruct(nullptr, structName, templateTypes, err.get(), codeLoc); + Struct *externalSpiceStruct = sourceScope->matchStruct(nullptr, structName, templateTypes, codeLoc); assert(externalSpiceStruct); externalSpiceStruct->setUsed(); @@ -2369,7 +2376,7 @@ SymbolType AnalyzerVisitor::initExtGlobal(SymbolTable *sourceScope, const std::s // Check if external global var is declared SymbolTableEntry *externalGlobalSymbol = sourceScope->lookup(globalName); if (!externalGlobalSymbol) - throw err->get(codeLoc, REFERENCED_UNDEFINED_VARIABLE, "Could not find global variable '" + newGlobalName + "'"); + throw SemanticError(codeLoc, REFERENCED_UNDEFINED_VARIABLE, "Could not find global variable '" + newGlobalName + "'"); // Set to DECLARED, so that the generator can set it to DEFINED as soon as the LLVM struct type was generated once Capture newGlobalCapture = Capture(externalGlobalSymbol, newGlobalName, DECLARED); diff --git a/src/analyzer/AnalyzerVisitor.h b/src/analyzer/AnalyzerVisitor.h index 9710c41ce..63e7222cc 100644 --- a/src/analyzer/AnalyzerVisitor.h +++ b/src/analyzer/AnalyzerVisitor.h @@ -27,7 +27,6 @@ const std::vector RESERVED_KEYWORDS = {"new", "switch", "case", "yi // Forward declarations class OpRuleManager; struct CliOptions; -class ErrorFactory; class ThreadFactory; class LinkerInterface; class SymbolTable; @@ -110,7 +109,6 @@ class AnalyzerVisitor : public AstVisitor { std::shared_ptr context; std::shared_ptr> builder; std::unique_ptr opRuleManager; - std::unique_ptr err; const ThreadFactory &threadFactory; bool requiresMainFct = true; bool hasMainFunction = false; diff --git a/src/analyzer/OpRuleManager.cpp b/src/analyzer/OpRuleManager.cpp index ff35577eb..ad9c7b83f 100644 --- a/src/analyzer/OpRuleManager.cpp +++ b/src/analyzer/OpRuleManager.cpp @@ -2,8 +2,6 @@ #include "OpRuleManager.h" -#include - SymbolType OpRuleManager::getAssignResultType(const CodeLoc &codeLoc, const SymbolType &lhs, const SymbolType &rhs) { // Skip type compatibility check if the lhs is of type dyn -> perform type inference if (lhs.is(TY_DYN)) @@ -213,12 +211,12 @@ SymbolType OpRuleManager::getPrefixBitwiseNotResultType(const CodeLoc &codeLoc, SymbolType OpRuleManager::getPrefixMulResultType(const CodeLoc &codeLoc, const SymbolType &lhs) { if (!lhs.isPointer()) - throw err->get(codeLoc, OPERATOR_WRONG_DATA_TYPE, "Cannot apply de-referencing operator on type " + lhs.getName(true)); + throw SemanticError(codeLoc, OPERATOR_WRONG_DATA_TYPE, "Cannot apply de-referencing operator on type " + lhs.getName(true)); return lhs.getContainedTy(); } -SymbolType OpRuleManager::getPrefixBitwiseAndResultType(const CodeLoc &codeLoc, const SymbolType& lhs) { - return lhs.toPointer(err, codeLoc); +SymbolType OpRuleManager::getPrefixBitwiseAndResultType(const CodeLoc &codeLoc, const SymbolType &lhs) { + return lhs.toPointer(codeLoc); } SymbolType OpRuleManager::getPostfixPlusPlusResultType(const CodeLoc &codeLoc, const SymbolType &lhs) { @@ -268,18 +266,21 @@ SymbolType OpRuleManager::validateUnaryOperation(const CodeLoc &codeLoc, const s SemanticError OpRuleManager::printErrorMessageBinary(const CodeLoc &codeLoc, const std::string &operatorName, const SymbolType &lhs, const SymbolType &rhs) { - return err->get(codeLoc, OPERATOR_WRONG_DATA_TYPE, - "Cannot apply '" + operatorName + "' operator on types " + lhs.getName(true) + " and " + rhs.getName(true)); + return SemanticError(codeLoc, OPERATOR_WRONG_DATA_TYPE, + "Cannot apply '" + operatorName + "' operator on types " + lhs.getName(true) + " and " + + rhs.getName(true)); } SemanticError OpRuleManager::printErrorMessageUnary(const CodeLoc &codeLoc, const std::string &operatorName, const SymbolType &lhs) { - return err->get(codeLoc, OPERATOR_WRONG_DATA_TYPE, "Cannot apply '" + operatorName + "' operator on type " + lhs.getName(true)); + return SemanticError(codeLoc, OPERATOR_WRONG_DATA_TYPE, + "Cannot apply '" + operatorName + "' operator on type " + lhs.getName(true)); } SemanticError OpRuleManager::printErrorMessageUnsafe(const CodeLoc &codeLoc, const std::string &operatorName, const SymbolType &lhs, const SymbolType &rhs) { - return err->get(codeLoc, UNSAFE_OPERATION_IN_SAFE_CONTEXT, - "Cannot apply '" + operatorName + "' operator on types " + lhs.getName(true) + " and " + rhs.getName(true) + - " as this is an unsafe operation. Please use unsafe blocks if you know what you are doing."); + return SemanticError(codeLoc, UNSAFE_OPERATION_IN_SAFE_CONTEXT, + "Cannot apply '" + operatorName + "' operator on types " + lhs.getName(true) + " and " + + rhs.getName(true) + + " as this is an unsafe operation. Please use unsafe blocks if you know what you are doing."); } \ No newline at end of file diff --git a/src/analyzer/OpRuleManager.h b/src/analyzer/OpRuleManager.h index 816601d6a..824b31499 100644 --- a/src/analyzer/OpRuleManager.h +++ b/src/analyzer/OpRuleManager.h @@ -581,8 +581,7 @@ const std::vector CAST_OP_RULES = { class OpRuleManager { public: // Constructors - explicit OpRuleManager(const ErrorFactory *errorFactory, const bool &isUnsafe) - : err(errorFactory), withinUnsafeBlock(isUnsafe) {} + explicit OpRuleManager(const bool &isUnsafe) : withinUnsafeBlock(isUnsafe) {} // Public methods SymbolType getAssignResultType(const CodeLoc &codeLoc, const SymbolType &lhs, const SymbolType &rhs); @@ -620,14 +619,13 @@ class OpRuleManager { SymbolType getPrefixNotResultType(const CodeLoc &codeLoc, const SymbolType &lhs); SymbolType getPrefixBitwiseNotResultType(const CodeLoc &codeLoc, const SymbolType &lhs); SymbolType getPrefixMulResultType(const CodeLoc &codeLoc, const SymbolType &lhs); - SymbolType getPrefixBitwiseAndResultType(const CodeLoc &codeLoc, const SymbolType& lhs); + SymbolType getPrefixBitwiseAndResultType(const CodeLoc &codeLoc, const SymbolType &lhs); SymbolType getPostfixPlusPlusResultType(const CodeLoc &codeLoc, const SymbolType &lhs); SymbolType getPostfixMinusMinusResultType(const CodeLoc &codeLoc, const SymbolType &lhs); SymbolType getCastResultType(const CodeLoc &codeLoc, const SymbolType &lhs, const SymbolType &); private: // Members - const ErrorFactory *err; const bool &withinUnsafeBlock; // Private methods diff --git a/src/analyzer/PreAnalyzerVisitor.cpp b/src/analyzer/PreAnalyzerVisitor.cpp index ff9d65a96..9a4c84f25 100644 --- a/src/analyzer/PreAnalyzerVisitor.cpp +++ b/src/analyzer/PreAnalyzerVisitor.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -35,8 +36,8 @@ std::any PreAnalyzerVisitor::visitImportStmt(ImportStmtNode *node) { if (stdPath.rfind(FileUtil::DIR_SEPARATOR) != stdPath.size() - 1) stdPath += FileUtil::DIR_SEPARATOR; } else { - throw ErrorFactory::get(node->codeLoc, STD_NOT_FOUND, - "Standard library could not be found. Check if the env var SPICE_STD_DIR exists"); + throw SemanticError(node->codeLoc, STD_NOT_FOUND, + "Standard library could not be found. Check if the env var SPICE_STD_DIR exists"); } // Check if source file exists std::string defaultPath = stdPath + sourceFileIden + ".spice"; @@ -50,8 +51,8 @@ std::any PreAnalyzerVisitor::visitImportStmt(ImportStmtNode *node) { } else if (FileUtil::fileExists(osArchPath)) { importPath = osArchPath; } else { - throw ErrorFactory::get(node->codeLoc, IMPORTED_FILE_NOT_EXISTING, - "The source file '" + node->importPath + ".spice' was not found in the standard library"); + throw SemanticError(node->codeLoc, IMPORTED_FILE_NOT_EXISTING, + "The source file '" + node->importPath + ".spice' was not found in the standard library"); } } else { // Include own source file // Check in module registry if the file can be imported @@ -69,14 +70,14 @@ std::any PreAnalyzerVisitor::visitImportStmt(ImportStmtNode *node) { } else if (FileUtil::fileExists(osArchPath)) { importPath = osArchPath; } else { - throw ErrorFactory::get(node->codeLoc, IMPORTED_FILE_NOT_EXISTING, - "The source file '" + node->importPath + ".spice' does not exist"); + throw SemanticError(node->codeLoc, IMPORTED_FILE_NOT_EXISTING, + "The source file '" + node->importPath + ".spice' does not exist"); } } CommonUtil::replaceAll(importPath, "/", std::string(1, FileUtil::DIR_SEPARATOR)); // Visit the imported file - sourceFile.addDependency(&err, node, node->importName, importPath, isImportStd); + sourceFile.addDependency(node, node->importName, importPath, isImportStd); return nullptr; } \ No newline at end of file diff --git a/src/analyzer/PreAnalyzerVisitor.h b/src/analyzer/PreAnalyzerVisitor.h index 6afb3b081..aa2541463 100644 --- a/src/analyzer/PreAnalyzerVisitor.h +++ b/src/analyzer/PreAnalyzerVisitor.h @@ -4,12 +4,9 @@ #include -#include - // Forward declarations struct CliOptions; class SourceFile; -class ErrorFactory; /** * Visitor for pre-analyzing all source files in hierarchical order from bottom to top. @@ -29,6 +26,5 @@ class PreAnalyzerVisitor : public AstVisitor { private: // Members const CliOptions &cliOptions; - ErrorFactory err = ErrorFactory(); SourceFile &sourceFile; }; \ No newline at end of file diff --git a/src/cli/CliInterface.cpp b/src/cli/CliInterface.cpp index 48dca48d1..59faa5eb7 100644 --- a/src/cli/CliInterface.cpp +++ b/src/cli/CliInterface.cpp @@ -2,6 +2,7 @@ #include "CliInterface.h" +#include #include #include @@ -86,13 +87,12 @@ void CliInterface::validate() const { // Check if all three of --target-arch, --target-vendor and --target-os are provided or none of them if (!((cliOptions.targetArch.empty() && cliOptions.targetVendor.empty() && cliOptions.targetOs.empty()) || (!cliOptions.targetArch.empty() && !cliOptions.targetVendor.empty() && !cliOptions.targetOs.empty()))) { - throw ErrorFactory::get(INCOMPLETE_TARGET_TRIPLE, - "You need to provide all three of --target-arch, --target-vendor and --target-os"); + throw CliError(INCOMPLETE_TARGET_TRIPLE, "You need to provide all three of --target-arch, --target-vendor and --target-os"); } // Error out when opt level > 0 and debug info enabled if (cliOptions.optLevel > 0 && cliOptions.generateDebugInfo) - throw ErrorFactory::get( + throw CliError( OPT_DEBUG_INFO_INCOMPATIBILITY, "Optimization does not work reliably when emitting debug info. The cli argument -g only works in combination with -O0."); } @@ -117,7 +117,7 @@ void CliInterface::enrich() { cliOptions.targetVendor = triple.getVendorName(); cliOptions.targetOs = triple.getOSName(); } - // Dump IR as well as symbol table if all debug output is enabled + // Dump AST, IR and symbol table if all debug output is enabled if (cliOptions.printDebugOutput) { cliOptions.dumpAST = true; cliOptions.dumpIR = true; diff --git a/src/cli/CliInterface.h b/src/cli/CliInterface.h index 55744bb78..345023738 100644 --- a/src/cli/CliInterface.h +++ b/src/cli/CliInterface.h @@ -4,8 +4,6 @@ #include -#include - #include "../../lib/cli11/CLI11.hpp" #ifdef __unix__ @@ -66,7 +64,6 @@ class CliInterface { // Members CLI::App app = CLI::App{"Spice Programming Language", "Spice"}; CliOptions cliOptions{}; - const ErrorFactory err = ErrorFactory(); bool compile = false; bool install = false; bool run = false; diff --git a/src/dependency/SourceFile.cpp b/src/dependency/SourceFile.cpp index 60cbb0fc1..343b1d040 100644 --- a/src/dependency/SourceFile.cpp +++ b/src/dependency/SourceFile.cpp @@ -161,7 +161,7 @@ void SourceFile::preAnalyze() { preAnalyzer.visit(ast.get()); antlrCtx.parser->reset(); - // Analyze the imported source files + // Pre-analyze the imported source files for (const auto &[_, sourceFile] : dependencies) { sourceFile.first->buildAST(); sourceFile.first->preAnalyze(); @@ -285,12 +285,10 @@ void SourceFile::generate(const std::shared_ptr &context, con } } -void SourceFile::addDependency(const ErrorFactory *err, const AstNode *declAstNode, const std::string &name, - const std::string &filePath, bool stdFile) { +void SourceFile::addDependency(const AstNode *declAstNode, const std::string &name, const std::string &filePath, bool stdFile) { // Check if this would cause a circular dependency if (isAlreadyImported(filePath)) - throw ErrorFactory::get(declAstNode->codeLoc, CIRCULAR_DEPENDENCY, - "Circular import detected while importing '" + filePath + "'"); + throw SemanticError(declAstNode->codeLoc, CIRCULAR_DEPENDENCY, "Circular import detected while importing '" + filePath + "'"); // Add the dependency dependencies.insert({name, {std::make_shared(options, this, name, filePath, stdFile), declAstNode}}); diff --git a/src/dependency/SourceFile.h b/src/dependency/SourceFile.h index c33ec935c..6a29a205f 100644 --- a/src/dependency/SourceFile.h +++ b/src/dependency/SourceFile.h @@ -58,7 +58,7 @@ class SourceFile { ThreadFactory &threadFactory); void generate(const std::shared_ptr &context, const std::shared_ptr> &builder, ThreadFactory &threadFactory, LinkerInterface &linker); - void addDependency(const ErrorFactory *err, const AstNode *declAstNode, const std::string &name, const std::string &filePath, + void addDependency(const AstNode *declAstNode, const std::string &name, const std::string &filePath, bool stdFile); [[nodiscard]] bool isAlreadyImported(const std::string &filePathSearch) const; diff --git a/src/exception/ErrorFactory.cpp b/src/exception/ErrorFactory.cpp deleted file mode 100644 index 5f5b0e57d..000000000 --- a/src/exception/ErrorFactory.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2021-2022 ChilliBits. All rights reserved. - -#include "ErrorFactory.h" - -#include - -LexerParserError ErrorFactory::get(const CodeLoc &codeLoc, LexerParserErrorType type, const std::string &message) { - return LexerParserError(codeLoc, type, message); -} - -SemanticError ErrorFactory::get(const CodeLoc &codeLoc, SemanticErrorType type, const std::string &message) { - return SemanticError(codeLoc, type, message); -} - -IRError ErrorFactory::get(const CodeLoc &codeLoc, IRErrorType type, const std::string &message) { - return IRError(codeLoc, type, message); -} - -IRError ErrorFactory::get(IRErrorType type, const std::string &message) { return IRError(type, message); } - -CliError ErrorFactory::get(CliErrorType type, const std::string &message) { return CliError(type, message); } - -LinkerError ErrorFactory::get(LinkerErrorType type, const std::string &message) { return LinkerError(type, message); } \ No newline at end of file diff --git a/src/exception/ErrorFactory.h b/src/exception/ErrorFactory.h deleted file mode 100644 index 9260aebde..000000000 --- a/src/exception/ErrorFactory.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2021-2022 ChilliBits. All rights reserved. - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -// Forward declarations -struct CodeLoc; - -class ErrorFactory { -public: - // Constructors - ErrorFactory() = default; - - // Public methods - [[nodiscard]] static LexerParserError get(const CodeLoc &codeLoc, LexerParserErrorType type, const std::string &message); - [[nodiscard]] static SemanticError get(const CodeLoc &codeLoc, SemanticErrorType type, const std::string &message); - [[nodiscard]] static IRError get(const CodeLoc &codeLoc, IRErrorType type, const std::string &message); - [[nodiscard]] static IRError get(IRErrorType type, const std::string &message); - [[nodiscard]] static CliError get(CliErrorType type, const std::string &message); - [[nodiscard]] static LinkerError get(LinkerErrorType type, const std::string &message); -}; \ No newline at end of file diff --git a/src/exception/IRError.cpp b/src/exception/IRError.cpp index 1382afa2f..27852d725 100644 --- a/src/exception/IRError.cpp +++ b/src/exception/IRError.cpp @@ -48,6 +48,8 @@ std::string IRError::getMessagePrefix(IRErrorType type) { return "Wrong type of output file"; case BRANCH_NOT_FOUND: return "Branch not found"; + case REFERENCED_UNDEFINED_FUNCTION_IR: + return "Referenced undefined function"; case UNEXPECTED_DYN_TYPE_IR: return "Unexpected type of dyn. Symbol table incomplete"; case PRINTF_NULL_TYPE: diff --git a/src/exception/IRError.h b/src/exception/IRError.h index d86c86684..717c13b04 100644 --- a/src/exception/IRError.h +++ b/src/exception/IRError.h @@ -15,6 +15,7 @@ enum IRErrorType { CANT_OPEN_OUTPUT_FILE, WRONG_TYPE, BRANCH_NOT_FOUND, + REFERENCED_UNDEFINED_FUNCTION_IR, UNEXPECTED_DYN_TYPE_IR, PRINTF_NULL_TYPE, INVALID_FUNCTION, diff --git a/src/exception/SemanticError.h b/src/exception/SemanticError.h index 0d5d331cb..1d060f764 100644 --- a/src/exception/SemanticError.h +++ b/src/exception/SemanticError.h @@ -70,7 +70,7 @@ enum SemanticErrorType { ASSERTION_CONDITION_BOOL, ARRAY_INDEX_OUT_OF_BOUNDS, RESERVED_KEYWORD, - COMING_SOON_SA, + COMING_SOON_SA }; /** diff --git a/src/generator/GeneratorVisitor.cpp b/src/generator/GeneratorVisitor.cpp index 5d3e61dec..ebbceb3a9 100644 --- a/src/generator/GeneratorVisitor.cpp +++ b/src/generator/GeneratorVisitor.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -41,12 +40,9 @@ GeneratorVisitor::GeneratorVisitor(const std::shared_ptr &con this->requiresMainFct = sourceFile.parent == nullptr; this->currentScope = this->rootScope = sourceFile.symbolTable.get(); - // Create error factory for this specific file - this->err = std::make_unique(); - // Create LLVM base components module = std::make_unique(FileUtil::getFileName(sourceFile.filePath), *context); - conversionsManager = std::make_unique(context, builder, err.get()); + conversionsManager = std::make_unique(context, builder); // Initialize LLVM llvm::InitializeAllTargetInfos(); @@ -62,7 +58,7 @@ GeneratorVisitor::GeneratorVisitor(const std::shared_ptr &con std::string error; const llvm::Target *target = llvm::TargetRegistry::lookupTarget(cliOptions.targetTriple, error); if (!target) - throw err->get(TARGET_NOT_AVAILABLE, "Selected target was not found: " + error); // GCOV_EXCL_LINE + throw IRError(TARGET_NOT_AVAILABLE, "Selected target was not found: " + error); // GCOV_EXCL_LINE llvm::TargetOptions opt; llvm::Optional rm = llvm::Optional(); @@ -119,11 +115,11 @@ void GeneratorVisitor::emit() { std::error_code errorCode; llvm::raw_fd_ostream dest(objectFile, errorCode, llvm::sys::fs::OF_None); if (errorCode) - throw err->get(CANT_OPEN_OUTPUT_FILE, "File '" + objectFile + "' could not be opened"); // GCOV_EXCL_LINE + throw IRError(CANT_OPEN_OUTPUT_FILE, "File '" + objectFile + "' could not be opened"); // GCOV_EXCL_LINE llvm::legacy::PassManager passManager; if (targetMachine->addPassesToEmitFile(passManager, dest, nullptr, llvm::CGFT_ObjectFile)) - throw err->get(WRONG_TYPE, "Target machine can't emit a file of this type"); // GCOV_EXCL_LINE + throw IRError(WRONG_TYPE, "Target machine can't emit a file of this type"); // GCOV_EXCL_LINE // Emit object file passManager.run(*module); @@ -142,7 +138,7 @@ std::string GeneratorVisitor::getIRString() { void GeneratorVisitor::dumpAsm() { llvm::legacy::PassManager passManager; if (targetMachine->addPassesToEmitFile(passManager, llvm::outs(), nullptr, llvm::CGFT_AssemblyFile)) - throw err->get(WRONG_TYPE, "Target machine can't emit a file of this type"); // GCOV_EXCL_LINE + throw IRError(WRONG_TYPE, "Target machine can't emit a file of this type"); // GCOV_EXCL_LINE // Emit object file passManager.run(*module); @@ -171,7 +167,7 @@ std::any GeneratorVisitor::visitEntry(EntryNode *node) { std::string output; llvm::raw_string_ostream oss(output); if (llvm::verifyModule(*module, &oss)) - throw err->get(node->codeLoc, INVALID_MODULE, oss.str()); + throw IRError(node->codeLoc, INVALID_MODULE, oss.str()); } return false; @@ -308,7 +304,7 @@ std::any GeneratorVisitor::visitMainFctDef(MainFctDefNode *node) { std::string output; llvm::raw_string_ostream oss(output); if (llvm::verifyFunction(*fct, &oss)) - throw err->get(node->codeLoc, INVALID_FUNCTION, oss.str()); + throw IRError(node->codeLoc, INVALID_FUNCTION, oss.str()); } // Change scope back @@ -495,7 +491,7 @@ std::any GeneratorVisitor::visitFctDef(FctDefNode *node) { std::string output; llvm::raw_string_ostream oss(output); if (llvm::verifyFunction(*fct, &oss)) - throw err->get(node->codeLoc, INVALID_FUNCTION, oss.str()); + throw IRError(node->codeLoc, INVALID_FUNCTION, oss.str()); } // Change scope back @@ -681,7 +677,7 @@ std::any GeneratorVisitor::visitProcDef(ProcDefNode *node) { std::string output; llvm::raw_string_ostream oss(output); if (llvm::verifyFunction(*proc, &oss)) - throw err->get(node->codeLoc, INVALID_FUNCTION, oss.str()); + throw IRError(node->codeLoc, INVALID_FUNCTION, oss.str()); } // Change scope back @@ -916,7 +912,7 @@ std::any GeneratorVisitor::visitThreadDef(ThreadDefNode *node) { std::string output; llvm::raw_string_ostream oss(output); if (llvm::verifyFunction(*threadFct, &oss)) - throw err->get(node->codeLoc, INVALID_FUNCTION, oss.str()); + throw IRError(node->codeLoc, INVALID_FUNCTION, oss.str()); // Change back to the original basic block moveInsertPointToBlock(bOriginal); @@ -2550,8 +2546,8 @@ std::any GeneratorVisitor::visitFunctionCall(FunctionCallNode *node) { if (i < node->functionNameFragments.size() - 1) { if (!symbolEntry) - throw err->get(node->codeLoc, REFERENCED_UNDEFINED_FUNCTION, - "Symbol '" + scopePath.getScopePrefix() + identifier + "' was used before defined"); + throw IRError(node->codeLoc, REFERENCED_UNDEFINED_FUNCTION_IR, + "Symbol '" + scopePath.getScopePrefix() + identifier + "' was used before defined"); thisValuePtr = symbolEntry->getAddress(); } else if (symbolEntry != nullptr && symbolEntry->getType().getBaseType().is(TY_STRUCT)) { Struct *spiceStruct = currentScope->getStructAccessPointer(node->codeLoc); @@ -2886,21 +2882,21 @@ std::any GeneratorVisitor::visitDataType(DataTypeNode *node) { DataTypeNode::TypeModifier typeModifier = tmQueue.front(); switch (typeModifier.modifierType) { case DataTypeNode::TYPE_PTR: { - symbolType = symbolType.toPointer(err.get(), node->codeLoc); + symbolType = symbolType.toPointer(node->codeLoc); break; } case DataTypeNode::TYPE_ARRAY: { if (!typeModifier.hasSize) { - symbolType = symbolType.toPointer(err.get(), node->codeLoc); + symbolType = symbolType.toPointer(node->codeLoc); } else if (typeModifier.isSizeHardcoded) { - symbolType = symbolType.toArray(err.get(), node->codeLoc, typeModifier.hardcodedSize); + symbolType = symbolType.toArray(node->codeLoc, typeModifier.hardcodedSize); } else { AssignExprNode *indexExpr = arraySizeExpr[assignExprCounter++]; assert(indexExpr != nullptr); auto sizeValuePtr = any_cast(visit(indexExpr)); llvm::Type *sizeType = indexExpr->getEvaluatedSymbolType().toLLVMType(*context, currentScope); dynamicArraySize = builder->CreateLoad(sizeType, sizeValuePtr); - symbolType = symbolType.toPointer(err.get(), node->codeLoc, dynamicArraySize); + symbolType = symbolType.toPointer(node->codeLoc, dynamicArraySize); } break; } diff --git a/src/generator/GeneratorVisitor.h b/src/generator/GeneratorVisitor.h index f90dd55be..d86f90635 100644 --- a/src/generator/GeneratorVisitor.h +++ b/src/generator/GeneratorVisitor.h @@ -116,7 +116,6 @@ class GeneratorVisitor : public AstVisitor { SymbolType currentSymbolType = SymbolType(TY_INVALID); ScopePath scopePath; ThreadFactory &threadFactory; - std::unique_ptr err; bool blockAlreadyTerminated = false; llvm::BasicBlock *allocaInsertBlock = nullptr; llvm::Instruction *allocaInsertInst = nullptr; diff --git a/src/generator/OpRuleConversionsManager.cpp b/src/generator/OpRuleConversionsManager.cpp index c70cffa1b..dea415997 100644 --- a/src/generator/OpRuleConversionsManager.cpp +++ b/src/generator/OpRuleConversionsManager.cpp @@ -4,7 +4,7 @@ #include -#include +#include #include llvm::Value *OpRuleConversionsManager::getPlusEqualInst(llvm::Value *lhs, llvm::Value *rhs, const SymbolType &lhsSTy, @@ -41,12 +41,10 @@ llvm::Value *OpRuleConversionsManager::getPlusEqualInst(llvm::Value *lhs, llvm:: return builder->CreateAdd(lhs, rhs); case COMB(TY_STRING, TY_CHAR): // ToDo(@marcauberer): Insert call to appendChar in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '+=' operator for lhs=string and rhs=char yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '+=' operator for lhs=string and rhs=char yet"); case COMB(TY_STRING, TY_STRING): // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '+=' operator for lhs=string and rhs=string yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '+=' operator for lhs=string and rhs=string yet"); case COMB(TY_PTR, TY_INT): // fallthrough case COMB(TY_PTR, TY_SHORT): // fallthrough case COMB(TY_PTR, TY_LONG): @@ -469,8 +467,7 @@ llvm::Value *OpRuleConversionsManager::getEqualInst(llvm::Value *lhs, llvm::Valu return builder->CreateICmpEQ(lhs, rhs); case COMB(TY_STRING, TY_STRING): // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '==' operator for lhs=string and rhs=string yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '==' operator for lhs=string and rhs=string yet"); case COMB(TY_BOOL, TY_BOOL): return builder->CreateICmpEQ(lhs, rhs); } @@ -575,8 +572,7 @@ llvm::Value *OpRuleConversionsManager::getNotEqualInst(llvm::Value *lhs, llvm::V return builder->CreateICmpNE(lhs, rhs); case COMB(TY_STRING, TY_STRING): // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '!=' operator for lhs=string and rhs=string yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '!=' operator for lhs=string and rhs=string yet"); case COMB(TY_BOOL, TY_BOOL): return builder->CreateICmpNE(lhs, rhs); } @@ -949,8 +945,7 @@ llvm::Value *OpRuleConversionsManager::getPlusInst(llvm::Value *lhs, llvm::Value return builder->CreateAdd(lhs, rhs); case COMB(TY_STRING, TY_STRING): // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '+' operator for lhs=string and rhs=string yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '+' operator for lhs=string and rhs=string yet"); case COMB(TY_PTR, TY_INT): // fallthrough case COMB(TY_PTR, TY_SHORT): // fallthrough case COMB(TY_PTR, TY_LONG): @@ -1060,13 +1055,11 @@ llvm::Value *OpRuleConversionsManager::getMulInst(llvm::Value *lhs, llvm::Value } case COMB(TY_INT, TY_CHAR): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=int and rhs=char yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=int and rhs=char yet"); } case COMB(TY_INT, TY_STRING): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=int and rhs=string yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=int and rhs=string yet"); } case COMB(TY_SHORT, TY_DOUBLE): { llvm::Value *lhsFP = builder->CreateSIToFP(lhs, rhsTy); @@ -1084,13 +1077,11 @@ llvm::Value *OpRuleConversionsManager::getMulInst(llvm::Value *lhs, llvm::Value } case COMB(TY_SHORT, TY_CHAR): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=short and rhs=char yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=short and rhs=char yet"); } case COMB(TY_SHORT, TY_STRING): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=short and rhs=string yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=short and rhs=string yet"); } case COMB(TY_LONG, TY_DOUBLE): { llvm::Value *lhsFP = builder->CreateSIToFP(lhs, rhsTy); @@ -1105,45 +1096,37 @@ llvm::Value *OpRuleConversionsManager::getMulInst(llvm::Value *lhs, llvm::Value return builder->CreateMul(lhs, rhs); case COMB(TY_LONG, TY_CHAR): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=long and rhs=char yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=long and rhs=char yet"); } case COMB(TY_LONG, TY_STRING): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=long and rhs=string yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=long and rhs=string yet"); } case COMB(TY_BYTE, TY_BYTE): return builder->CreateMul(lhs, rhs); case COMB(TY_CHAR, TY_INT): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=char and rhs=int yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=char and rhs=int yet"); } case COMB(TY_CHAR, TY_SHORT): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=char and rhs=short yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=char and rhs=short yet"); } case COMB(TY_CHAR, TY_LONG): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=char and rhs=long yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=char and rhs=long yet"); } case COMB(TY_STRING, TY_INT): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=string and rhs=int yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=string and rhs=int yet"); } case COMB(TY_STRING, TY_SHORT): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=string and rhs=short yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=string and rhs=short yet"); } case COMB(TY_STRING, TY_LONG): { // ToDo(@marcauberer): Insert call to concatStrings in the runtime lib - throw ErrorFactory::get(codeLoc, COMING_SOON_IR, - "The compiler does not support the '*' operator for lhs=string and rhs=long yet"); + throw IRError(codeLoc, COMING_SOON_IR, "The compiler does not support the '*' operator for lhs=string and rhs=long yet"); } } throw std::runtime_error("Internal compiler error: Operator fallthrough: *"); // GCOV_EXCL_LINE diff --git a/src/generator/OpRuleConversionsManager.h b/src/generator/OpRuleConversionsManager.h index 138249230..25c268154 100644 --- a/src/generator/OpRuleConversionsManager.h +++ b/src/generator/OpRuleConversionsManager.h @@ -16,9 +16,8 @@ struct CodeLoc; class OpRuleConversionsManager { public: - explicit OpRuleConversionsManager(const std::shared_ptr &context, std::shared_ptr> builder, - const ErrorFactory *errorFactory) - : context(context), builder(std::move(builder)), err(errorFactory) {} + explicit OpRuleConversionsManager(const std::shared_ptr &context, std::shared_ptr> builder) + : context(context), builder(std::move(builder)) {} // Public methods llvm::Value *getPlusEqualInst(llvm::Value *lhs, llvm::Value *rhs, const SymbolType &lhsTy, const SymbolType &rhsTy, @@ -67,5 +66,4 @@ class OpRuleConversionsManager { // Members const std::shared_ptr &context; std::shared_ptr> builder; - const ErrorFactory *err; }; \ No newline at end of file diff --git a/src/linker/LinkerInterface.cpp b/src/linker/LinkerInterface.cpp index 2dfb960bf..c704e0f7c 100644 --- a/src/linker/LinkerInterface.cpp +++ b/src/linker/LinkerInterface.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include @@ -22,7 +22,7 @@ const char *LINKER_EXECUTABLE_NAME = "gcc"; */ void LinkerInterface::link() { if (FileUtil::isCommandAvailable(std::string(LINKER_EXECUTABLE_NAME))) // GCOV_EXCL_START - throw ErrorFactory::get(LINKER_NOT_FOUND, "Please check if you have installed " + std::string(LINKER_EXECUTABLE_NAME) + + throw LinkerError(LINKER_NOT_FOUND, "Please check if you have installed " + std::string(LINKER_EXECUTABLE_NAME) + " and added it to the PATH variable"); // GCOV_EXCL_STOP // Check if the output path was set diff --git a/src/linker/LinkerInterface.h b/src/linker/LinkerInterface.h index 3424231e3..9cdd87b27 100644 --- a/src/linker/LinkerInterface.h +++ b/src/linker/LinkerInterface.h @@ -8,14 +8,13 @@ #include // Forward declarations -class ErrorFactory; class ThreadFactory; class LinkerInterface { public: // Constructors - explicit LinkerInterface(const ErrorFactory &errorFactory, const ThreadFactory &threadFactory, CliOptions &cliOptions) - : err(errorFactory), threadFactory(threadFactory), cliOptions(cliOptions), outputPath(cliOptions.outputPath){}; + explicit LinkerInterface(const ThreadFactory &threadFactory, CliOptions &cliOptions) + : threadFactory(threadFactory), cliOptions(cliOptions), outputPath(cliOptions.outputPath){}; // Public methods void link(); @@ -25,7 +24,6 @@ class LinkerInterface { private: // Members - const ErrorFactory &err; const ThreadFactory &threadFactory; const CliOptions &cliOptions; std::vector objectFilePaths; diff --git a/src/main.cpp b/src/main.cpp index f54e5a6dc..edb28c5e3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,12 +19,11 @@ void compileProject(CliOptions &options) { std::shared_ptr context = std::make_shared(); std::shared_ptr> builder = std::make_shared>(*context); - // Prepare instance of thread factory and error factory, which have to exist exactly once per executable + // Prepare instance of thread factory, which has to exist exactly once per executable ThreadFactory threadFactory = ThreadFactory(); - ErrorFactory err{}; // Prepare linker interface - LinkerInterface linker = LinkerInterface(err, threadFactory, options); + LinkerInterface linker = LinkerInterface(threadFactory, options); // Create source file instance for main source file SourceFile mainSourceFile = SourceFile(options, nullptr, "root", options.mainSourceFile, false); diff --git a/src/parser/AstBuilderVisitor.cpp b/src/parser/AstBuilderVisitor.cpp index 2ce20adcc..e3f71cff0 100644 --- a/src/parser/AstBuilderVisitor.cpp +++ b/src/parser/AstBuilderVisitor.cpp @@ -5,14 +5,9 @@ #include #include +#include #include -AstBuilderVisitor::AstBuilderVisitor(AstNode *rootNode, std::string fileName) - : currentNode(rootNode), fileName(std::move(fileName)) { - // Create error factory for this specific file - this->err = std::make_unique(); -} - std::any AstBuilderVisitor::visitEntry(SpiceParser::EntryContext *ctx) { auto entryNode = dynamic_cast(currentNode); for (const auto &subTree : ctx->children) { @@ -1584,7 +1579,7 @@ int8_t AstBuilderVisitor::parseChar(antlr4::tree::TerminalNode *terminal) { std::string input = terminal->toString(); if (input.length() != 3) { CodeLoc codeLoc = CodeLoc(terminal->getSymbol(), fileName); - throw err->get(codeLoc, PARSING_FAILED, "Invalid char literal " + input); + throw LexerParserError(codeLoc, PARSING_FAILED, "Invalid char literal " + input); } return input[1]; } @@ -1627,9 +1622,10 @@ T AstBuilderVisitor::parseNumeric(antlr4::tree::TerminalNode *terminal, std::fun return cb(input, 10); } catch (std::out_of_range &e) { CodeLoc codeLoc = CodeLoc(terminal->getSymbol(), fileName); - throw err->get(codeLoc, NUMBER_OUT_OF_RANGE, "The provided number is out of range"); + throw LexerParserError(codeLoc, NUMBER_OUT_OF_RANGE, "The provided number is out of range"); } catch (std::invalid_argument &e) { CodeLoc codeLoc = CodeLoc(terminal->getSymbol(), fileName); - throw err->get(codeLoc, NUMBER_OUT_OF_RANGE, "You tried to parse '" + input + "' as an integer, but it was no integer"); + throw LexerParserError(codeLoc, NUMBER_OUT_OF_RANGE, + "You tried to parse '" + input + "' as an integer, but it was no integer"); } } \ No newline at end of file diff --git a/src/parser/AstBuilderVisitor.h b/src/parser/AstBuilderVisitor.h index 67ac4c525..1c8386013 100644 --- a/src/parser/AstBuilderVisitor.h +++ b/src/parser/AstBuilderVisitor.h @@ -7,15 +7,13 @@ #include #include -#include - // Forward declarations class AstNode; class AstBuilderVisitor : public SpiceVisitor { public: // Constructors - explicit AstBuilderVisitor(AstNode *rootNode, std::string fileName); + explicit AstBuilderVisitor(AstNode *rootNode, std::string fileName) : currentNode(rootNode), fileName(fileName) {} // Public methods std::any visitEntry(SpiceParser::EntryContext *ctx) override; @@ -90,7 +88,6 @@ class AstBuilderVisitor : public SpiceVisitor { // Members AstNode *currentNode; std::string fileName; - std::unique_ptr err; // Private methods int32_t parseInt(antlr4::tree::TerminalNode *terminal); diff --git a/src/symbol/SymbolTable.cpp b/src/symbol/SymbolTable.cpp index c68fac644..181a0a59d 100644 --- a/src/symbol/SymbolTable.cpp +++ b/src/symbol/SymbolTable.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -327,7 +326,7 @@ size_t SymbolTable::getFieldCount() const { * @param function Function object * @param err Error factory */ -void SymbolTable::insertFunction(const Function &function, ErrorFactory *err) { +void SymbolTable::insertFunction(const Function &function) { const AstNode *declNode = function.getDeclNode(); // Open a new function declaration pointer list. Which gets filled by the 'insertSubstantiatedFunction' method @@ -359,7 +358,7 @@ void SymbolTable::insertFunction(const Function &function, ErrorFactory *err) { */ Function *SymbolTable::matchFunction(SymbolTable *currentScope, const std::string &callFunctionName, const SymbolType &callThisType, const std::vector &callArgTypes, - ErrorFactory *err, const CodeLoc &codeLoc) { + const CodeLoc &codeLoc) { std::vector matches; // Loop through functions and add any matches to the matches vector @@ -445,7 +444,7 @@ Function *SymbolTable::matchFunction(SymbolTable *currentScope, const std::strin if ((f.isMethodFunction() || f.isMethodProcedure()) && !fctThisType.getTemplateTypes().empty()) { SymbolTableEntry *thisEntry = childBlock->lookup(THIS_VARIABLE_NAME); assert(thisEntry != nullptr); - SymbolType newThisType = callThisType.toPointer(err, codeLoc); + SymbolType newThisType = callThisType.toPointer(codeLoc); thisEntry->updateType(newThisType, true); } } @@ -461,7 +460,7 @@ Function *SymbolTable::matchFunction(SymbolTable *currentScope, const std::strin // Throw error if more than one function matches the criteria if (matches.size() > 1) - throw ErrorFactory::get( + throw SemanticError( codeLoc, FUNCTION_AMBIGUITY, "More than one function matches your requested signature criteria. Please try to specify the return type explicitly"); @@ -523,8 +522,8 @@ void SymbolTable::insertSubstantiatedFunction(const Function &function, const As std::string mangledFctName = function.getMangledName(); for (const auto &[_, manifestations] : functions) { if (manifestations->contains(mangledFctName)) - throw ErrorFactory::get(declNode->codeLoc, FUNCTION_DECLARED_TWICE, - "The function/procedure '" + function.getSignature() + "' is declared twice"); + throw SemanticError(declNode->codeLoc, FUNCTION_DECLARED_TWICE, + "The function/procedure '" + function.getSignature() + "' is declared twice"); } // Add function to function list assert(functions.contains(declNode->codeLoc.toString())); @@ -539,12 +538,12 @@ void SymbolTable::insertSubstantiatedFunction(const Function &function, const As * @param s Struct object * @param err Error factory */ -void SymbolTable::insertStruct(const Struct &s, ErrorFactory *err) { +void SymbolTable::insertStruct(const Struct &s) { // Open a new function declaration pointer list. Which gets filled by the 'insertSubstantiatedFunction' method const AstNode *declNode = s.getDeclNode(); std::string codeLocStr = declNode->codeLoc.toString(); structs.insert({codeLocStr, std::make_shared>()}); - insertSubstantiatedStruct(s, err, declNode); + insertSubstantiatedStruct(s, declNode); } /** @@ -554,12 +553,11 @@ void SymbolTable::insertStruct(const Struct &s, ErrorFactory *err) { * @param currentScope Current scope * @param structName Struct name * @param templateTypes Template type requirements - * @param errorFactory Error factory * @param codeLoc Declaration code location for the error message * @return Matched struct or nullptr */ Struct *SymbolTable::matchStruct(SymbolTable *currentScope, const std::string &structName, - const std::vector &templateTypes, ErrorFactory *err, const CodeLoc &codeLoc) { + const std::vector &templateTypes, const CodeLoc &codeLoc) { std::vector matches; // Loop through structs and add any matches to the matches vector @@ -595,7 +593,7 @@ Struct *SymbolTable::matchStruct(SymbolTable *currentScope, const std::string &s SymbolTable *structScope = getChild(STRUCT_SCOPE_PREFIX + structName); Struct newStruct = s.substantiateGenerics(concreteTemplateTypes, structScope); if (!getChild(STRUCT_SCOPE_PREFIX + newStruct.getSignature())) { // Insert struct - insertSubstantiatedStruct(newStruct, err, s.getDeclNode()); + insertSubstantiatedStruct(newStruct, s.getDeclNode()); copyChildBlock(STRUCT_SCOPE_PREFIX + structName, STRUCT_SCOPE_PREFIX + newStruct.getSignature()); } @@ -607,14 +605,14 @@ Struct *SymbolTable::matchStruct(SymbolTable *currentScope, const std::string &s } if (matches.empty() && parent) - matches.push_back(parent->matchStruct(currentScope, structName, templateTypes, err, codeLoc)); + matches.push_back(parent->matchStruct(currentScope, structName, templateTypes, codeLoc)); if (matches.empty()) return nullptr; // Throw error if more than one struct matches the criteria if (matches.size() > 1) - throw ErrorFactory::get( + throw SemanticError( codeLoc, STRUCT_AMBIGUITY, "More than one struct matches your requested signature criteria. Please try to specify the return type explicitly"); @@ -668,12 +666,11 @@ Struct *SymbolTable::getStructAccessPointer(const CodeLoc &codeLoc) { * @param token Token, where the struct is declared * @param codeLoc Code location */ -void SymbolTable::insertSubstantiatedStruct(const Struct &s, ErrorFactory *err, const AstNode *declNode) { +void SymbolTable::insertSubstantiatedStruct(const Struct &s, const AstNode *declNode) { // Check if the struct exists already for (const auto &[_, manifestations] : structs) { if (manifestations->contains(s.getMangledName())) - throw ErrorFactory::get(declNode->codeLoc, STRUCT_DECLARED_TWICE, - "The struct '" + s.getSignature() + "' is declared twice"); + throw SemanticError(declNode->codeLoc, STRUCT_DECLARED_TWICE, "The struct '" + s.getSignature() + "' is declared twice"); } // Add struct to struct list assert(structs.at(declNode->codeLoc.toString()) != nullptr); diff --git a/src/symbol/SymbolTable.h b/src/symbol/SymbolTable.h index 139edb066..3c0591f6d 100644 --- a/src/symbol/SymbolTable.h +++ b/src/symbol/SymbolTable.h @@ -18,7 +18,6 @@ class Function; class Struct; class SymbolSpecifiers; class SymbolType; -class ErrorFactory; struct CodeLoc; enum ScopeType { @@ -73,21 +72,21 @@ class SymbolTable { std::map &getCaptures(); [[nodiscard]] size_t getFieldCount() const; - void insertFunction(const Function &function, ErrorFactory *err); + void insertFunction(const Function &function); Function *matchFunction(SymbolTable *currentScope, const std::string &callFunctionName, const SymbolType &callThisType, - const std::vector &callArgTypes, ErrorFactory *errorFactory, const CodeLoc &codeLoc); + const std::vector &callArgTypes, const CodeLoc &codeLoc); [[nodiscard]] std::map *getFunctionManifestations(const CodeLoc &defCodeLoc) const; void insertFunctionAccessPointer(const CodeLoc &codeLoc, Function *spiceFunc); Function *getFunctionAccessPointer(const CodeLoc &codeLoc); void insertSubstantiatedFunction(const Function &function, const AstNode *declNode); - void insertStruct(const Struct &s, ErrorFactory *err); + void insertStruct(const Struct &s); Struct *matchStruct(SymbolTable *currentScope, const std::string &structName, const std::vector &templateTypes, - ErrorFactory *errorFactory, const CodeLoc &codeLoc); + const CodeLoc &codeLoc); [[nodiscard]] std::map *getStructManifestations(const CodeLoc &defCodeLoc) const; void insertStructAccessPointer(const CodeLoc &codeLoc, Struct *spiceStruct); Struct *getStructAccessPointer(const CodeLoc &codeLoc); - void insertSubstantiatedStruct(const Struct &s, ErrorFactory *err, const AstNode *declNode); + void insertSubstantiatedStruct(const Struct &s, const AstNode *declNode); void purgeSubstantiationRemnants(); diff --git a/src/symbol/SymbolTableEntry.cpp b/src/symbol/SymbolTableEntry.cpp index 950ae1d9c..295ae7bef 100644 --- a/src/symbol/SymbolTableEntry.cpp +++ b/src/symbol/SymbolTableEntry.cpp @@ -3,10 +3,9 @@ #include "SymbolTableEntry.h" #include +#include #include -#include - /** * Retrieve the name of the current symbol * @@ -66,7 +65,7 @@ SymbolState SymbolTableEntry::getState() const { return state; } void SymbolTableEntry::updateState(SymbolState newState, const CodeLoc &codeLoc, bool force) { // Check if this is a constant variable and is already initialized if (state == INITIALIZED && specifiers.isConst() && !force) - throw ErrorFactory::get(codeLoc, REASSIGN_CONST_VARIABLE, "Not re-assignable variable '" + name + "'"); + throw SemanticError(codeLoc, REASSIGN_CONST_VARIABLE, "Not re-assignable variable '" + name + "'"); // Check if the type is known at time of initialization if (newState == INITIALIZED && type == SymbolType(TY_DYN)) // GCOV_EXCL_LINE throw std::runtime_error("Internal compiler error: could not determine type of variable '" + name + "'"); // GCOV_EXCL_LINE diff --git a/src/symbol/SymbolTableEntry.h b/src/symbol/SymbolTableEntry.h index 30b80b5bb..9449a6cb7 100644 --- a/src/symbol/SymbolTableEntry.h +++ b/src/symbol/SymbolTableEntry.h @@ -14,7 +14,6 @@ // Forward declarations class SymbolTable; -class ErrorFactory; struct AstNode; enum SymbolState { DECLARED, INITIALIZED }; diff --git a/src/symbol/SymbolType.cpp b/src/symbol/SymbolType.cpp index 80dd0cacf..396568274 100644 --- a/src/symbol/SymbolType.cpp +++ b/src/symbol/SymbolType.cpp @@ -5,9 +5,7 @@ #include #include -#include -#include #include #include #include @@ -24,10 +22,10 @@ SymbolType::TypeChain SymbolType::getTypeChain() const { return typeChain; } * * @return Pointer type of the current type */ -SymbolType SymbolType::toPointer(const ErrorFactory *err, const CodeLoc &codeLoc, llvm::Value *dynamicSize) const { +SymbolType SymbolType::toPointer(const CodeLoc &codeLoc, llvm::Value *dynamicSize) const { // Do not allow pointers of dyn if (typeChain.top().superType == TY_DYN) - throw ErrorFactory::get(codeLoc, DYN_POINTERS_NOT_ALLOWED, "Just use the dyn type without '*' instead"); + throw SemanticError(codeLoc, DYN_POINTERS_NOT_ALLOWED, "Just use the dyn type without '*' instead"); TypeChain newTypeChain = typeChain; newTypeChain.push({TY_PTR, "", {}, dynamicSize}); @@ -39,10 +37,10 @@ SymbolType SymbolType::toPointer(const ErrorFactory *err, const CodeLoc &codeLoc * * @return Array type of the current type */ -SymbolType SymbolType::toArray(const ErrorFactory *err, const CodeLoc &codeLoc, int size) const { +SymbolType SymbolType::toArray(const CodeLoc &codeLoc, int size) const { // Do not allow arrays of dyn if (typeChain.top().superType == TY_DYN) - throw ErrorFactory::get(codeLoc, DYN_ARRAYS_NOT_ALLOWED, "Just use the dyn type without '[]' instead"); + throw SemanticError(codeLoc, DYN_ARRAYS_NOT_ALLOWED, "Just use the dyn type without '[]' instead"); TypeChain newTypeChain = typeChain; newTypeChain.push({TY_ARRAY, std::to_string(size), {}, nullptr}); diff --git a/src/symbol/SymbolType.h b/src/symbol/SymbolType.h index cba8a8126..5ae03f65e 100644 --- a/src/symbol/SymbolType.h +++ b/src/symbol/SymbolType.h @@ -14,7 +14,6 @@ #include // Forward declarations -class ErrorFactory; class SymbolTable; struct CodeLoc; @@ -68,8 +67,8 @@ class SymbolType { // Public methods [[nodiscard]] TypeChain getTypeChain() const; - SymbolType toPointer(const ErrorFactory *err, const CodeLoc &codeLoc, llvm::Value *dynamicSize = nullptr) const; - SymbolType toArray(const ErrorFactory *err, const CodeLoc &codeLoc, int size = 0) const; + SymbolType toPointer(const CodeLoc &codeLoc, llvm::Value *dynamicSize = nullptr) const; + SymbolType toArray(const CodeLoc &codeLoc, int size = 0) const; [[nodiscard]] SymbolType getContainedTy() const; [[nodiscard]] SymbolType replaceBaseSubType(const std::string &newSubType) const; [[nodiscard]] SymbolType replaceBaseType(const SymbolType &newBaseType) const; diff --git a/test/GeneratorTest.cpp b/test/GeneratorTest.cpp index cab483a02..93585aecc 100644 --- a/test/GeneratorTest.cpp +++ b/test/GeneratorTest.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -89,9 +88,6 @@ void executeGeneratorTest(const TestCase &testCase) { std::shared_ptr context = std::make_shared(); std::shared_ptr> builder = std::make_shared>(*context); - // Create instance of error factory - ErrorFactory err{}; - // Prepare instance of thread factory, which has to exist exactly once per executable ThreadFactory threadFactory = ThreadFactory(); @@ -103,7 +99,7 @@ void executeGeneratorTest(const TestCase &testCase) { options = cli.getOptions(); // Create linker interface - LinkerInterface linker = LinkerInterface(err, threadFactory, options); + LinkerInterface linker = LinkerInterface(threadFactory, options); // Create main source file SourceFile mainSourceFile = SourceFile(options, nullptr, "root", sourceFile, false); diff --git a/test/StdTest.cpp b/test/StdTest.cpp index 56e62c8b8..6b6cbf3d8 100644 --- a/test/StdTest.cpp +++ b/test/StdTest.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -88,9 +87,6 @@ void executeStdTest(const TestCase &testCase) { std::shared_ptr context = std::make_shared(); std::shared_ptr> builder = std::make_shared>(*context); - // Create instance of error factory - ErrorFactory err{}; - // Prepare instance of thread factory, which has to exist exactly once per executable ThreadFactory threadFactory = ThreadFactory(); @@ -102,7 +98,7 @@ void executeStdTest(const TestCase &testCase) { options = cli.getOptions(); // Create linker interface - LinkerInterface linker = LinkerInterface(err, threadFactory, options); + LinkerInterface linker = LinkerInterface(threadFactory, options); // Create main source file SourceFile mainSourceFile = SourceFile(options, nullptr, "root", sourceFile, false);