diff --git a/.run/spice.run.xml b/.run/spice.run.xml index f5034be42..b7cdd68f7 100644 --- a/.run/spice.run.xml +++ b/.run/spice.run.xml @@ -1,5 +1,5 @@ - + diff --git a/media/test-project/test.spice b/media/test-project/test.spice index fdc13042e..d148ac687 100644 --- a/media/test-project/test.spice +++ b/media/test-project/test.spice @@ -1,17 +1,15 @@ -import "std/os/env"; -import "bootstrap/lexer/lexer"; +import "std/data/hash-table"; f main() { - String filePath = getEnv("SPICE_STD_DIR") + "/../test/test-files/bootstrap-compiler/standalone-lexer-test/test-file.spice"; - Lexer lexer = Lexer(filePath.getRaw()); - unsigned long tokenCount = 0l; - while (!lexer.isEOF()) { - Token token = lexer.getToken(); - token.print(); - lexer.advance(); - tokenCount++; - } - printf("\nLexed tokens: %d\n", tokenCount); + HashTable hashTable = HashTable(); + hashTable.upsert(1, 2); + hashTable.upsert(2, 3); + + Optional value = hashTable.get(1); + assert(value.get() == 2); + value = hashTable.get(2); + assert(value.get() == 3); + } /*import "bootstrap/util/block-allocator"; diff --git a/src-bootstrap/symboltablebuilder/scope.spice b/src-bootstrap/symboltablebuilder/scope.spice index 120661b2b..819d68d24 100644 --- a/src-bootstrap/symboltablebuilder/scope.spice +++ b/src-bootstrap/symboltablebuilder/scope.spice @@ -15,6 +15,8 @@ type ScopeType enum { WHILE_BODY, FOR_BODY, FOREACH_BODY, + CASE_BODY, + DEFAULT_BODY, UNSAFE_BODY, ANONYMOUS_BLOCK_BODY } diff --git a/src-bootstrap/util/common-util.spice b/src-bootstrap/util/common-util.spice index c1e61e4bc..9c211a7a0 100644 --- a/src-bootstrap/util/common-util.spice +++ b/src-bootstrap/util/common-util.spice @@ -1,7 +1,49 @@ -public f getLastFragment(String &haystack, const string needle) { +/** + * Split the given haystack by the needle and return the last fragment + * + * @param haystack Input string + * @param needle String to search + * @return Last fragment + */ +public f getLastFragment(const String &haystack, const string needle) { const unsigned long index = haystack.rfind(needle); if index == -1l { return haystack; } return haystack.getSubstring(index + getRawLength(needle)); +} + +/** + * Generate a circular import message from the given source files + * + * @param sourceFiles Source files building the circular dependency chain + * @return Error message + */ +public f getCircularImportMessage(const Vector& sourceFiles) { + String message; + message += "*-----*\n"; + message += "| |\n"; + for unsigned long i = 0l; i < sourceFiles.getSize(); i++ { + const SourceFile* sourceFile = sourceFiles[i]; + if i != 0 { message += "| |\n"; } + message += "| "; + message += sourceFile.fileName; + message += "\n"; + } + message += "| |\n"; + message += "*-----*\n"; + return message; +} + +/** + * Generate the version info string for the Spice driver + * + * @return Version info string + */ +public f getVersionInfo() { + String versionString; + versionString += "spice version" + SPICE_VERSION + " " + SPICE_TARGET_OS + "/" + SPICE_TARGET_ARCH + "\n"; + versionString += "built by: " + SPICE_BUILT_BY + "\n\n"; + versionString += "(c) Marc Auberer 2021-2024"; + return versionString; } \ No newline at end of file diff --git a/src-bootstrap/util/compiler-warning.spice b/src-bootstrap/util/compiler-warning.spice index e0845d538..81d2efd59 100644 --- a/src-bootstrap/util/compiler-warning.spice +++ b/src-bootstrap/util/compiler-warning.spice @@ -4,15 +4,21 @@ import "../reader/code-loc"; public type CompilerWarningType enum { UNUSED_FUNCTION, UNUSED_PROCEDURE, + UNUSED_METHOD, UNUSED_STRUCT, UNUSED_INTERFACE, UNUSED_IMPORT, UNUSED_FIELD, + UNUSED_ENUM_ITEM, + UNUSED_ALIAS, UNUSED_VARIABLE, - INTERFACE_WITHOUT_SIGNATURE, + UNUSED_RETURN_VALUE, + UNREACHABLE_CODE, + SHADOWED_VARIABLE, + IDENTITY_CAST, SINGLE_GENERIC_TYPE_CONDITION, BOOL_ASSIGN_AS_CONDITION, - INDEX_EXCEEDS_ARRAY_SIZE, + ASYNC_LAMBDA_CAPTURE_RULE_VIOLATION, UNINSTALL_FAILED, VERIFIER_DISABLED } @@ -31,7 +37,7 @@ public type CompilerWarning struct { * @param warningType Type of the warning * @param message Warning message suffix */ -public p CompilerWarning.ctor(const CodeLoc* codeLoc, const CompilerWarningType warningType, const string message) { +public p CompilerWarning.ctor(const CodeLoc& codeLoc, const CompilerWarningType warningType, const string message) { this.warningMessage = "[Warning] " + codeLoc.toPrettyString() + ": " + this.getMessagePrefix(warningType) + ": " + message; } @@ -60,24 +66,26 @@ public p CompilerWarning.print() { * @return Prefix string for the warning type */ f CompilerWarning.getMessagePrefix(const CompilerWarningType warningType) { - if warningType == CompilerWarningType.UNUSED_FUNCTION { return "Unused function"; } - if warningType == CompilerWarningType.UNUSED_PROCEDURE { return "Unused procedure"; } - if warningType == CompilerWarningType.UNUSED_STRUCT { return "Unused struct"; } - if warningType == CompilerWarningType.UNUSED_INTERFACE { return "Unused interface"; } - if warningType == CompilerWarningType.UNUSED_IMPORT { return "Unused import"; } - if warningType == CompilerWarningType.UNUSED_FIELD { return "Unused field"; } - if warningType == CompilerWarningType.UNUSED_VARIABLE { return "Unused variable"; } - if warningType == CompilerWarningType.INTERFACE_WITHOUT_SIGNATURE { return "Interface without signature"; } - if warningType == CompilerWarningType.SINGLE_GENERIC_TYPE_CONDITION { return "Only one type condition"; } - if warningType == CompilerWarningType.BOOL_ASSIGN_AS_CONDITION { return "Bool assignment as condition"; } - if warningType == CompilerWarningType.INDEX_EXCEEDS_ARRAY_SIZE { return "Array index exceeds its size"; } - if warningType == CompilerWarningType.UNINSTALL_FAILED { return "Uninstall failed"; } - if warningType == CompilerWarningType.VERIFIER_DISABLED { return "Verifier disabled"; } - return "Unknown warning"; -} - -f main() { - const CodeLoc codeLoc = CodeLoc(2l, 3l); - CompilerWarning warning = CompilerWarning(&codeLoc, CompilerWarningType.UNUSED_STRUCT, "Dies ist ein Test"); - warning.print(); + switch warningType { + case CompilerWarningType::UNUSED_FUNCTION: { return "Unused function"; } + case CompilerWarningType::UNUSED_PROCEDURE: { return "Unused procedure"; } + case CompilerWarningType::UNUSED_METHOD: { return "Unused method"; } + case CompilerWarningType::UNUSED_STRUCT: { return "Unused struct"; } + case CompilerWarningType::UNUSED_INTERFACE: { return "Unused interface"; } + case CompilerWarningType::UNUSED_IMPORT: { return "Unused import"; } + case CompilerWarningType::UNUSED_FIELD: { return "Unused field"; } + case CompilerWarningType::UNUSED_ENUM_ITEM: { return "Unused enum item"; } + case CompilerWarningType::UNUSED_ALIAS: { return "Unused type alias"; } + case CompilerWarningType::UNUSED_VARIABLE: { return "Unused variable"; } + case CompilerWarningType::UNUSED_RETURN_VALUE: { return "Unused return value"; } + case CompilerWarningType::UNREACHABLE_CODE: { return "Unreachable code detected"; } + case CompilerWarningType::SHADOWED_VARIABLE: { return "Shadowed variable"; } + case CompilerWarningType::IDENTITY_CAST: { return "Identity cast"; } + case CompilerWarningType::SINGLE_GENERIC_TYPE_CONDITION: { return "Only one type condition"; } + case CompilerWarningType::BOOL_ASSIGN_AS_CONDITION: { return "Bool assignment as condition"; } + case CompilerWarningType::ASYNC_LAMBDA_CAPTURE_RULE_VIOLATION: { return "Lambda violates async lambda capture rule"; } + case CompilerWarningType::UNINSTALL_FAILED: { return "Uninstall failed"; } + case CompilerWarningType::VERIFIER_DISABLED: { return "Verifier disabled"; } + default: { return "Unknown warning"; } + } } \ No newline at end of file diff --git a/src/typechecker/TypeChecker.cpp b/src/typechecker/TypeChecker.cpp index 1de79595a..7f81b8e43 100644 --- a/src/typechecker/TypeChecker.cpp +++ b/src/typechecker/TypeChecker.cpp @@ -153,6 +153,7 @@ std::any TypeChecker::visitForeachLoop(ForeachLoopNode *node) { AssignExprNode *iteratorNode = node->iteratorAssign(); SymbolType iteratorOrIterableType = std::any_cast(visit(iteratorNode)).type; HANDLE_UNRESOLVED_TYPE_PTR(iteratorOrIterableType) + iteratorOrIterableType = iteratorOrIterableType.removeReferenceWrapper(); // Retrieve iterator type SymbolType iteratorType = iteratorOrIterableType; diff --git a/std/data/hash-table.spice b/std/data/hash-table.spice index 62ac2c0c4..dab32bdc6 100644 --- a/std/data/hash-table.spice +++ b/std/data/hash-table.spice @@ -1,62 +1,62 @@ import "std/data/vector"; import "std/data/linked-list"; -import "std/data/pair"; +import "std/data/optional"; import "std/math/hash"; // Generic types for key and value type K dyn; type V dyn; +type HashEntry struct { + K key + V value +} + public type HashTable struct { - Vector>> table - unsigned long bucketCount + Vector>> table } public p HashTable.ctor(unsigned long bucketCount = 100l) { - this.bucketCount = bucketCount; - this.table = Vector>>(bucketCount); + this.table = Vector>>(bucketCount); + for unsigned long i = 0l; i < bucketCount; i++ { + this.table.pushBack(LinkedList>()); + } } -public p HashTable.insert(const K& key, const V& value) { +public p HashTable.upsert(const K& key, const V& value) { const unsigned long index = this.hash(key); - LinkedList>& bucket = this.table.get(index); - foreach Pair& pair : bucket { - if pair.getFirst() == key { - pair.setSecond(value); + const LinkedList>& list = this.table.get(index); + foreach const HashEntry& entry : list { + if (entry.key == key) { + entry.value = value; return; } } - this.table.pushBack(LinkedList>()); - LinkedList>& bucket = this.table.back(); - bucket.pushBack(Pair(key, value)); } -public p HashTable.delete(const K& key) { +public f> HashTable.get(const K& key) { const unsigned long index = this.hash(key); - const LinkedList>& bucket = this.table.at(index); - - for unsigned long i = 0l; i < bucket.getSize(); i++ { - Pair& candidate = bucket.at(i); - if candidate.getFirst() == key { - bucket.remove(i); - return; + const LinkedList>& list = this.table.get(index); + foreach const HashEntry& entry : list { + if (entry.key == key) { + return Optional(entry.value); } } + return Optional(); } -public f HashTable.get(const K& key) { - unsigned long index = this.hash(key); - const LinkedList>& bucket = this.table.at(index); - - for unsigned long i = 0l; i < bucket.getSize(); i++ { - Pair& candidate = bucket.at(i); - if candidate.getFirst() == key { - return &candidate.getSecond(); +public p HashTable.remove(const K& key) { + const unsigned long index = this.hash(key); + LinkedList>& list = this.table.get(index); + for (unsigned long i = 0l; i < list.getSize(); i++) { + if (list.get(i).key == key) { + list.remove(i); + return; } } - return nil; } inline f HashTable.hash(const K& key) { - return hash(key) % this.bucketCount; + K keyCopy = key; + return hash(keyCopy) % this.table.getSize(); } \ No newline at end of file diff --git a/std/data/linked-list.spice b/std/data/linked-list.spice index 513de1758..757991d51 100644 --- a/std/data/linked-list.spice +++ b/std/data/linked-list.spice @@ -41,6 +41,8 @@ public type LinkedList struct : IIterable { unsigned long size = 0l } +public p LinkedList.ctor() {} + public p LinkedList.pushBack(const T& value) { // Create new node heap Node* newNode = this.createNode(value); diff --git a/test/test-files/std/iterators/linked-list-iterators/cout.out b/test/test-files/std/iterators/linked-list-iterators/cout.out new file mode 100644 index 000000000..cebe8db0e --- /dev/null +++ b/test/test-files/std/iterators/linked-list-iterators/cout.out @@ -0,0 +1 @@ +All assertions passed! \ No newline at end of file diff --git a/test/test-files/std/iterators/linked-list-iterators/source.spice b/test/test-files/std/iterators/linked-list-iterators/source.spice new file mode 100644 index 000000000..c61a01ecc --- /dev/null +++ b/test/test-files/std/iterators/linked-list-iterators/source.spice @@ -0,0 +1,69 @@ +import "std/data/linked-list"; +import "std/data/pair"; + +f main() { + // Create test vector to iterate over + LinkedList lll = LinkedList(); + lll.pushBack(123l); + lll.pushBack(4321l); + lll.pushBack(9876l); + assert lll.getSize() == 3; + + // Test base functionality + dyn it = lll.getIterator(); + assert it.isValid(); + assert it.get() == 123l; + assert it.get() == 123l; + it.next(); + assert it.get() == 4321l; + assert it.isValid(); + it.next(); + dyn pair = it.getIdx(); + assert pair.getFirst() == 2; + assert pair.getSecond() == 9876l; + it.next(); + assert !it.isValid(); + + // Add new items to the vector + lll.pushBack(321l); + lll.pushBack(-99l); + assert it.isValid(); + + // Test overloaded operators + it -= 3; + assert it.get() == 123l; + assert it.isValid(); + it++; + assert it.get() == 4321l; + it--; + assert it.get() == 123l; + it += 4; + assert it.get() == -99l; + it.next(); + assert !it.isValid(); + + // Test foreach value + foreach long item : lll { + item++; + } + assert lll.get(0) == 123l; + assert lll.get(1) == 4321l; + assert lll.get(2) == 9876l; + + // Test foreach ref + foreach long& item : lll.getIterator() { + item++; + } + assert lll.get(0) == 124l; + assert lll.get(1) == 4322l; + assert lll.get(2) == 9877l; + + foreach long idx, long& item : lll { + item += idx; + } + assert lll.get(0) == 124l; + assert lll.get(1) == 4323l; + assert lll.get(2) == 9879l; + + printf("All assertions passed!"); +} \ No newline at end of file