← board

NilPy: remaining uforth walls past ~88% (closure-captured defaults, then exec)

Hangs off [[feature-nilpy-corpus-uforth]]. As of 2026-07-21 the wall is uforth.py:3829 — the first parse error is now this far in, after ~90 features landed this session (267 -> 3829, ~88% of the 4357-line file).

The current wall (3829)

def _compile_name_xt(vm2: VM, target: Word = w) -> None:

A parameter default that is a CAPTURED VARIABLE (w from the enclosing scope), Python's by-value loop-variable capture idiom. NilPy requires a CONSTANT default (None/bool/int/str). This is closure capture expressed as a default: the nested-def capture machinery (PyQueueNestedDef) already captures enclosing locals the body reads as trailing params — a non-constant default that names an enclosing local could be folded into that same capture, evaluated at def time.

What still remains after it

The wall has been bouncing between nested defs (3994 -> 3967 -> 3846 -> 3829) as each is fixed, because uforth registers ~200 native-word bodies as nested defs and the first failing one changes. Expect a tail of small per-def issues of the same kind already handled (variant subscript/slice, dynamic attrs, method chains) plus:

Status honestly

The file PARSES ~88% of the way. Reaching a compiled binary needs the tail of per-def fixes above; reaching a RUNNING uforth needs pyexec. Both are scoped; neither is a single edit.

Re-checked 2026-07-31 — two of three items now fixed, closing what's fixed

This ticket's own two items are done; the corpus's ACTUAL remaining walls (now past line 3829) are tracked in feature-nilpy-corpus-uforth's own 2026-07-31 recheck (next wall found near line 3887, not yet isolated).

Log

2026-08-02 — scope correction on the "closure-captured defaults: FIXED" claim

Re-measured while sweeping function semantics. That claim is correct but reads wider than it is: the def-time re-parse implementing it lives in PyNestedDefClosureValue, so it runs only when a nested def is materialised as a closure VALUE. A def that is merely CALLED never reaches it:

def outer():
    w = 7
    def inner(b=w):
        return b
    return inner()      # -> None, not 7

On the ordinary call path every non-constant default still silently becomes None — including b=[], b={}, b=() and any name. Filed as [[bug-nilpy-non-constant-parameter-defaults-silently-become-none]] (prio 70). This ticket stays resolved; only the scope of its claim is narrowed here, so the next reader does not take "defaults are fixed" at face value.