-- StackImpl.mesa -- Edited by Sweet, February 3, 1981 4:54 PM -- Edited by Satterthwaite, 7-Oct-81 8:53:36 DIRECTORY Alloc: TYPE USING [Notifier], Code: TYPE USING [codeptr, stking, tempcontext, tempstart], CodeDefs: TYPE USING [ Base, Byte, CCIndex, CCNull, codeType, EvalStackSize, LabelCCNull, Lexeme, StackIndex, StackItem, StackLocRec, StackNull, StackPos, TempAddr, VarComponent], FOpCodes: TYPE USING [qBNDCK, qDUP, qEXCH, qLLK, qNILCK, qNILCKL, qPOP], P5: TYPE USING [GenTempLex, PopEffect, PushEffect], P5L: TYPE USING [LoadComponent, StoreComponent], P5U: TYPE USING [CreateLabel, DeleteCell, FreeChunk, GetChunk, Out0, Out1], Stack: TYPE, Symbols: TYPE USING [Base, BitAddress, ContextLevel, ctxType, lZ, seType]; StackImpl: PROGRAM IMPORTS LCPtr: Code, P5, P5L, P5U EXPORTS Stack = BEGIN OPEN CodeDefs; CPtr: POINTER TO FRAME [Code] = LCPtr; cb: CodeDefs.Base; seb, ctxb: Symbols.Base; uBound: StackPos; StackImplNotify: PUBLIC Alloc.Notifier = BEGIN -- called by allocator whenever table area is repacked seb ← base[Symbols.seType]; ctxb ← base[Symbols.ctxType]; cb ← base[codeType]; END; stkHead: StackIndex ← StackNull; stkPtr: StackIndex; StackModelingError: PUBLIC SIGNAL = CODE; StkError: PRIVATE PROC = BEGIN SIGNAL StackModelingError END; Above: PUBLIC PROC [s: StackIndex, count: CARDINAL ← 1, nullOk: BOOLEAN ← FALSE] RETURNS [StackIndex] = BEGIN THROUGH [0..count) DO IF s = StackNull THEN StkError[]; s ← cb[s].uplink; ENDLOOP; IF s = StackNull AND ~nullOk THEN StkError[]; RETURN [s] END; Also: PUBLIC PROC [ n: CARDINAL ← 1, inLink: BOOLEAN ← FALSE, tOffset: TempAddr, tLevel: Symbols.ContextLevel ← Symbols.lZ] = BEGIN s: StackIndex ← Top[n]; THROUGH [0..n) DO IF cb[s].tag # onStack THEN StkError[]; cb[s].data ← onStack[alsoLink: inLink, tOffset: tOffset, tLevel: tLevel]; tOffset ← tOffset+1; s ← cb[s].uplink; ENDLOOP; END; Check: PUBLIC PROC [b: Byte] = BEGIN pusheffect: CARDINAL = P5.PushEffect[b]; popeffect: CARDINAL = P5.PopEffect[b]; extra: CARDINAL ← 0; s: StackIndex ← stkPtr; IF ~CPtr.stking THEN RETURN; THROUGH [0..popeffect) DO s ← cb[s].downlink ENDLOOP; WHILE s # stkHead DO IF cb[s].tag = onStack THEN extra ← extra + 1; s ← cb[s].downlink; ENDLOOP; IF extra + pusheffect > uBound THEN Dump[]; SELECT b FROM FOpCodes.qNILCK => Load[Top[1],1]; FOpCodes.qNILCKL => Load[Top[2],2]; FOpCodes.qBNDCK => {Load[Top[2],2]; Decr[1]}; ENDCASE => BEGIN IF popeffect # 0 THEN LoadToDepth[popeffect]; Incr[pusheffect]; END; END; Clear: PUBLIC PROC = BEGIN saveStking: BOOLEAN = CPtr.stking; CPtr.stking ← FALSE; -- Off[]; WHILE stkPtr # stkHead DO WITH cb[stkPtr] SELECT FROM inTemp, inLink => NULL; onStack => P5U.Out0[FOpCodes.qPOP]; ENDCASE => StkError[]; -- shouldn't go over a mark DelStackItem[stkPtr]; ENDLOOP; CPtr.stking ← saveStking; END; Decr: PUBLIC PROC [count: CARDINAL ← 1] = BEGIN THROUGH [0..count) DO IF cb[stkPtr].tag = mark THEN StkError[]; DelStackItem[stkPtr]; -- won't delete stkHead ENDLOOP; END; DeleteToMark: PUBLIC PROC = BEGIN ResetToMark[]; DelStackItem[stkPtr]; END; DelStackItem: PRIVATE PROC [s: StackIndex] = BEGIN up: StackIndex = cb[s].uplink; down: StackIndex = cb[s].downlink; WITH cb[s] SELECT FROM mark => BEGIN IF s = stkHead THEN StkError[]; -- fell off the end IF CPtr.codeptr = label THEN CPtr.codeptr ← cb[label].blink; P5U.DeleteCell[label]; END; ENDCASE; P5U.FreeChunk[s, SIZE[StackItem]]; IF up # StackNull THEN cb[up].downlink ← down ELSE stkPtr ← down; cb[down].uplink ← up; END; Depth: PUBLIC PROC RETURNS [d: StackPos] = BEGIN d ← 0; FOR s: StackIndex ← stkPtr, cb[s].downlink UNTIL s = stkHead DO WITH cb[s] SELECT FROM onStack => d ← d+1; ENDCASE; ENDLOOP; END; Dump: PUBLIC PROC = BEGIN extra: CARDINAL ← 0; s: StackIndex ← stkPtr; wa: CARDINAL; savec: CodeDefs.CCIndex = CPtr.codeptr; next: CodeDefs.CCIndex; saveStking: BOOLEAN = CPtr.stking; CPtr.stking ← FALSE; -- Off[]; WHILE s # stkHead DO WITH cb[s] SELECT FROM onStack => IF ~alsoLink AND tLevel = Symbols.lZ THEN extra ← extra + 1; ENDCASE; s ← cb[s].downlink; ENDLOOP; IF extra # 0 THEN BEGIN tlex: se Lexeme = P5.GenTempLex[extra]; a: Symbols.BitAddress = seb[tlex.lexsei].idValue; wa ← a.wd + extra-1; END; s ← stkPtr; WHILE s # stkHead DO WITH cb[s] SELECT FROM onStack => wa ← Store[s, wa]; mark => CPtr.codeptr ← label; ENDCASE; s ← cb[s].downlink; ENDLOOP; CPtr.codeptr ← savec; UNTIL (next ← cb[CPtr.codeptr].flink) = CCNull DO CPtr.codeptr ← next ENDLOOP; CPtr.stking ← saveStking; END; Dup: PUBLIC PROC [load: BOOLEAN ← FALSE] = BEGIN oldTop: StackIndex = stkPtr; saveStking: BOOLEAN = CPtr.stking; CPtr.stking ← FALSE; -- Off[]; IF Depth[]+1 > uBound THEN Dump[]; Incr[1]; WITH ss: cb[oldTop] SELECT FROM onStack => BEGIN P5U.Out0[FOpCodes.qDUP]; cb[stkPtr].data ← onStack[alsoLink: ss.alsoLink, tOffset: ss.tOffset, tLevel: ss.tLevel]; END; inTemp => BEGIN cb[stkPtr].data ← inTemp[tOffset: ss.tOffset, tLevel: ss.tLevel]; IF load THEN LoadItem[stkPtr]; END; inLink => BEGIN cb[stkPtr].data ← inLink[link: ss.link]; IF load THEN LoadItem[stkPtr]; END; ENDCASE => StkError[]; CPtr.stking ← saveStking; END; Exchange: PUBLIC PROC = BEGIN st1: StackIndex = stkPtr; st2: StackIndex = cb[st1].downlink; IF st2 = stkHead OR cb[st2].tag = mark THEN StkError[]; WITH cb[st1] SELECT FROM onStack => Load[st2, 1]; inTemp, inLink => BEGIN t: StackIndex = cb[st2].downlink; cb[t].uplink ← st1; cb[st1].downlink ← t; cb[st1].uplink ← st2; cb[st2].downlink ← st1; cb[st2].uplink ← StackNull; END; ENDCASE => StkError[]; stkPtr ← st2; END; Forget: PUBLIC PROC [s: StackIndex, count: CARDINAL ← 1] = BEGIN next: StackIndex; THROUGH [0..count) DO IF s = StackNull THEN StkError[]; next ← cb[s].uplink; DelStackItem[s]; s ← next; ENDLOOP; END; Incr: PUBLIC PROC [count: CARDINAL ← 1] = BEGIN s: StackIndex; THROUGH [0..count) DO cb[stkPtr].uplink ← s ← P5U.GetChunk[SIZE[StackItem]]; cb[s] ← [downlink: stkPtr, data: NULL]; cb[s].data ← onStack[]; stkPtr ← s; ENDLOOP; END; Init: PUBLIC PROC = BEGIN uBound ← EvalStackSize - 2; stkHead ← P5U.GetChunk[SIZE[StackItem]]; cb[stkHead] ← [downlink: stkHead, data: mark[LabelCCNull]]; stkPtr ← stkHead; CPtr.stking ← FALSE; END; KeepOnly: PUBLIC PROC [s: StackIndex, count: CARDINAL] = BEGIN -- used when taking initial field of larger stacked record n: CARDINAL ← 0; THROUGH [0..count) DO IF s = StackNull THEN StkError[]; s ← cb[s].uplink; ENDLOOP; WHILE s # StackNull DO n ← n+1; s ← cb[s].uplink; ENDLOOP; IF n # 0 THEN Pop[n]; END; Load: PUBLIC PROC [s: StackIndex, count: CARDINAL ← 1] = BEGIN loc: StackLocRec ← Loc[s, count]; first: StackIndex = s; last: StackIndex ← Above[first, count-1]; ts: StackIndex; saveStking: BOOLEAN = CPtr.stking; CPtr.stking ← FALSE; -- Off[]; BEGIN -- to set up linkToTop label WITH ll: loc SELECT FROM onStack => BEGIN ad: CARDINAL; IF ll.depth = 0 THEN GO TO done; ad ← 0; ts ← stkPtr; THROUGH [0..ll.depth) DO WITH cb[ts] SELECT FROM onStack => ad ← ad+1; ENDCASE => NULL; ts ← cb[ts].downlink; ENDLOOP; IF ad = 0 THEN GO TO linkToTop; IF ad = 1 AND count = 1 THEN {P5U.Out0[FOpCodes.qEXCH]; GO TO linkToTop}; StoreItems[cb[last].uplink, ll.depth]; GO TO linkToTop; END; inTemp => BEGIN IF Depth[] + count > uBound THEN Dump[]; ts ← first; THROUGH [0..count) DO LoadItem[ts]; ts ← cb[ts].uplink; ENDLOOP; GO TO linkToTop; END; inLink => BEGIN -- count = 1 IF Depth[] + 1 > uBound THEN Dump[]; LoadItem[first]; GO TO linkToTop; END; ENDCASE => BEGIN -- usually some things in temps with some loaded above toLoad: CARDINAL ← count; extra: CARDINAL; ts ← first; THROUGH [0..count) DO IF cb[ts].tag = onStack THEN toLoad ← toLoad-1; ts ← cb[ts].uplink; ENDLOOP; IF Depth[] + toLoad > uBound THEN Dump[]; IF toLoad = count-1 AND count <= 4 AND cb[last].tag = onStack THEN BEGIN IF ts # StackNull THEN StoreItems[ts, VDepthOf[ts]+1]; -- unlikely ts ← first; THROUGH [0..toLoad) DO LoadItem[ts]; P5U.Out0[FOpCodes.qEXCH]; ts ← cb[ts].uplink; ENDLOOP; GO TO linkToTop; END; ts ← first; extra ← count; THROUGH [0..count) DO IF cb[ts].tag # onStack THEN EXIT; extra ← extra-1; ts ← cb[ts].uplink; ENDLOOP; StoreItems[ts, VDepthOf[ts]+1]; -- in the unlikely case stuff is above THROUGH [0..extra) DO LoadItem[ts]; ts ← cb[ts].uplink; ENDLOOP; GO TO linkToTop; END; EXITS linkToTop => BEGIN rest: StackIndex = Above[first, count, TRUE]; IF rest # StackNull THEN BEGIN down: StackIndex = cb[first].downlink; cb[stkPtr].uplink ← first; cb[first].downlink ← stkPtr; cb[rest].downlink ← down; cb[down].uplink ← rest; cb[last].uplink ← StackNull; stkPtr ← last; END; END; done => NULL; END; CPtr.stking ← saveStking; END; LoadItem: PRIVATE PROC [s: StackIndex] = BEGIN -- stking is off when called off: TempAddr; lvl: Symbols.ContextLevel; var: VarComponent; WITH cb[s] SELECT FROM inTemp => BEGIN off ← tOffset; lvl ← tLevel; END; inLink => BEGIN P5U.Out1[FOpCodes.qLLK, link]; cb[s].data ← onStack [alsoLink: TRUE, tOffset: link]; RETURN; END; onStack => RETURN; ENDCASE => StkError[]; var ← [wSize: 1, space: frame[level: lvl, wd: off, immutable: TRUE]]; P5L.LoadComponent[var]; cb[s].data ← onStack[tOffset: off, tLevel: lvl]; END; LoadToDepth: PRIVATE PROC [n: StackPos] = BEGIN IF n = 0 THEN RETURN; Load[Top[n], n]; Decr[n]; END; Loc: PUBLIC PROC [s: StackIndex, count: CARDINAL ← 1] RETURNS [StackLocRec] = BEGIN WITH cb[s] SELECT FROM onStack => BEGIN d: StackPos ← 0; THROUGH (0..count) DO s ← cb[s].uplink; WITH cb[s] SELECT FROM onStack => NULL; mark => StkError[]; ENDCASE => RETURN[[mixed[]]]; ENDLOOP; WHILE s # stkPtr DO d ← d+1; s ← cb[s].uplink ENDLOOP; RETURN[[onStack[d]]]; END; inTemp => BEGIN lvl: Symbols.ContextLevel ← tLevel; off: TempAddr ← tOffset; FOR i: CARDINAL IN (0..count) DO s ← cb[s].uplink; WITH cb[s] SELECT FROM inTemp => IF tLevel # lvl OR tOffset # off+i THEN RETURN [[mixed[]]]; mark => StkError[]; ENDCASE => RETURN [[mixed[]]]; ENDLOOP; RETURN [[inTemp[tSize: count, tLevel: lvl, tOffset: off]]]; END; inLink => RETURN [IF count # 1 THEN [mixed[]] ELSE [inLink[link]]]; ENDCASE => StkError[]; -- shouldn't be a mark ERROR; -- Since compiler doesn't know StkError doesn't return END; Mark: PUBLIC PROC = BEGIN down: StackIndex = stkPtr; stkPtr ← P5U.GetChunk[SIZE[StackItem]]; cb[stkPtr] ← [downlink: down, data: mark[P5U.CreateLabel[]]]; cb[down].uplink ← stkPtr; END; MoveToTemp: PUBLIC PROC [firstIndex: StackIndex, count: CARDINAL ← 1] RETURNS [VarComponent] = BEGIN -- store "count" words from stack into contiguous temps s: StackIndex; tStart, tempPrev: TempAddr; ctlvl: Symbols.ContextLevel = ctxb[CPtr.tempcontext].level; lvlPrev: Symbols.ContextLevel; first: BOOLEAN ← TRUE; remaining: CARDINAL ← count; saveStking: BOOLEAN = CPtr.stking; above: StackIndex = Above[s: firstIndex, count: count, nullOk: TRUE]; PutBackJunk: PROC = BEGIN cb[stkPtr].uplink ← above; cb[above].downlink ← stkPtr; UNTIL cb[stkPtr].uplink = StackNull DO stkPtr ← cb[stkPtr].uplink ENDLOOP; END; CPtr.stking ← FALSE; -- Stack.Off[]; IF above # StackNull THEN BEGIN -- unlikely n: StackPos = VDepthOf[above]; IF cb[above].tag # mark AND n # 0 THEN StoreItems[above, n+1]; stkPtr ← cb[above].downlink; cb[stkPtr].uplink ← StackNull; -- temporarily unlink END; IF count = 1 THEN BEGIN -- trade space for clarity var: VarComponent; WITH cb[firstIndex] SELECT FROM onStack => StoreItems[firstIndex, 1]; ENDCASE; WITH cb[firstIndex] SELECT FROM inTemp => var ← [wSize: 1, space: frame[wd: tOffset, immutable: TRUE, level: tLevel]]; inLink => var ← [wSize: 1, space: link[wd: link]]; ENDCASE; DelStackItem[firstIndex]; CPtr.stking ← saveStking; IF above # StackNull THEN PutBackJunk[]; RETURN[var] END; BEGIN -- to set up moveRest label BEGIN -- to set up moveAll label FOR s ← firstIndex, cb[s].uplink WHILE s # StackNull DO WITH ss: cb[s] SELECT FROM inTemp => BEGIN IF first THEN BEGIN tStart ← ss.tOffset; lvlPrev ← ss.tLevel; first ← FALSE; END ELSE BEGIN IF ss.tLevel # lvlPrev OR ss.tOffset # tempPrev+1 THEN GO TO moveAll; -- not worth a check for hole after prev END; tempPrev ← ss.tOffset; remaining ← remaining-1; END; inLink => GO TO moveAll; onStack => BEGIN IF ss.tLevel # Symbols.lZ THEN BEGIN IF first THEN BEGIN tStart ← tempPrev ← ss.tOffset; lvlPrev ← ss.tLevel; first ← FALSE; END ELSE BEGIN IF ss.tLevel # lvlPrev OR ss.tOffset # tempPrev+1 THEN GO TO moveAll; -- not worth a check for hole after prev END; tempPrev ← ss.tOffset; remaining ← remaining-1; LOOP; END; IF first OR lvlPrev # ctlvl OR tempPrev # CPtr.tempstart-1 THEN GO TO moveAll; GO TO moveRest; END; ENDCASE => StkError[]; ENDLOOP; EXITS moveAll => BEGIN remaining ← count; tStart ← CPtr.tempstart; lvlPrev ← ctlvl; GO TO moveRest; END; END; EXITS moveRest => BEGIN tlex: se Lexeme = P5.GenTempLex[remaining]; a: Symbols.BitAddress = seb[tlex.lexsei].idValue; wa: CARDINAL ← a.wd + remaining - 1; THROUGH [0..remaining) DO -- fix someday to look for doubles LoadItem[stkPtr]; wa ← Store[stkPtr, wa, TRUE]; DelStackItem[stkPtr]; -- this updates stkPtr ENDLOOP; END; END; IF remaining < count THEN Pop[count-remaining]; CPtr.stking ← saveStking; IF above # StackNull THEN PutBackJunk[]; RETURN [[wSize: count, space: frame[wd: tStart, immutable: TRUE, level: lvlPrev]]]; END; New: PUBLIC PROC RETURNS [old: StackIndex] = BEGIN old ← cb[stkHead].uplink; cb[stkHead].uplink ← StackNull; stkPtr ← stkHead; END; Off: PUBLIC PROC = {CPtr.stking ← FALSE}; On: PUBLIC PROC = {CPtr.stking ← TRUE}; Pop: PUBLIC PROC [count: CARDINAL ← 1] = BEGIN saveStking: BOOLEAN = CPtr.stking; s, next: StackIndex; CPtr.stking ← FALSE; -- Off[]; FOR s ← stkPtr, next WHILE count > 0 DO next ← cb[s].downlink; SELECT cb[s].tag FROM onStack, inTemp, inLink => BEGIN IF cb[s].tag = onStack THEN P5U.Out0[FOpCodes.qPOP]; count ← count - 1; DelStackItem[s]; END; mark => NULL; ENDCASE => StkError[]; ENDLOOP; CPtr.stking ← saveStking; END; Prefix: PUBLIC PROC [sti: StackIndex] = BEGIN ts, bs: StackIndex; IF sti = StackNull THEN RETURN; FOR ts ← sti, cb[ts].uplink UNTIL cb[ts].uplink = StackNull DO ENDLOOP; bs ← cb[stkHead].uplink; cb[ts].uplink ← bs; IF bs = StackNull THEN stkPtr ← ts ELSE cb[bs].downlink ← ts; cb[stkHead].uplink ← sti; cb[sti].downlink ← stkHead; END; Require: PUBLIC PROC [n: StackPos] = BEGIN extra: CARDINAL ← 0; s: StackIndex ← stkPtr; THROUGH [0..n) DO s ← cb[s].downlink ENDLOOP; WHILE s # stkHead DO IF cb[s].tag = onStack THEN extra ← extra + 1; s ← cb[s].downlink; ENDLOOP; IF extra # 0 THEN Dump[]; END; Reset: PUBLIC PROC = BEGIN WHILE stkPtr # stkHead DO DelStackItem[stkPtr] ENDLOOP; END; ResetToMark: PUBLIC PROC = BEGIN n: CARDINAL ← 0; FOR s: StackIndex ← stkPtr, cb[s].downlink DO WITH cb[s] SELECT FROM mark => IF s = stkHead THEN StkError[] ELSE EXIT; ENDCASE => n ← n+1; ENDLOOP; IF n # 0 THEN LoadToDepth[n]; END; Restore: PUBLIC PROC [s: StackIndex] = BEGIN Reset[]; -- free all but head cb[stkHead].uplink ← s; stkPtr ← stkHead; UNTIL s = StackNull DO stkPtr ← s; s ← cb[stkPtr].uplink; ENDLOOP; END; RoomFor: PUBLIC PROC [n: CARDINAL] RETURNS [BOOLEAN] = BEGIN RETURN [Depth[]+n <= uBound] END; Store: PRIVATE PROC [ s: StackIndex, addr: TempAddr, storeNew: BOOLEAN ← FALSE] RETURNS [nextAddr: TempAddr] = BEGIN -- stack is off when called -- Store the top element at addr -- if storeNew = FALSE and in memory, then generate POP instead lvl: Symbols.ContextLevel; off: TempAddr; link: BOOLEAN; BEGIN -- to set up label: store WITH cb[s] SELECT FROM onStack => IF storeNew OR ~(alsoLink OR tLevel # Symbols.lZ) THEN GO TO store ELSE BEGIN P5U.Out0[FOpCodes.qPOP]; lvl ← tLevel; off ← tOffset; link ← alsoLink; END; inTemp, inLink => RETURN; ENDCASE => StkError[]; EXITS store => BEGIN link ← FALSE; off ← addr; lvl ← ctxb[CPtr.tempcontext].level; StoreWord[addr, lvl]; addr ← addr-1; END; END; IF link THEN cb[s].data ← inLink[off] ELSE cb[s].data ← inTemp[tOffset: off, tLevel: lvl]; RETURN[addr]; END; StoreItems: PRIVATE PROC [start: StackIndex, count: CARDINAL] = BEGIN -- not necessarily contiguously needed: CARDINAL ← 0; s, last: StackIndex; wa: CARDINAL; s ← start; THROUGH [0..count) DO IF s = StackNull THEN StkError[]; WITH ss: cb[s] SELECT FROM inTemp, inLink => NULL; onStack => IF ~(ss.alsoLink OR ss.tLevel # Symbols.lZ) THEN needed ← needed+1; ENDCASE => StkError[]; last ← s; s ← cb[s].uplink; ENDLOOP; IF needed # 0 THEN BEGIN tlex: se Lexeme ← P5.GenTempLex[needed]; a: Symbols.BitAddress ← seb[tlex.lexsei].idValue; wa ← a.wd + needed - 1; END; s ← last; THROUGH [0..count) DO WITH cb[s] SELECT FROM inTemp, inLink => NULL; onStack => wa ← Store[s, wa, FALSE]; ENDCASE; s ← cb[s].downlink; ENDLOOP; END; StoreWord: PRIVATE PROC [offset: TempAddr, lvl: Symbols.ContextLevel] = BEGIN var: VarComponent = [wSize: 1, space: frame[wd: offset, level: lvl]]; P5L.StoreComponent[var]; END; TempStore: PUBLIC PROC [count: CARDINAL ← 1] RETURNS [VarComponent] = BEGIN -- store top of stack into contiguous temps RETURN [MoveToTemp[Top[count], count]]; END; Top: PUBLIC PROC [count: CARDINAL ← 1] RETURNS [s: StackIndex] = BEGIN s ← stkPtr; THROUGH (0..count) DO s ← cb[s].downlink ENDLOOP; IF s = stkHead THEN StkError[]; RETURN END; UnMark: PUBLIC PROC = BEGIN n: CARDINAL ← 0; FOR s: StackIndex ← stkPtr, cb[s].downlink DO WITH cb[s] SELECT FROM mark => BEGIN IF s = stkHead THEN StkError[]; -- fell off the end DelStackItem[s]; LoadToDepth[n]; -- make sure loaded, also forget from where Incr[n]; -- remember how many things loaded RETURN END; ENDCASE => n ← n+1; ENDLOOP; END; VDepth: PUBLIC PROC RETURNS [StackPos] = BEGIN RETURN [VDepthOf[stkHead]]; END; VDepthOf: PUBLIC PROC [s: StackIndex] RETURNS [d: StackPos] = BEGIN d ← 0; IF s = StackNull THEN StkError[]; DO s ← cb[s].uplink; IF s = StackNull THEN RETURN; IF cb[s].tag # mark THEN d ← d+1; ENDLOOP; END; END.