compiler/
lib/
examples/
std/
arch/
rv64/
tests/
abi.sizes.rad
3.4 KiB
aggregate.return.rad
4.0 KiB
arith.assignment.rad
580 B
arith.basic.rad
176 B
arith.modulo.rad
96 B
arith.subword.rad
3.8 KiB
arith.sum.rad
177 B
arith.w64.rad
4.1 KiB
array.assign.rad
221 B
array.bounds.check.rad
321 B
array.index.assign.rad
367 B
array.index.rad
249 B
array.length.rad
262 B
array.math.rad
1.2 KiB
array.nested.assign.rad
319 B
array.nested.rad
325 B
array.record.elements.rad
1.7 KiB
array.repeat.edge.rad
548 B
array.repeat.rad
828 B
array.return.rad
330 B
array.slice.empty.rad
108 B
array.slice.gen.end.rad
126 B
array.slice.gen.index.rad
139 B
array.slice.gen.open.rad
125 B
array.slice.gen.start.end.rad
127 B
array.slice.gen.start.rad
126 B
array.slice.rad
759 B
as.precedence.rad
212 B
assert.basic.rad
387 B
assert.fail.rad
138 B
assert.false.rad
140 B
assert.true.rad
100 B
assign.mutable.rad
6.1 KiB
assign.rad
129 B
assign.shadow.mutable.rad
461 B
binop.bitwise.rad
1.2 KiB
binop.cmp.rad
426 B
bool.comparison.array.rad
688 B
bool.comparison.nested.gen.rad
1.0 KiB
bool.comparison.opt.rad
905 B
bool.comparison.record.gen.rad
1.0 KiB
bool.comparison.record.rad
1.1 KiB
bool.comparison.slice.gen.rad
157 B
bool.comparison.slice.rad
4.1 KiB
bool.comparison.slice.record.gen.rad
2.0 KiB
bool.comparison.slice.union.gen.rad
2.5 KiB
bool.comparison.union.ctor.rad
690 B
bool.comparison.union.gen.rad
1.2 KiB
bool.comparison.union.record.gen.rad
1.5 KiB
bool.comparison.union.simple.gen.rad
281 B
bool.operators.complex.rad
384 B
bool.operators.rad
831 B
bool.short.circuit.rad
2.3 KiB
bool.simple.rad
194 B
bool.values.rad
772 B
builtin.size.align.rad
1.3 KiB
builtin.sliceof.mut.rad
606 B
builtin.sliceof.rad
505 B
call.arg.clobber.rad
717 B
call.basic.rad
241 B
call.clobber.rad
462 B
cast.same.size.rad
1.0 KiB
casting.numbers.rad
1.5 KiB
char.literal.rad
165 B
compound.assign.field.rad
285 B
compound.assign.rad
1.1 KiB
cond.assign.rad
723 B
cond.expr.aggregate.rad
1.1 KiB
cond.expr.rad
1.8 KiB
cond.for.else.break.rad
326 B
cond.for.indexed.rad
238 B
cond.for.rad
165 B
cond.for.range.indexed.rad
526 B
cond.for.range.rad
171 B
cond.for.unsigned.range.rad
644 B
cond.forever.break.continue.rad
182 B
cond.forever.break.rad
232 B
cond.fused.rad
926 B
cond.if.case.rad
2.2 KiB
cond.if.else.min.rad
143 B
cond.if.else.rad
225 B
cond.if.elseif.rad
420 B
cond.if.noelse.rad
120 B
cond.if.rad
865 B
cond.match.fallthrough.rad
369 B
cond.match.guard.rad
1.4 KiB
cond.match.guard.regalloc.rad
1.3 KiB
cond.while.else.break.rad
282 B
cond.while.rad
119 B
const.array.copy.mutate.rad
386 B
const.array.rad
195 B
const.basic.rad
325 B
const.char.rad
159 B
const.fn.array.rad
664 B
const.record.array.rad
1.2 KiB
const.record.array.simple.rad
523 B
const.record.ctor.rad
170 B
const.record.fn.rad
353 B
const.record.rad
182 B
const.slice.param.rad
333 B
const.union.payload.ctor.rad
349 B
const.union.record.literal.rad
359 B
data.array.rad
767 B
data.bool.rad
216 B
data.i16.rad
261 B
data.i32.rad
281 B
data.i8.rad
248 B
data.record.rad
561 B
data.simple.rad
436 B
data.u16.rad
220 B
data.u32.rad
240 B
data.u8.rad
208 B
data.union.rad
886 B
debug.tag.rad
557 B
edge.cases.2.rad
337 B
edge.cases.3.rad
594 B
edge.cases.4.rad
1.2 KiB
edge.cases.5.rad
1.0 KiB
edge.cases.6.rad
2.6 KiB
edge.cases.7.addr.bug.rad
224 B
edge.cases.8.bug.rad
508 B
edge.cases.rad
223 B
error.basic.rad
159 B
error.catch.rad
1.6 KiB
error.division.zero.rad
164 B
error.modulo.zero.rad
162 B
error.multi.basic.rad
672 B
error.multi.catch.rad
772 B
error.multi.catch.typed.binding.rad
791 B
error.multi.catch.typed.catchall.rad
1.0 KiB
error.multi.catch.typed.rad
1.1 KiB
error.multi.propagate.multi.rad
953 B
error.multi.propagate.rad
825 B
error.multi.try.optional.rad
507 B
error.slice.bounds.rad
219 B
error.try.bang.success.rad
370 B
error.try.catch.binding.rad
2.0 KiB
error.try.optional.rad
1.8 KiB
error.try.rad
4.0 KiB
fn.block.scope.rad
508 B
fn.callback.nested.rad
1.2 KiB
fn.default.rad
131 B
fn.local.rad
140 B
fn.recursion.2.rad
239 B
fn.void.rad
150 B
for.else.continue.rad
1.1 KiB
frame.large.rad
567 B
if-let-mut.rad
1.1 KiB
iflet.shadow.leak.rad
317 B
integer.bitwise.basic.rad
693 B
integer.overflow.rad
1.8 KiB
large.blit.store.rad
2.1 KiB
let.guard.rad
1.9 KiB
literal.w64.rad
1.7 KiB
loc.addr.offset.bug.rad
410 B
loc.addr.opt.to.opt.rad
433 B
loc.addr.optional.assign.rad
408 B
loc.addr.record.assign.rad
443 B
loop.complex.flow.rad
1007 B
loop.sealblock.rad
911 B
match.array.rad
3.4 KiB
match.char.rad
1.6 KiB
match.multi.seal.rad
987 B
match.multi.survive.rad
1.6 KiB
match.mutref.push.rad
1.0 KiB
match.mutref.union.rad
662 B
match.nested.call.rad
1.7 KiB
match.nested.deep.rad
2.2 KiB
match.nested.deref.rad
3.7 KiB
match.nested.guard.rad
1.6 KiB
match.nested.iflet.guard.rad
1.6 KiB
match.nested.iflet.rad
1.4 KiB
match.nested.letelse.rad
813 B
match.nested.letelse.union.rad
1.3 KiB
match.nested.literal.rad
3.1 KiB
match.nested.multi.rad
2.4 KiB
match.nested.pattern.rad
5.2 KiB
match.nested.record.rad
2.0 KiB
match.nested.union.rad
2.3 KiB
match.nested.whilelet.rad
2.4 KiB
match.string.rad
1.8 KiB
match.value.copy.rad
2.0 KiB
match.void.then.or.rad
1.6 KiB
memzero.result.bug.rad
806 B
memzero.union.bug.rad
576 B
mutref.loop.bug.rad
1.8 KiB
opt.assignment.bug.rad
1.3 KiB
opt.bug.test.rad
1.4 KiB
opt.if.let.complex.rad
6.2 KiB
opt.if.let.guard.rad
809 B
opt.if.let.rad
956 B
opt.nil.check.rad
1.5 KiB
opt.record.eq.rad
842 B
opt.record.rad
655 B
opt.return.array.rad
289 B
opt.return.nested.rad
797 B
opt.return.record.rad
344 B
opt.slice.npo.rad
2.8 KiB
opt.type.rad
200 B
opt.while.let.complex.rad
404 B
panic.rad
111 B
placeholder.basic.rad
133 B
placeholder.comprehensive.rad
562 B
pointer.copy.edge.case.rad
1.3 KiB
pointer.slice.index.rad
269 B
pointer.slice.store.rad
881 B
prog.ackermann.rad
5.0 KiB
prog.bignum.rad
9.4 KiB
prog.binsearch.rad
2.4 KiB
prog.bubblesort.rad
2.0 KiB
prog.cordic.rad
6.9 KiB
prog.crc32.rad
2.7 KiB
prog.dijkstra.rad
7.7 KiB
prog.eval.rad
6.2 KiB
prog.hanoi.rad
3.8 KiB
prog.huffman.rad
9.3 KiB
prog.hybridsort.rad
3.0 KiB
prog.linkedlist.rad
5.8 KiB
prog.lzw.rad
6.7 KiB
prog.matmul.rad
2.9 KiB
prog.mersenne.rad
5.2 KiB
prog.nqueens.rad
3.4 KiB
prog.rbtree.rad
8.2 KiB
prog.regex.rad
10.2 KiB
prog.sha256.rad
7.0 KiB
prog.sieve.rad
2.8 KiB
prog.symtab.rad
10.1 KiB
prog.tokenizer.rad
13.8 KiB
prog.vm.rad
17.4 KiB
ptr.assign.rad
137 B
ptr.deref.rad
622 B
ptr.eq.rad
966 B
ptr.mutate.rad
244 B
ptr.opaque.rad
1.4 KiB
record.access.rad
285 B
record.alignment.rad
179 B
record.array.elements.rad
1.7 KiB
record.copy.rad
2.0 KiB
record.field.assign.rad
184 B
record.nested.calls.2.rad
612 B
record.nested.calls.3.rad
734 B
record.param.lit.rad
353 B
record.ptr.access.rad
227 B
record.ptr.mutate.rad
243 B
record.shorthand.rad
1.5 KiB
record.unlabeled.rad
407 B
ref.if.bug.rad
519 B
ref.immut.loop.bug.rad
670 B
ref.mut.ptr.rad
261 B
regalloc.callee.save.rad
1.5 KiB
regalloc.spill.reuse.rad
473 B
reserve.loop.rad
392 B
result.void.success.rad
716 B
slice.alloc.loop.rad
788 B
slice.append.rad
3.3 KiB
slice.cap.rad
941 B
slice.delete.rad
971 B
slice.of.rad
460 B
slice.subslice.rad
1.4 KiB
spill.blockarg.clobber.rad
3.5 KiB
spill.loop.rad
1.6 KiB
stack.local.corrupt.rad
320 B
static.array.mutate.rad
387 B
static.basic.rad
327 B
static.fn.array.rad
628 B
static.record.array.rad
503 B
static.slice.index.assign.rad
408 B
static.slice.offset.rad
668 B
string.basic.rad
149 B
string.escape.rad
349 B
string.index.rad
116 B
switch.blockargs.clobber.rad
1.3 KiB
trait.aggregate.ret.rad
1.5 KiB
trait.array.optional.rad
1.7 KiB
trait.basic.rad
565 B
trait.control.flow.rad
1.1 KiB
trait.fn.param.rad
1.6 KiB
trait.multiple.methods.rad
1.2 KiB
trait.multiple.traits.rad
1.2 KiB
trait.multiple.types.rad
1.3 KiB
trait.supertrait.rad
2.5 KiB
trait.throws.rad
1.0 KiB
trait.writer.rad
2.6 KiB
type.unify.rad
4.5 KiB
undefined.rad
417 B
union-tag.rad
911 B
union.bitfield.rad
1.2 KiB
union.discriminant.cast.rad
389 B
union.edge.case.2.rad
679 B
union.edge.case.3.rad
608 B
union.mixed.assign.rad
977 B
union.payload.mutref.rad
1.4 KiB
union.payload.rad
580 B
union.record.forward.rad
1.3 KiB
union.void.match.rad
403 B
union.void.rad
824 B
unsigned.compare.rad
1.9 KiB
var.align.rad
1013 B
var.infer.rad
549 B
decode.rad
14.6 KiB
emit.rad
24.4 KiB
encode.rad
19.9 KiB
isel.rad
41.1 KiB
printer.rad
13.0 KiB
tests.rad
15.7 KiB
rv64.rad
13.0 KiB
collections/
lang/
sys/
arch.rad
65 B
collections.rad
36 B
fmt.rad
3.8 KiB
intrinsics.rad
206 B
io.rad
1.2 KiB
lang.rad
222 B
mem.rad
2.2 KiB
sys.rad
167 B
testing.rad
2.4 KiB
tests.rad
11.6 KiB
vec.rad
3.1 KiB
std.rad
231 B
scripts/
seed/
test/
vim/
.gitignore
353 B
.gitsigners
112 B
LICENSE
1.1 KiB
Makefile
3.1 KiB
README
2.5 KiB
std.lib
987 B
std.lib.test
252 B
lib/std/arch/rv64/tests/prog.symtab.rad
raw
| 1 | //! Symbol table. |
| 2 | //! Implement a multi-scope symbol table with hash-based lookup, scope |
| 3 | //! push/pop, and symbol resolution. Exercises: optionals, if-let, |
| 4 | //! while-let, let-else, for-in with indexing, records with pointer |
| 5 | //! fields, and complex interactions between data structures. |
| 6 | |
| 7 | const MAX_SYMBOLS: u32 = 256; |
| 8 | const MAX_SCOPES: u32 = 16; |
| 9 | const HASH_SIZE: u32 = 64; |
| 10 | const NIL: u32 = 0xFFFFFFFF; |
| 11 | |
| 12 | /// A symbol entry in the table. |
| 13 | record Symbol { |
| 14 | /// Name of the symbol (hash for comparison). |
| 15 | nameHash: u32, |
| 16 | /// The value associated with this symbol. |
| 17 | value: i32, |
| 18 | /// Scope depth at which this symbol was defined. |
| 19 | depth: u32, |
| 20 | /// Next symbol in hash chain. |
| 21 | next: u32, |
| 22 | /// Previous symbol with the same name (for shadowing). |
| 23 | shadow: u32, |
| 24 | } |
| 25 | |
| 26 | /// A scope boundary marker. |
| 27 | record ScopeMarker { |
| 28 | /// Number of symbols when scope was entered. |
| 29 | symbolCount: u32, |
| 30 | } |
| 31 | |
| 32 | /// The symbol table. |
| 33 | record SymTab { |
| 34 | symbols: *mut [Symbol], |
| 35 | symbolCount: u32, |
| 36 | scopes: *mut [ScopeMarker], |
| 37 | scopeDepth: u32, |
| 38 | buckets: *mut [u32], |
| 39 | } |
| 40 | |
| 41 | /// Simple string hash function. |
| 42 | fn hashName(name: *[u8]) -> u32 { |
| 43 | let mut h: u32 = 5381; |
| 44 | for ch in name { |
| 45 | h = ((h << 5) + h) + ch as u32; |
| 46 | } |
| 47 | return h; |
| 48 | } |
| 49 | |
| 50 | /// Initialize the symbol table. |
| 51 | fn init(tab: *mut SymTab) { |
| 52 | tab.symbolCount = 0; |
| 53 | tab.scopeDepth = 0; |
| 54 | |
| 55 | for i in 0..HASH_SIZE { |
| 56 | tab.buckets[i] = NIL; |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | /// Push a new scope. |
| 61 | fn pushScope(tab: *mut SymTab) { |
| 62 | tab.scopes[tab.scopeDepth] = ScopeMarker { symbolCount: tab.symbolCount }; |
| 63 | tab.scopeDepth += 1; |
| 64 | } |
| 65 | |
| 66 | /// Pop the current scope, removing all symbols defined in it. |
| 67 | fn popScope(tab: *mut SymTab) { |
| 68 | if tab.scopeDepth == 0 { |
| 69 | return; |
| 70 | } |
| 71 | tab.scopeDepth -= 1; |
| 72 | let marker = tab.scopes[tab.scopeDepth]; |
| 73 | |
| 74 | // Remove symbols added in this scope (in reverse order). |
| 75 | while tab.symbolCount > marker.symbolCount { |
| 76 | tab.symbolCount -= 1; |
| 77 | let sym = tab.symbols[tab.symbolCount]; |
| 78 | let bucket = sym.nameHash % HASH_SIZE; |
| 79 | |
| 80 | // Remove from hash chain. |
| 81 | tab.buckets[bucket] = sym.next; |
| 82 | |
| 83 | // Restore shadowed symbol if any. |
| 84 | if sym.shadow != NIL { |
| 85 | // The shadowed symbol is still in the symbols array; |
| 86 | // re-link it into the hash chain. |
| 87 | let shadowIdx = sym.shadow; |
| 88 | tab.symbols[shadowIdx].next = tab.buckets[bucket]; |
| 89 | tab.buckets[bucket] = shadowIdx; |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | /// Define a symbol in the current scope. |
| 95 | fn define(tab: *mut SymTab, name: *[u8], value: i32) -> u32 { |
| 96 | let h = hashName(name); |
| 97 | let bucket = h % HASH_SIZE; |
| 98 | |
| 99 | // Check for shadowed symbol with same name. |
| 100 | let mut shadowIdx: u32 = NIL; |
| 101 | let mut cur = tab.buckets[bucket]; |
| 102 | while cur != NIL { |
| 103 | if tab.symbols[cur].nameHash == h { |
| 104 | // Found existing symbol with same hash - shadow it. |
| 105 | // Remove it from hash chain first. |
| 106 | shadowIdx = cur; |
| 107 | // Remove the shadowed symbol from the bucket chain. |
| 108 | if tab.buckets[bucket] == cur { |
| 109 | tab.buckets[bucket] = tab.symbols[cur].next; |
| 110 | } |
| 111 | break; |
| 112 | } |
| 113 | cur = tab.symbols[cur].next; |
| 114 | } |
| 115 | |
| 116 | let idx = tab.symbolCount; |
| 117 | tab.symbols[idx] = Symbol { |
| 118 | nameHash: h, |
| 119 | value, |
| 120 | depth: tab.scopeDepth, |
| 121 | next: tab.buckets[bucket], |
| 122 | shadow: shadowIdx, |
| 123 | }; |
| 124 | tab.buckets[bucket] = idx; |
| 125 | tab.symbolCount += 1; |
| 126 | return idx; |
| 127 | } |
| 128 | |
| 129 | /// Look up a symbol by name. Returns the value if found. |
| 130 | fn lookup(tab: *SymTab, name: *[u8]) -> ?i32 { |
| 131 | let h = hashName(name); |
| 132 | let bucket = h % HASH_SIZE; |
| 133 | let mut cur = tab.buckets[bucket]; |
| 134 | |
| 135 | while cur != NIL { |
| 136 | if tab.symbols[cur].nameHash == h { |
| 137 | return tab.symbols[cur].value; |
| 138 | } |
| 139 | cur = tab.symbols[cur].next; |
| 140 | } |
| 141 | return nil; |
| 142 | } |
| 143 | |
| 144 | /// Update a symbol's value. Returns true if the symbol was found. |
| 145 | fn update(tab: *mut SymTab, name: *[u8], newValue: i32) -> bool { |
| 146 | let h = hashName(name); |
| 147 | let bucket = h % HASH_SIZE; |
| 148 | let mut cur = tab.buckets[bucket]; |
| 149 | |
| 150 | while cur != NIL { |
| 151 | if tab.symbols[cur].nameHash == h { |
| 152 | tab.symbols[cur].value = newValue; |
| 153 | return true; |
| 154 | } |
| 155 | cur = tab.symbols[cur].next; |
| 156 | } |
| 157 | return false; |
| 158 | } |
| 159 | |
| 160 | /// Test basic define and lookup. |
| 161 | fn testBasic(tab: *mut SymTab) -> i32 { |
| 162 | init(tab); |
| 163 | pushScope(tab); |
| 164 | |
| 165 | define(tab, "x", 10); |
| 166 | define(tab, "y", 20); |
| 167 | define(tab, "z", 30); |
| 168 | |
| 169 | let x = lookup(tab, "x") else { |
| 170 | return 1; |
| 171 | }; |
| 172 | assert x == 10; |
| 173 | |
| 174 | let y = lookup(tab, "y") else { |
| 175 | return 3; |
| 176 | }; |
| 177 | assert y == 20; |
| 178 | |
| 179 | let z = lookup(tab, "z") else { |
| 180 | return 5; |
| 181 | }; |
| 182 | assert z == 30; |
| 183 | |
| 184 | // Lookup nonexistent symbol. |
| 185 | if let val = lookup(tab, "w") { |
| 186 | return 7; |
| 187 | } |
| 188 | |
| 189 | popScope(tab); |
| 190 | return 0; |
| 191 | } |
| 192 | |
| 193 | /// Test scope shadowing. |
| 194 | fn testShadowing(tab: *mut SymTab) -> i32 { |
| 195 | init(tab); |
| 196 | pushScope(tab); |
| 197 | define(tab, "x", 1); |
| 198 | |
| 199 | // Verify outer x. |
| 200 | let x1 = lookup(tab, "x") else { |
| 201 | return 1; |
| 202 | }; |
| 203 | assert x1 == 1; |
| 204 | |
| 205 | // Push inner scope, shadow x. |
| 206 | pushScope(tab); |
| 207 | define(tab, "x", 2); |
| 208 | |
| 209 | let x2 = lookup(tab, "x") else { |
| 210 | return 3; |
| 211 | }; |
| 212 | assert x2 == 2; |
| 213 | |
| 214 | // Pop inner scope, x should revert. |
| 215 | popScope(tab); |
| 216 | |
| 217 | let x3 = lookup(tab, "x") else { |
| 218 | return 5; |
| 219 | }; |
| 220 | assert x3 == 1; |
| 221 | |
| 222 | popScope(tab); |
| 223 | return 0; |
| 224 | } |
| 225 | |
| 226 | /// Test deep nesting with shadowing. |
| 227 | fn testDeepNesting(tab: *mut SymTab) -> i32 { |
| 228 | init(tab); |
| 229 | |
| 230 | // Define x at each of 8 scope levels. |
| 231 | let mut i: u32 = 0; |
| 232 | while i < 8 { |
| 233 | pushScope(tab); |
| 234 | define(tab, "x", i as i32 * 10); |
| 235 | i += 1; |
| 236 | } |
| 237 | |
| 238 | // x should be the innermost value. |
| 239 | let x = lookup(tab, "x") else { |
| 240 | return 1; |
| 241 | }; |
| 242 | assert x == 70; |
| 243 | |
| 244 | // Pop scopes one by one and check. |
| 245 | i = 7; |
| 246 | while i > 0 { |
| 247 | popScope(tab); |
| 248 | let val = lookup(tab, "x") else { |
| 249 | return 3; |
| 250 | }; |
| 251 | let expected = (i - 1) as i32 * 10; |
| 252 | assert val == expected; |
| 253 | i -= 1; |
| 254 | } |
| 255 | |
| 256 | popScope(tab); |
| 257 | return 0; |
| 258 | } |
| 259 | |
| 260 | /// Test multiple symbols per scope. |
| 261 | fn testMultipleSymbols(tab: *mut SymTab) -> i32 { |
| 262 | init(tab); |
| 263 | pushScope(tab); |
| 264 | |
| 265 | // Define a bunch of symbols. |
| 266 | let names: [*[u8]; 8] = ["a", "bb", "ccc", "dddd", "eeeee", "ff", "ggg", "h"]; |
| 267 | let values: [i32; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; |
| 268 | |
| 269 | for name, i in names { |
| 270 | define(tab, name, values[i]); |
| 271 | } |
| 272 | |
| 273 | // Verify all of them. |
| 274 | let mut sum: i32 = 0; |
| 275 | for name, i in names { |
| 276 | if let val = lookup(tab, name) { |
| 277 | sum += val; |
| 278 | } else { |
| 279 | return 1; |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | // 1+2+3+4+5+6+7+8 = 36 |
| 284 | assert sum == 36; |
| 285 | |
| 286 | popScope(tab); |
| 287 | return 0; |
| 288 | } |
| 289 | |
| 290 | /// Test update functionality. |
| 291 | fn testUpdate(tab: *mut SymTab) -> i32 { |
| 292 | init(tab); |
| 293 | pushScope(tab); |
| 294 | |
| 295 | define(tab, "counter", 0); |
| 296 | |
| 297 | // Increment counter 10 times. |
| 298 | let mut i: u32 = 0; |
| 299 | while i < 10 { |
| 300 | let cur = lookup(tab, "counter") else { |
| 301 | return 1; |
| 302 | }; |
| 303 | assert update(tab, "counter", cur + 1); |
| 304 | i += 1; |
| 305 | } |
| 306 | |
| 307 | let finalVal = lookup(tab, "counter") else { |
| 308 | return 3; |
| 309 | }; |
| 310 | assert finalVal == 10; |
| 311 | |
| 312 | // Update nonexistent symbol should fail. |
| 313 | if update(tab, "nonexistent", 99) { |
| 314 | return 5; |
| 315 | } |
| 316 | |
| 317 | popScope(tab); |
| 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | /// Test scope isolation: symbols in popped scopes are gone. |
| 322 | fn testScopeIsolation(tab: *mut SymTab) -> i32 { |
| 323 | init(tab); |
| 324 | |
| 325 | pushScope(tab); |
| 326 | define(tab, "outer", 1); |
| 327 | |
| 328 | pushScope(tab); |
| 329 | define(tab, "inner", 2); |
| 330 | |
| 331 | // Both visible. |
| 332 | if let val = lookup(tab, "outer") { |
| 333 | assert val == 1; |
| 334 | } else { |
| 335 | return 2; |
| 336 | } |
| 337 | if let val = lookup(tab, "inner") { |
| 338 | assert val == 2; |
| 339 | } else { |
| 340 | return 4; |
| 341 | } |
| 342 | |
| 343 | popScope(tab); |
| 344 | |
| 345 | // outer still visible, inner gone. |
| 346 | if let val = lookup(tab, "outer") { |
| 347 | assert val == 1; |
| 348 | } else { |
| 349 | return 6; |
| 350 | } |
| 351 | if let val = lookup(tab, "inner") { |
| 352 | return 7; |
| 353 | } |
| 354 | |
| 355 | popScope(tab); |
| 356 | return 0; |
| 357 | } |
| 358 | |
| 359 | /// Test interleaved defines and lookups across scopes using while-let. |
| 360 | fn testInterleaved(tab: *mut SymTab) -> i32 { |
| 361 | init(tab); |
| 362 | pushScope(tab); |
| 363 | |
| 364 | define(tab, "a", 100); |
| 365 | define(tab, "b", 200); |
| 366 | |
| 367 | pushScope(tab); |
| 368 | define(tab, "a", 111); |
| 369 | define(tab, "c", 300); |
| 370 | |
| 371 | // Verify values with expected lookup results. |
| 372 | let queries: [*[u8]; 4] = ["a", "b", "c", "d"]; |
| 373 | let expected: [?i32; 4] = [111, 200, 300, nil]; |
| 374 | let mut failures: u32 = 0; |
| 375 | |
| 376 | for name, i in queries { |
| 377 | let result = lookup(tab, name); |
| 378 | if let exp = expected[i] { |
| 379 | // We expect a value. |
| 380 | if let r = result { |
| 381 | if r != exp { |
| 382 | failures += 1; |
| 383 | } |
| 384 | } else { |
| 385 | failures += 1; |
| 386 | } |
| 387 | } else { |
| 388 | // We expect nil. |
| 389 | if let _ = result { |
| 390 | failures += 1; |
| 391 | } |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | if failures != 0 { |
| 396 | return failures as i32; |
| 397 | } |
| 398 | |
| 399 | popScope(tab); |
| 400 | popScope(tab); |
| 401 | return 0; |
| 402 | } |
| 403 | |
| 404 | @default fn main() -> i32 { |
| 405 | let mut symbols: [Symbol; 256] = [Symbol { nameHash: 0, value: 0, depth: 0, next: NIL, shadow: NIL }; 256]; |
| 406 | let mut scopes: [ScopeMarker; 16] = [ScopeMarker { symbolCount: 0 }; 16]; |
| 407 | let mut buckets: [u32; 64] = [NIL; 64]; |
| 408 | |
| 409 | let mut tab = SymTab { |
| 410 | symbols: &mut symbols[..], |
| 411 | symbolCount: 0, |
| 412 | scopes: &mut scopes[..], |
| 413 | scopeDepth: 0, |
| 414 | buckets: &mut buckets[..], |
| 415 | }; |
| 416 | |
| 417 | let r1 = testBasic(&mut tab); |
| 418 | if r1 != 0 { |
| 419 | return 10 + r1; |
| 420 | } |
| 421 | |
| 422 | let r2 = testShadowing(&mut tab); |
| 423 | if r2 != 0 { |
| 424 | return 20 + r2; |
| 425 | } |
| 426 | |
| 427 | let r3 = testDeepNesting(&mut tab); |
| 428 | if r3 != 0 { |
| 429 | return 30 + r3; |
| 430 | } |
| 431 | |
| 432 | let r4 = testMultipleSymbols(&mut tab); |
| 433 | if r4 != 0 { |
| 434 | return 40 + r4; |
| 435 | } |
| 436 | |
| 437 | let r5 = testUpdate(&mut tab); |
| 438 | if r5 != 0 { |
| 439 | return 50 + r5; |
| 440 | } |
| 441 | |
| 442 | let r6 = testScopeIsolation(&mut tab); |
| 443 | if r6 != 0 { |
| 444 | return 60 + r6; |
| 445 | } |
| 446 | |
| 447 | let r7 = testInterleaved(&mut tab); |
| 448 | if r7 != 0 { |
| 449 | return 70 + r7; |
| 450 | } |
| 451 | |
| 452 | return 0; |
| 453 | } |