Skip to main content

max / quasi

Make the shape-function population reproducible Five counts of it existed and none reproduced, because none had written its predicate down: 289, 375, 402, 425, 487. `scripts/population.py` is the predicate, executable, and it answers 514 -- mnw 173, goingson 184, audiofiles 157 -- which is the number the phase-1 record measured by hand. Comments, strings, raw strings and char literals are blanked in place before anything is matched, so a type named in a doc comment is not a construction. `#[cfg(test)]` is stripped only where an inline block follows it: goingson writes `#[cfg(test)] mod tests;` with no block, and a stripper that brace-matches from the next `{` eats four real functions. Wiki quasi-declare-form section 2 is the predicate in prose; this is the command behind it.
Author: Max Johnson <me@maxj.phd> · 2026-09-03 12:36 UTC
Signed with PGP, not checked
Commit: 9667dc27a09baf83779db7023bac66ff9d92d52e
Parent: 19c825f
1 file changed, +402 insertions, -0 deletions
@@ -1,0 +1,402 @@
1 + #!/usr/bin/env python3
2 + """Count the shape functions the `declare!` form has to cover.
3 +
4 + The population is the denominator of phase 1 of the declaration transition
5 + (GoingsOn `65d99281`, wiki `quasi-declare-form`). Five counts of it existed
6 + before this script -- 289, 375, 402, 425, 487 -- and none reproduced, because
7 + none had written its predicate down. This is the predicate, executable.
8 +
9 + THE PREDICATE. A Rust `fn` item with a body, in one of the three shape
10 + directories, outside `tests.rs` and `parity.rs`, outside any `tests/` path
11 + segment, outside every inline `#[cfg(test)]` block, whose return type -- after
12 + stripping `Vec`, `Option`, `Result`, `Box`, `Arc`, `Rc` and `Cow` -- names a
13 + `quasi_router` description type, with `use ... as` aliases resolved and
14 + file-local types of the same name excluded.
15 +
16 + Comments, string literals, raw strings and char literals are blanked in place
17 + before anything is matched, so a type named in a doc comment is not a
18 + construction. That is the defect that produced two of the five old numbers.
19 +
20 + python3 scripts/population.py # the count, per tree
21 + python3 scripts/population.py --list # one `path:line name -> type` per line
22 + python3 scripts/population.py --json # the same, machine-readable
23 +
24 + Paths are read relative to `~/Code` by default; pass `--root` for a tree
25 + somewhere else.
26 + """
27 +
28 + from __future__ import annotations
29 +
30 + import argparse
31 + import json
32 + import re
33 + import sys
34 + from pathlib import Path
35 +
36 + # The three shape directories, relative to the root. A fourth app joining the
37 + # transition is one line here and a re-run.
38 + SHAPE_DIRS = [
39 + "MNW/server/src/quasi",
40 + "Apps/goingson/src-tauri/src/quasi",
41 + "Apps/audiofiles/crates/audiofiles-browser/src/quasi",
42 + ]
43 +
44 + # The vocabulary. Every public description type `quasi_router` exports that a
45 + # shape function can return, plus the two enums that are returned as leaves.
46 + # `Node` alone is a fifth of the population.
47 + VOCABULARY = {
48 + "Node", "Screen", "Slot", "Run", "Field", "Row", "Cell", "Cells",
49 + "Act", "Action", "Choice", "Column", "Tag", "Part", "Figure",
50 + "Placed", "Consult", "Meter", "Image", "Lexeme", "Document", "Feed",
51 + }
52 +
53 + # Returned by a handful of leaf suppliers that section 6 of `quasi-declare-form`
54 + # defers, so they sit outside the ratified population and inside `--wide`.
55 + # Adding all five moves the count by 10 on the trees as they stand.
56 + DEFERRED_LEAVES = {"Rest", "Candidate", "Accepted", "RegionKind", "Chrome"}
57 +
58 + # Wrappers stripped before the name is looked up. A `-> Result<Vec<Row>, E>` is
59 + # a shape function returning rows; the wrapper says how it fails, not what it is.
60 + WRAPPERS = {"Vec", "Option", "Result", "Box", "Arc", "Rc", "Cow"}
61 +
62 +
63 + def blank_noncode(src: str) -> str:
64 + """Replace every comment, string, raw string and char literal with spaces.
65 +
66 + Newlines are kept so line numbers still hold. Nothing is deleted, so every
67 + offset in the returned text is the offset in the original.
68 + """
69 + out = list(src)
70 + i, n = 0, len(src)
71 + while i < n:
72 + c = src[i]
73 + # Raw string, with any hash count: r"..", r#".."#, br##".."##
74 + m = re.match(r'(?:b)?r(#*)"', src[i:])
75 + if m and (i == 0 or not (src[i - 1].isalnum() or src[i - 1] == "_")):
76 + hashes = m.group(1)
77 + close = '"' + hashes
78 + end = src.find(close, i + m.end())
79 + end = n if end < 0 else end + len(close)
80 + for j in range(i, end):
81 + if out[j] != "\n":
82 + out[j] = " "
83 + i = end
84 + continue
85 + if c == "/" and i + 1 < n and src[i + 1] == "/":
86 + end = src.find("\n", i)
87 + end = n if end < 0 else end
88 + for j in range(i, end):
89 + out[j] = " "
90 + i = end
91 + continue
92 + if c == "/" and i + 1 < n and src[i + 1] == "*":
93 + depth, j = 1, i + 2
94 + while j < n and depth:
95 + if src.startswith("/*", j):
96 + depth += 1
97 + j += 2
98 + elif src.startswith("*/", j):
99 + depth -= 1
100 + j += 2
101 + else:
102 + j += 1
103 + for k in range(i, j):
104 + if out[k] != "\n":
105 + out[k] = " "
106 + i = j
107 + continue
108 + if c == '"':
109 + j = i + 1
110 + while j < n:
111 + if src[j] == "\\":
112 + j += 2
113 + continue
114 + if src[j] == '"':
115 + j += 1
116 + break
117 + j += 1
118 + for k in range(i, min(j, n)):
119 + if out[k] != "\n":
120 + out[k] = " "
121 + i = j
122 + continue
123 + if c == "'":
124 + # A char literal, not a lifetime: `'a` is a lifetime, `'a'` is a char.
125 + m = re.match(r"'(?:\\.|[^\\'])'", src[i:])
126 + if m:
127 + for k in range(i, i + m.end()):
128 + out[k] = " "
129 + i += m.end()
130 + continue
131 + i += 1
132 + return "".join(out)
133 +
134 +
135 + def strip_cfg_test(src: str) -> str:
136 + """Blank every `#[cfg(test)]` item that is followed by an inline block.
137 +
138 + Only a block: `#[cfg(test)] mod tests;` names a file that is excluded by
139 + path anyway, and blanking to the next `}` there would eat the rest of the
140 + file.
141 + """
142 + out = list(src)
143 + for m in re.finditer(r"#\[cfg\(test\)\]", src):
144 + j = m.end()
145 + while j < len(src) and src[j] not in "{;":
146 + j += 1
147 + if j >= len(src) or src[j] == ";":
148 + continue
149 + depth, k = 0, j
150 + while k < len(src):
151 + if src[k] == "{":
152 + depth += 1
153 + elif src[k] == "}":
154 + depth -= 1
155 + if depth == 0:
156 + k += 1
157 + break
158 + k += 1
159 + for p in range(m.start(), k):
160 + if out[p] != "\n":
161 + out[p] = " "
162 + return "".join(out)
163 +
164 +
165 + def local_types(src: str) -> set[str]:
166 + """Types the file declares itself, which shadow the vocabulary name."""
167 + return set(
168 + re.findall(r"^\s*(?:pub(?:\([^)]*\))?\s+)?(?:struct|enum|type|union)\s+(\w+)",
169 + src, re.M)
170 + )
171 +
172 +
173 + def imported(src: str) -> tuple[set[str], dict[str, str]]:
174 + """Names brought in from `quasi_router`, and every `as` alias in the file.
175 +
176 + Returns (names in scope from quasi_router, alias -> real name). Braced and
177 + single-item `use` forms both, at any nesting depth: the only thing that
178 + matters is whether the path starts at `quasi_router`.
179 + """
180 + names: set[str] = set()
181 + aliases: dict[str, str] = {}
182 + for m in re.finditer(r"use\s+([^;]+);", src):
183 + body = m.group(1)
184 + if "quasi_router" not in body and "quasi_webview" not in body:
185 + continue
186 + for leaf in re.findall(r"(\w+)(?:\s+as\s+(\w+))?", body):
187 + name, alias = leaf
188 + if name in VOCABULARY:
189 + names.add(alias or name)
190 + if alias:
191 + aliases[alias] = name
192 + return names, aliases
193 +
194 +
195 + def split_top(s: str) -> list[str]:
196 + """Split on commas at bracket depth zero."""
197 + out, depth, start = [], 0, 0
198 + for i, c in enumerate(s):
199 + if c in "<([":
200 + depth += 1
201 + elif c in ">)]":
202 + depth -= 1
203 + elif c == "," and depth == 0:
204 + out.append(s[start:i])
205 + start = i + 1
206 + out.append(s[start:])
207 + return [p for p in (x.strip() for x in out) if p]
208 +
209 +
210 + def head_type(ret: str) -> str | None:
211 + """The vocabulary name a return type resolves to, or None.
212 +
213 + Strips the wrappers outside-in, takes the last `::` segment, and stops at
214 + the first name that is not a wrapper. `Result<Vec<Node>, RouteError>` is
215 + `Node`; `Result<(), E>` is nothing.
216 + """
217 + ret = ret.strip()
218 + for _ in range(6):
219 + ret = ret.strip().lstrip("&").strip()
220 + ret = re.sub(r"^'\w+\s+", "", ret)
221 + m = re.match(r"([\w:]+)\s*<(.*)>$", ret, re.S)
222 + if not m:
223 + break
224 + head = m.group(1).split("::")[-1]
225 + if head not in WRAPPERS:
226 + return head
227 + inner = m.group(2)
228 + # `Result<T, E>`: the first argument at depth zero is the payload.
229 + depth, cut = 0, len(inner)
230 + for i, c in enumerate(inner):
231 + if c in "<([":
232 + depth += 1
233 + elif c in ">)]":
234 + depth -= 1
235 + elif c == "," and depth == 0:
236 + cut = i
237 + break
238 + ret = inner[:cut]
239 + ret = ret.strip().lstrip("&").strip()
240 + ret = re.sub(r"^'\w+\s+", "", ret)
241 + # A tuple return. One site in the trees as they stand (goingson
242 + # `data.rs:380 csv_rows`), and `shaped` names one type, so the form
243 + # defers it -- but it is a shape function and it counts.
244 + if ret.startswith("(") and ret.endswith(")"):
245 + for part in split_top(ret[1:-1]):
246 + head = head_type(part)
247 + if head in VOCABULARY:
248 + return head
249 + return None
250 + name = ret.split("::")[-1].strip()
251 + return name or None
252 +
253 +
254 + def match_angle(src: str, start: int) -> int:
255 + """Index just past the `>` closing the `<` at `start`."""
256 + depth, i = 0, start
257 + while i < len(src):
258 + if src[i] == "<":
259 + depth += 1
260 + elif src[i] == ">":
261 + depth -= 1
262 + if depth == 0:
263 + return i + 1
264 + elif src[i] == ";":
265 + return -1
266 + i += 1
267 + return -1
268 +
269 +
270 + def match_paren(src: str, start: int) -> int:
271 + depth, i = 0, start
272 + while i < len(src):
273 + if src[i] == "(":
274 + depth += 1
275 + elif src[i] == ")":
276 + depth -= 1
277 + if depth == 0:
278 + return i + 1
279 + i += 1
280 + return -1
281 +
282 +
283 + def shapes_in(path: Path) -> list[dict]:
284 + raw = path.read_text(encoding="utf-8", errors="replace")
285 + src = strip_cfg_test(blank_noncode(raw))
286 + scope, aliases = imported(raw)
287 + shadowed = local_types(src)
288 + found = []
289 + for m in re.finditer(r"\bfn\s+(\w+)\s*(?=[<(])", src):
290 + name = m.group(1)
291 + i = m.end()
292 + if src[i] == "<":
293 + i = match_angle(src, i)
294 + if i < 0:
295 + continue
296 + while i < len(src) and src[i].isspace():
297 + i += 1
298 + if i >= len(src) or src[i] != "(":
299 + continue
300 + i = match_paren(src, i)
301 + if i < 0:
302 + continue
303 + # Return type runs to the body's `{`, the `where` clause, or a `;`.
304 + j = i
305 + depth = 0
306 + while j < len(src):
307 + c = src[j]
308 + if c in "<([":
309 + depth += 1
310 + elif c in ">)]":
311 + depth -= 1
312 + elif c == "{" and depth <= 0:
313 + break
314 + elif c == ";" and depth <= 0:
315 + j = -1
316 + break
317 + j += 1
318 + if j < 0 or j >= len(src):
319 + continue # no body: a trait signature or an extern declaration
320 + sig = src[i:j]
321 + if re.search(r"\bwhere\b", sig):
322 + sig = sig[: sig.index("where")]
323 + sig = sig.strip()
324 + if not sig.startswith("->"):
325 + continue # returns unit
326 + ret = sig[2:].strip()
327 + head = head_type(ret)
328 + if head is None:
329 + continue
330 + real = aliases.get(head, head)
331 + if real not in VOCABULARY:
332 + continue
333 + if head in shadowed:
334 + continue # a file-local type wearing a vocabulary name
335 + if head not in scope and real not in scope:
336 + # Reached by path (`quasi_router::Node`) rather than by import.
337 + if "quasi_router" not in ret and "quasi_webview" not in ret:
338 + continue
339 + found.append(
340 + {"file": str(path), "line": src.count("\n", 0, m.start()) + 1,
341 + "fn": name, "returns": " ".join(ret.split())}
342 + )
343 + return found
344 +
345 +
346 + def main() -> int:
347 + ap = argparse.ArgumentParser(description=__doc__,
348 + formatter_class=argparse.RawDescriptionHelpFormatter)
349 + ap.add_argument("--root", default=str(Path.home() / "Code"))
350 + ap.add_argument("--list", action="store_true")
351 + ap.add_argument("--json", action="store_true")
352 + ap.add_argument("--wide", action="store_true",
353 + help="also count the deferred leaf suppliers (Rest, Candidate, "
354 + "Accepted, RegionKind, Chrome)")
355 + args = ap.parse_args()
356 +
357 + if args.wide:
358 + VOCABULARY.update(DEFERRED_LEAVES)
359 +
360 + root = Path(args.root)
361 + per_tree: dict[str, list[dict]] = {}
362 + missing = []
363 + for rel in SHAPE_DIRS:
364 + d = root / rel
365 + if not d.is_dir():
366 + missing.append(rel)
367 + continue
368 + hits: list[dict] = []
369 + for f in sorted(d.rglob("*.rs")):
370 + parts = f.relative_to(root).parts
371 + if f.name in ("tests.rs", "parity.rs") or "tests" in parts:
372 + continue
373 + hits.extend(shapes_in(f))
374 + for h in hits:
375 + h["file"] = str(Path(h["file"]).relative_to(root))
376 + per_tree[rel] = hits
377 +
378 + total = sum(len(v) for v in per_tree.values())
379 +
380 + if args.json:
381 + json.dump({"total": total,
382 + "per_tree": {k: len(v) for k, v in per_tree.items()},
383 + "shapes": [h for v in per_tree.values() for h in v]},
384 + sys.stdout, indent=1)
385 + print()
386 + return 0
387 +
388 + if args.list:
389 + for rel, hits in per_tree.items():
390 + for h in hits:
391 + print(f"{h['file']}:{h['line']} {h['fn']} -> {h['returns']}")
392 +
393 + for rel, hits in per_tree.items():
394 + print(f"{len(hits):5} {rel}")
395 + print(f"{total:5} TOTAL")
396 + for rel in missing:
397 + print(f" ? {rel} (not present under {root})", file=sys.stderr)
398 + return 0
399 +
400 +
401 + if __name__ == "__main__":
402 + raise SystemExit(main())