Skip to content

docs(machine/README): scope rules for indexed forms + call(name)#110

Merged
mellonis merged 1 commit into
masterfrom
docs/scope-rules
Jun 13, 2026
Merged

docs(machine/README): scope rules for indexed forms + call(name)#110
mellonis merged 1 commit into
masterfrom
docs/scope-rules

Conversation

@mellonis

Copy link
Copy Markdown
Owner

Summary

Two scope rules were implicit in the README before — shown by example in the Subroutines section but never stated as rules. Making them explicit removes the kind of ambiguity that bites consumers and tooling.

The rules being added

  1. The `ix` in any indexed form references the scope the call is written in. Applies to `mark(ix)`, `erase(ix)`, `noop(ix)`, `left(ix)`, `right(ix)`, `check(ix1, ix0)`, and `call(name, ix)`'s second arg. Top-level when at top; subroutine-local when inside a subroutine body. No implicit jump to top-level from within a subroutine.

  2. `call(name)` resolves names by walking the lexical scope chain from the current scope outward, first match wins. Subroutines can nest — a `call('inner')` from inside `outer` finds `outer.inner` before checking the top-level program. This mirrors identifier resolution in lexically-scoped languages, and matches the path notation (`foo::bar::1`) already documented at "Naming convention".

Where it lives

A new "Scope rules for indexed forms" subsection right after the Author's extensions table in the Commands section, plus a TOC pointer. Placed BEFORE the Subroutines section so a reader encountering `mark(20)` / `check(20, 40)` examples knows what they mean. A short scope-of-the-literal-arg table covers the common cases without bloating the section.

Why now

The rules surfaced during a cross-reference linter for the demo (the linter validates literal arg references against the surrounding `new PostMachine({ … })`). To get `call(name, ix)`'s scope right I had to ask twice; an initial "subroutines are flat / always top-level" reading was corrected to "subroutines nest / lexical chain." Writing the rules down means the next person (or tool) gets them on the first read.

Test plan

  • Prose only — no executable code examples added (the inline shapes like `mark(ix)` and the scope table are illustrative, not runnable). No `examples.spec.ts` change needed per the README→examples test convention.
  • `npm run lint` clean.
  • `npm test` — 332 / 332 specs still passing.

Two scope rules were implicit before — only shown by example in the
Subroutines section, never stated. Making them explicit:

1. The `ix` in any indexed form (`mark(ix)`, `check(ix1, ix0)`, `call(_, ix)`
   etc.) references the scope the call is written in. Lexical resolution;
   no implicit jump to top-level from inside a subroutine.

2. `call(name)`'s name resolves by walking the lexical scope chain from
   the current scope outward, first match wins. Subroutines nest, and a
   `call('foo')` from inside `outer` finds `outer.foo` before checking
   the top-level program.

Adds a short "Scope rules for indexed forms" subsection right after the
Author's extensions table, plus a TOC pointer. Prose only — no
executable examples — so no test changes.

The rules were established during machines-demo#114 Phase 2 (cross-ref
linter) work where I had to ask twice to be sure of `call(name, ix)`'s
scope. With them written down, anyone (or any tool) building on the
engine can pick them up directly.
@mellonis mellonis merged commit 675f019 into master Jun 13, 2026
4 checks passed
@mellonis mellonis deleted the docs/scope-rules branch June 13, 2026 10:01
@github-project-automation github-project-automation Bot moved this from Todo to Done in @mellonis's machines Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant