Skip to content

Commit

Permalink
Extend HashTable std (#491)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Mar 6, 2024
1 parent 688f730 commit eb38e30
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .run/spice.run.xml
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -O0 -d ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="build -O0 -d -g ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="LLVM_BUILD_INCLUDE_DIR" value="D:/LLVM/build-release/include" />
<env name="LLVM_INCLUDE_DIR" value="D:/LLVM/llvm/include" />
Expand Down
22 changes: 10 additions & 12 deletions media/test-project/test.spice
@@ -1,17 +1,15 @@
import "std/os/env";
import "bootstrap/lexer/lexer";
import "std/data/hash-table";

f<int> 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<int, int> hashTable = HashTable<int, int>();
hashTable.upsert(1, 2);
hashTable.upsert(2, 3);

Optional<int> value = hashTable.get(1);
assert(value.get() == 2);
value = hashTable.get(2);
assert(value.get() == 3);

}

/*import "bootstrap/util/block-allocator";
Expand Down
2 changes: 2 additions & 0 deletions src-bootstrap/symboltablebuilder/scope.spice
Expand Up @@ -15,6 +15,8 @@ type ScopeType enum {
WHILE_BODY,
FOR_BODY,
FOREACH_BODY,
CASE_BODY,
DEFAULT_BODY,
UNSAFE_BODY,
ANONYMOUS_BLOCK_BODY
}
Expand Down
44 changes: 43 additions & 1 deletion src-bootstrap/util/common-util.spice
@@ -1,7 +1,49 @@
public f<String> 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<String> 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<String> getCircularImportMessage(const Vector<const SourceFile*>& 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<String> 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;
}
54 changes: 31 additions & 23 deletions src-bootstrap/util/compiler-warning.spice
Expand Up @@ -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
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -60,24 +66,26 @@ public p CompilerWarning.print() {
* @return Prefix string for the warning type
*/
f<string> 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<int> 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"; }
}
}
1 change: 1 addition & 0 deletions src/typechecker/TypeChecker.cpp
Expand Up @@ -153,6 +153,7 @@ std::any TypeChecker::visitForeachLoop(ForeachLoopNode *node) {
AssignExprNode *iteratorNode = node->iteratorAssign();
SymbolType iteratorOrIterableType = std::any_cast<ExprResult>(visit(iteratorNode)).type;
HANDLE_UNRESOLVED_TYPE_PTR(iteratorOrIterableType)
iteratorOrIterableType = iteratorOrIterableType.removeReferenceWrapper();

// Retrieve iterator type
SymbolType iteratorType = iteratorOrIterableType;
Expand Down
62 changes: 31 additions & 31 deletions 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<K, V> struct {
K key
V value
}

public type HashTable<K, V> struct {
Vector<LinkedList<Pair<K, V>>> table
unsigned long bucketCount
Vector<LinkedList<HashEntry<K, V>>> table
}

public p HashTable.ctor(unsigned long bucketCount = 100l) {
this.bucketCount = bucketCount;
this.table = Vector<LinkedList<Pair<K, V>>>(bucketCount);
this.table = Vector<LinkedList<HashEntry<K, V>>>(bucketCount);
for unsigned long i = 0l; i < bucketCount; i++ {
this.table.pushBack(LinkedList<HashEntry<K, V>>());
}
}

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<Pair<K, V>>& bucket = this.table.get(index);
foreach Pair<K, V>& pair : bucket {
if pair.getFirst() == key {
pair.setSecond(value);
const LinkedList<HashEntry<K, V>>& list = this.table.get(index);
foreach const HashEntry<K, V>& entry : list {
if (entry.key == key) {
entry.value = value;
return;
}
}
this.table.pushBack(LinkedList<Pair<K, V>>());
LinkedList<Pair<K, V>>& bucket = this.table.back();
bucket.pushBack(Pair<K, V>(key, value));
}

public p HashTable.delete(const K& key) {
public f<Optional<V>> HashTable.get(const K& key) {
const unsigned long index = this.hash(key);
const LinkedList<Pair<K, V>>& bucket = this.table.at(index);

for unsigned long i = 0l; i < bucket.getSize(); i++ {
Pair<K, V>& candidate = bucket.at(i);
if candidate.getFirst() == key {
bucket.remove(i);
return;
const LinkedList<HashEntry<K, V>>& list = this.table.get(index);
foreach const HashEntry<K, V>& entry : list {
if (entry.key == key) {
return Optional<V>(entry.value);
}
}
return Optional<V>();
}

public f<V*> HashTable.get(const K& key) {
unsigned long index = this.hash(key);
const LinkedList<Pair<K, V>>& bucket = this.table.at(index);

for unsigned long i = 0l; i < bucket.getSize(); i++ {
Pair<K, V>& 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<HashEntry<K, V>>& 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<V*>;
}

inline f<unsigned long> HashTable.hash(const K& key) {
return hash(key) % this.bucketCount;
K keyCopy = key;
return hash(keyCopy) % this.table.getSize();
}
2 changes: 2 additions & 0 deletions std/data/linked-list.spice
Expand Up @@ -41,6 +41,8 @@ public type LinkedList<T> struct : IIterable<T> {
unsigned long size = 0l
}

public p LinkedList.ctor() {}

public p LinkedList.pushBack(const T& value) {
// Create new node
heap Node<T>* newNode = this.createNode(value);
Expand Down
@@ -0,0 +1 @@
All assertions passed!
69 changes: 69 additions & 0 deletions test/test-files/std/iterators/linked-list-iterators/source.spice
@@ -0,0 +1,69 @@
import "std/data/linked-list";
import "std/data/pair";

f<int> main() {
// Create test vector to iterate over
LinkedList<long> lll = LinkedList<long>();
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!");
}

0 comments on commit eb38e30

Please sign in to comment.