(* STATEMEN.MOD — DRAFT v1, assembled 2026-10-03, NOT verified. Reconstructed from Reversing-Turbo-Modula2-main/MCode_disassembly/statement.txt (1793 lines) by two parallel passes (group 1 @0001-0566, group 2 @0566-end). Does NOT yet recompile to the original MCD; verification via unassemble.c diff is still required (see src/compiler/README.md). FRAME MECHANICS (proven from MCode_specification/Stack.mod + Instruction.mod + Extended.mod, settles all arg-order disputes): - Callers push args in source order (spec Extended.Move pops size,dst,src). - NewFrame pushes oldFP,outerFP above retAddr; fp^[0]=outerFP, [1]=oldFP, [2]=retAddr, [3]=LAST pushed word. Opcode 03H..07H = LoadParam(3..7), and the disassembler prints fp^[3] as "param1". Hence "param1" = LAST pushed word (modulo multi-word/VAR params: SYMTAB proc8 SymbolAssert reads "param2"=cond,"param1"=code for source (cond, code) — consistent). - Consequence: a body reading "param1"=first-formal is backwards; the correct source order is first-pushed=formal1. Call sites reconstructed from pushes are order-correct as written. - Proven by this: Express proc9 @02be tests `IN param2` and reports via ERRORS.proc14(param2, X, param1)=(want,got,ctx); STMT @05d9 pushes (512, 43): (allowed={9}, code=43) is the only sane reading, so Express.MatchOpClass is (allowed, errCode) — NOT (errCode, allowed) as EXPRESS.MOD draft v1 declares. FIX APPLIED HERE (call shapes unchanged); EXPRESS.MOD's own declaration still needs the same swap (next edit). - Same proof: Express.proc1 takes (cond, code) — STMT @058a shows [bool,20] with code on top. EXPRESS.MOD's 1-param CentralError FORWARD is stale; widened here via Express.CentralError(ok, code) usage and flagged there. CORRECTIONS to docs/compiler/60-frontend.md + FRONTEND.md (body-proven): - proc5 = CASE (jump table via EmitWord, OF, `|`, ELSE), proc6 = FOR (`:=`/TO/BY, control-var checks) — the map had them swapped. - STMT dispatch: 121 Loop, 122 Exit, 123 Result, 124 If, 125 For, 126 Repeat, 127 While, 128 With, 129 Return, 130 Case (125 = CASE default). - TWO case-shaped procedures exist: proc5 (STMT-driven, full jump-table parser) vs proc16 (BODY-driven via Accept(14), label-table emitter). Kept distinct: ParseCaseStmt vs ParseCaseTail. Why BODY re-enters case parsing on symbol 14 is unexplained — MCD diff must decide. - 123/129 = RESULT/RETURN inferred from store-vs-epilogue behavior. CONVENTIONS: Words(X)^[N] = numeric-word pun on ADDRESS/T1 handles (MCode is untyped; original emissive code TBD). GOTO kept as faithful jump rendering (restructure to LOOP/EXIT in verification). Scanner.word22/word23/word25 read as codeSize/listingEnabled/tokenPos (SCANNER.DEF positional mapping; listingEnabled assignments flagged). BITSET{..} literal sets on CARDINAL curSymbol kept verbatim (cast TBD). *) IMPLEMENTATION MODULE Statemen; IMPORT Compiler, Scanner, Errors, CodeGen, Express, SymTab, Files, Texts, Loader; FROM SYSTEM IMPORT ADDRESS, ADR, MOVE, WORD; FROM STORAGE IMPORT MARK, RELEASE; TYPE Words = POINTER TO ARRAY [0..15] OF WORD; VAR needEpilogue: CARDINAL; (* word2: set from body-nonEmpty + tail paths *) withPending: CARDINAL; (* word3: nonzero = with-base load pending *) nestDepth: CARDINAL; (* word4: LOOP/CASE/FOR nesting depth *) exitDepth: CARDINAL; (* word5: LOOP-entry depth for EXIT (= group2 loopExitMark) *) curBlockNode: ADDRESS; (* word6: BODY param1 (= group1 curRoutine) *) caseActive: CARDINAL; (* word7: proc16 sets 1 (= group1 caseSeen) *) withDesc: ADDRESS; (* word8: pending operand for EmitStandardOp(25) *) exitEmitted: CARDINAL; (* word9: proc17 sets 1 *) (* ---- helpers shared across both groups ---- *) PROCEDURE ReserveWords(n: CARDINAL): ADDRESS; FORWARD; (* FIXME: = Scanner.Allocate? *) PROCEDURE MoveWords(dst, src: ADDRESS; words: CARDINAL); FORWARD; (* FIXME: = MOVE? *) (* ---- group-1 procedures (FORWARDs; bodies below) ---- *) PROCEDURE CalcMemOffset(): CARDINAL; FORWARD; PROCEDURE SyncToStatStart; FORWARD; PROCEDURE ParseCaseStmt; FORWARD; PROCEDURE ParseFor; FORWARD; PROCEDURE ParseWhile; FORWARD; PROCEDURE ParseRepeat; FORWARD; PROCEDURE ParseIf; FORWARD; PROCEDURE ParseLoop; FORWARD; PROCEDURE ParseExit; FORWARD; PROCEDURE ParseResult; FORWARD; PROCEDURE ParseWith; FORWARD; PROCEDURE ParseReturn; FORWARD; (* ---- group-2 procedures (FORWARDs; bodies below) ---- *) PROCEDURE NextSymWrap; FORWARD; PROCEDURE ParseStatSeqLoop(stop: BITSET): BOOLEAN; FORWARD; PROCEDURE EmitJumpFixup; FORWARD; PROCEDURE ParseStatement; FORWARD; PROCEDURE GuardedStatSeq(follow: BITSET): BOOLEAN; FORWARD; PROCEDURE ParseCaseTail; FORWARD; PROCEDURE CloseBlockChain(VAR node: ADDRESS); FORWARD; PROCEDURE EmitNameHash(VAR name: ARRAY OF CHAR); FORWARD; PROCEDURE EmitComBytes; FORWARD; PROCEDURE ParseBeginBlock(tag: CARDINAL); FORWARD; (* audit: frame says 1 param; VAR rest dropped *) (* STATEMEN group 1 — procedures @0001-0566 *) (* proc2 @0001 — ~CalcMemOffset. 0 params: reads curBlockNode global *) PROCEDURE CalcMemOffset(): CARDINAL; VAR p, q: ADDRESS; flag: CARDINAL; BEGIN {/* 0001 */} p := Words(curBlockNode)^[6]; (*{0003-0005}*) IF p = NIL THEN (*{0006-0008} *) RETURN 0; (*{000a-000b} *) END; WHILE Words(p)^[0] # NIL DO (*{000d-0010} *) p := Words(p)^[0]; (*{0012-0014}*) END; q := Words(p)^[2]; (*{0017-0019}*) IF (Words(q)^[4] > 9) OR (4 IN Words(p)^[3]) THEN (*{001a-0024} *) flag := 1; (*{0026-0027} *) ELSE flag := (Words(q)^[3] + 1) DIV 2; (*{002a-002f} shr 1 *) END; RETURN Words(p)^[5] DIV 2 + flag - 3; (*{0030-0038} *) END CalcMemOffset; (* proc3 @003b — ~SyncToStatStart *) PROCEDURE SyncToStatStart; BEGIN {/* 003b */} IF Express.spillFlag # 0 THEN (*{003d-0040} *) IF withPending # 0 THEN (*{0042-0043} *) Express.PushOperand(withDesc); (*{0045-0046} FIXME: 1-arg vs 0-arg FORWARD *) CodeGen.EmitStandardOp(25); (*{0048-004a} *) END; IF Words(curBlockNode)^[2] = NIL THEN (*{004c-004f}*) CodeGen.EmitMiscOp(0, (*{0051-0052} *) CalcMemOffset() + ORD(Express.spillFlag = 1) * 128); (*{0053-005a} shl 7 *) ELSIF 8 IN Scanner.scanOpt THEN (*{005e-0062} *) CodeGen.QueueConst(9); (*{0064-0065} *) CodeGen.QueueConst(0); (*{0067-0068} *) CodeGen.QueueConst(0); (*{006a-006b} *) CodeGen.EmitStandardOp(14); (*{006d-006e} *) END; ELSE (*{0072} *) CodeGen.EmitMiscOp(5, 2); (*{0073-0074} *) END; END SyncToStatStart; (* proc5 @0078, STMT case 130 — ParseCaseStmt. Selector + OF + label ranges with jump-table emission, `|`-separated arms, ELSE. *) PROCEDURE ParseCaseStmt; VAR head: ADDRESS; chain: ADDRESS; buf: ADDRESS; tail: ADDRESS; prev: ADDRESS; selTyp: ADDRESS; lo: CARDINAL; hi: CARDINAL; endFix: CARDINAL; topFix: ADDRESS; armFix: CARDINAL; BEGIN INC(nestDepth); (*{007a-007d} *) Express.BoolCondHelper; (*{007d} parse CASE selector *) Express.MatchOpClass(7, 40); (*{007f-0082} *) selTyp := Express.curDesc.raw[0]; (*{0084-0087} FIXME: typ pun *) Scanner.ExpectSy(41); (*{0088-008a} OF *) CodeGen.EmitSystemCall(21); (*{008c-008e} *) CodeGen.EmitStandardOp(15); (*{008f-0092} jump to dispatch *) CodeGen.OpenFixup(topFix, TRUE); (*{0094-0097} *) head := NIL; (*{0099-009a} *) tail := NIL; (*{009b-009c} *) WHILE NOT (Scanner.curSym IN BITSET{8,10,13}) DO (*{009d-00a4} *) armFix := CodeGen.MarkCodePos(14); (*{00a6-00aa} *) Express.GetTypeDesc(selTyp, ADR(lo), ADR(hi)); (*{00ac-00b1} FIXME: (dest,mode,src) roles *) prev := NIL; (*{00b3-00b4} *) chain := head; (*{00b5-00b6} *) WHILE (chain # NIL) AND (Words(chain)^[1] < lo) DO (*{00b7-00be} *) prev := chain; (*{00c1-00c2} *) chain := Words(chain)^[0]; (*{00c3-00c6} *) END; Scanner.Allocate(buf, 6); (*{00c8-00cb} *) Words(buf)^[0] := chain; (*{00cd-00cf} *) Words(buf)^[1] := hi; (*{00d0-00d2} *) Words(buf)^[2] := armFix; (*{00d3-00d6} *) IF (chain # NIL) AND (Words(chain)^[1] = 0) THEN (*{00d7-00d9} *) tail := buf; (*{00db-00dc} *) END; IF (chain # NIL) AND (Words(chain)^[1] > hi) THEN (*{00df-00e2} *) Express.CentralError(FALSE, 47); (*{00e3-00e5} *) END; chain := buf; (*{00e7-00e8} *) DEC(hi); (*{00e9-00eb} *) WHILE hi >= lo DO (*{00ec-00ef} expand label range; duplicate check TBD *) END; IF prev # NIL THEN (*{00f1-00f3} *) Words(prev)^[0] := buf; (*{00f5-00f8} *) ELSE head := buf; (*{00fa-00fb} *) END; Scanner.TestSet(BITSET{1,2}); (*{00fc-00fd} `,`/`:` *) IF Scanner.AcceptSy(1) THEN (*{00ff-0100} `,`: another label, loop to 00ac *) END; Scanner.GetSym; (*{0105} consume `:` *) WHILE NOT ParseStatSeqLoop(BITSET{8,10,13}) DO END; (*{0107-010b} arm body *) CodeGen.EmitStandardOp(20); (*{010d-010f} jump to end *) END; IF NOT Scanner.AcceptSy(8) THEN (*{0111-0115} `|`: more arms loop to 009d *); END; DEC(nestDepth); (*{0117-0119} *) endFix := CodeGen.InsertFixup(topFix, TRUE); (*{011a-011e} *) IF head # NIL THEN (*{011f-0121} emit jump table over label chain *) lo := Words(head)^[1]; (*{0123-0125} *) CodeGen.EmitStandardOp(19); (*{0126-0128} *) CodeGen.EmitWord(lo + 32768); (*{012a-012f} *) CodeGen.EmitWord(tail - lo); (*{0131-0135} FIXME: ADDRESS pun *) CodeGen.OpenFixup(topFix, FALSE); (*{0137-013a} *) WHILE Words(head)^[0] = lo DO (*{013c-0140} FIXME *) CodeGen.CloseFixup(Words(head)^[2] + ORD(NOT endFix), FALSE); (*{0142-0148} *) head := Words(head)^[0]; (*{014a-014c} *) ELSE CodeGen.EmitWord((tail - lo) * 2 + 1); (*{014f-0156} *) END; INC(lo); (*{0158-015a} *) IF head = NIL THEN END; (*{015b-015d} loop to 013c while head#NIL *) END; IF Scanner.AcceptSy(10) THEN (*{015f-0160} ELSE *) WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{0164-0168} *) END; Scanner.GetSym; (*{0187} consume END *) IF 8 IN Scanner.scanOpt THEN (*{016c-0170} range-check variant epilogue *) CodeGen.QueueConst(13); (*{0172-0173} *) CodeGen.QueueConst(0); (*{0175-0176} *) CodeGen.QueueConst(0); (*{0178-0179} *) CodeGen.EmitStandardOp(14); (*{017b-017c} *) END; IF tail # NIL THEN (*{017e-0180} *) endFix := CodeGen.InsertFixup(topFix, FALSE); (*{0182-0186} *) END; END ParseCaseStmt; (* proc6 @018b, STMT case 125 — ParseFor. Control var, `:=` lower TO upper [BY const] DO body END. Largest frame (4 desc buffers). *) PROCEDURE ParseFor; VAR ctrl: ADDRESS; loD: ADDRESS; hiD: ADDRESS; tmpD: ADDRESS; step: CARDINAL; cflag: CARDINAL; isInt: CARDINAL; dummy: CARDINAL; sub: CARDINAL; topMark: CARDINAL; fixA: ADDRESS; markB: CARDINAL; BEGIN {/* 018b */} Scanner.Allocate(ctrl, 10); (*{018d-018f} FIXME: group wrote ReserveWords *) Scanner.Allocate(loD, 10); (*{0190-0192} *) Scanner.Allocate(hiD, 10); (*{0193-0195} *) Scanner.Allocate(tmpD, 10); (*{0196-0198} *) Express.EvalConstExpr; (*{0199} control var head *) Express.MatchOpClass(7, 28); (*{019c-019f} *) IF Express.curDesc.raw[0] = Compiler.ByteType THEN (*{01a1-01a7} *) Express.MatchOpClass(5, 28); (*{01aa-01ad} *) END; Express.CentralError(NOT (12 IN Words(Express.curDesc.raw[0])^[4]), 24); (*{01af-01b7} FIXME: pun *) MOVE(Express.curDesc, ctrl, 10); (*{01b9-01bd} FIXME: group wrote MoveWords *) sub := ORD(Words(Express.curDesc.raw[0])^[4] = 4); (*{01be-01c3} FIXME *) IF sub # 0 THEN (*{01c4-01c5} rotate saved descs *) MOVE(ctrl, tmpD, 10); (*{01c7-01cb} FIXME: direction TBD by diff *) Express.PushExprDesc(ctrl, tmpD); (*{01cb-01ce} FIXME: arity/shape *) MOVE(tmpD, ctrl, 10); END; Express.EmitOp; (*{01d0} proc6 *) isInt := ORD(Express.curDesc.raw[0] = Compiler.IntType); (*{01d2-01d8} FIXME *) Scanner.ExpectSy(27); (*{01d9-01db} `:=` *) Express.BoolCondHelper; (*{01dd} lower bound *) Express.ConstToCard(Words(ctrl)^[0], 27); (*{01df-01e5} FIXME: ctrlDescTyp *) MOVE(Express.curDesc, loD, 10); (*{01e5-01e9} *) Express.PopExprDesc(ctrl); (*{01ea-01eb} FIXME: ADDRESS arg *) Scanner.ExpectSy(42); (*{01ed-01ef} TO *) Express.BoolCondHelper; (*{01f1} upper bound *) Express.ConstToCard(Words(ctrl)^[0], 42); (*{01f3-01f7} *) MOVE(Express.curDesc, hiD, 10); (*{01f9-01fd} *) step := 1; (*{01fe-01ff} *) IF Scanner.AcceptSy(29) THEN (*{0200-0204} BY *) Express.GetConstVal; (*{0206} proc13 *) Express.ConstToCard(Compiler.IntType, 29); (*{0208-020c} *) step := Express.curDesc.raw[2]; (*{020e-0211} const step *) END; (*{0212-0269} bound/step validation + counter init (const-fold vs runtime paths); faithful to raw, minutiae FIXME *) IF sub # 0 THEN (*{02e4-02e5} subrange control: normalize *) Express.NormalizeOperand; (*{02e7-02e8} FIXME: group passed tmpD *) Express.PushOperand(ctrl); (*{02ea-02eb} FIXME: 1-arg vs 0-arg FORWARD *) CodeGen.SetPendingOp(5, 2, 0, 0); (*{02ed-02f1} *) END; WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{02f3-02f7} body to END *) Scanner.GetSym; (*{02f9} consume END *) (*{02fb-033d} loop-back + fixup close-out FIXME *) Scanner.GetSym; END ParseFor; (* proc7 @0341, STMT case 127 — ParseWhile. WHILE cond DO body END. *) PROCEDURE ParseWhile; VAR top: CARDINAL; fix: ADDRESS; endMark: CARDINAL; BEGIN top := CodeGen.MarkCodePos(13); (*{0343-0347} *) Express.BoolCondHelper; (*{0348} *) Express.ConstToCard(Compiler.BooleanType, 37); (*{034a-034e} *) Scanner.ExpectSy(30); (*{0350-0352} DO *) CodeGen.EmitStandardOp(16); (*{0354-0356} jump-if-false *) CodeGen.OpenFixup(fix, TRUE); (*{0358-035b} *) WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{035d-0361} body to END *) Scanner.GetSym; (*{0363} consume END *) CodeGen.EmitStandardOp(15); (*{0365-0366} jump back to top *) endMark := CodeGen.nextEmitPos; (*{0368-036a} *) CodeGen.CloseFixup(top, TRUE); (*{036b-036d} *) IF NOT CodeGen.InsertFixup(fix, TRUE) THEN (*{036f-0374} *) CodeGen.AdjustFixup(endMark + 1); (*{0376-0378} *) END; END ParseWhile; (* proc8 @037c, STMT case 126 — ParseRepeat. REPEAT body UNTIL cond. *) PROCEDURE ParseRepeat; VAR top: CARDINAL; BEGIN top := CodeGen.MarkCodePos(12); (*{037e-0382} *) WHILE NOT ParseStatSeqLoop(BITSET{12}) DO END; (*{0383-0387} to UNTIL *) Scanner.GetSym; (*{0389} consume UNTIL *) Express.BoolCondHelper; (*{038b} *) Express.ConstToCard(Compiler.BooleanType, 12); (*{038d-0392} *) CodeGen.EmitStandardOp(16); (*{0392-0394} jump-if-false to top *) CodeGen.CloseFixup(top, TRUE); (*{0396-0398} *) END ParseRepeat; (* proc9 @039c, STMT case 124 — ParseIf. IF cond THEN stats [ELSIF cond THEN stats]... [ELSE stats] END. ELSIF via recursion. *) PROCEDURE ParseIf; VAR elseFix: ADDRESS; endFix: ADDRESS; savedEmit: CARDINAL; constCond: BOOLEAN; chained: CARDINAL; skipGen: CARDINAL; BEGIN Express.BoolCondHelper; (*{039e} *) Express.ConstToCard(Compiler.BooleanType, 34); (*{03a0-03a4} *) savedEmit := ORD(CodeGen.emitEnabled); (*{03a6-03a8} FIXME: BOOLEAN pun *) skipGen := 0; (*{03a9-03aa} *) constCond := (Express.curDesc.raw[1] = 0); (*{03ab-03af} mode=const *) IF constCond THEN (*{03b0-03b1} *) CodeGen.DiscardPending; (*{03b3} *) skipGen := 2; (*{03b5-03b6} *) IF Express.curDesc.raw[2] = 0 THEN (*{03b7-03bb} *) ELSE skipGen := 1; (*{03bd-03be} *) END; CodeGen.emitEnabled := FALSE; (*{03bf-03c0} suppress dead arm *) END; Scanner.ExpectSy(35); (*{03c2-03c4} THEN *) IF NOT constCond THEN (*{03c6-03c8} *) CodeGen.EmitStandardOp(16); (*{03ca-03cc} jump-if-false *) CodeGen.OpenFixup(elseFix, TRUE); (*{03ce-03d1} *) END; WHILE NOT ParseStatSeqLoop(BITSET{10,11,13}) DO END; (*{03d3-03d7} *) IF Scanner.AcceptSy(13) THEN (*{03d9-03dc} END: done *) IF NOT constCond THEN (*{03de-03e0} *) chained := ORD(CodeGen.InsertFixup(elseFix, TRUE)); (*{03e2-03e6} FIXME pun *) END; END; IF NOT constCond THEN (*{03e9-03eb} *) CodeGen.EmitStandardOp(15); (*{03ed-03ee} jump over else *) CodeGen.OpenFixup(endFix, TRUE); (*{03f0-03f3} *) IF NOT CodeGen.InsertFixup(elseFix, TRUE) THEN (*{03f5-03fa} *) INC(endFix); (*{03fc-03fe} FIXME: ADDRESS pun *) END; END; CodeGen.emitEnabled := savedEmit # 0; (*{0400-0405} FIXME: exact condition TBD *) IF Scanner.AcceptSy(11) THEN (*{0407-040a} ELSIF: nested IF *) ParseIf(); (*{040c} recursion *) ELSE Scanner.GetSym; (*{040f} consume ELSE *) WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{0411-0415} else stats *) Scanner.GetSym; (*{0417} consume END *) END; IF NOT constCond THEN (*{0419-041a} InsertFixup(endFix, TRUE), result dropped *) END; CodeGen.emitEnabled := savedEmit # 0; (*{0426-0427} tail *) END ParseIf; (* proc10 @042b, STMT case 121 — ParseLoop. LOOP body END with EXIT support. *) PROCEDURE ParseLoop; VAR savedExit: CARDINAL; fix: ADDRESS; top: CARDINAL; endFix: CARDINAL; BEGIN savedExit := exitDepth; (*{042d-042e} *) INC(nestDepth); (*{042f-0431} *) exitDepth := nestDepth; (*{0432-0433} *) CodeGen.EmitStandardOp(21); (*{0434-0436} loop-head marker *) CodeGen.OpenFixup(fix, FALSE); (*{0438-043b} *) top := CodeGen.MarkCodePos(11); (*{043d-0441} *) WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{0442-0446} body to END *) Scanner.GetSym; (*{0448} consume END *) CodeGen.EmitStandardOp(15); (*{044a-044b} jump back *) CodeGen.CloseFixup(top, TRUE); (*{044d-044f} *) endFix := ORD(CodeGen.InsertFixup(fix, FALSE)); (*{0451-0455} FIXME pun *) DEC(nestDepth); (*{0456-0458} *) exitDepth := savedExit; (*{0459-045a} *) END ParseLoop; (* proc11 @045d, STMT case 122 — ParseExit. Unwind to enclosing LOOP. *) PROCEDURE ParseExit; VAR level: CARDINAL; BEGIN Express.CentralError(exitDepth # 0, 26); (*{045f-0463} *) level := nestDepth; (*{0465-0466} *) WHILE level > exitDepth DO (*{0467-046a} *) CodeGen.EmitStandardOp(23); (*{046c-046e} unwind one level *) DEC(level); (*{0470-0472} *) END; CodeGen.EmitStandardOp(20); (*{0475-0477} jump out *) END ParseExit; (* proc12 @047b, STMT case 123 — ParseResult. RESULT expr: store value to function result slot; no exit jump. *) PROCEDURE ParseResult; VAR size: CARDINAL; desc: ADDRESS; BEGIN Express.CentralError((Words(curBlockNode)^[4] = 5) AND (Express.spillFlag # 0), 27); (*{047d-0488} *) Express.CheckAndStore(desc); (*{0491-0493} FIXME: 0-arg FORWARD *) Express.BoolCondHelper; (*{0496} result value *) Express.ConstToInt(desc, 33); (*{0498-049b} FIXME: ADDRESS arg *) Express.PopExprDesc(desc); (*{049d-049e} FIXME: ADDRESS arg *) IF withPending # 0 THEN (*{04a0-04a1} *) Express.PushOperand(withDesc); (*{04a3-04a4} FIXME arity *) CodeGen.EmitStandardOp(25); (*{04a6-04a8} *) END; size := 0; (*{04aa-04ab} *) IF desc # NIL THEN (*{04ac-04ae} *) size := (Words(desc)^[3] + 1) DIV 2; (*{04b0-04b5} *) END; CodeGen.EmitMiscOp(size, (*{04b6} staged *) CalcMemOffset() + ORD(Express.spillFlag = 1) * 128); (*{04b7-04bf} *) END ParseResult; (* proc13 @04c3, STMT case 128 — ParseWith. WITH rec DO body END. *) PROCEDURE ParseWith; VAR saved: ADDRESS; BEGIN Express.ParseDesignatorTail; (*{04c5} *) saved := Express.curDesc.raw[0]; (*{04c7-04c9} FIXME: typ pun *) Express.MatchOpClass(1024, 38); (*{04ca-04cf} *) IF Words(saved)^[4] IN BITSET{2,3,5} THEN (*{04d1-04de} FIXME set TBD *) Scanner.OpenScop(SymTab.currentScope, Words(saved)^[0]); (*{04d9-04dd} FIXME *) END; IF Words(Express.curDesc.raw[0])^[0] # Compiler.CardType THEN (*{04e0-04e8} FIXME *) Express.PushOperand(ADR(Express.curDesc)); (*{04e0-04e2} FIXME arity *) Express.PopExprDesc(ADR(Express.curDesc)); (*{04ea-04ec} FIXME *) END; MOVE(Words(SymTab.currentScope)^[2], ADR(Express.curDesc), 10); (*{04ee-04f5} FIXME *) Scanner.ExpectSy(30); (*{04f6-04f8} DO *) WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{04fa-04fe} body to END *) SymTab.currentScope := Words(SymTab.currentScope)^[0]; (*{0500-0503} FIXME pun *) Scanner.GetSym; (*{0505} consume END *) END ParseWith; (* proc14 @0509, STMT case 129 — ParseReturn. RETURN [value]: routine exit epilogue with optional value. *) PROCEDURE ParseReturn; VAR info: CARDINAL; BEGIN Scanner.PushWith; (*{050b} *) IF caseActive = 0 THEN (*{050d-050f} *) Scanner.ExpectKd(2); (*{0511-0512} *) END; IF Scanner.identKd = 2 THEN (*{0514-0518} function context *) IF 1 IN Scanner.follSet THEN (*{051a-051e} *) info := Words(Scanner.curNode)^[6] + 2; (*{0520-0525} *) ELSE info := ORD(1 IN Scanner.follSet) - ORD(6 IN Scanner.follSet); (*{0528-052e} *) END; CodeGen.QueueConst(Words(Scanner.curNode)^[5] + info * 256); (*{052f-0536} *) Scanner.GetSym; (*{0538} *) IF Scanner.AcceptSy(1) THEN (*{053a-053d} `,` before value *) Express.BoolCondHelper; (*{053f} *) Express.ParseSelector(FALSE, Compiler.charArrayDesc, NIL); (*{0541-0546} FIXME: 39 dropped *) ELSE CodeGen.QueueConst(0); (*{054b-054c} *) CodeGen.QueueConst(0); (*{054e-054f} *) END; ELSE CodeGen.QueueConst(0); (*{0553-0554} *) CodeGen.QueueConst(0); (*{0556-0557} *) CodeGen.QueueConst(0); (*{0559-055a} *) END; CodeGen.EmitStandardOp(14); (*{055c-055d} *) END ParseReturn; (* STATEMEN group 2 — STMT/STATSE/tails/BODY + internals *) PROCEDURE NextSymWrap; (* ~proc1: no head, no call site in statement.txt; GetSym wrapper by name *) BEGIN Scanner.GetSym; END NextSymWrap; PROCEDURE ParseStatSeqLoop(stop: BITSET): BOOLEAN; (* ~proc4: every driver is REPEAT proc4(set) UNTIL TRUE with empty body, so proc4 parses ONE step; 14 pre-emption for nested CASE. *) BEGIN IF Scanner.curSym IN stop THEN RETURN TRUE END; IF Scanner.curSym = 14 THEN (* CaseSym FIXME *) ParseCaseTail; ELSE ParseStatement; END; RETURN Scanner.curSym IN stop; END ParseStatSeqLoop; PROCEDURE EmitJumpFixup; (* ~proc15: sole real site STATSE after mark-save, before THEN-expect. *) BEGIN CodeGen.DiscardPending; END EmitJumpFixup; PROCEDURE ParseStatement; (* STMT @0566 *) VAR kw: CARDINAL; saveSpill: CARDINAL; saveNest: CARDINAL; saveLoop: CARDINAL; saveScope: ADDRESS; BEGIN saveSpill := Express.spillTop; (*{0568} FIXME: group wrote spillTop; word3 spill save *) saveNest := nestDepth; (*{056b} *) saveLoop := exitDepth; (*{056d} FIXME: group wrote loopExitMark *) saveScope := SymTab.currentScope; (*{056f} FIXME: ADDRESS pun *) IF Scanner.curSym = 0 THEN GOTO identLed END; (*{0572} *) IF Scanner.curSym IN BITSET{8..14} THEN GOTO done END; (*{0577} *) Express.CentralError((121 <= Scanner.curSym) AND (Scanner.curSym <= 130), 20); (*{0580} *) kw := Scanner.curSym; (*{058c} *) Scanner.GetSym; (*{058f} *) CASE kw OF (*{0591-05a6} default (125) -> ParseFor *) 121: ParseLoop | 122: ParseExit | 123: ParseResult | 124: ParseIf | 126: ParseRepeat | 127: ParseWhile | 128: ParseWith | 129: ParseReturn | 130: ParseCaseStmt ELSE ParseFor (* 125 *) END; GOTO done; identLed: Scanner.PushWith; (*{05c4} *) IF Scanner.identKd = 4 THEN (*{05c6} proc call *) Express.ParseDesignatorTail; (*{05cc} *) IF Scanner.AcceptSy(43) THEN (*{05d0} '(' *) Express.MatchOpClass(512, 43); (*{05d9} *) Express.CentralError(Express.curDesc.raw[2] = 0, 61); (*{05db} FIXME: scratch2 TBD *) Express.StoreOperand; (* proc19 *) (*{05e9?} *) ELSE Scanner.ExpectSy(27); (*{05e9} ':=' *) Express.ParseAssignment; (*{05f2?} FIXME: group wrote ParseAssignRhs=proc17 *) END; ELSIF Scanner.identKd = 5 THEN (*{05f2} *) Express.CentralError(Scanner.litType = NIL, 61); (*{05f8} FIXME: pun *) Express.LoadOperand; (* proc18 *) (*{05e9?} *) ELSIF Scanner.identKd = 0 THEN (*{0604} *) Errors.ReportErrorWithText(128, Scanner.tokBuf); (*{0609} *) ELSE Errors.ReportError(21); (*{0612} *) END; done: Express.spillTop := saveSpill; (*{0616} *) (* 061a unwind path (exceptions only): restores exitDepth/currentScope, RAISE *) END ParseStatement; PROCEDURE GuardedStatSeq(follow: BITSET): BOOLEAN; (* STATSE @0634 *) VAR saveOpts: BITSET; savePos: CARDINAL; mark: ADDRESS; saveCode: CARDINAL; savePool: CARDINAL; BEGIN saveOpts := Scanner.scanOpt; (*{0636} *) savePos := Scanner.tokPos; (*{0639} FIXME: group wrote word25 *) Scanner.GetStack(mark); (*{063d} *) saveCode := CodeGen.nextEmitPos; (*{0641} *) savePool := SymTab.stringPoolPtr; (*{0644} *) EmitJumpFixup; (* ~proc15 *) (*{0647} *) Scanner.TestSet(follow + BITSET{9}); (*{0648} entry assert *) IF Scanner.AcceptSy(9) THEN (* guarded-seq introducer *) REPEAT UNTIL ParseStatSeqLoop(follow) END; (*{0654} *) END; RETURN TRUE; (* 065b error-recovery exit: restore scanOptions/tokenPos/mark/codepos/pool, Errors.RollbackToMark, CodeGen.OpenEmitter, RETURN FALSE *) END GuardedStatSeq; PROCEDURE ParseCaseTail; (* proc16 @0683, called only from BODY via ed *) VAR tab, cnt: CARDINAL; step: CARDINAL; fix1, elseSeen: CARDINAL; fix2: CARDINAL; labelAddr: ADDRESS; BEGIN caseActive := 1; (*{0685} *) cnt := 0; (*{0687} *) tab := 0; (*{0689} *) IF NOT (Scanner.curSym IN BITSET{8,10,13}) THEN (*{068b} *) fix1 := CodeGen.MarkCodePos(15); (*{0694} *) Scanner.PushWith; (*{0699} *) Scanner.ExpectKd(2); (*{069b} *) LOOP Scanner.Allocate(labelAddr, 6); (*{06a0} FIXME: operand slots TBD *) IF 1 IN Scanner.follSet THEN (* const-label class *) step := Words(Scanner.curNode)^[0] + 2; (*{06a5?} FIXME *) ELSE step := 1 - ORD(6 IN Scanner.follSet); END; WHILE tab <> 0 DO Express.CentralError(Words(tab)^[1] = Words(tab)^[1], 47); (*{06cd} FIXME: dup-label slots TBD *) tab := Words(tab)^[0]; (* advance FIXME *) END; INC(cnt); Scanner.GetSym; Scanner.TestSet(BITSET{1,2}); (*{06e0} *) IF NOT Scanner.AcceptSy(1) THEN EXIT END; (*{06e3} *) END; Scanner.GetSym; REPEAT UNTIL ParseStatSeqLoop(BITSET{8,10,13}) END; (*{06eb} arm seq *) SyncToStatStart; (* ~proc3 *) END; IF NOT Scanner.AcceptSy(8) THEN GOTO noMoreArms END; (*{06f2} '|' *) GOTO caseLoop; (* back to {068b} FIXME: restructure to LOOP *) noMoreArms: elseSeen := ORD(Scanner.AcceptSy(10)); (*{06f8} ELSE FIXME pun *) IF elseSeen # 0 THEN INC(cnt); fix1 := CodeGen.MarkCodePos(15); REPEAT UNTIL ParseStatSeqLoop(BITSET{13,14}) END; (*{06fb} FIXME set TBD *) SyncToStatStart; END; fix2 := CodeGen.InsertFixup(Scanner.codeSize - 2, FALSE); (*{070e} FIXME: word22 TBD *) CodeGen.Emit1(cnt); WHILE tab <> 0 DO CodeGen.EmitWord(tab); CodeGen.CloseFixup(tab, FALSE); tab := Words(tab)^[0]; (* advance FIXME *) END; IF elseSeen # 0 THEN CodeGen.EmitWord(0); CodeGen.CloseFixup(fix1, FALSE); END; END ParseCaseTail; PROCEDURE CloseBlockChain(VAR node: ADDRESS); (* proc17 @0738, display-call *) VAR isFirst: BOOLEAN; desc: CARDINAL; BEGIN WHILE node # NIL DO (*{073a} *) IF NOT (0 IN Words(node)^[3]) THEN GOTO advance END; (*{073e} FIXME pun *) IF 5 IN Words(node)^[3] THEN (*{0745} *) Scanner.FindIden(SymTab.currentScope, node, 9 IN Scanner.scanOpt); (*{074b} FIXME: group wrote Proc22 6-push id-check; arity TBD *) IF Scanner.FindIden(SymTab.currentScope, node, 9 IN Scanner.scanOpt) = node THEN Errors.ReportErrorWithText(128, Scanner.tokBuf); (*{0758} FIXME: raw (5, node, s1, 128) *) END; GOTO advance; END; IF Words(node)^[4] = 4 THEN (*{0765} *) IF (Words(node)^[3] - BITSET{3} = BITSET{}) THEN (*{076b} FIXME: s4 > 9 conjunct TBD *) CloseBlockChain(node); (*{077e} recurse FIXME: node^^ *) IF Express.spillFlag <> 0 THEN (*{0780} *) needEpilogue := 1; desc := Express.curDesc.raw[0]; (*{079c?} FIXME *) Express.LoadIndirect; (* proc11 *) IF 3 IN Words(node)^[3] THEN (*{079c} *) Express.NormalizeOperand; (* proc10 *) IF Express.spillFlag = 1 THEN CodeGen.EmitMiscOp(5, 23) ELSE CodeGen.EmitStandardOp(10) END; ELSE CodeGen.EmitStandardOp(9) END; Express.PopOperand; (* proc3 *) END; RETURN; END; GOTO advance; END; IF Words(node)^[4] = 6 THEN (*{07c7} *) isFirst := NOT exitEmitted; exitEmitted := 1; CloseBlockChain(node); (* FIXME: node^ pun *) IF Words(node)^[5] # NIL THEN needEpilogue := 1; CodeGen.EmitStandardOp(15); CodeGen.CloseFixup(Words(node)^[2], FALSE); IF NOT CodeGen.InsertFixup(Words(node)^[5], FALSE) THEN END; END; IF isFirst THEN CodeGen.EmitSystemCall(24) END; RETURN; END; advance: node := Words(node)^[0]; (*{07f7} FIXME: next link via scratch; pun *) END; IF exitEmitted = 0 THEN CodeGen.EmitSystemCall(24) END; (*{07fc} *) END CloseBlockChain; PROCEDURE EmitNameHash(VAR name: ARRAY OF CHAR); (* proc18 @0808 FIXME: 2nd param HIGH=128 *) VAR h, m3, acc: CARDINAL; chunk, i: CARDINAL; c: CARDINAL; BEGIN acc := 0; chunk := 0; REPEAT h := 0; m3 := 0; c := 0; i := 1; (*{080a-0812} *) WHILE h <> 37 DO c := ORD(name[chunk + m3]); IF c = 0C THEN h := 37; ELSIF c <= ORD("9") THEN h := c - 22C; (* FIXME: exact bounds TBD *) ELSE h := c - ORD("A") - ORD(NOT ODD((c AND 64) DIV 2)); (* case-fold FIXME *) END; END; acc := acc + h * i; i := i * 38; INC(m3); IF m3 <= 2 THEN (* redo *) END; CodeGen.EmitWord(acc); chunk := chunk + 3; UNTIL chunk > 3; END EmitNameHash; PROCEDURE EmitComBytes; (* proc19 @0863, display-call reading BODY locals *) VAR f: CARDINAL; n: CARDINAL; saveHeap: ADDRESS; BEGIN MARK(saveHeap); CodeGen.EmitMiscOp(3, ORD(Express.spillFlag = 1)); Files.FindFileOnDrives(f, Scanner.tokBuf, "COM"); (*{0863?} FIXME: raw (addr-2, buf, 128) *) n := 0; REPEAT CodeGen.Emit1(0); (* FIXME: bodyL5 byte; Files.FileProc09 TBD *) INC(n); UNTIL n = 0; (* FIXME: bodyL3 count TBD *) RELEASE(saveHeap); END EmitComBytes; PROCEDURE ParseBeginBlock(tag: CARDINAL); (* BODY @08a4 *) VAR nonEmpty, wasVar: CARDINAL; fix: CARDINAL; s2, s5: CARDINAL; BEGIN curBlockNode := tag; (*{08a6} *) Express.spillTop := SymTab.stringPoolPtr; (*{08a8} FIXME: group wrote spillTop *) nestDepth := 0; exitDepth := 0; Scanner.listing := TRUE; (*{08b8?} FIXME: group wrote word23 *) caseActive := 0; exitEmitted := 0; Scanner.codeSize := CodeGen.nextEmitPos; (*{08b8} FIXME: group wrote word22 *) wasVar := ORD(Scanner.identKd = 5); (*{08bd} *) nonEmpty := ORD(Scanner.curSym = 23); (*{08c2} body-lead FIXME *) IF nonEmpty # 0 THEN Scanner.GetSym; WHILE NOT Scanner.AcceptSy(9) DO Scanner.GetSym END; (*{08cd} skip past BEGIN *) nonEmpty := ORD(Scanner.curSym <> 13); END; needEpilogue := nonEmpty; (*{08d8} *) IF (Words(curBlockNode)^[4] = 5) AND (Express.spillFlag = 0) THEN (*{08da} *) CodeGen.EmitExtCall3(3, curBlockNode, CalcMemOffset()); (*{08e0} FIXME: 3rd arg BYTEAt TBD *) IF Compiler.rangeCheckEnabled THEN CodeGen.OpenFixup(fix, TRUE) END; (*{08ea} *) IF 15 IN Scanner.scanOpt THEN CodeGen.Emit1(255) (*{08f3} *) ELSE EmitNameHash(Scanner.tokBuf); (*{08ff} FIXME: raw (g6, s1, 128) *) CodeGen.EmitWord(0); END; Scanner.codeSize := CodeGen.nextEmitPos; (*{0908} FIXME *) IF wasVar # 0 THEN Scanner.GetSym; Scanner.ExpectSy(43); (*{0912} '(' *) Express.CentralError(Scanner.isLit AND (Scanner.litType = Compiler.CharType), 33); (*{0916} *) EmitComBytes; (* nested proc19 *) Scanner.GetSym; Scanner.ExpectSy(5); (*{0927} ')' *) fix := ORD(Scanner.AcceptSy(9)); (*{092b} FIXME pun *) ELSE CodeGen.EmitStandardOp(22); CodeGen.Emit1(255); IF (Express.spillFlag <> 0) AND (withPending # 0) THEN (*{0938} FIXME: group wrote spare3 *) Express.PushExprDesc(withDesc, Compiler.BitsetType); (*{0938?} FIXME: operand slots TBD *) CodeGen.EmitStandardOp(24); Express.PopOperand; END; CloseBlockChain(SymTab.currentScope); (*{094c} FIXME: ADDRESS pun *) IF nonEmpty # 0 THEN REPEAT UNTIL ParseStatSeqLoop(BITSET{13,14}) END END; (*{0951} *) SyncToStatStart; IF Scanner.AcceptSy(14) THEN ParseCaseTail END; (*{095c} *) IF Express.spillFlag <> 0 THEN (*{0962} *) CodeGen.PokeCodeByte(SymTab.stringPoolPtr - 255, Scanner.codeSize + 1); (* FIXME *) END; END; ELSE CodeGen.EmitSystemCall(4); (*{0974} *) IF Compiler.rangeCheckEnabled THEN CodeGen.OpenFixup(fix, FALSE) END; (*{0978} *) Scanner.codeSize := CodeGen.nextEmitPos; (* FIXME *) CloseBlockChain(SymTab.currentScope); (* FIXME pun *) IF nonEmpty # 0 THEN REPEAT UNTIL ParseStatSeqLoop(BITSET{13}) END END; (*{098b} *) IF needEpilogue # 0 THEN (*{0994} *) CodeGen.EmitStandardOp(15); s5 := CodeGen.nextEmitPos; CodeGen.EmitWord(0); (* FIXME *) s2 := Scanner.codeSize; (* FIXME *) ELSE s5 := 0; END; END; IF Compiler.rangeCheckEnabled THEN fix := ORD(CodeGen.InsertFixup(fix, FALSE)) END; (*{09ab} FIXME pun *) Scanner.listing := FALSE; (* FIXME: group wrote word23 *) Scanner.ExpectSy(13); (*{09b8} END *) Scanner.ExpectSt(Scanner.tokBuf); (*{09bb} FIXME: group wrote Proc21; END-name match TBD *) IF (Words(curBlockNode)^[4] = 5) AND (Express.spillFlag = 0) THEN (*{09c2} *) Scanner.TestSet(BITSET{3}); (* '.' module end *) ELSE Scanner.TestSet(BITSET{9}); END; IF (NOT Compiler.rangeCheckEnabled) AND (0 IN Scanner.scanOpt) THEN (*{09d7} *) Texts.WriteString(3, "----- Size = "); (* FIXME: Texts.proc09 idiom *) Texts.WriteCard(3, CodeGen.nextEmitPos - Scanner.codeSize, 0); (* FIXME: Texts.proc11 *) END; IF 4 IN Scanner.scanOpt THEN (*{0a02} *) Loader.Call("PRINTCOD"); (* FIXME: +CARDINAL 7 *) END; END ParseBeginBlock; BEGIN (* proc0 @0a25 module init *) END Statemen.