Expand the abbreviated pointer types to full words on the language surface: ptr -> pointer (a raw address / FFI handle) ptrs -> pointers (a buffer of pointers) The Ludic type name is distinct from LLVM's own `ptr` spelling: llty() maps `pointer`/`pointers` to LLVM `ptr`, and the emitted IR keeps `ptr`, so only the Ludic-level surface changes. Rewrites type annotations across all sources, the 8 hardcoded pointer type-tags, the `pointers`-buffer indexing in emit_addr, the grammars/LSP/JetBrains tokens, and the docs (type-ptr -> type-pointer, type-ptrs -> type-pointers). int/bool keep their conventional short spelling (like Math). Reseeded; C-free fixpoint holds; all suites green (45/24/29); site + check.py OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
294 lines
14 KiB
Text
294 lines
14 KiB
Text
# emit_list.ludic — the List.* namespace over []T slices. A slice is the
|
|
# { data, len, cap } %LSlice header (see emit_new.ludic); every op here reads or
|
|
# mutates that header in place, so all holders of the slice observe the change.
|
|
# Element comparison (contains/index_of) is by value for scalars and by identity
|
|
# for reference elements (structs/strings), matching how `==` behaves elsewhere.
|
|
|
|
# address of element `idx` (an i32 code) in slice header `h`, element LLVM type `elt`
|
|
function list_elem_addr(h: pointer, elt: pointer, idx: pointer) -> pointer {
|
|
let dp = slice_field(h, 0)
|
|
let data = emit_bind(`load ptr, ptr {dp}`)
|
|
return emit_bind(`getelementptr inbounds {elt}, ptr {data}, i32 {idx}`)
|
|
}
|
|
|
|
function is_list_ns(meth: pointer) -> bool {
|
|
if (meth == "len") or (meth == "push") or (meth == "clear") { return true }
|
|
if (meth == "first") or (meth == "last") or (meth == "pop") or (meth == "swap") { return true }
|
|
if (meth == "contains") or (meth == "index_of") or (meth == "reverse") { return true }
|
|
if (meth == "insert") or (meth == "remove_at") or (meth == "remove") or (meth == "sort") { return true }
|
|
return false
|
|
}
|
|
|
|
# grow the slice's backing buffer if it is full, exactly as push does (double,
|
|
# or 8 from empty). Leaves length untouched; only capacity/data may change.
|
|
function list_grow_if_full(h: pointer, elt: pointer) -> void {
|
|
let lp = slice_field(h, 1); let cp = slice_field(h, 2); let dp = slice_field(h, 0)
|
|
let l = emit_bind(`load i32, ptr {lp}`)
|
|
let c = emit_bind(`load i32, ptr {cp}`)
|
|
let full = emit_bind(`icmp sge i32 {l}, {c}`)
|
|
let grow = lbl("ig"); let done = lbl("igd")
|
|
emit(" br i1 "); emit(full); emit(", label %"); emit(grow); emit(", label %"); emit(done); emit("\n")
|
|
emit(grow); emit(":\n")
|
|
let dbl = emit_bind(`mul i32 {c}, 2`)
|
|
let isz = emit_bind(`icmp eq i32 {c}, 0`)
|
|
let nc = emit_bind(`select i1 {isz}, i32 8, i32 {dbl}`)
|
|
let esz = emit_sizeof(elt)
|
|
let ncw = emit_bind(`zext i32 {nc} to i64`)
|
|
let byts = emit_bind(`mul i64 {ncw}, {esz}`)
|
|
let old = emit_bind(`load ptr, ptr {dp}`)
|
|
let nd = emit_bind(`call ptr @realloc(ptr {old}, i64 {byts})`)
|
|
emit(" store ptr "); emit(nd); emit(", ptr "); emit(dp); emit("\n")
|
|
emit(" store i32 "); emit(nc); emit(", ptr "); emit(cp); emit("\n")
|
|
emit(" br label %"); emit(done); emit("\n")
|
|
emit(done); emit(":\n")
|
|
}
|
|
|
|
function emit_list_ns(meth: pointer, e: Node) -> Val {
|
|
if (meth == "len") { return emit_len(e) } # same header length as len(s)
|
|
if (meth == "push") { return emit_push(e) } # same as push(s, v)
|
|
let s = emit_expr(e.kids[0])
|
|
if not is_slice_ty(s.ty) { perr(`List.{meth} needs a slice`) }
|
|
let el = slice_elem(s.ty)
|
|
let elt = llty(el)
|
|
let h = s.code
|
|
let lp = slice_field(h, 1)
|
|
|
|
if (meth == "clear") { # drop to length 0 (keeps capacity)
|
|
emit(" store i32 0, ptr "); emit(lp); emit("\n")
|
|
return val("0", "void")
|
|
}
|
|
if (meth == "first") { # element 0 (assumes non-empty)
|
|
let a = list_elem_addr(h, elt, "0")
|
|
return val(emit_bind(`load {elt}, ptr {a}`), el)
|
|
}
|
|
if (meth == "last") { # element len-1 (assumes non-empty)
|
|
let l = emit_bind(`load i32, ptr {lp}`)
|
|
let l1 = emit_bind(`sub i32 {l}, 1`)
|
|
let a = list_elem_addr(h, elt, l1)
|
|
return val(emit_bind(`load {elt}, ptr {a}`), el)
|
|
}
|
|
if (meth == "pop") { # remove & return the last element
|
|
let l = emit_bind(`load i32, ptr {lp}`)
|
|
let l1 = emit_bind(`sub i32 {l}, 1`)
|
|
let a = list_elem_addr(h, elt, l1)
|
|
let v = emit_bind(`load {elt}, ptr {a}`)
|
|
emit(" store i32 "); emit(l1); emit(", ptr "); emit(lp); emit("\n")
|
|
return val(v, el)
|
|
}
|
|
if (meth == "swap") { # exchange elements i and j
|
|
let i = emit_expr(e.kids[1]); let j = emit_expr(e.kids[2])
|
|
let ai = list_elem_addr(h, elt, i.code)
|
|
let aj = list_elem_addr(h, elt, j.code)
|
|
let vi = emit_bind(`load {elt}, ptr {ai}`)
|
|
let vj = emit_bind(`load {elt}, ptr {aj}`)
|
|
emit(" store "); emit(elt); emit(" "); emit(vj); emit(", ptr "); emit(ai); emit("\n")
|
|
emit(" store "); emit(elt); emit(" "); emit(vi); emit(", ptr "); emit(aj); emit("\n")
|
|
return val("0", "void")
|
|
}
|
|
if (meth == "contains") or (meth == "index_of") { # linear scan; index_of -> -1 if absent
|
|
let needle = emit_expr(e.kids[1])
|
|
let l = emit_bind(`load i32, ptr {lp}`)
|
|
let res = emit_alloca("i32")
|
|
store_at("i32", "-1", res)
|
|
let ix = emit_alloca("i32")
|
|
store_at("i32", "0", ix)
|
|
let cl = lbl("lc_cond"); let bl = lbl("lc_body"); let hit = lbl("lc_hit")
|
|
let nx = lbl("lc_next"); let en = lbl("lc_end")
|
|
emit(" br label %"); emit(cl); emit("\n")
|
|
emit(cl); emit(":\n")
|
|
let iv = emit_bind(`load i32, ptr {ix}`)
|
|
let more = emit_bind(`icmp slt i32 {iv}, {l}`)
|
|
emit(" br i1 "); emit(more); emit(", label %"); emit(bl); emit(", label %"); emit(en); emit("\n")
|
|
emit(bl); emit(":\n")
|
|
let a = list_elem_addr(h, elt, iv)
|
|
let ev = emit_bind(`load {elt}, ptr {a}`)
|
|
let eq = emit_bind(`icmp eq {elt} {ev}, {needle.code}`)
|
|
emit(" br i1 "); emit(eq); emit(", label %"); emit(hit); emit(", label %"); emit(nx); emit("\n")
|
|
emit(hit); emit(":\n")
|
|
store_at("i32", iv, res)
|
|
emit(" br label %"); emit(en); emit("\n")
|
|
emit(nx); emit(":\n")
|
|
let iv1 = emit_bind(`add i32 {iv}, 1`)
|
|
store_at("i32", iv1, ix)
|
|
emit(" br label %"); emit(cl); emit("\n")
|
|
emit(en); emit(":\n")
|
|
let found = emit_bind(`load i32, ptr {res}`)
|
|
if (meth == "index_of") { return val(found, "int") }
|
|
let hasit = emit_bind(`icmp sge i32 {found}, 0`) # contains -> found >= 0
|
|
return val(emit_bind(`zext i1 {hasit} to i32`), "bool")
|
|
}
|
|
if (meth == "insert") { # insert v at index i, shifting the rest up
|
|
let idx = emit_expr(e.kids[1]); let vv = emit_expr(e.kids[2])
|
|
list_grow_if_full(h, elt)
|
|
let l = emit_bind(`load i32, ptr {lp}`)
|
|
let jp = emit_alloca("i32")
|
|
store_at("i32", l, jp)
|
|
let cl = lbl("li_c"); let bl = lbl("li_b"); let en = lbl("li_e")
|
|
emit(" br label %"); emit(cl); emit("\n"); emit(cl); emit(":\n")
|
|
let j = emit_bind(`load i32, ptr {jp}`)
|
|
let go = emit_bind(`icmp sgt i32 {j}, {idx.code}`)
|
|
emit(" br i1 "); emit(go); emit(", label %"); emit(bl); emit(", label %"); emit(en); emit("\n")
|
|
emit(bl); emit(":\n")
|
|
let jm1 = emit_bind(`sub i32 {j}, 1`)
|
|
let src = list_elem_addr(h, elt, jm1)
|
|
let sv = emit_bind(`load {elt}, ptr {src}`)
|
|
let dst = list_elem_addr(h, elt, j)
|
|
emit(" store "); emit(elt); emit(" "); emit(sv); emit(", ptr "); emit(dst); emit("\n")
|
|
store_at("i32", jm1, jp)
|
|
emit(" br label %"); emit(cl); emit("\n")
|
|
emit(en); emit(":\n")
|
|
let at = list_elem_addr(h, elt, idx.code)
|
|
emit(" store "); emit(elt); emit(" "); emit(vv.code); emit(", ptr "); emit(at); emit("\n")
|
|
let l1 = emit_bind(`add i32 {l}, 1`)
|
|
emit(" store i32 "); emit(l1); emit(", ptr "); emit(lp); emit("\n")
|
|
return val("0", "void")
|
|
}
|
|
if (meth == "remove_at") { # remove index i, shifting the rest down
|
|
let idx = emit_expr(e.kids[1])
|
|
let l = emit_bind(`load i32, ptr {lp}`)
|
|
let lm1 = emit_bind(`sub i32 {l}, 1`)
|
|
let jp = emit_alloca("i32")
|
|
store_at("i32", idx.code, jp)
|
|
let cl = lbl("lr_c"); let bl = lbl("lr_b"); let en = lbl("lr_e")
|
|
emit(" br label %"); emit(cl); emit("\n"); emit(cl); emit(":\n")
|
|
let j = emit_bind(`load i32, ptr {jp}`)
|
|
let go = emit_bind(`icmp slt i32 {j}, {lm1}`)
|
|
emit(" br i1 "); emit(go); emit(", label %"); emit(bl); emit(", label %"); emit(en); emit("\n")
|
|
emit(bl); emit(":\n")
|
|
let jp1 = emit_bind(`add i32 {j}, 1`)
|
|
let src = list_elem_addr(h, elt, jp1)
|
|
let sv = emit_bind(`load {elt}, ptr {src}`)
|
|
let dst = list_elem_addr(h, elt, j)
|
|
emit(" store "); emit(elt); emit(" "); emit(sv); emit(", ptr "); emit(dst); emit("\n")
|
|
store_at("i32", jp1, jp)
|
|
emit(" br label %"); emit(cl); emit("\n")
|
|
emit(en); emit(":\n")
|
|
emit(" store i32 "); emit(lm1); emit(", ptr "); emit(lp); emit("\n")
|
|
return val("0", "void")
|
|
}
|
|
if (meth == "remove") { # remove the first element equal to v
|
|
let needle = emit_expr(e.kids[1])
|
|
let l = emit_bind(`load i32, ptr {lp}`)
|
|
# scan for the index of v (-1 if absent)
|
|
let fp = emit_alloca("i32")
|
|
store_at("i32", "-1", fp)
|
|
let ip = emit_alloca("i32")
|
|
store_at("i32", "0", ip)
|
|
let sc = lbl("lv_c"); let sb = lbl("lv_b"); let sh = lbl("lv_h"); let sn = lbl("lv_n"); let se = lbl("lv_e")
|
|
emit(" br label %"); emit(sc); emit("\n"); emit(sc); emit(":\n")
|
|
let iv = emit_bind(`load i32, ptr {ip}`)
|
|
let more = emit_bind(`icmp slt i32 {iv}, {l}`)
|
|
let seen = emit_bind(`load i32, ptr {fp}`)
|
|
let notyet = emit_bind(`icmp slt i32 {seen}, 0`)
|
|
let cont = emit_bind(`and i1 {more}, {notyet}`)
|
|
emit(" br i1 "); emit(cont); emit(", label %"); emit(sb); emit(", label %"); emit(se); emit("\n")
|
|
emit(sb); emit(":\n")
|
|
let a = list_elem_addr(h, elt, iv)
|
|
let ev = emit_bind(`load {elt}, ptr {a}`)
|
|
let eq = emit_bind(`icmp eq {elt} {ev}, {needle.code}`)
|
|
emit(" br i1 "); emit(eq); emit(", label %"); emit(sh); emit(", label %"); emit(sn); emit("\n")
|
|
emit(sh); emit(":\n"); store_at("i32", iv, fp); emit(" br label %"); emit(sc); emit("\n")
|
|
emit(sn); emit(":\n")
|
|
let iv1 = emit_bind(`add i32 {iv}, 1`)
|
|
store_at("i32", iv1, ip)
|
|
emit(" br label %"); emit(sc); emit("\n")
|
|
emit(se); emit(":\n")
|
|
# if found, shift the tail down and shrink
|
|
let found = emit_bind(`load i32, ptr {fp}`)
|
|
let has = emit_bind(`icmp sge i32 {found}, 0`)
|
|
let doit = lbl("lv_do"); let end = lbl("lv_end")
|
|
emit(" br i1 "); emit(has); emit(", label %"); emit(doit); emit(", label %"); emit(end); emit("\n")
|
|
emit(doit); emit(":\n")
|
|
let lm1 = emit_bind(`sub i32 {l}, 1`)
|
|
let jp = emit_alloca("i32")
|
|
store_at("i32", found, jp)
|
|
let dc = lbl("lv_dc"); let db = lbl("lv_db"); let de = lbl("lv_de")
|
|
emit(" br label %"); emit(dc); emit("\n"); emit(dc); emit(":\n")
|
|
let j = emit_bind(`load i32, ptr {jp}`)
|
|
let go = emit_bind(`icmp slt i32 {j}, {lm1}`)
|
|
emit(" br i1 "); emit(go); emit(", label %"); emit(db); emit(", label %"); emit(de); emit("\n")
|
|
emit(db); emit(":\n")
|
|
let jp1 = emit_bind(`add i32 {j}, 1`)
|
|
let src = list_elem_addr(h, elt, jp1)
|
|
let sv = emit_bind(`load {elt}, ptr {src}`)
|
|
let dst = list_elem_addr(h, elt, j)
|
|
emit(" store "); emit(elt); emit(" "); emit(sv); emit(", ptr "); emit(dst); emit("\n")
|
|
store_at("i32", jp1, jp)
|
|
emit(" br label %"); emit(dc); emit("\n")
|
|
emit(de); emit(":\n")
|
|
emit(" store i32 "); emit(lm1); emit(", ptr "); emit(lp); emit("\n")
|
|
emit(" br label %"); emit(end); emit("\n")
|
|
emit(end); emit(":\n")
|
|
return val("0", "void")
|
|
}
|
|
if (meth == "sort") { # ascending insertion sort, in place
|
|
let l = emit_bind(`load i32, ptr {lp}`)
|
|
let ip = emit_alloca("i32")
|
|
store_at("i32", "1", ip)
|
|
let oc = lbl("ls_oc"); let ob = lbl("ls_ob"); let oe = lbl("ls_oe")
|
|
emit(" br label %"); emit(oc); emit("\n"); emit(oc); emit(":\n")
|
|
let i = emit_bind(`load i32, ptr {ip}`)
|
|
let omore = emit_bind(`icmp slt i32 {i}, {l}`)
|
|
emit(" br i1 "); emit(omore); emit(", label %"); emit(ob); emit(", label %"); emit(oe); emit("\n")
|
|
emit(ob); emit(":\n")
|
|
let ai = list_elem_addr(h, elt, i)
|
|
let key = emit_bind(`load {elt}, ptr {ai}`)
|
|
let jp = emit_alloca("i32")
|
|
let im1 = emit_bind(`sub i32 {i}, 1`)
|
|
store_at("i32", im1, jp)
|
|
let ic = lbl("ls_ic"); let ib = lbl("ls_ib"); let ie = lbl("ls_ie")
|
|
emit(" br label %"); emit(ic); emit("\n"); emit(ic); emit(":\n")
|
|
let j = emit_bind(`load i32, ptr {jp}`)
|
|
let jok = emit_bind(`icmp sge i32 {j}, 0`)
|
|
let aj = list_elem_addr(h, elt, j)
|
|
let ev = emit_bind(`load {elt}, ptr {aj}`)
|
|
let gt = emit_bind(`icmp sgt {elt} {ev}, {key}`)
|
|
let shift = emit_bind(`and i1 {jok}, {gt}`)
|
|
emit(" br i1 "); emit(shift); emit(", label %"); emit(ib); emit(", label %"); emit(ie); emit("\n")
|
|
emit(ib); emit(":\n")
|
|
let jp1 = emit_bind(`add i32 {j}, 1`)
|
|
let dst = list_elem_addr(h, elt, jp1)
|
|
emit(" store "); emit(elt); emit(" "); emit(ev); emit(", ptr "); emit(dst); emit("\n")
|
|
let jm1 = emit_bind(`sub i32 {j}, 1`)
|
|
store_at("i32", jm1, jp)
|
|
emit(" br label %"); emit(ic); emit("\n")
|
|
emit(ie); emit(":\n")
|
|
let j2 = emit_bind(`load i32, ptr {jp}`)
|
|
let slot = emit_bind(`add i32 {j2}, 1`)
|
|
let sa = list_elem_addr(h, elt, slot)
|
|
emit(" store "); emit(elt); emit(" "); emit(key); emit(", ptr "); emit(sa); emit("\n")
|
|
let i1 = emit_bind(`add i32 {i}, 1`)
|
|
store_at("i32", i1, ip)
|
|
emit(" br label %"); emit(oc); emit("\n")
|
|
emit(oe); emit(":\n")
|
|
return val("0", "void")
|
|
}
|
|
# reverse: swap ends inward until the cursors meet
|
|
let l = emit_bind(`load i32, ptr {lp}`)
|
|
let loi = emit_alloca("i32")
|
|
store_at("i32", "0", loi)
|
|
let hii = emit_alloca("i32")
|
|
let lm1 = emit_bind(`sub i32 {l}, 1`)
|
|
store_at("i32", lm1, hii)
|
|
let cl = lbl("lr_cond"); let bl = lbl("lr_body"); let en = lbl("lr_end")
|
|
emit(" br label %"); emit(cl); emit("\n")
|
|
emit(cl); emit(":\n")
|
|
let lo = emit_bind(`load i32, ptr {loi}`)
|
|
let hi = emit_bind(`load i32, ptr {hii}`)
|
|
let go = emit_bind(`icmp slt i32 {lo}, {hi}`)
|
|
emit(" br i1 "); emit(go); emit(", label %"); emit(bl); emit(", label %"); emit(en); emit("\n")
|
|
emit(bl); emit(":\n")
|
|
let alo = list_elem_addr(h, elt, lo)
|
|
let ahi = list_elem_addr(h, elt, hi)
|
|
let vlo = emit_bind(`load {elt}, ptr {alo}`)
|
|
let vhi = emit_bind(`load {elt}, ptr {ahi}`)
|
|
emit(" store "); emit(elt); emit(" "); emit(vhi); emit(", ptr "); emit(alo); emit("\n")
|
|
emit(" store "); emit(elt); emit(" "); emit(vlo); emit(", ptr "); emit(ahi); emit("\n")
|
|
let lo1 = emit_bind(`add i32 {lo}, 1`)
|
|
store_at("i32", lo1, loi)
|
|
let hi1 = emit_bind(`sub i32 {hi}, 1`)
|
|
store_at("i32", hi1, hii)
|
|
emit(" br label %"); emit(cl); emit("\n")
|
|
emit(en); emit(":\n")
|
|
return val("0", "void")
|
|
}
|