feat(cli): 0.R5 - ludic deps says what a function can come to change (widest_write_reach, --wreach N); a port member and a registry field reach only themselves
A reach through Port.member() follows that member's binding (or its default), and Registry[i].field - or a local holding Registry[i] - follows that field in each entry, so a question asked of a port or a table that also holds verbs no longer reaches the verbs. In Maroon Lake that took the valley's 'what is this Thing called' from 47 states it could change to 1. examples/state/write_reach.ludic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c64f8750e2
commit
5df0747642
7 changed files with 46858 additions and 43583 deletions
7
changes/deps-write-reach.md
Normal file
7
changes/deps-write-reach.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
bump: minor
|
||||
type: feat
|
||||
**`ludic deps` says what a function can come to CHANGE** (`widest_write_reach`, and `--wreach N`
|
||||
lists the functions by it): the states it reaches as `mut`, through calls, `fn` values and step
|
||||
lists. Reach itself is sharper: `Port.member()` reaches that member's binding only, and
|
||||
`Registry[i].field` (or a local holding `Registry[i]`) reaches that field only - a question asked of
|
||||
a port or a table that also holds verbs no longer reaches the verbs.
|
||||
35
examples/state/write_reach.ludic
Normal file
35
examples/state/write_reach.ludic
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# ludic deps --wreach: what a function can come to CHANGE. A port asked one member reaches that
|
||||
# member's binding only, and a registry read for one field reaches that field only - so a
|
||||
# question stays a question even when the port or the table beside it also holds a verb.
|
||||
program WriteReach {
|
||||
state Store { n: int = 0 }
|
||||
state Log { lines: int = 0 }
|
||||
function store_count(s: Store) -> int { return s.n }
|
||||
function store_add(s: mut Store) -> void { s.n += 1 }
|
||||
port Shelf {
|
||||
count: fn() -> int
|
||||
add: fn() -> void
|
||||
}
|
||||
bind Shelf { count: fn store_count, add: fn store_add }
|
||||
function log_note(l: mut Log) -> void { l.lines += 1 }
|
||||
property Kind {
|
||||
key: string = ""
|
||||
size: int = 0
|
||||
note: fn() -> void = null
|
||||
}
|
||||
registry Kinds of Kind as KD
|
||||
def Kinds box { size: 3, note: fn log_note }
|
||||
function asks() -> int { return Shelf.count() + Kinds[KD_BOX].size }
|
||||
function asks_local() -> int {
|
||||
let k = Kinds[KD_BOX]
|
||||
return k.size
|
||||
}
|
||||
function changes() -> void {
|
||||
Shelf.add()
|
||||
Kinds[KD_BOX].note()
|
||||
}
|
||||
entry {
|
||||
changes()
|
||||
print(`{asks()} {asks_local()}`)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
# alias <owner> <global> <from> <file>:<line> <local> (a write through a local, below)
|
||||
# width <n> <name> <file>:<line> <pkg> (the states it takes)
|
||||
# reach <n> <name> <file>:<line> <pkg> (the states it can come to, fn values too)
|
||||
# wreach <n> <name> <file>:<line> <pkg> (of those, the ones it can come to change: `mut`)
|
||||
var g_dp_on: int = -1
|
||||
var g_dp_key: []pointer = new []pointer # "from to"
|
||||
var g_dp_cnt: []int = new []int
|
||||
|
|
@ -273,7 +274,10 @@ var g_dr_name: []pointer = new []pointer # the declarations a name can re
|
|||
var g_dr_node: []Node = new []Node
|
||||
var g_dr_refs: [][]int = new [][]int # what each one names
|
||||
var g_dr_bits: [][]int = new [][]int # the states it reaches, 60 to a word
|
||||
var g_dr_wbits: [][]int = new [][]int # ... and of them the ones it takes `mut` somewhere down
|
||||
var g_dr_find_k: []pointer = new []pointer
|
||||
var g_dr_rl_name: []pointer = new []pointer # a local holding one entry of a registry ...
|
||||
var g_dr_rl_reg: []pointer = new []pointer # ... and which registry
|
||||
var g_dr_find_v: []Node = new []Node
|
||||
function dr_state_ix(t: pointer) -> int {
|
||||
var i = 0
|
||||
|
|
@ -292,9 +296,20 @@ function dr_index(name: pointer) -> int {
|
|||
}
|
||||
function dr_walk(n: Node, refs: []int) -> void {
|
||||
if n == null { return }
|
||||
if dr_port_member(n, refs) { return }
|
||||
if n.kind == S_LET and n.s != null and n.a != null and n.a.kind == E_INDEX and n.a.a != null and n.a.a.kind == E_ID and n.a.a.s != null and reg_find(n.a.a.s) >= 0 {
|
||||
push(g_dr_rl_name, n.s)
|
||||
push(g_dr_rl_reg, n.a.a.s)
|
||||
dr_walk(n.a.b, refs)
|
||||
return
|
||||
}
|
||||
if dr_reg_field(n, refs) { return }
|
||||
if (n.kind == E_ID or n.kind == E_FNREF) and n.s != null {
|
||||
let k = dr_index(n.s)
|
||||
if k >= 0 { push(refs, k) }
|
||||
# a registry entry held in a local and handed on whole reaches the whole registry
|
||||
let rl = dr_reg_local(n.s)
|
||||
if k < 0 and rl != null and dr_index(rl) >= 0 { push(refs, dr_index(rl)) }
|
||||
}
|
||||
dr_walk(n.a, refs)
|
||||
dr_walk(n.b, refs)
|
||||
|
|
@ -307,11 +322,75 @@ function dr_walk(n: Node, refs: []int) -> void {
|
|||
}
|
||||
}
|
||||
}
|
||||
function dr_own(d: Node, bits: []int) -> void {
|
||||
# `Port.member` reaches what that member is bound to (or its default), not every member of the port:
|
||||
# a port asked for its save is not asked for its load
|
||||
function dr_port_member(n: Node, refs: []int) -> bool {
|
||||
if n.kind != E_MEMBER or n.a == null or n.a.kind != E_ID or n.a.s == null or n.s == null { return false }
|
||||
let k = dr_index(n.a.s)
|
||||
if k < 0 { return false }
|
||||
let v = g_dr_node[k]
|
||||
if not is_port_var(v) { return false }
|
||||
if v.a != null and v.a.kind == E_NEW and v.a.a != null {
|
||||
let rec = v.a.a
|
||||
var i = 0
|
||||
while i < len(rec.kids) {
|
||||
if (rec.kids[i].s == n.s) {
|
||||
dr_walk(rec.kids[i].a, refs)
|
||||
return true
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
let c = g_pt_comp[port_find(v.s)]
|
||||
var j = 0
|
||||
while j < len(c.kids) {
|
||||
if (c.kids[j].s == n.s) { dr_walk(c.kids[j].a, refs) }
|
||||
j += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
# `Registry[i].field` reaches what that field holds in each entry, not every fn of every field: a
|
||||
# kind asked whether it is personal is not asked to be used
|
||||
function dr_reg_local(name: pointer) -> pointer {
|
||||
var i = len(g_dr_rl_name) - 1
|
||||
while i >= 0 {
|
||||
if (g_dr_rl_name[i] == name) { return g_dr_rl_reg[i] }
|
||||
i -= 1
|
||||
}
|
||||
return null
|
||||
}
|
||||
function dr_reg_field(n: Node, refs: []int) -> bool {
|
||||
if n.kind != E_MEMBER or n.s == null or n.a == null { return false }
|
||||
var reg: pointer = null
|
||||
if n.a.kind == E_INDEX and n.a.a != null and n.a.a.kind == E_ID and n.a.a.s != null and reg_find(n.a.a.s) >= 0 {
|
||||
reg = n.a.a.s
|
||||
dr_walk(n.a.b, refs)
|
||||
}
|
||||
if reg == null and n.a.kind == E_ID and n.a.s != null { reg = dr_reg_local(n.a.s) }
|
||||
if reg == null { return false }
|
||||
let k = dr_index(reg)
|
||||
if k < 0 { return false }
|
||||
let v = g_dr_node[k]
|
||||
if v.kind != N_VAR or v.a == null or v.a.kind != E_LIST { return false }
|
||||
var i = 0
|
||||
while i < len(v.a.kids) {
|
||||
let e = v.a.kids[i]
|
||||
if e.kind == E_NEW and e.a != null {
|
||||
var j = 0
|
||||
while j < len(e.a.kids) {
|
||||
if (e.a.kids[j].s == n.s) { dr_walk(e.a.kids[j].a, refs) }
|
||||
j += 1
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return true
|
||||
}
|
||||
function dr_own(d: Node, bits: []int, only_mut: bool) -> void {
|
||||
if d.kind == N_FN {
|
||||
var k = 0
|
||||
while k < len(d.kids) {
|
||||
if d.kids[k].kind == N_PARAM {
|
||||
if d.kids[k].kind == N_PARAM and (not only_mut or d.kids[k].uns == 1) {
|
||||
let s = dr_state_ix(d.kids[k].ty)
|
||||
if s >= 0 { dr_set(bits, s) }
|
||||
}
|
||||
|
|
@ -332,6 +411,20 @@ function dr_count(bits: []int) -> int {
|
|||
}
|
||||
return n
|
||||
}
|
||||
# `mine` takes in `theirs`; true when it grew
|
||||
function dr_join(mine: []int, theirs: []int, nw: int) -> bool {
|
||||
var grew = false
|
||||
var w = 0
|
||||
while w < nw {
|
||||
let u = mine[w] | theirs[w]
|
||||
if u != mine[w] {
|
||||
mine[w] = u
|
||||
grew = true
|
||||
}
|
||||
w += 1
|
||||
}
|
||||
return grew
|
||||
}
|
||||
function deps_reach(f: pointer) -> void {
|
||||
ck_tab_init(g_dr_find_k, g_dr_find_v)
|
||||
var i = 0
|
||||
|
|
@ -350,6 +443,8 @@ function deps_reach(f: pointer) -> void {
|
|||
var j = 0
|
||||
while j < len(g_dr_node) {
|
||||
let refs = new []int
|
||||
g_dr_rl_name = new []pointer
|
||||
g_dr_rl_reg = new []pointer
|
||||
dr_walk(g_dr_node[j], refs)
|
||||
push(g_dr_refs, refs)
|
||||
let bits = new []int
|
||||
|
|
@ -358,8 +453,16 @@ function deps_reach(f: pointer) -> void {
|
|||
push(bits, 0)
|
||||
w += 1
|
||||
}
|
||||
dr_own(g_dr_node[j], bits)
|
||||
dr_own(g_dr_node[j], bits, false)
|
||||
push(g_dr_bits, bits)
|
||||
let wb = new []int
|
||||
var w3 = 0
|
||||
while w3 < nw {
|
||||
push(wb, 0)
|
||||
w3 += 1
|
||||
}
|
||||
dr_own(g_dr_node[j], wb, true)
|
||||
push(g_dr_wbits, wb)
|
||||
j += 1
|
||||
}
|
||||
var changed = true
|
||||
|
|
@ -367,20 +470,11 @@ function deps_reach(f: pointer) -> void {
|
|||
changed = false
|
||||
j = 0
|
||||
while j < len(g_dr_node) {
|
||||
let mine = g_dr_bits[j]
|
||||
let refs = g_dr_refs[j]
|
||||
var r = 0
|
||||
while r < len(refs) {
|
||||
let theirs = g_dr_bits[refs[r]]
|
||||
var w2 = 0
|
||||
while w2 < nw {
|
||||
let u = mine[w2] | theirs[w2]
|
||||
if u != mine[w2] {
|
||||
mine[w2] = u
|
||||
changed = true
|
||||
}
|
||||
w2 += 1
|
||||
}
|
||||
if dr_join(g_dr_bits[j], g_dr_bits[refs[r]], nw) { changed = true }
|
||||
if dr_join(g_dr_wbits[j], g_dr_wbits[refs[r]], nw) { changed = true }
|
||||
r += 1
|
||||
}
|
||||
j += 1
|
||||
|
|
@ -394,6 +488,8 @@ function deps_reach(f: pointer) -> void {
|
|||
var pkg = 0
|
||||
if not (pkg_of_file(d.file) == "") { pkg = 1 }
|
||||
if n > 0 { deps_line(f, `reach {itoa(n)} {d.s} {d.file}:{itoa(d.line)} {itoa(pkg)}`) }
|
||||
let wn = dr_count(g_dr_wbits[j])
|
||||
if wn > 0 { deps_line(f, `wreach {itoa(wn)} {d.s} {d.file}:{itoa(d.line)} {itoa(pkg)}`) }
|
||||
}
|
||||
j += 1
|
||||
}
|
||||
|
|
|
|||
45105
selfhost/ludicc.seed.ll
45105
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -15,6 +15,7 @@
|
|||
# ludic deps --baseline FILE write the numbers to FILE
|
||||
# ludic deps --widest N the N functions that take the most states, and what each reaches
|
||||
# ludic deps --reach N the same, the N that reach the most
|
||||
# ludic deps --wreach N the N that can come to CHANGE the most states
|
||||
#
|
||||
# dependencies: module pairs (a, b) where a uses something of b, not counting b when b uses no
|
||||
# other module (a leaf); largest_cycle: the largest set of modules that all reach each other, not
|
||||
|
|
@ -23,7 +24,8 @@
|
|||
# globals, each counted once; widest_function: the most states any function or entry point of the
|
||||
# program's own takes (0.R2 - a reducer takes one, and the dispatchers should come down to a few);
|
||||
# widest_reach: the most states any of them can come to - what it takes, and through calls, `fn f`
|
||||
# and globals holding fn values (a step list, a registry of systems) what those take (0.R4).
|
||||
# and globals holding fn values (a step list, a registry of systems) what those take (0.R4);
|
||||
# widest_write_reach: of those, the most it can come to change - taken `mut` somewhere down (0.R5).
|
||||
var dp_mods: []pointer = null
|
||||
var dp_pkg: []int = null
|
||||
var dp_uses: []pointer = null
|
||||
|
|
@ -45,6 +47,9 @@ var dp_reach_own: int = 0 # ... of which it takes itself
|
|||
var dp_fn_at: []pointer = null # each of the program's own functions: where, what it takes, what it reaches
|
||||
var dp_fn_w: []int = null
|
||||
var dp_fn_r: []int = null
|
||||
var dp_fn_x: []int = null # ... and the states it can come to change
|
||||
var dp_wreach_n: int = 0
|
||||
var dp_wreach_at: pointer = ""
|
||||
|
||||
function dp_words(line: pointer) -> []pointer {
|
||||
let out = new []pointer
|
||||
|
|
@ -79,7 +84,8 @@ function dp_load(path: pointer) -> bool {
|
|||
dp_ef = new []int; dp_et = new []int; dp_ec = new []int; dp_en = new []pointer
|
||||
dp_wide_n = 0; dp_wide_at = ""
|
||||
dp_reach_n = 0; dp_reach_at = ""; dp_reach_own = 0
|
||||
dp_fn_at = new []pointer; dp_fn_w = new []int; dp_fn_r = new []int
|
||||
dp_fn_at = new []pointer; dp_fn_w = new []int; dp_fn_r = new []int; dp_fn_x = new []int
|
||||
dp_wreach_n = 0; dp_wreach_at = ""
|
||||
dp_writes = new []pointer; dp_aliases = new []pointer; dp_wowner = new []pointer; dp_wname = new []pointer; dp_wfrom = new []pointer
|
||||
let lines = split_lines(text)
|
||||
for i in 0 .. len(lines) {
|
||||
|
|
@ -105,9 +111,9 @@ function dp_load(path: pointer) -> bool {
|
|||
dp_wide_n = s_to_int(w[1])
|
||||
dp_wide_at = `{dp_fn_name(w[2])} ({w[3]})`
|
||||
}
|
||||
if len(w) >= 5 and (w[0] == "width" or w[0] == "reach") and w[4] == "0" {
|
||||
if len(w) >= 5 and (w[0] == "width" or w[0] == "reach" or w[0] == "wreach") and w[4] == "0" {
|
||||
let k = dp_fn(`{dp_fn_name(w[2])} ({w[3]})`)
|
||||
if w[0] == "width" { dp_fn_w[k] = s_to_int(w[1]) } else { dp_fn_r[k] = s_to_int(w[1]) }
|
||||
if w[0] == "width" { dp_fn_w[k] = s_to_int(w[1]) } else if w[0] == "reach" { dp_fn_r[k] = s_to_int(w[1]) } else { dp_fn_x[k] = s_to_int(w[1]) }
|
||||
}
|
||||
if len(w) >= 5 and w[0] == "write" {
|
||||
push(dp_wowner, w[1])
|
||||
|
|
@ -124,6 +130,7 @@ function dp_fn(at: pointer) -> int {
|
|||
push(dp_fn_at, at)
|
||||
push(dp_fn_w, 0)
|
||||
push(dp_fn_r, 0)
|
||||
push(dp_fn_x, 0)
|
||||
return len(dp_fn_at) - 1
|
||||
}
|
||||
# after the load: the widest reach (a function reaches at least what it takes)
|
||||
|
|
@ -135,42 +142,51 @@ function dp_reach_best() -> void {
|
|||
dp_reach_at = dp_fn_at[i]
|
||||
dp_reach_own = dp_fn_w[i]
|
||||
}
|
||||
if dp_fn_x[i] > dp_wreach_n {
|
||||
dp_wreach_n = dp_fn_x[i]
|
||||
dp_wreach_at = dp_fn_at[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
# --widest N / --reach N: the top N functions by what they take (or by what they reach), both shown
|
||||
function dp_top(n: int, by_reach: bool) -> void {
|
||||
function dp_top(n: int, by: int) -> void {
|
||||
let order = new []int
|
||||
for i in 0 .. len(dp_fn_at) { push(order, i) }
|
||||
for a in 1 .. len(order) {
|
||||
let x = order[a]
|
||||
var b = a - 1
|
||||
while b >= 0 and dp_top_before(x, order[b], by_reach) {
|
||||
while b >= 0 and dp_top_before(x, order[b], by) {
|
||||
order[b + 1] = order[b]
|
||||
b -= 1
|
||||
}
|
||||
order[b + 1] = x
|
||||
}
|
||||
print("takes reaches function")
|
||||
print("takes reaches changes function")
|
||||
var shown = 0
|
||||
for i in 0 .. len(order) {
|
||||
let k = order[i]
|
||||
if shown < n and (dp_fn_w[k] > 0 or dp_fn_r[k] > 0) {
|
||||
print(`{dp_pad(string(dp_fn_w[k]), 5)} {dp_pad(string(dp_fn_r[k]), 7)} {dp_fn_at[k]}`)
|
||||
print(`{dp_pad(string(dp_fn_w[k]), 5)} {dp_pad(string(dp_fn_r[k]), 7)} {dp_pad(string(dp_fn_x[k]), 7)} {dp_fn_at[k]}`)
|
||||
shown += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
function dp_top_before(x: int, y: int, by_reach: bool) -> bool {
|
||||
# by: 0 what it takes, 1 what it reaches, 2 what it can come to change
|
||||
function dp_top_before(x: int, y: int, by: int) -> bool {
|
||||
var kx = dp_fn_w[x]
|
||||
var ky = dp_fn_w[y]
|
||||
var tx = dp_fn_r[x]
|
||||
var ty = dp_fn_r[y]
|
||||
if by_reach {
|
||||
if by == 1 {
|
||||
kx = dp_fn_r[x]
|
||||
ky = dp_fn_r[y]
|
||||
tx = dp_fn_w[x]
|
||||
ty = dp_fn_w[y]
|
||||
}
|
||||
if by == 2 {
|
||||
kx = dp_fn_x[x]
|
||||
ky = dp_fn_x[y]
|
||||
}
|
||||
if kx != ky { return kx > ky }
|
||||
return tx > ty
|
||||
}
|
||||
|
|
@ -290,7 +306,7 @@ var dp_vals: []int = null
|
|||
var dp_cycle: []int = null
|
||||
var dp_cycle_all: []int = null # the same with the layers' own edges counted
|
||||
function dp_numbers() -> void {
|
||||
dp_names = ["modules", "dependencies", "largest_cycle", "cross_writes", "globals_written_from_outside", "widest_function", "widest_reach"]
|
||||
dp_names = ["modules", "dependencies", "largest_cycle", "cross_writes", "globals_written_from_outside", "widest_function", "widest_reach", "widest_write_reach"]
|
||||
var mods = 0
|
||||
for k in 0 .. len(dp_mods) { if dp_own(k) { mods += 1 } }
|
||||
var deps = 0
|
||||
|
|
@ -312,7 +328,7 @@ function dp_numbers() -> void {
|
|||
}
|
||||
}
|
||||
dp_reach_best()
|
||||
dp_vals = [mods, deps, len(dp_cycle), writes, len(seen), dp_wide_n, dp_reach_n]
|
||||
dp_vals = [mods, deps, len(dp_cycle), writes, len(seen), dp_wide_n, dp_reach_n, dp_wreach_n]
|
||||
}
|
||||
|
||||
# ---- the command --------------------------------------------------------------
|
||||
|
|
@ -322,6 +338,8 @@ function dp_print_numbers() -> void {
|
|||
if dp_wide_n == 1 { print(`the widest function: {dp_wide_at}, 1 state`) }
|
||||
if dp_wide_n > 1 { print(`the widest function: {dp_wide_at}, {string(dp_wide_n)} states`) }
|
||||
if dp_reach_n > dp_reach_own { print(`the widest reach: {dp_reach_at}, {string(dp_reach_n)} states ({string(dp_reach_n - dp_reach_own)} through calls and fn values it does not take)`) }
|
||||
if dp_wreach_n == 1 { print(`the widest write reach: {dp_wreach_at}, 1 state it can come to change`) }
|
||||
if dp_wreach_n > 1 { print(`the widest write reach: {dp_wreach_at}, {string(dp_wreach_n)} states it can come to change`) }
|
||||
let layers = dp_layers()
|
||||
if layers != "" {
|
||||
print(`layers: {layers}`)
|
||||
|
|
@ -464,7 +482,7 @@ function cmd_deps() -> int {
|
|||
if a == "--dot" { mode = "dot" }
|
||||
else if a == "--graph" { mode = "graph" }
|
||||
else if a == "--writes" { mode = "writes" }
|
||||
else if a == "--uses" or a == "--check" or a == "--baseline" or a == "--widest" or a == "--reach" {
|
||||
else if a == "--uses" or a == "--check" or a == "--baseline" or a == "--widest" or a == "--reach" or a == "--wreach" {
|
||||
if ai + 1 >= arg_count() { err(`ludic deps: {a} needs an argument\n`); return 2 }
|
||||
mode = sslice(a, 2, slen(a))
|
||||
ai += 1
|
||||
|
|
@ -472,7 +490,7 @@ function cmd_deps() -> int {
|
|||
}
|
||||
else if a[0] == '-' {
|
||||
err(`ludic deps: unknown option {a}\n`)
|
||||
err(" usage: ludic deps [file] [--graph | --dot | --writes | --uses MOD | --check FILE | --baseline FILE | --widest N | --reach N]\n")
|
||||
err(" usage: ludic deps [file] [--graph | --dot | --writes | --uses MOD | --check FILE | --baseline FILE | --widest N | --reach N | --wreach N]\n")
|
||||
return 2
|
||||
}
|
||||
else { src = a }
|
||||
|
|
@ -505,8 +523,9 @@ function cmd_deps() -> int {
|
|||
return 0
|
||||
}
|
||||
if mode == "uses" { dp_uses_of(arg2); return 0 }
|
||||
if mode == "widest" { dp_top(s_to_int(arg2), false); return 0 }
|
||||
if mode == "reach" { dp_top(s_to_int(arg2), true); return 0 }
|
||||
if mode == "widest" { dp_top(s_to_int(arg2), 0); return 0 }
|
||||
if mode == "reach" { dp_top(s_to_int(arg2), 1); return 0 }
|
||||
if mode == "wreach" { dp_top(s_to_int(arg2), 2); return 0 }
|
||||
if mode == "check" { return dp_check(arg2) }
|
||||
if mode == "baseline" { return dp_baseline(arg2) }
|
||||
dp_print_numbers()
|
||||
|
|
|
|||
|
|
@ -476,7 +476,7 @@ function deps_case() -> void {
|
|||
let lbl = "ludic deps: the numbers, --check against a baseline, --dot, --writes"
|
||||
let p = "examples/modules/tangle.ludic"
|
||||
let got = capture(`bin/ludic deps {p} 2>&1`)
|
||||
let want = "modules: 3\ndependencies: 2\nlargest_cycle: 2\ncross_writes: 1\nglobals_written_from_outside: 1\nwidest_function: 1\nwidest_reach: 1\nin the largest cycle: a b\nthe widest function: a_run (examples/modules/tangle/a/index.ludic:3), 1 state\n"
|
||||
let want = "modules: 3\ndependencies: 2\nlargest_cycle: 2\ncross_writes: 1\nglobals_written_from_outside: 1\nwidest_function: 1\nwidest_reach: 1\nwidest_write_reach: 1\nin the largest cycle: a b\nthe widest function: a_run (examples/modules/tangle/a/index.ludic:3), 1 state\nthe widest write reach: a_run (examples/modules/tangle/a/index.ludic:3), 1 state it can come to change\n"
|
||||
if not (s_trim(got) == s_trim(want)) { bad2(lbl, `said [{s_trim(got)}]`); return }
|
||||
let base = `{tmp_dir()}/deps-base.txt`
|
||||
if not shq(`bin/ludic deps {p} --baseline {base} > /dev/null`) { bad2(lbl, "--baseline failed"); return }
|
||||
|
|
@ -590,9 +590,16 @@ function deps_reach_case() -> void {
|
|||
let got = capture(`bin/ludic deps examples/state/reach.ludic 2>&1`)
|
||||
if not s_contains(got, "widest_reach: 3") or not s_contains(got, "the widest reach: one (examples/state/reach.ludic:13), 3 states (2 through calls and fn values it does not take)") { bad2(lbl, s_trim(got)); return }
|
||||
let top = capture(`bin/ludic deps examples/state/reach.ludic --reach 2 2>&1`)
|
||||
if not s_contains(top, " 1 3 one (examples/state/reach.ludic:13)\n 0 2 run_steps (examples/state/reach.ludic:10)") { bad2(lbl, s_trim(top)); return }
|
||||
if not s_contains(top, " 1 3 3 one (examples/state/reach.ludic:13)\n 0 2 2 run_steps (examples/state/reach.ludic:10)") { bad2(lbl, s_trim(top)); return }
|
||||
let wide = capture(`bin/ludic deps examples/state/reach.ludic --widest 1 2>&1`)
|
||||
if s_contains(wide, "takes reaches function") and s_contains(wide, "one (examples/state/reach.ludic:13)") and not s_contains(wide, "step_a") { ok(lbl) } else { bad2(lbl, s_trim(wide)) }
|
||||
if s_contains(wide, "takes reaches changes function") and s_contains(wide, "one (examples/state/reach.ludic:13)") and not s_contains(wide, "step_a") { ok(lbl) } else { bad2(lbl, s_trim(wide)) }
|
||||
}
|
||||
# 0.R5: what a function can come to CHANGE; a port member and a registry field reach only themselves
|
||||
function deps_wreach_case() -> void {
|
||||
let lbl = "ludic deps: --wreach, a question through a port or a registry field changes nothing"
|
||||
let top = capture(`bin/ludic deps examples/state/write_reach.ludic --wreach 10 2>&1`)
|
||||
if not s_contains(top, " 0 2 2 changes (examples/state/write_reach.ludic:27)") or not s_contains(top, " 0 1 0 asks (examples/state/write_reach.ludic:22)") or s_contains(top, "asks_local") { bad2(lbl, s_trim(top)); return }
|
||||
ok(lbl)
|
||||
}
|
||||
# a program built headless with the toolchain and run: its first line
|
||||
function headless_case(path: pointer, exp: pointer, label: pointer) -> void {
|
||||
|
|
@ -1017,6 +1024,7 @@ function cmd_dev_test() -> int {
|
|||
feat_case("actions/modules", "", "2", "modules.ludic (0.R: an action in one module, its reducer in another)")
|
||||
actions_deps_case()
|
||||
deps_reach_case()
|
||||
deps_wreach_case()
|
||||
feat_case("state/reach", "", "3", "reach.ludic (0.R4: a step list of fn values supplies each step its states)")
|
||||
feat_case("actions/phases", "dd a q", "1 0 2 0 2 1 1 1 1 2 1 2", "phases.ludic (0.R: the frame loop drains the queue after every phase - Input's actions are reduced before Update)")
|
||||
feat_case("actions/runaway", "", "actions: Ping is still being dispatched after 64 rounds of reducers - a reducer dispatches what dispatches it", "runaway.ludic (0.R: a reducer that dispatches what dispatches it is stopped by name)")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue