Skip to content

Commit

Permalink
Prelude: Add Map#remove method (#520)
Browse files Browse the repository at this point in the history
  • Loading branch information
kengorab authored Dec 19, 2024
1 parent c629605 commit 0da01d2
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 78 deletions.
49 changes: 37 additions & 12 deletions projects/compiler/example.abra
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
// val chars = "a£→😀".chars()
val m = {
a: 1,
b: 2,
c: 3,
s: 4
}
println(m)

// /// Expect: a 97 [0b1100001]
// /// Expect: £ 163 [0b11000010, 0b10100011]
// /// Expect: → 65515 [0b11101111, 0b10111111, 0b10101011]
// /// Expect: 😀 128512 [0b11110000, 0b10011111, 0b10011000, 0b10000000]
// for ch in chars {
// println(ch, ch.asInt(), ch.bytes().map(b => b.binary()))
// }
// println("a", m._getKeyHash("a", m._entries._capacity))
// println("b", m._getKeyHash("b", m._entries._capacity))
// println("c", m._getKeyHash("c", m._entries._capacity))
// println("d", m._getKeyHash("d", m._entries._capacity))
// println("e", m._getKeyHash("e", m._entries._capacity))
// println("f", m._getKeyHash("f", m._entries._capacity))
// println("g", m._getKeyHash("g", m._entries._capacity))
// println("h", m._getKeyHash("h", m._entries._capacity))
// println("i", m._getKeyHash("i", m._entries._capacity))
// println("j", m._getKeyHash("j", m._entries._capacity))
// println("k", m._getKeyHash("k", m._entries._capacity))
// println("l", m._getKeyHash("l", m._entries._capacity))
// println("m", m._getKeyHash("m", m._entries._capacity))
// println("n", m._getKeyHash("n", m._entries._capacity))
// println("o", m._getKeyHash("o", m._entries._capacity))
// println("p", m._getKeyHash("p", m._entries._capacity))
// println("q", m._getKeyHash("q", m._entries._capacity))
// println("r", m._getKeyHash("r", m._entries._capacity))
// println("s", m._getKeyHash("s", m._entries._capacity))
// println("t", m._getKeyHash("t", m._entries._capacity))
// println("u", m._getKeyHash("u", m._entries._capacity))
// println("v", m._getKeyHash("v", m._entries._capacity))
// println("w", m._getKeyHash("w", m._entries._capacity))
// println("x", m._getKeyHash("x", m._entries._capacity))
// println("y", m._getKeyHash("y", m._entries._capacity))
// println("z", m._getKeyHash("z", m._entries._capacity))

// val ch = Char.fromInt(0xD800)
// /// Expect: �
// println(ch)
println(m.remove("d"))
println(m)

println(0.hex())
println(m.remove("s"))
println(m)
3 changes: 1 addition & 2 deletions projects/compiler/src/typechecker.abra
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ export type ModuleLoader {
if !invalidated return Ok(m)
}

val parsedModule = if self._loadFileContents(modulePath) |contents| { //match fs.readFile(modulePath) {
// Ok(contents) => {
val parsedModule = if self._loadFileContents(modulePath) |contents| {
match Lexer.tokenize(contents) {
Ok(tokens) => {
val module = match Parser.parse(tokens) {
Expand Down
136 changes: 81 additions & 55 deletions projects/compiler/test/compiler/maps.abra
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Test raw construction of map
// (() => {
func test_mapRawConstruction() {
(() => {
val m0: Map<Int, String> = Map.new()
/// Expect: {}
println(m0)
Expand All @@ -22,13 +21,10 @@ func test_mapRawConstruction() {
m3.insert(m3._capacity + 1, "world")
/// Expect: { 1: hello, 17: world }
println(m3)
// })()
}
test_mapRawConstruction()
})()

// Test map literal construction
// (() => {
func test_mapLiteralConstruction() {
(() => {
val m0: Map<Int, String> = {}
/// Expect: {}
println(m0)
Expand All @@ -45,13 +41,10 @@ func test_mapLiteralConstruction() {
val m3 = { (1): "hello", (17): "world" }
/// Expect: { 1: hello, 17: world }
println(m3)
// })()
}
test_mapLiteralConstruction()
})()

// == operator (also Map#eq)
// (() => {
func test_mapEq() {
(() => {
val map = { a: "abc", b: "def" }

/// Expect: true
Expand All @@ -73,68 +66,53 @@ func test_mapEq() {
// println(map == #{1, 2, 3})
// /// Expect: false
// println(map == { (1): "a", (2): "b" })
// })()
}
test_mapEq()
})()

// Map.fromPairs
// (() => {
func test_mapFromPairs() {
(() => {
// TODO: The typechecker won't recognize this if it's inlined for some reason
val pairs = [("a", 1), ("b", 2), ("c", 3)]
val m = Map.fromPairs(pairs)

/// Expect: { c: 3, b: 2, a: 1 }
println(m)
// })()
}
test_mapFromPairs()
})()

// Map#keys
// (() => {
func test_mapKeys() {
(() => {
val map1 = { a: 1, b: 2 }
/// Expect: #{b, a}
println(map1.keys())

val map2 = { ((1, 1)): true, ((0, 1)): false }
/// Expect: #{(0, 1), (1, 1)}
println(map2.keys())
// })()
}
test_mapKeys()
})()

// Map#values
// (() => {
func test_mapValues() {
(() => {
val map1 = { a: 1, b: 2 }
/// Expect: [2, 1]
println(map1.values())

val map2 = { ((1, 1)): true, ((0, 1)): false }
/// Expect: [false, true]
println(map2.values())
// })()
}
test_mapValues()
})()

// Map#entries
// (() => {
func test_mapEntries() {
(() => {
val map1 = { a: 1, b: 2 }
/// Expect: #{("b", 2), ("a", 1)}
println(map1.entries())

val map2 = { ((1, 1)): true, ((0, 1)): false }
/// Expect: #{((1, 1), true), ((0, 1), false)}
println(map2.entries())
// })()
}
test_mapEntries()
})()

// Map#iterator
// (() => {
func test_mapIterator() {
(() => {
val m = { a: 1, b: 2, c: 3 }
val iter = m.iterator()

Expand All @@ -148,9 +126,7 @@ func test_mapIterator() {
println(iter.next())
/// Expect: Option.None
println(iter.next())
// })()
}
test_mapIterator()
})()

// For-loops
/// Expect: ("c", 3) 0
Expand All @@ -159,17 +135,14 @@ test_mapIterator()
for ch, idx in { a: 1, b: 2, c: 3 } { println(ch, idx) }

// Map#containsKey
// (() => {
func test_mapContainsKey() {
(() => {
val m = { (1): "a", (2): "b" }

/// Expect: true
println(m.containsKey(1))
/// Expect: false
println(m.containsKey(3))
// })()
}
test_mapContainsKey()
})()

// Map#mapValues
(() => {
Expand All @@ -185,8 +158,7 @@ test_mapContainsKey()
})()

// Map#insert (also []= operator)
// (() => {
func test_mapInsert() {
(() => {
val m = { a: 1, b: 2 }
m.insert("c", 3)
m["d"] = 4
Expand All @@ -202,13 +174,10 @@ func test_mapInsert() {
println(m)
/// Expect: false
println(m._needsResize())
// })()
}
test_mapInsert()
})()

// Map#get
// (() => {
func test_mapGet() {
(() => {
val m = { (1): "a", (2): "b" }

/// Expect: Option.Some(value: "a")
Expand All @@ -217,9 +186,7 @@ func test_mapGet() {
println(m.get(2))
/// Expect: Option.None
println(m.get(3))
// })()
}
test_mapGet()
})()

// Map#getOr
(() => {
Expand Down Expand Up @@ -274,3 +241,62 @@ test_mapGet()
/// Expect: Option.None
println(old2)
})()

// Map#remove
(() => {
val m = {
a: 1,
b: 2,
c: 3,
d: 4,
s: 5,
t: 6
}
/// Expect: { d: 4, t: 6, c: 3, s: 5, b: 2, a: 1 }
println(m)

/// Expect: Option.None
println(m.remove("x"))
/// Expect: 6
println(m.size)

/// Expect: Option.Some(value: 2)
println(m.remove("b"))
/// Expect: { d: 4, t: 6, c: 3, s: 5, a: 1 }
println(m)
/// Expect: 5
println(m.size)

// Kinda dirty, but needed in order to test removing keys with the same hash
/// Expect: true
println(m._getKeyHash("c", m._entries._capacity) == m._getKeyHash("s", m._entries._capacity))

/// Expect: Option.Some(value: 5)
println(m.remove("s"))
/// Expect: { d: 4, t: 6, c: 3, a: 1 }
println(m)
/// Expect: 4
println(m.size)
/// Expect: Option.Some(value: 3)
println(m.remove("c"))
/// Expect: { d: 4, t: 6, a: 1 }
println(m)
/// Expect: 3
println(m.size)

/// Expect: true
println(m._getKeyHash("d", m._entries._capacity) == m._getKeyHash("t", m._entries._capacity))

/// Expect: Option.Some(value: 4)
println(m.remove("d"))
/// Expect: { t: 6, a: 1 }
println(m)
/// Expect: 2
println(m.size)
/// Expect: Option.Some(value: 6)
println(m.remove("t"))
/// Expect: { a: 1 }
println(m)
/// Expect: 1
println(m.size)
})()
54 changes: 45 additions & 9 deletions projects/std/src/prelude.abra
Original file line number Diff line number Diff line change
Expand Up @@ -1181,10 +1181,23 @@ type Set<T> {
}
}

// @Intrinsic("uninitialized")
// func uninitialized<T>(): T

type MapEntry<K, V> {
key: K
value: V
next: MapEntry<K, V>? = None
// _empty: Bool = false

// func empty<K, V>(): MapEntry<K, V> {
// MapEntry(
// key: intrinsics.uninitialized(),
// value: intrinsics.uninitialized(),
// next: None
// _empty: true
// )
// }
}

type MapIterator<K, V> {
Expand Down Expand Up @@ -1390,14 +1403,13 @@ type Map<K, V> {
func _getEntry(self, key: K): MapEntry<K, V>? {
val hash = self._getKeyHash(key, self._entries._capacity)

if flattenOption(self._entries[hash]) |bucket| {
var cursor: MapEntry<K, V>? = Some(bucket)
while cursor |entry| {
if entry.key == key {
return Some(entry)
} else {
cursor = entry.next
}
val bucketRootEntry = if flattenOption(self._entries[hash]) |b| b else return None
var cursor = Some(bucketRootEntry)
while cursor |entry| {
if entry.key == key {
return Some(entry)
} else {
cursor = entry.next
}
}

Expand Down Expand Up @@ -1430,5 +1442,29 @@ type Map<K, V> {
}
}

@Stub func remove(self, key: K): V?
func remove(self, key: K): V? {
val hash = self._getKeyHash(key, self._entries._capacity)

val bucketRootEntry = if flattenOption(self._entries[hash]) |e| e else return None
if bucketRootEntry.key == key {
self._entries[hash] = bucketRootEntry.next
self.size -= 1
return Some(bucketRootEntry.value)
}

var prev = bucketRootEntry
var cursor = bucketRootEntry.next
while cursor |entry| {
if entry.key == key {
prev.next = entry.next
self.size -= 1
return Some(entry.value)
} else {
prev = entry
cursor = entry.next
}
}

None
}
}

0 comments on commit 0da01d2

Please sign in to comment.