Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend HashTable std #491

Merged
merged 1 commit into from Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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!
@@ -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!");
}