-- file Pass3M.Mesa -- last modified by Satterthwaite, November 12, 1979 4:38 PM DIRECTORY ComData: FROM "comdata" USING [ bodyIndex, idUNWIND, mainBody, ownSymbols, seAnon, stopping, textIndex, typeCONDITION, typeLOCK], Log: FROM "log" USING [Error, ErrorSei, ErrorTree], Pass3: FROM "pass3" USING [continued, currentBody, lockHeld, lockNode, markCatch], P3: FROM "p3" USING [ Attr, BodyData, NPUse, BoundNP, MergeNP, SequenceNP, SetNP, pathNP, phraseNP, Apply, BumpArgRefs, BumpCount, CanonicalType, CheckLocals, ClearRefStack, Exp, LongPath, MakePointerType, MatchFields, OperandInline, OperandInternal, OperandLhs, OperandType, PopCtx, PushCtx, RAttr, RPop, RPush, RType, SealRefStack, SearchCtxList, Stmt, UnsealRefStack, UpdateTreeAttr, VoidExp], StringDefs: FROM "stringdefs" USING [SubStringDescriptor], Symbols: FROM "symbols" USING [seType, ctxType, mdType, bodyType, SERecord, BodyRecord, SEIndex, ISEIndex, CSEIndex, RecordSEIndex, CTXIndex, BTIndex, CBTIndex, HTNull, SENull, ISENull, CSENull, RecordSENull, CBTNull, lG, lZ, typeANY], SymbolOps: FROM "symbolops" USING [ EnterString, MakeNonCtxSe, NextSe, NormalType, TransferTypes, TypeRoot, UnderType, XferMode], Table: FROM "table" USING [Base, Notifier, Bounds], Tree: FROM "tree" USING [treeType, Index, Link, Map, NodeName, Null, NullIndex, Scan], TreeOps: FROM "treeops" USING [ CopyTree, FreeNode, FreeTree, GetNode, MakeList, MakeNode, PopTree, PushTree, PushSe, PushNode, ScanList, SetAttr, SetInfo, Shared, TestTree, UpdateList], Types: FROM "types" USING [Assignable, Equivalent]; Pass3M: PROGRAM IMPORTS Log, P3, SymbolOps, Table, TreeOps, Types, dataPtr: ComData, passPtr: Pass3 EXPORTS P3 = BEGIN OPEN SymbolOps, Symbols, P3, TreeOps; InsertCatchLabel: PUBLIC SIGNAL [catchSeen, exit: BOOLEAN] = CODE; tb: Table.Base; -- tree base address (local copy) seb: Table.Base; -- se table base address (local copy) ctxb: Table.Base; -- context table base (local copy) mdb: Table.Base; -- module table base (local copy) bb: Table.Base; -- body table base (local copy) MiscNotify: PUBLIC Table.Notifier = BEGIN -- called by allocator whenever table area is repacked tb ← base[Tree.treeType]; seb ← base[seType]; ctxb ← base[ctxType]; mdb ← base[mdType]; bb ← base[bodyType]; END; current: POINTER TO P3.BodyData = @passPtr.currentBody; -- statements MiscStmt: PUBLIC PROCEDURE [node: Tree.Index] RETURNS [val: Tree.Link] = BEGIN val ← [subtree[index: node]]; -- the default SELECT tb[node].name FROM signal, error, start, join, wait => BEGIN IF tb[node].name = xerror AND current.catchDepth # 0 THEN Log.Error[misplacedReturn]; PushTree[SELECT tb[node].name FROM start => Start[node], join => Join[node], wait => Wait[node], ENDCASE => Signal[node]]; SELECT RType[] FROM CSENull, typeANY => NULL; ENDCASE => Log.Error[nonVoidStmt]; SetInfo[dataPtr.textIndex]; val ← PopTree[]; RPop[]; pathNP ← SequenceNP[pathNP][phraseNP]; IF TestTree[val, error] THEN current.reachable ← FALSE; END; xerror => BEGIN subNode: Tree.Index; IF current.catchDepth # 0 THEN Log.Error[misplacedReturn]; tb[node].name ← error; val ← MiscStmt[node]; subNode ← GetNode[val]; SELECT tb[subNode].name FROM error, errorx => tb[subNode].name ← xerror; ENDCASE => NULL; tb[subNode].attr1 ← current.entry; IF current.entry THEN tb[subNode].attr2 ← CheckLocals[tb[subNode].son[2]]; current.reachable ← FALSE; END; resume => Resume[node]; continue, retry => BEGIN SIGNAL InsertCatchLabel[catchSeen:FALSE, exit:tb[node].name=continue]; current.reachable ← FALSE; END; restart => BEGIN val ← Restart[node]; pathNP ← SequenceNP[pathNP][phraseNP] END; stop => BEGIN IF dataPtr.bodyIndex # dataPtr.mainBody OR current.catchDepth # 0 OR current.returnRecord # SENull THEN Log.Error[misplacedStop]; IF tb[node].son[1] # Tree.Null THEN [] ← CatchPhrase[tb[node].son[1]]; dataPtr.stopping ← TRUE; pathNP ← SetNP[pathNP]; END; notify, broadcast => BEGIN OPEN tb[node]; type: CSEIndex; IF ~passPtr.lockHeld THEN Log.Error[misplacedMonitorRef]; son[1] ← Exp[son[1], typeANY]; IF ~OperandLhs[son[1]] THEN Log.ErrorTree[nonLHS, son[1]]; type ← RType[]; RPop[]; pathNP ← SequenceNP[pathNP][phraseNP]; IF type # dataPtr.typeCONDITION THEN Log.ErrorTree[typeClash, son[1]]; END; dst, lst, lstf => BEGIN OPEN tb[node]; v: Tree.Link; v ← son[1] ← Exp[son[1], typeANY]; RPop[]; SELECT name FROM lstf => current.reachable ← FALSE; dst => IF ~OperandLhs[son[1]] THEN GO TO fail; ENDCASE; IF name # dst THEN phraseNP ← SetNP[phraseNP]; pathNP ← SequenceNP[pathNP][phraseNP]; -- check for simple addressability DO WITH v SELECT FROM symbol => IF seb[index].constant THEN GO TO fail ELSE EXIT; subtree => BEGIN IF tb[index].name # dollar THEN GO TO fail; v ← tb[index].son[1] END; ENDCASE => GO TO fail; ENDLOOP; EXITS fail => Log.ErrorTree[nonLHS, tb[node].son[1]]; END; enable => BEGIN OPEN tb[node]; saveEnabled: BOOLEAN = current.unwindEnabled; IF CatchPhrase[son[1]].unwindCaught THEN current.unwindEnabled ← TRUE; IF phraseNP # none THEN pathNP ← unsafe; son[2] ← UpdateList[son[2], Stmt]; current.unwindEnabled ← saveEnabled; END; ENDCASE => Log.Error[unimplemented]; RETURN END; -- control transfers MiscXfer: PUBLIC PROCEDURE [node: Tree.Index, target: CSEIndex] RETURNS [val: Tree.Link] = BEGIN SELECT tb[node].name FROM signalx, errorx => val ← Signal[node]; new => val ← New[node, target]; startx => val ← Start[node]; fork => val ← Fork[node, target]; joinx => val ← Join[node]; ENDCASE => BEGIN Log.Error[unimplemented]; val ← [subtree[node]] END; RETURN END; MakeFrameRecord: PUBLIC PROCEDURE [t: Tree.Link] RETURNS [rSei: CSEIndex] = BEGIN bti: CBTIndex = XferBody[t]; IF bti # CBTNull THEN rSei ← AllocFrameRecord[bti, TransferTypes[bb[bti].ioType].typeIn] ELSE BEGIN Log.Error[nonTypeCons]; rSei ← typeANY END; RETURN END; AllocFrameRecord: PROCEDURE [bti: CBTIndex, link: SEIndex] RETURNS [sei: RecordSEIndex] = BEGIN sei ← LOOPHOLE[MakeNonCtxSe[SIZE[linked record cons SERecord]]]; seb[sei] ← SERecord[mark3: TRUE, mark4: FALSE, body: cons[record[ machineDep: FALSE, argument: FALSE, hints: [ unifield: FALSE, variant: FALSE, comparable: FALSE, privateFields: TRUE], fieldCtx: bb[bti].localCtx, length: 0 -- n*wordlength --, lengthUsed: FALSE, monitored: bb[bti].monitored, linkPart: linked[link]]]]; RETURN END; XferBody: PROCEDURE [t: Tree.Link] RETURNS [bti: CBTIndex] = BEGIN sei: ISEIndex; type: CSEIndex; WITH t SELECT FROM symbol => BEGIN sei ← index; type ← UnderType[seb[sei].idType]; bti ← WITH seb[type] SELECT FROM transfer => IF ~seb[sei].immutable THEN CBTNull ELSE SELECT mode FROM program => IF seb[sei].mark4 THEN IF seb[sei].constant THEN seb[sei].idInfo ELSE CBTNull ELSE dataPtr.mainBody, procedure => IF sei = bb[dataPtr.bodyIndex].id THEN dataPtr.bodyIndex ELSE CBTNull, ENDCASE => CBTNull, ENDCASE => CBTNull; END; ENDCASE => bti ← CBTNull; RETURN END; XferForFrame: PUBLIC PROCEDURE [ctx: CTXIndex] RETURNS [CSEIndex] = BEGIN bti: BTIndex; btLimit: BTIndex = LOOPHOLE[Table.Bounds[bodyType].size]; bti ← FIRST[BTIndex]; UNTIL bti = btLimit DO WITH entry: bb[bti] SELECT FROM Callable => BEGIN IF entry.localCtx = ctx THEN RETURN [UnderType[entry.ioType]]; bti ← bti + (WITH entry SELECT FROM Inner => SIZE[Inner Callable BodyRecord], ENDCASE => SIZE[Outer Callable BodyRecord]); END; ENDCASE => bti ← bti + SIZE[Other BodyRecord]; ENDLOOP; ERROR END; New: PROCEDURE [node: Tree.Index, target: CSEIndex] RETURNS [val: Tree.Link] = BEGIN subNode: Tree.Index; val ← ForceApplication[tb[node].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; subNode ← GetNode[val]; BEGIN OPEN tb[subNode]; type, mType, rType: CSEIndex; attr: Attr; NewError: PROCEDURE = BEGIN Log.ErrorTree[typeClash, son[1]]; type ← typeANY END; name ← new; attr1 ← TRUE; son[1] ← Exp[son[1], typeANY]; mType ← RType[]; attr ← RAttr[]; RPop[]; phraseNP ← SetNP[phraseNP]; WITH seb[mType] SELECT FROM transfer => IF mode = program THEN SELECT XferBody[son[1]] FROM CBTNull => type ← mType; dataPtr.mainBody => BEGIN type ← IF seb[target].typeTag = pointer THEN MakePointerType[MakeFrameRecord[son[1]], target] ELSE mType; attr1 ← FALSE; END; ENDCASE => NewError[] ELSE NewError[]; pointer => BEGIN type ← mType; dereferenced ← TRUE; rType ← UnderType[refType]; WITH seb[rType] SELECT FROM record => SELECT TRUE FROM (ctxb[fieldCtx].level # lG) => NewError[]; (seb[target].typeTag = transfer) => type ← XferForFrame[fieldCtx]; ENDCASE; ENDCASE => IF refType # typeANY THEN NewError[]; END; ENDCASE => IF mType # typeANY THEN type ← typeANY ELSE NewError[]; IF son[2] # Tree.Null THEN BEGIN Log.ErrorTree[noApplication, son[1]]; son[2] ← UpdateList[son[2], VoidExp]; END; IF nSons > 2 THEN [] ← CatchPhrase[son[3]]; attr.const ← attr.noXfer ← FALSE; RPush[type, attr]; END; RETURN END; Start: PROCEDURE [node: Tree.Index] RETURNS [val: Tree.Link] = BEGIN subNode: Tree.Index; val ← ForceApplication[tb[node].son[1]]; subNode ← GetNode[val]; Apply[subNode, typeANY, TRUE]; SELECT tb[subNode].name FROM start, startx, apply => NULL; ENDCASE => Log.ErrorTree[typeClash, tb[subNode].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; RETURN END; Restart: PROCEDURE [node: Tree.Index] RETURNS [val: Tree.Link] = BEGIN subNode: Tree.Index; type: CSEIndex; val ← ForceApplication[tb[node].son[1]]; subNode ← GetNode[val]; BEGIN OPEN tb[subNode]; name ← tb[node].name; info ← tb[node].info; son[1] ← Exp[son[1], typeANY]; type ← RType[]; RPop[]; phraseNP ← SetNP[phraseNP]; WITH seb[type] SELECT FROM pointer => NULL; -- a weak check for now transfer => IF mode # program OR XferBody[son[1]] # CBTNull THEN Log.ErrorTree[typeClash, son[1]]; ENDCASE => IF type # typeANY THEN Log.ErrorTree[typeClash, son[1]]; IF son[2] # Tree.Null THEN BEGIN Log.ErrorTree[noApplication, son[1]]; son[2] ← UpdateList[son[2], VoidExp]; END; IF nSons > 2 THEN [] ← CatchPhrase[son[3]]; END; tb[node].son[1] ← Tree.Null; FreeNode[node]; RETURN END; Fork: PROCEDURE [node: Tree.Index, target: CSEIndex] RETURNS [val: Tree.Link] = BEGIN subNode: Tree.Index; type, subType: CSEIndex; attr: Attr; val ← ForceApplication[tb[node].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; subNode ← GetNode[val]; Apply[subNode, typeANY, TRUE]; attr ← RAttr[]; RPop[]; SELECT tb[subNode].name FROM call, callx => BEGIN IF passPtr.lockHeld AND OperandInternal[tb[subNode].son[1]] THEN Log.ErrorTree[internalCall, tb[subNode].son[1]]; subType ← OperandType[tb[subNode].son[1]]; WITH procType: seb[subType] SELECT FROM transfer => BEGIN type ← MakeNonCtxSe[SIZE[transfer cons SERecord]]; seb[type] ← SERecord[mark3: TRUE, mark4: TRUE, body: cons[transfer[ mode: process, inRecord: RecordSENull, outRecord: procType.outRecord]]]; IF OperandInline[tb[subNode].son[1]] THEN Log.ErrorTree[misusedInline, tb[subNode].son[1]]; END; ENDCASE => ERROR; tb[subNode].name ← fork; END; apply => type ← typeANY; ENDCASE => BEGIN Log.ErrorTree[typeClash, tb[node].son[1]]; type ← typeANY; END; tb[subNode].info ← type; attr.const ← attr.noXfer ← FALSE; RPush[type, attr]; RETURN END; Join: PROCEDURE [node: Tree.Index] RETURNS [val: Tree.Link] = BEGIN subNode: Tree.Index; val ← ForceApplication[tb[node].son[1]]; subNode ← GetNode[val]; Apply[subNode, typeANY, TRUE]; SELECT tb[subNode].name FROM join, joinx => NULL; apply => NULL; ENDCASE => Log.ErrorTree[typeClash, tb[subNode].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; RETURN END; Wait: PROCEDURE [node: Tree.Index] RETURNS [val: Tree.Link] = BEGIN subNode: Tree.Index; saveNP: NPUse; IF ~passPtr.lockHeld THEN Log.Error[misplacedMonitorRef]; val ← ForceApplication[tb[node].son[1]]; subNode ← GetNode[val]; Apply[subNode, typeANY, TRUE]; SELECT tb[subNode].name FROM wait => NULL; apply => NULL; ENDCASE => Log.ErrorTree[typeClash, tb[subNode].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; IF ~OperandLhs[tb[subNode].son[1]] THEN Log.ErrorTree[nonLHS, tb[subNode].son[1]]; [] ← FreeTree[tb[subNode].son[2]]; saveNP ← phraseNP; tb[subNode].son[2] ← tb[subNode].son[1]; tb[subNode].son[1] ← CopyLock[]; phraseNP ← MergeNP[saveNP][phraseNP]; RETURN END; -- monitors LockVar: PUBLIC PROCEDURE [t: Tree.Link] RETURNS [val: Tree.Link] = BEGIN type, nType: CSEIndex; desc: StringDefs.SubStringDescriptor; sei: ISEIndex; nDerefs: CARDINAL; long, b: BOOLEAN; Dereference: PROCEDURE [type: CSEIndex] = BEGIN PushTree[val]; PushNode[uparrow, 1]; SetInfo[type]; SetAttr[1, long]; val ← PopTree[]; END; val ← Exp[t, typeANY]; long ← LongPath[val]; type ← RType[]; RPop[]; nDerefs ← 0; DO IF type = dataPtr.typeLOCK THEN BEGIN IF nDerefs # 0 THEN Dereference[type]; GO TO success END; type ← TypeRoot[type]; nType ← NormalType[type]; WITH seb[nType] SELECT FROM record => BEGIN IF monitored THEN BEGIN desc ← ["LOCK"L, 0, ("LOCK"L).length]; [b, sei] ← SearchCtxList[EnterString[@desc], fieldCtx]; IF ~b THEN BEGIN Log.Error[noAccess]; sei ← dataPtr.seAnon END; PushTree[val]; PushSe[sei]; PushNode[IF nDerefs = 0 THEN dollar ELSE dot, 2]; SetInfo[dataPtr.typeLOCK]; SetAttr[1, long]; val ← PopTree[]; GO TO success; END; GO TO failure; END; pointer => BEGIN IF (nDerefs ← nDerefs + 1) > 255 THEN GO TO failure; IF nDerefs > 1 THEN Dereference[type]; long ← seb[type].typeTag = long; dereferenced ← TRUE; type ← UnderType[refType]; END; ENDCASE => GO TO failure; REPEAT success => NULL; failure => Log.ErrorTree[typeClash, val]; ENDLOOP; IF ~OperandLhs[val] THEN Log.ErrorTree[nonLHS, val]; RETURN END; FindLockParams: PUBLIC PROCEDURE RETURNS [formal, actual: ISEIndex] = BEGIN node: Tree.Index = GetNode[tb[passPtr.lockNode].son[1]]; found: BOOLEAN; IF node = Tree.NullIndex THEN formal ← actual ← ISENull ELSE BEGIN WITH tb[node].son[1] SELECT FROM symbol => formal ← index; ENDCASE => ERROR; IF current.inputRecord = SENull THEN found ← FALSE ELSE [found, actual] ← SearchCtxList[ seb[formal].hash, seb[current.inputRecord].fieldCtx]; IF ~found THEN actual ← ISENull; END; RETURN END; LambdaApply: PROCEDURE [t: Tree.Link, formal, actual: ISEIndex] RETURNS [Tree.Link] = BEGIN Substitute: Tree.Map = BEGIN sei: ISEIndex; WITH t SELECT FROM symbol => BEGIN sei ← index; IF sei = formal THEN sei ← actual; BumpCount[sei]; v ← [symbol[index: sei]]; END; subtree => IF Shared[t] THEN BEGIN [] ← UpdateTreeAttr[t]; v ← t END ELSE v ← CopyTree[[baseP:@tb, link:t], Substitute]; ENDCASE => v ← t; RETURN END; RETURN [Substitute[t]]; END; CopyLock: PUBLIC PROCEDURE RETURNS [val: Tree.Link] = BEGIN formal, actual: ISEIndex; SELECT TRUE FROM passPtr.lockNode = Tree.NullIndex => val ← Tree.Null; tb[current.bodyNode].son[4] # Tree.Null => val ← LambdaApply[tb[current.bodyNode].son[4], ISENull, ISENull]; ENDCASE => BEGIN [formal:formal, actual:actual] ← FindLockParams[]; IF formal # SENull THEN BEGIN IF actual = SENull THEN BEGIN Log.ErrorSei[missingLock, formal]; actual ← dataPtr.seAnon; END; IF ~Types.Assignable[ [dataPtr.ownSymbols, UnderType[seb[formal].idType]], [dataPtr.ownSymbols, UnderType[seb[actual].idType]]] THEN Log.ErrorSei[typeClash, actual]; END; val ← LambdaApply[tb[passPtr.lockNode].son[2], formal, actual]; END; RETURN END; -- signals Signal: PUBLIC PROCEDURE [node: Tree.Index] RETURNS [val: Tree.Link] = BEGIN subNode: Tree.Index; nodeTag: Tree.NodeName = tb[node].name; val ← ForceApplication[tb[node].son[1]]; subNode ← GetNode[val]; Apply[subNode, typeANY, TRUE]; SELECT tb[subNode].name FROM signal, signalx => tb[subNode].name ← nodeTag; error, errorx => BEGIN SELECT nodeTag FROM signal, signalx => Log.ErrorTree[typeClash, tb[subNode].son[1]]; ENDCASE => NULL; tb[subNode].name ← nodeTag; END; apply => NULL; ENDCASE => Log.ErrorTree[typeClash, tb[subNode].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; RETURN END; ForceApplication: PROCEDURE [t: Tree.Link] RETURNS [Tree.Link] = BEGIN IF TestTree[t, apply] THEN RETURN [t]; PushTree[t]; PushTree[Tree.Null]; RETURN [MakeNode[apply, 2]] END; -- catch phrases CatchPhrase: PUBLIC PROCEDURE [t: Tree.Link] RETURNS [unwindCaught: BOOLEAN] = BEGIN saveReachable: BOOLEAN = current.reachable; savePathNP: NPUse = pathNP; entryNP, exitNP: NPUse; CatchItem: Tree.Scan = BEGIN node: Tree.Index = GetNode[t]; type: CSEIndex; mixed: BOOLEAN; saveIndex: CARDINAL = dataPtr.textIndex; CatchLabel: Tree.Map = BEGIN subType: CSEIndex; v ← Exp[t, typeANY]; subType ← CanonicalType[RType[]]; RPop[]; entryNP ← SequenceNP[entryNP][phraseNP]; SELECT XferMode[subType] FROM signal, error => IF type = typeANY THEN type ← subType ELSE IF ~Types.Equivalent[ [dataPtr.ownSymbols, type], [dataPtr.ownSymbols, subType]] THEN mixed ← TRUE; ENDCASE => IF subType # typeANY THEN Log.ErrorTree[typeClash, t]; RETURN END; dataPtr.textIndex ← tb[node].info; type ← typeANY; mixed ← FALSE; tb[node].son[1] ← UpdateList[tb[node].son[1], CatchLabel]; IF mixed THEN type ← typeANY; tb[node].son[2] ← CatchBody[tb[node].son[2], type]; IF tb[node].son[1] = Tree.Link[symbol[index: dataPtr.idUNWIND]] THEN BEGIN unwindCaught ← TRUE; IF current.entry AND ~current.unwindEnabled AND current.catchDepth = 0 THEN BEGIN PushTree[tb[node].son[2]]; PushTree[CopyLock[]]; PushNode[unlock, 1]; SetInfo[dataPtr.textIndex]; tb[node].son[2] ← MakeList[2]; END; END; tb[node].info ← IF type # typeANY THEN type ELSE SENull; dataPtr.textIndex ← saveIndex; RETURN END; CatchBody: PROCEDURE [body: Tree.Link, type: CSEIndex] RETURNS [val: Tree.Link] = BEGIN saveRecord: RecordSEIndex = current.resumeRecord; saveFlag: BOOLEAN = current.resumeFlag; current.catchDepth ← current.catchDepth + 1; WITH seb[type] SELECT FROM transfer => BEGIN current.resumeFlag ← mode = signal; PushArgCtx[inRecord]; BumpArgRefs[inRecord, TRUE]; PushArgCtx[current.resumeRecord ← outRecord]; ClearRefStack[]; END; ENDCASE => BEGIN current.resumeFlag ← FALSE; current.resumeRecord ← RecordSENull; END; current.reachable ← TRUE; pathNP ← entryNP; val ← UpdateList[body, Stmt ! InsertCatchLabel => IF catchSeen THEN RESUME]; exitNP ← BoundNP[exitNP][pathNP]; WITH seb[type] SELECT FROM transfer => BEGIN PopArgCtx[outRecord]; PopArgCtx[inRecord] END; ENDCASE; current.catchDepth ← current.catchDepth - 1; current.resumeRecord ← saveRecord; current.resumeFlag ← saveFlag; RETURN END; setLabel, continued: BOOLEAN; node: Tree.Index = GetNode[t]; SealRefStack[]; setLabel ← continued ← unwindCaught ← FALSE; entryNP ← exitNP ← none; BEGIN ENABLE InsertCatchLabel => IF ~catchSeen THEN BEGIN setLabel ← TRUE; IF exit THEN continued ← TRUE; SIGNAL InsertCatchLabel[catchSeen:TRUE, exit:exit]; RESUME END; ScanList[tb[node].son[1], CatchItem]; IF tb[node].nSons > 1 THEN tb[node].son[2] ← CatchBody[tb[node].son[2], typeANY]; END; IF setLabel THEN BEGIN passPtr.markCatch ← TRUE; passPtr.continued ← continued END; UnsealRefStack[]; current.reachable ← saveReachable; phraseNP ← exitNP; pathNP ← savePathNP; RETURN END; PushArgCtx: PROCEDURE [rSei: RecordSEIndex] = BEGIN ctx: CTXIndex; IF rSei # RecordSENull THEN BEGIN ctx ← seb[rSei].fieldCtx; ctxb[ctx].level ← current.level + current.catchDepth; PushCtx[ctx]; END; END; PopArgCtx: PROCEDURE [rSei: RecordSEIndex] = BEGIN IF rSei # RecordSENull THEN BEGIN PopCtx[]; ctxb[seb[rSei].fieldCtx].level ← lZ END; END; Resume: PROCEDURE [node: Tree.Index] = BEGIN OPEN tb[node]; rSei: RecordSEIndex = current.resumeRecord; n: CARDINAL; sei: ISEIndex; IF ~current.resumeFlag THEN Log.Error[misplacedResume]; IF rSei = SENull OR son[1] # Tree.Null THEN BEGIN son[1] ← MatchFields[rSei, son[1], FALSE]; RPop[] END ELSE BEGIN n ← 0; BumpArgRefs[rSei, FALSE]; FOR sei ← ctxb[seb[rSei].fieldCtx].seList, NextSe[sei] UNTIL sei = SENull DO n ← n+1; IF n=1 AND seb[sei].hash = HTNull THEN Log.Error[illDefinedReturn]; PushSe[sei]; ENDLOOP; son[1] ← MakeList[n]; END; current.reachable ← FALSE; END; END.