feat(base): 0.R4 - a queue keeps its own count, so every package verb takes only its own state; ludic migrate state --prune
ludic.base's Queue<T> carries a QueueTag (its name and pending count): queue_new(name), q_push(q, v), q_drain(q), q_clear(q) take no BaseState, and core_undrained(tags) names the given queues still holding facts. A reducer on a package's state can now call that package's verbs (wallet_earn(wallet_st, n)). ludic migrate state --prune (ludicc --migrate-prune) takes out each state parameter a function no longer uses, nor anything it calls, and the argument that fills it - including an argument for a parameter the callee has dropped, which is taken out before the call is checked, so a generic's T is told by the argument that says it. A reducer keeps its state. --dry-run now counts the edits it would make. Every package was moved with it: 608 base_st parameters and their arguments, 1889 edits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8cf4b3fed6
commit
71735b10a2
151 changed files with 58668 additions and 54719 deletions
|
|
@ -70,7 +70,10 @@ function ck_expr(e: Node) -> pointer {
|
|||
}
|
||||
function ck_id(e: Node) -> pointer {
|
||||
let li = ck_local(e.s)
|
||||
if li >= 0 { return ck_tys[li] }
|
||||
if li >= 0 {
|
||||
mg_local_use(e, ck_tys[li]) # 0.S2 --migrate-prune: a state used here
|
||||
return ck_tys[li]
|
||||
}
|
||||
let g = ck_global(e.s)
|
||||
if g != null {
|
||||
mg_ref(e, g)
|
||||
|
|
|
|||
|
|
@ -73,6 +73,8 @@ function ck_write_check(t: Node, what: pointer) -> void {
|
|||
if r == null { return }
|
||||
let li = ck_local(r.s)
|
||||
if li >= 0 {
|
||||
mg_local_write(ck_tys[li]) # 0.S2 --migrate-prune: a state changed here
|
||||
if li < len(ck_ro_st) { mg_local_write(ck_ro_st[li]) }
|
||||
if li < len(ck_alias) and ck_alias[li] != null {
|
||||
let ag = ck_alias[li]
|
||||
mg_write(t, ag)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@
|
|||
# ? <file>:<line> <what> what the tool cannot decide, for a person
|
||||
var g_migrate: bool = false
|
||||
var g_mg_runtime: bool = false # --migrate-runtime: the runtime's own vars too
|
||||
var g_mg_prune: bool = false # --migrate-prune: a declared state nothing uses is taken out
|
||||
var g_ma_node: []Node = new []Node # each state argument a call passes (an E_ID), its call, its state
|
||||
var g_ma_call: []int = new []int
|
||||
var g_ma_state: []int = new []int
|
||||
var g_ma_excess: []bool = new []bool # an argument for a parameter the callee no longer has: it goes
|
||||
var g_mg_plan: pointer = null
|
||||
var g_mg_vars: []Node = new []Node # the vars that move
|
||||
var g_mg_vstate: []int = new []int # the state each moves into
|
||||
|
|
@ -280,8 +285,9 @@ function mg_unit(n: Node, kind: int) -> int {
|
|||
push(g_mu_need, ",")
|
||||
push(g_mu_mut, ",")
|
||||
n.mg = len(g_mu_node) - 1
|
||||
# an already-migrated function: its declared states are needs
|
||||
if kind == 0 {
|
||||
# an already-migrated function: its declared states are needs - unless pruning, where only what
|
||||
# it uses (and what what it calls uses) is
|
||||
if kind == 0 and not g_mg_prune {
|
||||
var i = 0
|
||||
while i < len(n.kids) and n.kids[i].kind == N_PARAM and is_state_ty(n.kids[i].ty) {
|
||||
let s = mg_state_named(n.kids[i].ty)
|
||||
|
|
@ -426,6 +432,7 @@ function mg_call(e: Node, f: Node) -> void {
|
|||
push(g_mc_call, e)
|
||||
push(g_mc_unit, g_mg_cur)
|
||||
push(g_mc_callee, mg_unit(f, 0))
|
||||
mg_state_args(e, f, len(g_mc_call) - 1)
|
||||
var passed = 0
|
||||
if len(e.kids) > 0 and e.kids[0].kind == E_ID {
|
||||
let li = ck_local(e.kids[0].s)
|
||||
|
|
@ -433,6 +440,65 @@ function mg_call(e: Node, f: Node) -> void {
|
|||
}
|
||||
push(g_mc_passed, passed)
|
||||
}
|
||||
# the states a call hands on as plain names: a passing is not a use (the callee's use, through the
|
||||
# fixed point, is), and with --migrate-prune one the callee does not take (any more) goes
|
||||
function mg_state_args(e: Node, f: Node, c: int) -> void {
|
||||
var np = 0
|
||||
var k = 0
|
||||
while k < len(f.kids) {
|
||||
if f.kids[k].kind == N_PARAM { np += 1 }
|
||||
k += 1
|
||||
}
|
||||
var excess = len(e.kids) - np # arguments for parameters the callee no longer has
|
||||
let keep = new []Node
|
||||
var i = 0
|
||||
while i < len(e.kids) {
|
||||
let a = e.kids[i]
|
||||
var gone = false
|
||||
if a.kind == E_ID {
|
||||
let li = ck_local(a.s)
|
||||
if li >= 0 and is_state_ty(ck_tys[li]) {
|
||||
var passed = mg_declared_ty(f, ck_tys[li])
|
||||
if not passed and excess > 0 {
|
||||
gone = true
|
||||
excess -= 1
|
||||
}
|
||||
if passed or gone {
|
||||
a.mg = -2 # marked: ck_id does not count it as a use
|
||||
push(g_ma_node, a)
|
||||
push(g_ma_call, c)
|
||||
push(g_ma_state, mg_state_named(ck_tys[li]))
|
||||
push(g_ma_excess, gone)
|
||||
}
|
||||
}
|
||||
}
|
||||
if not (gone and g_mg_prune) { push(keep, a) }
|
||||
i += 1
|
||||
}
|
||||
# with --migrate-prune the call is checked as it will read: without the arguments that go, so the
|
||||
# rest meet their parameters (and a generic's T is told by the argument that says it)
|
||||
if g_mg_prune and len(keep) < len(e.kids) { e.kids = keep }
|
||||
}
|
||||
function mg_declared_ty(f: Node, ty: pointer) -> bool {
|
||||
var i = 0
|
||||
while i < len(f.kids) {
|
||||
if f.kids[i].kind == N_PARAM and (f.kids[i].ty == ty) { return true }
|
||||
i += 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
# with --migrate-prune, a state local used in place (read, written, handed to anything but a
|
||||
# function taking it) is a need of the unit it is used in
|
||||
function mg_local_use(e: Node, ty: pointer) -> void {
|
||||
if not g_migrate or not g_mg_prune or g_mg_cur < 0 or e.mg == -2 or e.pos < 0 { return }
|
||||
if not is_state_ty(ty) { return }
|
||||
mg_need(g_mg_cur, mg_state_named(ty), false)
|
||||
}
|
||||
function mg_local_write(ty: pointer) -> void {
|
||||
if not g_migrate or not g_mg_prune or g_mg_cur < 0 or ty == null { return }
|
||||
if not is_state_ty(ty) { return }
|
||||
mg_need(g_mg_cur, mg_state_named(ty), true)
|
||||
}
|
||||
function mg_say(n: Node, what: pointer) -> void {
|
||||
var at = "?"
|
||||
if n != null and n.file != null { at = `{n.file}:{itoa(n.line)}` }
|
||||
|
|
@ -522,7 +588,7 @@ function mg_norm_line(line: pointer) -> pointer {
|
|||
}
|
||||
if len(w) < 2 { return line }
|
||||
let k = w[0]
|
||||
if (k == "VAR") or (k == "REF") or (k == "INS") or (k == "LET") or (k == "CLOSE") { w[1] = mg_norm(w[1]) }
|
||||
if (k == "VAR") or (k == "REF") or (k == "INS") or (k == "LET") or (k == "CLOSE") or (k == "DELITEM") { w[1] = mg_norm(w[1]) }
|
||||
if (k == "?") { w[1] = mg_norm(w[1]) }
|
||||
if (k == "VAR") and len(w) > 5 { w[5] = mg_norm_key(w[5]) }
|
||||
if (k == "STATE") and len(w) > 6 {
|
||||
|
|
@ -678,6 +744,7 @@ function mg_units() -> void {
|
|||
continue
|
||||
}
|
||||
mg_mut_upgrades(u, n)
|
||||
if g_mg_prune { mg_prune_unit(u, n) }
|
||||
let need = mg_list(g_mu_need[u])
|
||||
if len(need) > 0 {
|
||||
var text = ""
|
||||
|
|
@ -694,6 +761,50 @@ function mg_units() -> void {
|
|||
u += 1
|
||||
}
|
||||
}
|
||||
# --migrate-prune: each state a unit declares and does not need (nor anything it calls) goes
|
||||
function mg_prune_unit(u: int, n: Node) -> void {
|
||||
if n.file == null or (is_runtime_file(n.file) and not g_mg_runtime) { return }
|
||||
let kind = g_mu_kind[u]
|
||||
if kind == 0 {
|
||||
if n.vw >= 0 { return }
|
||||
if s_has_prefix(n.s, "ludic_reduce__") { return } # a reducer takes its state whether it reads it or not
|
||||
var i = 0
|
||||
while i < len(n.kids) {
|
||||
let p = n.kids[i]
|
||||
if p.kind == N_PARAM and is_state_ty(p.ty) and p.pos >= 0 and p.file != null {
|
||||
if not has_sub(g_mu_need[u], `,{itoa(mg_state_named(p.ty))},`) { mg_emit(`DELITEM {p.file} {itoa(p.pos)}`, "") }
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
var body = n.a
|
||||
if kind == 5 { body = n }
|
||||
if body == null { return }
|
||||
var j = 0
|
||||
while j < len(body.kids) and body.kids[j].tps != null and (body.kids[j].tps == "state") {
|
||||
let l = body.kids[j]
|
||||
if l.pos >= 0 and l.file != null and not has_sub(g_mu_need[u], `,{itoa(mg_state_named(l.ty))},`) {
|
||||
mg_emit(`DELITEM {l.file} {itoa(l.pos)}`, "")
|
||||
}
|
||||
j += 1
|
||||
}
|
||||
}
|
||||
# --migrate-prune: a state a call hands on that its callee does not take, or no longer needs
|
||||
function mg_prune_args() -> void {
|
||||
var a = 0
|
||||
while a < len(g_ma_node) {
|
||||
let x = g_ma_node[a]
|
||||
let c = g_ma_call[a]
|
||||
let to = g_mc_callee[c]
|
||||
let callee = g_mu_node[to]
|
||||
let unused = callee.cm < 0 and not has_sub(g_mu_need[to], `,{itoa(g_ma_state[a])},`)
|
||||
if x.pos >= 0 and x.file != null and (g_ma_excess[a] or unused) {
|
||||
mg_emit(`DELITEM {x.file} {itoa(x.pos)}`, "")
|
||||
}
|
||||
a += 1
|
||||
}
|
||||
}
|
||||
var g_mv_need: []pointer = new []pointer # per view: the states its functions need
|
||||
var g_mv_mut: []pointer = new []pointer
|
||||
function mg_unit_edit(u: int, n: Node, text: pointer) -> void {
|
||||
|
|
@ -784,8 +895,20 @@ function mg_comp_acc(u: int, c: int) -> void {
|
|||
# each component's header: the states its members need that it does not declare yet, and `mut` on
|
||||
# one it declares read-only that a member now changes
|
||||
function mg_comps() -> void {
|
||||
while len(g_mcm_need) < len(g_cm_name) {
|
||||
push(g_mcm_need, ",")
|
||||
push(g_mcm_mut, ",")
|
||||
}
|
||||
var c = 0
|
||||
while c < len(g_mcm_need) {
|
||||
if g_mg_prune { # a header state no member needs
|
||||
var q = 0
|
||||
while q < len(g_cm_ps[c]) {
|
||||
let p = g_cm_ps[c][q]
|
||||
if p.pos >= 0 and not has_sub(g_mcm_need[c], `,{itoa(mg_state_named(p.ty))},`) { mg_emit(`DELITEM {g_cm_file[c]} {itoa(p.pos)}`, "") }
|
||||
q += 1
|
||||
}
|
||||
}
|
||||
let need = mg_list(g_mcm_need[c])
|
||||
let ps = g_cm_ps[c]
|
||||
var text = ""
|
||||
|
|
@ -972,6 +1095,7 @@ function mg_finish() -> void {
|
|||
mg_refs()
|
||||
mg_binds()
|
||||
mg_units()
|
||||
if g_mg_prune { mg_prune_args() }
|
||||
mg_comps()
|
||||
mg_views()
|
||||
mg_calls()
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ function entry_bind(body: Node, ps: []Node) -> void {
|
|||
l.uns = p.uns # 1: a `mut` state; 0: read-only
|
||||
l.tps = "state" # an entry point's state: checked to be one
|
||||
l.pos2 = p.pos2
|
||||
l.pos = p.pos
|
||||
let id = node(E_ID)
|
||||
id.s = state_global(p.ty)
|
||||
id.file = p.file
|
||||
|
|
|
|||
54695
selfhost/ludicc.seed.ll
54695
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -160,6 +160,7 @@ entry {
|
|||
g_migrate = true
|
||||
g_allow_globals = true
|
||||
}
|
||||
else if a == "--migrate-prune" { g_mg_prune = true } # 0.S2: state parameters nothing uses go too
|
||||
else if a == "--migrate-runtime" { g_mg_runtime = true } # 0.S: the toolchain's own programs keep module-level vars # parse, type, module and port checks; nothing emitted
|
||||
else if a == "--coverage" { g_coverage = true }
|
||||
else if a == "--target" { ai += 1; if ai < arg_count() { target = arg(ai) } }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue