diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2dde56d4f..8909fa92d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -222,7 +222,7 @@ jobs: - name: Run GoReleaser uses: goreleaser/goreleaser-action@v3 with: - version: v1.11.2 + version: v1.11.3 args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Options.cmake b/Options.cmake index c69b648c2..4afa40e4a 100644 --- a/Options.cmake +++ b/Options.cmake @@ -55,4 +55,11 @@ if (SPICE_IS_GH_ACTIONS) ADD_DEFINITIONS(-DSPICE_IS_GH_ACTIONS) else() message(STATUS "Spice: Running all the tests.") -endif() \ No newline at end of file +endif() + +# Add additional definitions +IF (WIN32) + ADD_DEFINITIONS(-DOS_WINDOWS) +ELSE() + ADD_DEFINITIONS(-DOS_UNIX) +ENDIF() \ No newline at end of file diff --git a/media/test-project/os-test.spice b/media/test-project/os-test.spice index a5e374d34..f0f5d46e2 100644 --- a/media/test-project/os-test.spice +++ b/media/test-project/os-test.spice @@ -1,4 +1,4 @@ -/*import "std/data/vector" as vec; +import "std/data/vector" as vec; import "std/data/pair" as pair; f main() { @@ -8,7 +8,7 @@ f main() { pair::Pair p1 = pairVector.get(1); printf("Hello %s!", p1.getSecond()); -}*/ +} /*import "std/net/http" as http; @@ -17,21 +17,14 @@ f main() { server.serve("/test", "Hello World!"); }*/ -import "std/runtime/string_rt" as _rt_str; +/*public f src(bool x, bool y) { + return ((x ? 1 : 4) & (y ? 1 : 4)) == 0; +} + +public f tgt(int x, int y) { + return x ^ y; +} f main() { - //_rt_str::String s = _rt_str::String("Test"); - //printf("%s", s.getRaw()); - // Plus - printf("Result: %s\n", "Hello " + "World!"); - string s1 = "Hello " + "World!"; - printf("Result: %s\n", s1); - // Mul - printf("Result: %s\n", 4s * "Hi"); - string s2 = "Hello " * 5; - printf("Result: %s\n", s2); - printf("Result: %s\n", 20 * 'a'); - string s3 = 2 * 'c' * 7; - printf("Result: %s\n", s3); - //printf("%s", s1 + s2); -} \ No newline at end of file + printf("Result: %d, %d", src(false, true), tgt(0, 1)); +}*/ \ No newline at end of file diff --git a/src/analyzer/AnalyzerVisitor.cpp b/src/analyzer/AnalyzerVisitor.cpp index 2f8d374c0..2af5aa7be 100644 --- a/src/analyzer/AnalyzerVisitor.cpp +++ b/src/analyzer/AnalyzerVisitor.cpp @@ -15,10 +15,9 @@ #include #include -AnalyzerVisitor::AnalyzerVisitor(const llvm::LLVMContext *context, const llvm::IRBuilder<> *builder, - const ThreadFactory &threadFactory, const SourceFile &sourceFile, CliOptions &options, - bool requiresMainFct, bool isStdFile) - : context(context), builder(builder), threadFactory(threadFactory), requiresMainFct(requiresMainFct), isStdFile(isStdFile) { +AnalyzerVisitor::AnalyzerVisitor(const llvm::LLVMContext *context, const llvm::IRBuilder<> *builder, const SourceFile &sourceFile, + CliOptions &options, RuntimeModules &runtimeModules, bool requiresMainFct, bool isStdFile) + : context(context), builder(builder), runtimeModules(runtimeModules), requiresMainFct(requiresMainFct), isStdFile(isStdFile) { // Retrieve symbol table this->currentScope = this->rootScope = sourceFile.symbolTable.get(); @@ -67,7 +66,7 @@ std::any AnalyzerVisitor::visitEntry(EntryNode *node) { } std::any AnalyzerVisitor::visitMainFctDef(MainFctDefNode *node) { - std::string mainSignature = MAIN_FUNCTION_NAME + "()"; + std::string mainSignature = std::string(MAIN_FUNCTION_NAME) + "()"; if (runNumber == 1) { // First run // Check if the function is already defined @@ -2305,8 +2304,13 @@ std::any AnalyzerVisitor::visitCustomDataType(CustomDataTypeNode *node) { } SymbolType AnalyzerVisitor::insertAnonStringStructSymbol(const AstNode *declNode) { + // Insert anonymous string symbol SymbolType stringStructType(TY_STRING, "", {.isStringStruct = true}, {}); currentScope->insertAnonymous(stringStructType, declNode); + + // Enable string runtime + runtimeModules.stringRuntime = true; + return stringStructType; } diff --git a/src/analyzer/AnalyzerVisitor.h b/src/analyzer/AnalyzerVisitor.h index c51ffa9ce..de3121027 100644 --- a/src/analyzer/AnalyzerVisitor.h +++ b/src/analyzer/AnalyzerVisitor.h @@ -13,15 +13,15 @@ #include #include -const std::string MAIN_FUNCTION_NAME = "main"; -const std::string RETURN_VARIABLE_NAME = "result"; -const std::string THIS_VARIABLE_NAME = "this"; -const std::string FOREACH_DEFAULT_IDX_VARIABLE_NAME = "idx"; -const std::string CTOR_VARIABLE_NAME = "ctor"; -const std::string DTOR_VARIABLE_NAME = "dtor"; -const std::string STRUCT_SCOPE_PREFIX = "struct:"; -const std::string ENUM_SCOPE_PREFIX = "enum:"; -const std::string UNUSED_VARIABLE_NAME = "_"; +const char *const MAIN_FUNCTION_NAME = "main"; +const char *const RETURN_VARIABLE_NAME = "result"; +const char *const THIS_VARIABLE_NAME = "this"; +const char *const FOREACH_DEFAULT_IDX_VARIABLE_NAME = "idx"; +const char *const CTOR_VARIABLE_NAME = "ctor"; +const char *const DTOR_VARIABLE_NAME = "dtor"; +const char *const STRUCT_SCOPE_PREFIX = "struct:"; +const char *const ENUM_SCOPE_PREFIX = "enum:"; +const char *const UNUSED_VARIABLE_NAME = "_"; const std::vector RESERVED_KEYWORDS = {"new", "switch", "case", "yield", "stash", "pick", "sync", "class"}; // Forward declarations @@ -32,6 +32,7 @@ class LinkerInterface; class SymbolTable; class SymbolTableEntry; class SourceFile; +struct RuntimeModules; /** * Visitor for analyzing a source file. @@ -42,12 +43,13 @@ class SourceFile; * - Type inference * - Type checking * - Resolve generic functions/procedure/structs + * - Detect usages of runtime modules */ class AnalyzerVisitor : public AstVisitor { public: // Constructors - explicit AnalyzerVisitor(const llvm::LLVMContext *context, const llvm::IRBuilder<> *builder, const ThreadFactory &threadFactory, - const SourceFile &sourceFile, CliOptions &options, bool requiresMainFct, bool stdFile); + explicit AnalyzerVisitor(const llvm::LLVMContext *context, const llvm::IRBuilder<> *builder, const SourceFile &sourceFile, + CliOptions &options, RuntimeModules &runtimeModules, bool requiresMainFct, bool stdFile); // Friend classes friend class OpRuleManager; @@ -111,7 +113,7 @@ class AnalyzerVisitor : public AstVisitor { const llvm::LLVMContext *context; const llvm::IRBuilder<> *builder; std::unique_ptr opRuleManager; - const ThreadFactory &threadFactory; + RuntimeModules &runtimeModules; bool requiresMainFct = true; bool hasMainFunction = false; bool isStdFile = false; diff --git a/src/analyzer/PreAnalyzerVisitor.cpp b/src/analyzer/PreAnalyzerVisitor.cpp index 9a4c84f25..f89796d75 100644 --- a/src/analyzer/PreAnalyzerVisitor.cpp +++ b/src/analyzer/PreAnalyzerVisitor.cpp @@ -28,17 +28,10 @@ std::any PreAnalyzerVisitor::visitImportStmt(ImportStmtNode *node) { if (node->importPath.rfind("std/", 0) == 0) { // Include source file from standard library std::string sourceFileIden = node->importPath.substr(node->importPath.find("std/") + 4); // Find std library - std::string stdPath; - if (FileUtil::fileExists("/usr/lib/spice/std")) { - stdPath = "/usr/lib/spice/std/"; - } else if (std::getenv("SPICE_STD_DIR") && FileUtil::dirExists(std::string(std::getenv("SPICE_STD_DIR")))) { - stdPath = std::string(std::getenv("SPICE_STD_DIR")); - if (stdPath.rfind(FileUtil::DIR_SEPARATOR) != stdPath.size() - 1) - stdPath += FileUtil::DIR_SEPARATOR; - } else { + std::string stdPath = FileUtil::getStdDir(); + if (stdPath.empty()) 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"; std::string osPath = stdPath + sourceFileIden + "_" + cliOptions.targetOs + ".spice"; diff --git a/src/ast/AstNodes.h b/src/ast/AstNodes.h index 70d6e9192..738ab6e9a 100644 --- a/src/ast/AstNodes.h +++ b/src/ast/AstNodes.h @@ -83,8 +83,6 @@ class AstNode { SymbolType setEvaluatedSymbolType(const SymbolType &symbolType) { size_t index = getSymbolTypeIndex(); - // if (symbolTypes.capacity() <= index) - // symbolTypes.reserve(index + 1); symbolTypes.insert(symbolTypes.begin() + index, symbolType); return symbolType; } @@ -126,7 +124,7 @@ class AstNode { const CodeLoc codeLoc; size_t symbolTypeIndex = SIZE_MAX; std::vector symbolTypes; - CompileTimeValue compileTimeValue; + CompileTimeValue compileTimeValue = {}; std::string compileTimeStringValue; bool hasDirectCompileTimeValue = false; }; diff --git a/src/cli/CliInterface.cpp b/src/cli/CliInterface.cpp index fb99d66ab..7a6c751be 100644 --- a/src/cli/CliInterface.cpp +++ b/src/cli/CliInterface.cpp @@ -37,7 +37,7 @@ void CliInterface::createInterface() { std::string installPath = FileUtil::getSpiceBinDir(); FileUtil::createDirs(installPath); installPath += FileUtil::getFileName(cliOptions.mainSourceFile.substr(0, cliOptions.mainSourceFile.length() - 6)); -#ifdef OS_WINDOWS +#if OS_WINDOWS installPath += ".exe"; #endif cliOptions.outputPath = installPath; @@ -48,7 +48,7 @@ void CliInterface::createInterface() { cliOptions.outputPath = "."; if (cliOptions.outputPath == "." || cliOptions.outputPath == "..") { cliOptions.outputPath = FileUtil::getFileName(cliOptions.mainSourceFile.substr(0, cliOptions.mainSourceFile.length() - 6)); -#ifdef OS_WINDOWS +#if OS_WINDOWS cliOptions.outputPath += ".exe"; #endif } @@ -307,7 +307,7 @@ void CliInterface::addUninstallSubcommand() { subCmd->callback([&]() { std::string installPath = FileUtil::getSpiceBinDir(); installPath += FileUtil::getFileName(cliOptions.mainSourceFile.substr(0, cliOptions.mainSourceFile.length() - 6)); -#ifdef OS_WINDOWS +#if OS_WINDOWS installPath += ".exe"; #endif if (!FileUtil::fileExists(installPath)) { diff --git a/src/cli/CliInterface.h b/src/cli/CliInterface.h index 345023738..37e4e2589 100644 --- a/src/cli/CliInterface.h +++ b/src/cli/CliInterface.h @@ -6,12 +6,6 @@ #include "../../lib/cli11/CLI11.hpp" -#ifdef __unix__ -#define OS_UNIX -#elif defined(_WIN32) || defined(WIN32) -#define OS_WINDOWS -#endif - /** * Representation of the various cli options */ @@ -42,7 +36,6 @@ class CliInterface { public: // Constructors explicit CliInterface() = default; - explicit CliInterface(CliOptions options) : cliOptions(std::move(options)) {} // Public methods void createInterface(); diff --git a/src/dependency/SourceFile.cpp b/src/dependency/SourceFile.cpp index ee271ae41..2a36c33a7 100644 --- a/src/dependency/SourceFile.cpp +++ b/src/dependency/SourceFile.cpp @@ -21,10 +21,10 @@ #include SourceFile::SourceFile(llvm::LLVMContext *context, llvm::IRBuilder<> *builder, ThreadFactory &threadFactory, - LinkerInterface &linker, CliOptions &options, SourceFile *parent, std::string name, - const std::string &filePath, bool stdFile) - : context(context), builder(builder), threadFactory(threadFactory), linker(linker), name(std::move(name)), filePath(filePath), - stdFile(stdFile), parent(parent), options(options) { + RuntimeModules &runtimeModules, LinkerInterface &linker, CliOptions &options, SourceFile *parent, + std::string name, const std::string &filePath, bool stdFile) + : context(context), builder(builder), threadFactory(threadFactory), runtimeModules(runtimeModules), linker(linker), + name(std::move(name)), filePath(filePath), stdFile(stdFile), parent(parent), options(options) { this->objectFilePath = options.outputDir + FileUtil::DIR_SEPARATOR + FileUtil::getFileName(filePath) + ".o"; // Deduce fileName and fileDir @@ -71,8 +71,8 @@ void SourceFile::visualizeCST() { dotCode += " label=\"" + replacedFilePath + "\";\n "; // Visualize the imported source files - for (const auto &[_, sourceFile] : dependencies) - sourceFile.first->visualizeCST(); + for (const auto &sourceFile : dependencies) + sourceFile.second.first->visualizeCST(); // Generate dot code for this source file CSTVisualizerVisitor visualizerVisitor(antlrCtx.lexer, antlrCtx.parser); @@ -106,8 +106,8 @@ void SourceFile::visualizeCST() { void SourceFile::buildAST() { // Transform the imported source files - for (const auto &[_, sourceFile] : dependencies) - sourceFile.first->buildAST(); + for (const auto &sourceFile : dependencies) + sourceFile.second.first->buildAST(); // Transform this source file AstBuilderVisitor astBuilder(ast.get(), filePath); @@ -126,8 +126,8 @@ void SourceFile::visualizeAST() { dotCode += " label=\"" + replacedFilePath + "\";\n "; // Visualize the imported source files - for (const auto &[_, sourceFile] : dependencies) - sourceFile.first->visualizeAST(); + for (const auto &sourceFile : dependencies) + sourceFile.second.first->visualizeAST(); // Generate dot code for this source file ASTVisualizerVisitor visualizerVisitor(ast.get()); @@ -165,9 +165,9 @@ void SourceFile::preAnalyze() { antlrCtx.parser->reset(); // Pre-analyze the imported source files - for (const auto &[_, sourceFile] : dependencies) { - sourceFile.first->buildAST(); - sourceFile.first->preAnalyze(); + for (const auto &sourceFile : dependencies) { + sourceFile.second.first->buildAST(); + sourceFile.second.first->preAnalyze(); } } @@ -188,7 +188,7 @@ void SourceFile::analyze() { } // Analyze this source file - analyzer = std::make_shared(context, builder, threadFactory, *this, options, parent == nullptr, stdFile); + analyzer = std::make_shared(context, builder, *this, options, runtimeModules, parent == nullptr, stdFile); analyzer->visit(ast.get()); antlrCtx.parser->reset(); } @@ -207,8 +207,27 @@ void SourceFile::reAnalyze() { } while (repetitionRequired); // Re-analyze the imported source files - for (const auto &[importName, sourceFile] : dependencies) - sourceFile.first->reAnalyze(); + for (const auto &sourceFile : dependencies) + sourceFile.second.first->reAnalyze(); + + // If this is the main source file, import the required runtime modules + if (parent == nullptr) { + std::vector> runtimeFiles; + if (runtimeModules.stringRuntime) + runtimeFiles.emplace_back("__rt_string", "string_rt"); + if (runtimeModules.threadRuntime) + runtimeFiles.emplace_back("__rt_thread", "thread_rt"); + + std::string runtimePath = FileUtil::getStdDir() + "runtime" + FileUtil::DIR_SEPARATOR; + for (const auto &[importName, fileName] : runtimeFiles) { + addDependency(ast.get(), importName, runtimePath + fileName + ".spice", true); + auto &[stringRuntimeFile, _] = dependencies.at(importName); + stringRuntimeFile->buildAST(); + stringRuntimeFile->preAnalyze(); + stringRuntimeFile->analyze(); + stringRuntimeFile->reAnalyze(); + } + } // Save the JSON version in the compiler output compilerOutput.symbolTableString = symbolTable->toJSON().dump(2); @@ -222,8 +241,8 @@ void SourceFile::reAnalyze() { void SourceFile::generate() { // Generate the imported source files - for (const auto &[_, sourceFile] : dependencies) - sourceFile.first->generate(); + for (const auto &sourceFile : dependencies) + sourceFile.second.first->generate(); // Generate this source file generator = std::make_shared(context, builder, threadFactory, linker, options, *this, objectFilePath); @@ -257,8 +276,8 @@ void SourceFile::optimize() { return; // Optimize the imported source files - for (const auto &[_, sourceFile] : dependencies) - sourceFile.first->optimize(); + for (const auto &sourceFile : dependencies) + sourceFile.second.first->optimize(); generator->optimize(); @@ -275,8 +294,8 @@ void SourceFile::optimize() { void SourceFile::emitObjectFile() { // Optimize the imported source files - for (const auto &[_, sourceFile] : dependencies) - sourceFile.first->emitObjectFile(); + for (const auto &sourceFile : dependencies) + sourceFile.second.first->emitObjectFile(); // Dump assembly code if (options.dumpAssembly) { // GCOV_EXCL_START @@ -302,10 +321,9 @@ void SourceFile::addDependency(const AstNode *declAstNode, const std::string &na throw SemanticError(declAstNode->codeLoc, CIRCULAR_DEPENDENCY, "Circular import detected while importing '" + filePath + "'"); // Add the dependency - dependencies.insert( - {name, - {std::make_shared(context, builder, threadFactory, linker, options, this, name, filePath, stdFile), - declAstNode}}); + auto sourceFile = std::make_shared(context, builder, threadFactory, runtimeModules, linker, options, this, name, + filePath, stdFile); + dependencies.insert({name, {sourceFile, declAstNode}}); } bool SourceFile::isAlreadyImported(const std::string &filePathSearch) const { diff --git a/src/dependency/SourceFile.h b/src/dependency/SourceFile.h index 4b3f0204f..804787eff 100644 --- a/src/dependency/SourceFile.h +++ b/src/dependency/SourceFile.h @@ -17,7 +17,6 @@ class AnalyzerVisitor; class GeneratorVisitor; class AntlrThrowingErrorListener; class SymbolTable; -class ErrorFactory; struct CliOptions; class LinkerInterface; class ThreadFactory; @@ -42,12 +41,18 @@ struct CompilerOutput { std::string irOptString; }; +// Structs +struct RuntimeModules { + bool stringRuntime = false; + bool threadRuntime = false; +}; + class SourceFile { public: // Constructors explicit SourceFile(llvm::LLVMContext *context, llvm::IRBuilder<> *builder, ThreadFactory &threadFactory, - LinkerInterface &linker, CliOptions &options, SourceFile *parent, std::string name, - const std::string &filePath, bool stdFile); + RuntimeModules &runtimeModules, LinkerInterface &linker, CliOptions &options, SourceFile *parent, + std::string name, const std::string &filePath, bool stdFile); // Public methods void visualizeCST(); @@ -85,4 +90,5 @@ class SourceFile { llvm::IRBuilder<> *builder; ThreadFactory &threadFactory; LinkerInterface &linker; + RuntimeModules &runtimeModules; }; \ No newline at end of file diff --git a/src/generator/GeneratorVisitor.cpp b/src/generator/GeneratorVisitor.cpp index d90170d3f..8f50721ba 100644 --- a/src/generator/GeneratorVisitor.cpp +++ b/src/generator/GeneratorVisitor.cpp @@ -351,7 +351,7 @@ std::any GeneratorVisitor::visitFctDef(FctDefNode *node) { spiceFunc.getThisType().getBaseType().getTemplateTypes()); // Get the LLVM type of the struct symbol SymbolType thisSymbolType = spiceFunc.getThisType(); - argNames.push_back(THIS_VARIABLE_NAME); + argNames.emplace_back(THIS_VARIABLE_NAME); llvm::Type *thisType = thisSymbolType.toLLVMType(*context, accessScope)->getPointerTo(); argTypes.push_back(thisType); // Change scope to struct @@ -547,7 +547,7 @@ std::any GeneratorVisitor::visitProcDef(ProcDefNode *node) { std::string structSignature = Struct::getSignature(spiceProc.getThisType().getBaseType().getSubType(), spiceProc.getThisType().getBaseType().getTemplateTypes()); // Get the LLVM type of the struct symbol - argNames.push_back(THIS_VARIABLE_NAME); + argNames.emplace_back(THIS_VARIABLE_NAME); llvm::Type *thisType = spiceProc.getThisType().toLLVMType(*context, accessScope)->getPointerTo(); argTypes.push_back(thisType); // Change scope to struct diff --git a/src/linker/LinkerInterface.cpp b/src/linker/LinkerInterface.cpp index b18bc4318..61de7f3a7 100644 --- a/src/linker/LinkerInterface.cpp +++ b/src/linker/LinkerInterface.cpp @@ -9,13 +9,7 @@ #include #include -#ifdef __unix__ -#define OS_UNIX -#elif defined(_WIN32) || defined(WIN32) -#define OS_WINDOWS -#endif - -const char *LINKER_EXECUTABLE_NAME = "gcc"; +const char *const LINKER_EXECUTABLE_NAME = "gcc"; /** * Start the linking process @@ -23,7 +17,7 @@ const char *LINKER_EXECUTABLE_NAME = "gcc"; void LinkerInterface::link() { if (FileUtil::isCommandAvailable(std::string(LINKER_EXECUTABLE_NAME))) // GCOV_EXCL_START 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 + " and added it to the PATH variable"); // GCOV_EXCL_STOP // Check if the output path was set if (outputPath.empty()) diff --git a/src/main.cpp b/src/main.cpp index 8df73bbc2..8b544b5d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,15 +21,14 @@ bool compileProject(CliOptions &options) { llvm::LLVMContext context; llvm::IRBuilder<> builder(context); - // Prepare instance of thread factory, which has to exist exactly once per executable + // Prepare global Spice assets ThreadFactory threadFactory = ThreadFactory(); - - // Prepare linker interface LinkerInterface linker = LinkerInterface(threadFactory, options); + RuntimeModules runtimeModules = {false, false}; // Create source file instance for main source file - SourceFile mainSourceFile = - SourceFile(&context, &builder, threadFactory, linker, options, nullptr, "root", options.mainSourceFile, false); + SourceFile mainSourceFile(&context, &builder, threadFactory, runtimeModules, linker, options, nullptr, "root", + options.mainSourceFile, false); // Visualize the parse tree (only runs in debug mode) mainSourceFile.visualizeCST(); diff --git a/src/symbol/SymbolTable.cpp b/src/symbol/SymbolTable.cpp index 0c9235423..d1c0c1e4f 100644 --- a/src/symbol/SymbolTable.cpp +++ b/src/symbol/SymbolTable.cpp @@ -866,4 +866,4 @@ nlohmann::json SymbolTable::toJSON() const { result["captures"] = jsonCaptures; result["children"] = jsonChildren; return result; -} +} \ No newline at end of file diff --git a/src/util/FileUtil.cpp b/src/util/FileUtil.cpp index e4aa3d6a7..cf806f61a 100644 --- a/src/util/FileUtil.cpp +++ b/src/util/FileUtil.cpp @@ -3,15 +3,10 @@ #include "FileUtil.h" #include -#include #include #include -#ifdef __unix__ -#define OS_UNIX -#elif defined(_WIN32) || defined(WIN32) -#define OS_WINDOWS -#endif +#include /** * Checks if a certain file exists on the file system @@ -97,7 +92,7 @@ ExecResult FileUtil::exec(const std::string &cmd) { result += buffer; } int exitCode = pclose(pipe) / 256; - return { result, exitCode }; + return {result, exitCode}; } /** @@ -107,20 +102,44 @@ ExecResult FileUtil::exec(const std::string &cmd) { * @return Present or not */ bool FileUtil::isCommandAvailable(const std::string &cmd) { +#if OS_WINDOWS + std::string checkCmd = "where " + cmd + " > nul 2>&1"; +#else std::string checkCmd = "which " + cmd + " > /dev/null 2>&1"; -#ifdef OS_WINDOWS - checkCmd = "where " + cmd + " > nul 2>&1"; #endif return std::system(checkCmd.c_str()); } +/** + * Retrieve the dir, where the standard library lives. + * Returns an empty string if the std was not found. + * + * @return Std directory + */ +std::string FileUtil::getStdDir() { +#ifdef OS_UNIX + if (FileUtil::fileExists("/usr/lib/spice/std")) + return "/usr/lib/spice/std/"; +#endif + if (std::getenv("SPICE_STD_DIR") && FileUtil::dirExists(std::string(std::getenv("SPICE_STD_DIR")))) { + std::string stdPath = std::string(std::getenv("SPICE_STD_DIR")); +#ifdef OS_WINDOWS + CommonUtil::replaceAll(stdPath, "/", "\\"); +#endif + if (stdPath.rfind(FileUtil::DIR_SEPARATOR) != stdPath.size() - 1) + stdPath += FileUtil::DIR_SEPARATOR; + return stdPath; + } + return ""; +} + /** * Retrieve the dir, where output binaries should go when installing them * * @return Installation directory */ std::string FileUtil::getSpiceBinDir() { -#ifdef _WIN32 +#if OS_WINDOWS return std::string(std::getenv("USERPROFILE")) + R"(\spice\bin\)"; #else return "/usr/local/bin/"; diff --git a/src/util/FileUtil.h b/src/util/FileUtil.h index cd55603cb..ee558b589 100644 --- a/src/util/FileUtil.h +++ b/src/util/FileUtil.h @@ -29,5 +29,6 @@ class FileUtil { static std::string getFileDir(const std::string &filePath); static ExecResult exec(const std::string &cmd); static bool isCommandAvailable(const std::string &cmd); + static std::string getStdDir(); static std::string getSpiceBinDir(); }; \ No newline at end of file diff --git a/std/data/binary-tree.spice b/std/data/binary-tree.spice new file mode 100644 index 000000000..4f44a2b91 --- /dev/null +++ b/std/data/binary-tree.spice @@ -0,0 +1,52 @@ +// Link external functions +ext malloc(long); +ext free(byte*); + +// Add generic type definitions +type T dyn; + +/** + * Node of a BinaryTree + */ +public type Node struct { + Node* childLeft + Node* childRight + T value +} + +/** + * A binary tree is a data structure to fasten up search speeds. Binary trees (when balanced) can be searched in O(log n). + * Insert operations, on the other hand, are rather slow, because the tree might get re-balanced. + */ +public type BinaryTree struct { + Node* rootNode + bool isBalanced +} + +public p BinaryTree.ctor() { + this.rootNode = nil*>; + this.isBalanced = false; +} + +public p BinaryTree.dtor() { + if this.rootNode != nil*> { + this.rootNode.dtor(); + free((byte*) this.rootNode); + } +} + +public p Node.dtor() { + if this.childLeft != nil*> { + this.childLeft.dtor(); + free((byte*) this.childLeft); + } + if this.childRight != nil*> { + this.childRight.dtor(); + free((byte*) this.childRight); + } +} + +public p BinaryTree.insert(T newValue, Node* baseNode = nil*>) { + // Search position where to insert + // ToDo +} \ No newline at end of file diff --git a/std/data/doubly-linked-list.spice b/std/data/doubly-linked-list.spice new file mode 100644 index 000000000..51f075f04 --- /dev/null +++ b/std/data/doubly-linked-list.spice @@ -0,0 +1,81 @@ +// Link external functions +ext malloc(long); +ext free(byte*); + +// Add generic type definitions +type T dyn; + +/** + * Node of a DoublyLinkedList + */ +public type Node struct { + Node* prev + T value + Node* next +} + +/** + * A doubly linked list is a common, dynamically resizable data structure to store uniform data in order. + * It is characterized by the pointer for every item, pointing to the next one and the pointer, pointing + * to the previous one. + */ +public type DoublyLinkedList struct { + Node* head + Node* tail +} + +public p Node.dtor() { + if this.next != nil*> { + this.next.dtor(); + free((byte*) this.next); + } +} + +public p DoublyLinkedList.insert(T newValue, Node* prevNode = nil*>) { + // Create new node + Node* newNode; + unsafe { + newNode = (Node*) malloc(sizeof(type Node)); + } + newNode.value = newValue; + + if prevNode != nil*> { // Previous node was passed -> insert after this node + // Link the previous to this one + newNode.prev = prevNode; + // Link the next node to this one + newNode.next = prevNode.next; + // Link this node to the next node + prevNode.next.prev = newNode; + // Link this node to the previous node + prevNode.next = newNode; + + // Check if the previous node was the last node + if prevNode == tail { + this.tail = newNode; + } + } else { // No previous node was passed -> insert at head + newNode.next = this.head; + this.head = newNode; + } +} + +public p DoublyLinkedList.insertHead(T newValue) { + this.insert(newValue); +} + +public inline p DoublyLinkedList.insertTail(T newValue) { + this.insert(newValue, this.tail); +} + +/*public f*> DoublyLinkedList.find(T value) { + Node* currentNode = this.head; + while currentNode != nil*> { + // Check condition + if currentNode.value == value { + return currentNode; + } + // Move to next node + currentNode = currentNode.next; + } + return nil*>; +}*/ \ No newline at end of file diff --git a/std/data/linked-list.spice b/std/data/linked-list.spice new file mode 100644 index 000000000..1a3e9ab9e --- /dev/null +++ b/std/data/linked-list.spice @@ -0,0 +1,75 @@ +// Link external functions +ext malloc(long); +ext free(byte*); + +// Add generic type definitions +type T dyn; + +/** + * Node of a LinkedList + */ +public type Node struct { + T value + Node* next +} + +/** + * A linked list is a common, dynamically resizable data structure to store uniform data in order. + * It is characterized by the pointer for every item, pointing to the next one. + */ +public type LinkedList struct { + Node* head + Node* tail +} + +public p Node.dtor() { + if this.next != nil*> { + this.next.dtor(); + free((byte*) this.next); + } +} + +public p LinkedList.insert(T newValue, Node* prevNode = nil*>) { + // Create new node + Node* newNode; + unsafe { + newNode = (Node*) malloc(sizeof(type Node)); + } + newNode.value = newValue; + + if prevNode != nil*> { // Previous node was passed -> insert after this node + // Link the next node to this one + newNode.next = prevNode.next; + // Link this node to the previous node + prevNode.next = newNode; + + // Check if the previous node was the last node + if prevNode == tail { + this.tail = newNode; + } + } else { // No previous node was passed -> insert at head + newNode.next = this.head; + this.head = newNode; + } +} + +public p LinkedList.insertHead(T newValue) { + this.insert(newValue); +} + +public inline p LinkedList.insertTail(T newValue) { + this.insert(newValue, this.tail); +} + +/*public f*> LinkedList.find(T value) { + Node* currentNode = this.head; + while currentNode != nil*> { + // Check condition + if currentNode.value == value { + return currentNode; + } + // Move to next node + currentNode = currentNode.next; + } + return nil*>; +}*/ \ No newline at end of file diff --git a/test/TestRunner.cpp b/test/TestRunner.cpp index 28d4c1f0b..964abef1c 100644 --- a/test/TestRunner.cpp +++ b/test/TestRunner.cpp @@ -51,11 +51,12 @@ void execTestCase(const TestCase &testCase) { llvm::IRBuilder<> builder(context); ThreadFactory threadFactory = ThreadFactory(); LinkerInterface linker = LinkerInterface(threadFactory, options); + RuntimeModules runtimeModules = {false, false}; try { // Create source file instance for main source file - SourceFile mainSourceFile = - SourceFile(&context, &builder, threadFactory, linker, options, nullptr, "root", options.mainSourceFile, false); + SourceFile mainSourceFile = SourceFile(&context, &builder, threadFactory, runtimeModules, linker, options, nullptr, "root", + options.mainSourceFile, false); // Check CST TestUtil::checkRefMatch(testCase.testPath + FileUtil::DIR_SEPARATOR + REF_NAME_PARSE_TREE, [&]() { diff --git a/test/TestUtil.cpp b/test/TestUtil.cpp index 7e8c98b7a..d3b89a3ee 100644 --- a/test/TestUtil.cpp +++ b/test/TestUtil.cpp @@ -2,24 +2,19 @@ // GCOV_EXCL_START -#ifdef __unix__ -#define OS_UNIX -#elif defined(_WIN32) || defined(WIN32) -#define OS_WINDOWS -#endif - #include "TestUtil.h" #include #include -#ifdef OS_UNIX -#include // Required by builds on Linux -#endif #include #include +#ifdef OS_UNIX +#include // Required by builds on Linux +#endif + /** * Collect the test cases in a particular test suite * @@ -142,7 +137,7 @@ std::string TestUtil::toCamelCase(std::string input) { */ std::string TestUtil::getDefaultExecutableName() { std::string executableName = "./source"; -#ifdef OS_WINDOWS +#if OS_WINDOWS executableName = ".\\source.exe"; #endif return executableName; diff --git a/test/TestUtil.h b/test/TestUtil.h index ce09560fa..347edf4ff 100644 --- a/test/TestUtil.h +++ b/test/TestUtil.h @@ -12,12 +12,10 @@ #include -#ifdef __unix__ -#define OS_UNIX -const char *const PATH_TEST_FILES = "./test-files/"; -#elif defined(_WIN32) || defined(WIN32) -#define OS_WINDOWS +#if OS_WINDOWS const char *const PATH_TEST_FILES = ".\\test-files\\"; +#else +const char *const PATH_TEST_FILES = "./test-files/"; #endif const unsigned int EXPECTED_NUMBER_OF_TESTS = 250; const unsigned int IR_FILE_SKIP_LINES = 4; // Skip the first couple of lines, because they contain target dependent information diff --git a/test/test-files/std/runtime/string-basic-operations/source.spice b/test/test-files/std/runtime/string-basic-operations/source.spice index 026af58f9..4a1d45039 100644 --- a/test/test-files/std/runtime/string-basic-operations/source.spice +++ b/test/test-files/std/runtime/string-basic-operations/source.spice @@ -1,5 +1,3 @@ -import "std/runtime/string_rt" as _rt_str; - f main() { _rt_str::String s = _rt_str::String("Hello "); printf("Content: %s\n", s.getRaw()); diff --git a/test/test-files/std/runtime/string-operators/ir-code-O2.ll b/test/test-files/std/runtime/string-operators/ir-code-O2.ll index 8c78a849c..c15ba551c 100644 --- a/test/test-files/std/runtime/string-operators/ir-code-O2.ll +++ b/test/test-files/std/runtime/string-operators/ir-code-O2.ll @@ -18,7 +18,7 @@ target triple = "x86_64-w64-windows-gnu" @10 = private unnamed_addr constant [12 x i8] c"Result: %s\0A\00", align 1 define i32 @main() local_unnamed_addr { -entry.l3: +entry.l1: %0 = alloca %_s__String__charptr_long_long, align 8 %1 = alloca %_s__String__charptr_long_long, align 8 %2 = alloca %_s__String__charptr_long_long, align 8 @@ -91,7 +91,6 @@ entry.l3: call void @_mp__String__opMul__int(ptr nonnull %10, i32 3) %33 = call ptr @_mf__String__getRaw(ptr nonnull %10) %34 = call i32 (ptr, ...) @printf(ptr nonnull @10, ptr %33) - call void @_mp__String__dtor(ptr nonnull %5) call void @_mp__String__dtor(ptr nonnull %6) call void @_mp__String__dtor(ptr nonnull %7) call void @_mp__String__dtor(ptr nonnull %8) @@ -100,6 +99,7 @@ entry.l3: call void @_mp__String__dtor(ptr nonnull %0) call void @_mp__String__dtor(ptr nonnull %2) call void @_mp__String__dtor(ptr nonnull %4) + call void @_mp__String__dtor(ptr nonnull %5) ret i32 0 } diff --git a/test/test-files/std/runtime/string-operators/source.spice b/test/test-files/std/runtime/string-operators/source.spice index 0cfed9962..b1909bbf0 100644 --- a/test/test-files/std/runtime/string-operators/source.spice +++ b/test/test-files/std/runtime/string-operators/source.spice @@ -1,5 +1,3 @@ -import "std/runtime/string_rt" as _rt_str; - f main() { // Plus printf("Result: %s\n", "Hello " + "World!");