feat(compiler): list literals, typed compound assignment, file:line diagnostics
- `[a, b, c]` list literals (E_LIST → emit_list); static_type learns slice-element, `new T`, list, string and literal kinds - `x op= y` lowers through the same path as `x = x op y` (emit_bin_vals): fixed `*=`/`/=` use the Q16.16 64-bit paths, string `+=` concatenates, int→long widens; unary `-` keeps a fixed operand's type (arith_ty) - one `unescape()` table for "strings", 'chars' and `interpolation`; `'\''`, `'\\'`, `'\"'` no longer read as 0; unterminated char literals and unexpected characters are errors instead of silently skipped - every diagnostic is `file:line: error: msg` (g_parse_file / g_err_file, Node.file + Node.line set by node()); tok_desc() in expectation errors; duplicate `function` names and unknown `phase` names are reported in source terms (phase_id used to default unknown phases to Overlay) - interpolation holes skip braces inside string literals - hand-IR preludes move from the user `@fn_` prefix to `@lp_` so a user `is_ws` / `str_eq` / `path_join` no longer collides at link time - `@ClearColor(expr)` accepts any constant expression; `Os.pid()` added (docs page + inventory); `str_starts()` in support/str - main.ludic: `else if` flag ladder, char literals, stale script comments - examples/lang/operators.ludic covers all of the above; os.ludic covers Os.pid; docs pages for Os.pid and the Overlay phase; ten changesets - reseeded: selfhost/ludicc.seed.ll is the new compiler's own fixpoint Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
ad548840c7
commit
647dfec334
88 changed files with 30081 additions and 29179 deletions
|
|
@ -6,7 +6,7 @@ function emit_block(b: Node) -> void {
|
|||
while i < len(b.kids) {
|
||||
if g_term { return }
|
||||
emit_stmt(b.kids[i])
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15,6 +15,11 @@ function store_at(lt: pointer, v: pointer, addr: pointer) -> void {
|
|||
emit(" store "); emit(lt); emit(" "); emit(v); emit(", ptr "); emit(addr); emit("\n")
|
||||
}
|
||||
|
||||
# the binary operator behind a compound assignment token: "+=" -> "+"
|
||||
function compound_op(tok: pointer) -> pointer {
|
||||
return tok[0..1]
|
||||
}
|
||||
|
||||
function emit_assign(st: Node) -> void {
|
||||
# resolve the target's address and type
|
||||
let t = st.a
|
||||
|
|
@ -39,17 +44,16 @@ function emit_assign(st: Node) -> void {
|
|||
else { perr("bad assignment target") } }
|
||||
}
|
||||
let lt = llty(ty)
|
||||
let rv = emit_expr(st.b)
|
||||
var rv = emit_expr(st.b)
|
||||
if not (st.s == "=") {
|
||||
# `x op= y` is `x = x op y`: reload the target and lower through the same
|
||||
# path as a binary expression, so fixed `*=`, string `+=` and long promotion
|
||||
# all behave exactly like their spelled-out forms.
|
||||
let cur = val(emit_bind(`load {lt}, ptr {addr}`), ty)
|
||||
rv = emit_bin_vals(compound_op(st.s), cur, rv, false)
|
||||
}
|
||||
var v = coerce_code(rv, ty)
|
||||
if (lt == "i8") { v = emit_bind(`trunc i32 {v} to i8`) } # narrow to a byte for p[i] = v
|
||||
if not (st.s == "=") {
|
||||
let cur = emit_bind(`load {lt}, ptr {addr}`)
|
||||
var opc = "add"
|
||||
if (st.s == ("-=")) { opc = "sub" }
|
||||
if (st.s == ("*=")) { opc = "mul" }
|
||||
if (st.s == ("/=")) { opc = "sdiv" }
|
||||
v = emit_bind(`{opc} {lt} {cur}, {v}`)
|
||||
}
|
||||
store_at(lt, v, addr)
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +127,7 @@ function emit_return(st: Node) -> void {
|
|||
|
||||
function arm_is_default(arm: Node) -> bool {
|
||||
var p = 0
|
||||
while p < len(arm.kids) { if arm.kids[p].kind == E_ID and (arm.kids[p].s == "_") { return true }; p = p + 1 }
|
||||
while p < len(arm.kids) { if arm.kids[p].kind == E_ID and (arm.kids[p].s == "_") { return true }; p += 1 }
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +151,7 @@ function emit_bind_payload(en: Node, pat: Node, sv: Val) -> void {
|
|||
let lv = emit_bind(`load {lt}, ptr {p}`)
|
||||
let slot = emit_alloca(lt); store_at(lt, lv, slot)
|
||||
loc_push(pat.kids[k].s, slot, pty)
|
||||
k = k + 1
|
||||
k += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +182,7 @@ function emit_match_tagged(st: Node, sv: Val, en: Node) -> void {
|
|||
covered = covered | (1 << ord)
|
||||
let c = emit_bind(`icmp eq i32 {tag}, {itoa(ord)}`)
|
||||
if first { acc = c; first = false } else { acc = emit_bind(`or i1 {acc}, {c}`) }
|
||||
p = p + 1
|
||||
p += 1
|
||||
}
|
||||
let bodyl = lbl("mbody"); let nextl = lbl("marm")
|
||||
emit(" br i1 "); emit(acc); emit(", label %"); emit(bodyl); emit(", label %"); emit(nextl); emit("\n")
|
||||
|
|
@ -190,7 +194,7 @@ function emit_match_tagged(st: Node, sv: Val, en: Node) -> void {
|
|||
if not g_term { emit(" br label %"); emit(endl); emit("\n") }
|
||||
emit(nextl); emit(":\n"); g_term = false
|
||||
}
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
if (deflt != null) { emit_block(deflt.a) }
|
||||
else {
|
||||
|
|
@ -199,7 +203,7 @@ function emit_match_tagged(st: Node, sv: Val, en: Node) -> void {
|
|||
var m = 0
|
||||
while m < len(en.kids) {
|
||||
if (covered & (1 << m)) == 0 { perr(`match on {en.s} is not exhaustive: variant {en.kids[m].s} is unhandled (add it or a _ arm)`) }
|
||||
m = m + 1
|
||||
m += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -226,7 +230,7 @@ function emit_match(st: Node) -> void {
|
|||
let c = emit_bind(`icmp eq i32 {sv.code}, {pv.code}`)
|
||||
if first { acc = c; first = false }
|
||||
else { acc = emit_bind(`or i1 {acc}, {c}`) }
|
||||
p = p + 1
|
||||
p += 1
|
||||
}
|
||||
let bodyl = lbl("mbody"); let nextl = lbl("marm")
|
||||
emit(" br i1 "); emit(acc); emit(", label %"); emit(bodyl); emit(", label %"); emit(nextl); emit("\n")
|
||||
|
|
@ -235,7 +239,7 @@ function emit_match(st: Node) -> void {
|
|||
if not g_term { emit(" br label %"); emit(endl); emit("\n") }
|
||||
emit(nextl); emit(":\n"); g_term = false
|
||||
}
|
||||
i = i + 1
|
||||
i += 1
|
||||
}
|
||||
if (deflt != null) { emit_block(deflt.a) }
|
||||
if not g_term { emit(" br label %"); emit(endl); emit("\n") }
|
||||
|
|
@ -256,14 +260,14 @@ function emit_emit(st: Node) -> Val {
|
|||
let fd = ev.kids[f]
|
||||
var av: Node = null # the caller's value for this field, if given
|
||||
var j = 0
|
||||
while j < len(st.a.kids) { if (st.a.kids[j].s == fd.s) { av = st.a.kids[j].a }; j = j + 1 }
|
||||
while j < len(st.a.kids) { if (st.a.kids[j].s == fd.s) { av = st.a.kids[j].a }; j += 1 }
|
||||
let lt = llty(fd.ty)
|
||||
var code = "0"
|
||||
if (lt == "ptr") { code = "null" }
|
||||
if (av != null) { let v = emit_expr(av); code = v.code }
|
||||
else { if (fd.a != null) { let dv = emit_expr(fd.a); code = dv.code } } # declared default
|
||||
push(fcodes, code); push(ftys, lt)
|
||||
f = f + 1
|
||||
f += 1
|
||||
}
|
||||
# N4: a remote event (@ToServer/@ToClients) serializes its payload as
|
||||
# [i32 event_id][packed fields] and net_send in its direction — the far side's
|
||||
|
|
@ -276,8 +280,8 @@ function emit_emit(st: Node) -> Val {
|
|||
while k < len(ev.kids) {
|
||||
let dp = nreg(); emit(" "); emit(dp); emit(" = getelementptr inbounds i8, ptr @L_sendbuf, i32 "); emit(itoa(off)); emit("\n")
|
||||
emit(" store "); emit(ftys[k]); emit(" "); emit(fcodes[k]); emit(", ptr "); emit(dp); emit("\n")
|
||||
off = off + net_field_ibytes(ev.kids[k].ty)
|
||||
k = k + 1
|
||||
off += net_field_ibytes(ev.kids[k].ty)
|
||||
k += 1
|
||||
}
|
||||
var peer = "0"
|
||||
if (ev.ty == "toclients") { peer = "-1" } # broadcast (loopback ignores the peer id)
|
||||
|
|
@ -292,7 +296,7 @@ function emit_emit(st: Node) -> Val {
|
|||
while g < len(fcodes) {
|
||||
if g > 0 { buf_puts(args, ", ") }
|
||||
buf_puts(args, ftys[g]); buf_puts(args, " "); buf_puts(args, fcodes[g])
|
||||
g = g + 1
|
||||
g += 1
|
||||
}
|
||||
if ev.ival == 1 {
|
||||
let r = nreg()
|
||||
|
|
@ -304,6 +308,7 @@ function emit_emit(st: Node) -> Val {
|
|||
}
|
||||
|
||||
function emit_stmt(st: Node) -> void {
|
||||
g_err_file = st.file; g_err_line = st.line # so a lowering error names its statement
|
||||
emit_cov_hit(st.line) # --coverage: bump this line's hit counter (no-op otherwise)
|
||||
if st.kind == S_LET {
|
||||
var ty = st.ty
|
||||
|
|
@ -347,6 +352,6 @@ function emit_stmt(st: Node) -> void {
|
|||
function loop_push(cont: pointer, brk: pointer) -> void {
|
||||
if nloop < len(cnt_lbl) { cnt_lbl[nloop] = cont; brk_lbl[nloop] = brk }
|
||||
else { push(cnt_lbl, cont); push(brk_lbl, brk) }
|
||||
nloop = nloop + 1
|
||||
nloop += 1
|
||||
}
|
||||
function loop_pop() -> void { nloop = nloop - 1 }
|
||||
function loop_pop() -> void { nloop -= 1 }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue