Delete SymTab.FwdVarRef/FwdVarName/FwdVarResolve, Check.NoteForward/
FlushForward and the Design forward branch, plus the Op=1 marker —
by binding names before they are checked.
Design defers a name it cannot find inside a procedure because it
may be a module-level declaration that appears later in the file.
The resolve/check pass happens after the parse, so it cannot help with
binding: binding happens during the parse, and the declaration simply
has not been read yet. The only fix is to read the declarations before
the bodies — a two-phase (or two-pass) parse.
The grammar/parser stays almost entirely as-is; only the order in which the parser visits text changes.
ProcDecl sees a
module-level PROCEDURE, record the body's source span and consume
its tokens without parsing them, then carry on with the remaining
declarations. Result: every module-level name is in SymTab once the
module's declarations are done.Block production. Every module-level name now
resolves, so no forward reference is ever created.Consequences:
Design's forward branch never fires → the whole FwdVar* path is
dead and removable.scanner.frm, and expose it in M2S.def's
template): PROCEDURE SeekTo (p: INT32) — set bp0/bp/nextPos to
p and recompute line/col (scan from offset 0 counting CHR(10);
CharAt already indexes the in-memory buffer, so this is O(p)).PROCEDURE keyword, consume tokens counting block openers
{PROCEDURE, IF, CASE, WHILE, FOR, LOOP, WITH, RECORD}; stop when the
count returns to 0 — that END closes the procedure. BEGIN is
not an opener (a block's BEGIN…END shares the declaration's
END), and REPEAT…UNTIL/EXIT/RETURN are not counted.
Verify afterwards that the token is END and the following identifier
equals the procedure name (guards a mis-skip).ProcDecl (module level only): skip + record the
span; the declaration node is a placeholder NkProcDecl.Block) driven at the
end of Unit, walking the recorded spans.-twophase (default off) until the suite
and the fixpoint are byte-identical, then flip.Make every expression/statement action resolution-free and bind/type/
check in Check, walking with scopes mirrored from the AST. This is the
"true" two-phase and is much larger: it touches every body action, and
parse decisions that need types (typed brace literals, method calls,
L.x qualified names, HIGH/LEN) would have to be deferred too.
END. The
post-skip check (END + name) turns this into a clear error rather
than silent corruption.line, col, lookahead) and error
positions inside re-parsed bodies.Lower ordering. Bodies are parsed after the declarations, so
node allocation order changes. Lower walks by structure (order-
independent), but any parse-time ordinal that assumes file order must
be re-checked.SeekTo + the skipper behind the flag; inert.FwdVarRef/FwdVarName/FwdVarResolve
and Check.NoteForward/FlushForward, the Design forward branch,
and the Op=1 marker.Pieces 1–2 are a few hundred lines across the scanner frame, the grammar
and the driver, and are the largest single change in the resolve/check
thread — the reason FwdVar* could not be deleted by moving checks
alone.