MODULEIMPLEMENTATION:; IMPORT Atom, AtomIntTbl; TYPE Entry = RECORD a: Atom.T; n: INTEGER END; Stack = REF ARRAY OF Entry; REVEAL T = Public BRANDED "StackTbl.T" OBJECT tbl: AtomIntTbl.Default := NIL; stack: Stack := NIL; sp: CARDINAL OVERRIDES init := Init END; CONST InitSize = 20; StackTbl
The stack table is implemented with a table and a stack of (Atom.T,
INTEGER) pairs. The table maps Atom.T's to INTEGER's, and it holds the
true mapping at any point in time. Hence, the Lookup procedure simply
looks up the supplied name in the table. The stack is used to store marks,
and old (Atom.T, INTEGER) associations.
The Mark procedure pushes a special (NIL, 0) pair on the stack. The
procedure Push(nm) pushes the pair (nm, oldVal) to the stack if nm
was associated with the integer oldVal in the table, or pushes the pair
(nm, 0) if nm was not previously associated in the table. It also
associates nm with the next index in the table. The PushFormal
procedure is similar.
Finally the PopToMark procedure pops entries from the stack until it gets
to a distinguished (NIL, 0) pair. For each entry (nm, val), it restores
the association for nm in table according to val: if val is non-zero,
it associates nm with val in the table, and if val is zero, it
deletes nm from the table.
PROCEDUREMark (t: T) =
Push a special NIL marker on the stack.
BEGIN
PushP(t, NIL, 0)
END Mark;
PROCEDURE PopToMark (t: T) =
VAR dummy: INTEGER; BEGIN
DEC(t.sp);
LOOP
WITH entry = t.stack[t.sp] DO
IF entry.a = NIL THEN EXIT END;
IF entry.n = 0
THEN EVAL t.tbl.delete(entry.a, dummy)
ELSE EVAL t.tbl.put(entry.a, entry.n)
END
END;
DEC(t.sp)
END
END PopToMark;
PROCEDURE Push (t: T; nm: Atom.T) =
VAR n: INTEGER; BEGIN
IF t.tbl.get(nm, n)
THEN PushP(t, nm, n);
ELSE PushP(t, nm, 0);
END;
EVAL t.tbl.put(nm, t.next_index);
INC(t.next_index)
END Push;
PROCEDURE PushFormal (t: T; nm: Atom.T) =
VAR n: INTEGER; BEGIN
IF t.tbl.get(nm, n)
THEN PushP(t, nm, n);
ELSE PushP(t, nm, 0);
END;
EVAL t.tbl.put(nm, t.next_formal);
DEC(t.next_formal)
END PushFormal;
PROCEDURE Lookup (t: T; nm: Atom.T): INTEGER =
VAR n: INTEGER; BEGIN
IF NOT t.tbl.get(nm, n) THEN n := 0 END;
RETURN n
END Lookup;
PROCEDURE PushP (t: T; a: Atom.T; n: INTEGER) =
BEGIN
IF NUMBER(t.stack^) = t.sp THEN
(* grow the stack by a factor of 2 *)
VAR new := NEW(Stack, 2 * NUMBER(t.stack^)); BEGIN
SUBARRAY(new^, 0, NUMBER(t.stack^)) := t.stack^;
t.stack := new;
END
END;
t.stack[t.sp].a := a;
t.stack[t.sp].n := n;
INC(t.sp)
END PushP;
PROCEDURE Init (t: T): T =
BEGIN
t.next_index := 1;
t.next_formal := -1;
IF t.tbl = NIL
THEN t.tbl := NEW(AtomIntTbl.Default).init(sizeHint := InitSize)
ELSE EVAL t.tbl.init(sizeHint := InitSize)
END;
IF t.stack = NIL THEN
t.stack := NEW(Stack, InitSize)
END;
t.sp := 0;
RETURN t
END Init;
BEGIN
END StackTbl.