Something went wrong. Try again.
Jam Programming Language jamlang.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367// `Vec(T)` from std.collections; `Option(T)` from std.option.const { assert } = import("test");const { Vec } = import("std").collections;const { Option } = import("std").option;
const VecI32 = Vec(i32);const VecU8 = Vec(u8);
// Small helper: unwrap Option(i32) or fall back. Real code pattern-// matches directly; the tests use this so they can compare to ints.fn unwrapI32(o: Option(i32), fallback: i32) i32 { return match (o) { Option(i32).Some(x) { x } Option(i32).None { fallback } };}
fn unwrapU8(o: Option(u8), fallback: u8) u8 { return match (o) { Option(u8).Some(x) { x } Option(u8).None { fallback } };}
tfn pushAndGetI32() { var v: VecI32 = VecI32.empty(); v.push(42); v.push(43); v.push(44); assert(v.len() as i32, 3); assert(unwrapI32(v.get(0), 0), 42); assert(unwrapI32(v.get(1), 0), 43); assert(unwrapI32(v.get(2), 0), 44);}
tfn pushBeyondInitialCapacity() { // Initial cap is 8; push 20 items to exercise the grow path // through at least two doublings (8 → 16 → 32). var v: VecI32 = VecI32.empty(); var i: i32 = 0; while (i < 20) { v.push(i); i = i + 1; } assert(v.len() as i32, 20); assert(unwrapI32(v.get(0), -1), 0); assert(unwrapI32(v.get(10), -1), 10); assert(unwrapI32(v.get(19), -1), 19);}
tfn popReducesLength() { var v: VecI32 = VecI32.empty(); v.push(1); v.push(2); v.push(3); assert(unwrapI32(v.pop(), 0), 3); assert(unwrapI32(v.pop(), 0), 2); assert(v.len() as i32, 1); assert(unwrapI32(v.get(0), 0), 1);}
tfn popEmptyReturnsNone() { var v: VecI32 = VecI32.empty(); // Initial length is 0; pop returns None, not garbage. var sentinel: i32 = -42; assert(unwrapI32(v.pop(), sentinel), sentinel);}
tfn getOutOfBoundsReturnsNone() { var v: VecI32 = VecI32.empty(); v.push(7); // len == 1; v.get(5) is out of bounds → None → sentinel. var sentinel: i32 = -42; assert(unwrapI32(v.get(5), sentinel), sentinel);}
tfn vecOfU8() { // Different element type — verifies generic instantiation flows // through and @sizeOf(T) picks up the right size per // instantiation. var v: VecU8 = VecU8.empty(); v.push(10); v.push(20); v.push(30); assert(unwrapU8(v.get(0), 0) as i32, 10); assert(unwrapU8(v.get(1), 0) as i32, 20); assert(unwrapU8(v.get(2), 0) as i32, 30);}
tfn initialCapacityForU8() { // sizeOf(u8) == 1 ⇒ initial cap 8 (Rust's heuristic). var v: VecU8 = VecU8.empty(); assert(v.capacity as i32, 8);}
tfn initialCapacityForI32() { // sizeOf(i32) == 4, ≤ 1024 ⇒ initial cap 4. var v: VecI32 = VecI32.empty(); assert(v.capacity as i32, 4);}
tfn withCapacityPreallocates() { var v: VecI32 = VecI32.withCapacity(100); // No pushes yet; length is zero but capacity reflects the // preallocation. push(...) up to 100 elements must not regrow. assert(v.len() as i32, 0); assert(v.capacity as i32, 100); var i: i32 = 0; while (i < 100) { v.push(i); i = i + 1; } // Capacity unchanged after 100 pushes (no regrow happened). assert(v.capacity as i32, 100); assert(v.len() as i32, 100); assert(unwrapI32(v.get(99), -1), 99);}
tfn withCapacityZeroClampsToOne() { // Caller asked for 0 slots; we clamp to 1 to avoid the // implementation-defined malloc(0) result. var v: VecI32 = VecI32.withCapacity(0); assert(v.capacity as i32, 1); v.push(42); assert(unwrapI32(v.get(0), 0), 42);}
tfn clearResetsLength() { var v: VecI32 = VecI32.empty(); v.push(1); v.push(2); v.push(3); v.clear(); assert(v.len() as i32, 0); // Buffer is reusable: pushing again works without reallocating. v.push(99); assert(v.len() as i32, 1); assert(unwrapI32(v.get(0), 0), 99);}
tfn clearKeepsCapacity() { var v: VecI32 = VecI32.withCapacity(50); v.push(7); v.clear(); // clear() must not free or shrink — capacity survives. assert(v.capacity as i32, 50);}
tfn truncateShrinks() { var v: VecI32 = VecI32.empty(); v.push(10); v.push(20); v.push(30); v.push(40); v.truncate(2); assert(v.len() as i32, 2); assert(unwrapI32(v.get(0), -1), 10); assert(unwrapI32(v.get(1), -1), 20); // Index 2 is past the new length even though the byte is still // there in the buffer. var sentinel: i32 = -42; assert(unwrapI32(v.get(2), sentinel), sentinel);}
tfn truncateNoOpWhenLarger() { var v: VecI32 = VecI32.empty(); v.push(1); v.push(2); v.truncate(99); assert(v.len() as i32, 2);}
tfn setLenSetsExactLength() { var v: VecI32 = VecI32.withCapacity(10); // Caller fills the buffer through `.ptr` then announces how // much they wrote. v.ptr[0] = 100; v.ptr[1] = 200; v.ptr[2] = 300; v.setLen(3); assert(v.len() as i32, 3); assert(unwrapI32(v.get(2), 0), 300);}
tfn setLenClampsToCapacity() { var v: VecI32 = VecI32.withCapacity(5); // Overshooting must not silently corrupt — clamp to capacity. v.setLen(99); assert(v.len() as i32, 5);}
tfn reserveGrowsCapacity() { // Empty VecI32 starts at capacity 4; doubling to cover an // extra 100 elements lands at 128 (4 → 8 → 16 → 32 → 64 → 128). var v: VecI32 = VecI32.empty(); v.reserve(100); assert(v.capacity as i32, 128); assert(v.len() as i32, 0);}
tfn reserveNoOpWhenEnough() { var v: VecI32 = VecI32.withCapacity(100); v.reserve(50); // length 0 + additional 50 ≤ capacity 100, so no growth. assert(v.capacity as i32, 100);}
tfn reserveAccountsForCurrentLength() { var v: VecI32 = VecI32.withCapacity(10); v.push(1); v.push(2); // length 2 + additional 9 = 11 > 10, so grow once (10 → 20). v.reserve(9); assert(v.capacity as i32, 20); assert(v.len() as i32, 2);}
tfn appendSliceFromStringLiteral() { var v: VecU8 = VecU8.empty(); var s: []u8 = "hi"; v.appendSlice(s); assert(v.len() as i32, 2); // 'h' == 104, 'i' == 105. assert(unwrapU8(v.get(0), 0) as i32, 104); assert(unwrapU8(v.get(1), 0) as i32, 105);}
tfn appendSliceTriggersGrow() { // VecU8 starts at capacity 8; appending 20 bytes forces grow // through 8 → 16 → 32. Pre-reserved path means only one // realloc, not log(N). var v: VecU8 = VecU8.empty(); var s: []u8 = "abcdefghijklmnopqrst"; v.appendSlice(s); assert(v.len() as i32, 20); assert(v.capacity as i32, 32); assert(unwrapU8(v.get(0), 0) as i32, 97); // 'a' assert(unwrapU8(v.get(19), 0) as i32, 116); // 't'}
tfn appendSliceAfterExistingData() { var v: VecU8 = VecU8.empty(); v.push(65); // 'A' v.push(66); // 'B' var s: []u8 = "CD"; v.appendSlice(s); assert(v.len() as i32, 4); assert(unwrapU8(v.get(0), 0) as i32, 65); assert(unwrapU8(v.get(3), 0) as i32, 68);}
// `v[i] = x` and `x = v[i]` route through the compiler's special// `at(self, i) *mut T` dispatch — no `.ptr` reach-through in user// code. The semantics match raw `*mut[] T` indexing (no bounds// check); bounds-checked reads remain available via `get(i)`.
tfn indexedWriteAndRead() { var v: VecI32 = VecI32.withCapacity(4); v[0] = 10; v[1] = 20; v[2] = 30; v[3] = 40; // Direct reads through the same `at` path. assert(v[0], 10); assert(v[1], 20); assert(v[2], 30); assert(v[3], 40);}
tfn indexedReadAfterPush() { var v: VecI32 = VecI32.empty(); v.push(7); v.push(8); v.push(9); // Pushed values are visible through indexed reads (the buffer // is the same — push just bumps length). assert(v[0], 7); assert(v[1], 8); assert(v[2], 9);}
tfn indexedWriteVisibleThroughGet() { var v: VecI32 = VecI32.empty(); v.push(1); v.push(2); v.push(3); v[1] = 99; // `get(i)` reads through the same backing store, so the // indexed write is visible — confirms both sides agree on the // pointer-math vs the Vec's element view. assert(unwrapI32(v.get(1), 0), 99);}
tfn indexedU8Vec() { // VecU8 — different T, exercises generic instantiation of // `at` plus the compiler's u64→u32 narrowing on the index. var v: VecU8 = VecU8.withCapacity(3); v[0] = 100; v[1] = 200; v[2] = 250; assert(v[0] as i32, 100); assert(v[1] as i32, 200); assert(v[2] as i32, 250);}
tfn indexedCompoundExpression() { // The index can be an arbitrary u32-typed expression, not // just a literal. Verifies the index parameter is lowered // through the normal expression path before `at` is called. var v: VecI32 = VecI32.withCapacity(8); var i: u32 = 0; while (i < 8) { v[i] = (i as i32) * 10; i = i + 1; } assert(v[0], 0); assert(v[3], 30); assert(v[7], 70);}
// Inline `Vec(T)` (no alias) goes through the same `at` dispatch.// The use-site annotation produces a TypeKind::GenericCall instead// of TypeKind::Named, so the compiler's struct-dispatch lookup has// to chase through lookupStruct's GenericCall arm.
tfn inlineVecTypeAnnotation() { var v: Vec(i32) = Vec(i32).withCapacity(4); v[0] = 11; v[1] = 22; v[2] = 33; v[3] = 44; assert(v[0], 11); assert(v[3], 44);}
tfn inlineVecU8WithCapacity() { var v: Vec(u8) = Vec(u8).withCapacity(3); v[0] = 1; v[1] = 2; v[2] = 3; assert(v[0] as i32, 1); assert(v[2] as i32, 3);}
tfn inlineVecEmpty() { // Empty constructor exercises the same instantiation path — // confirms `at` is wired up regardless of which factory the // Vec came from. var v: Vec(i32) = Vec(i32).empty(); v.push(7); v.push(8); assert(v[0], 7); assert(v[1], 8); v[0] = 70; assert(v[0], 70);}
tfn inlineVecMethodsStillWork() { // Non-index methods (push, len, get) still resolve when the // declared type is the inline `Vec(T)` form. var v: Vec(u8) = Vec(u8).empty(); v.push(100); v.push(101); assert(v.len() as i32, 2); assert(unwrapU8(v.get(0), 0) as i32, 100);}