-- file PackTreeImpl.mesa
-- last modified by Satterthwaite, July 25, 1980 5:11 PM
-- last edited by Lewis on 2-Apr-81 15:50:15
-- last edited by Levin on July 6, 1982 4:52 pm
DIRECTORY
Alloc: TYPE USING [AddNotify, DropNotify, FreeChunk, GetChunk, Handle, Notifier],
LongStorage: TYPE USING [FreeWords, Words],
PackagerDefs USING [globalData],
Segments USING [Address],
SymTabDefs USING [HTIndex],
Table: TYPE USING [Base, Finger],
Tree: FROM "PackTree" USING [
Id, Index, Link, Map, Node, NodeName, Scan, Test, MaxNSons, Null,
NullIndex, treeType],
TreeOps: FROM "PackTreeOps" USING [];
PackTreeImpl: PROGRAM
IMPORTS Alloc, LongStorage, PackagerDefs
EXPORTS TreeOps =
PUBLIC
BEGIN
EndIndex: Tree.Index = LAST[Tree.Index];
EndMark: Tree.Link = [subtree[index: EndIndex]];
initialized: PRIVATE BOOLEAN ← FALSE;
LinkStack: PRIVATE TYPE = LONG DESCRIPTOR FOR ARRAY OF Tree.Link;
stack: PRIVATE LinkStack;
sI: PRIVATE CARDINAL;
tb: PRIVATE Table.Base; -- tree base
table: PRIVATE Alloc.Handle ← NIL;
UpdateBase: PRIVATE Alloc.Notifier = {tb ← base[Tree.treeType]};
Initialize: PROC = {
IF initialized THEN Finalize[];
stack ← AllocStack[256]; sI ← 0;
table ← PackagerDefs.globalData.ownTable;
table.AddNotify[UpdateBase];
IF MakeNode[none,0] # Tree.Null THEN ERROR; -- reserve null
initialized ← TRUE};
Reset: PROC = {
IF initialized AND LENGTH[stack] > 256
THEN {FreeStack[stack]; stack ← AllocStack[256]}};
Finalize: PROC = {
initialized ← FALSE; table.DropNotify[UpdateBase]; table ← NIL; FreeStack[stack]};
AllocStack: PRIVATE PROC [size: CARDINAL] RETURNS [LinkStack] = {
base: Segments.Address = LongStorage.Words[size * SIZE[Tree.Link]];
RETURN [DESCRIPTOR[base, size]]};
FreeStack: PRIVATE PROC [s: LinkStack] = {
IF LENGTH[s] # 0 THEN LongStorage.FreeWords[BASE[s]]};
ExpandStack: PRIVATE PROC = {
newStack: LinkStack = AllocStack[LENGTH[stack]+256];
FOR i: CARDINAL IN [0 .. LENGTH[stack]) DO newStack[i] ← stack[i] ENDLOOP;
FreeStack[stack]; stack ← newStack};
PushTree: PROC [v: Tree.Link] = {
IF sI >= LENGTH[stack] THEN ExpandStack[];
stack[sI] ← v; sI ← sI+1};
PopTree: PROC RETURNS [Tree.Link] = {RETURN [stack[sI←sI-1]]};
InsertTree: PROC [v: Tree.Link, n: CARDINAL] = {
i: CARDINAL;
IF sI >= LENGTH[stack] THEN ExpandStack[];
i ← sI; sI ← sI+1;
THROUGH [1 .. n) DO stack[i] ← stack[i-1]; i ← i-1 ENDLOOP;
stack[i] ← v};
ExtractTree: PROC [n: CARDINAL] RETURNS [v: Tree.Link] = {
i: CARDINAL;
i ← sI - n; v ← stack[i];
THROUGH [1 .. n) DO stack[i] ← stack[i+1]; i ← i+1 ENDLOOP;
sI ← sI - 1;
RETURN [v]};
MakeNode: PROC [name: Tree.NodeName, count: INTEGER] RETURNS [Tree.Link] = {
PushNode[name, count]; RETURN [PopTree[]]};
MakeList: PROC [size: INTEGER] RETURNS [Tree.Link] = {
PushList[size]; RETURN [PopTree[]]};
PushNode: PROC [name: Tree.NodeName, count: INTEGER] = {
nSons: CARDINAL = ABS[count];
node: Tree.Index = table.GetChunk[SIZE[Tree.Node]+nSons];
i: CARDINAL;
tb[node].name ← name; tb[node].nSons ← nSons;
tb[node].info ← 0; tb[node].shared ← FALSE;
tb[node].attr1 ← tb[node].attr2 ← tb[node].attr3 ← FALSE;
IF count >= 0
THEN FOR i ← nSons, i-1 WHILE i >= 1
DO tb[node].son[i] ← stack[sI←sI-1] ENDLOOP
ELSE FOR i ← 1, i+1 WHILE i <= nSons
DO tb[node].son[i] ← stack[sI←sI-1] ENDLOOP;
IF sI >= LENGTH[stack] THEN ExpandStack[];
stack[sI] ← [subtree[index: node]]; sI ← sI+1};
PushList: PROC [size: INTEGER] = {
nSons: CARDINAL = ABS[size];
node: Tree.Index;
i: CARDINAL;
SELECT nSons FROM
1 => NULL;
0 => PushTree[Tree.Null];
ENDCASE => {
IF nSons IN (0..Tree.MaxNSons]
THEN node ← table.GetChunk[SIZE[Tree.Node]+nSons]
ELSE {
node ← table.GetChunk[SIZE[Tree.Node]+(nSons+1)];
tb[node].son[nSons+1] ← EndMark};
tb[node].name ← list;
tb[node].info ← 0; tb[node].shared ← FALSE;
tb[node].attr1 ← tb[node].attr2 ← tb[node].attr3 ← FALSE;
tb[node].nSons ← IF nSons IN (0..Tree.MaxNSons] THEN nSons ELSE 0;
IF size > 0
THEN FOR i ← nSons, i-1 WHILE i >= 1
DO tb[node].son[i] ← stack[sI←sI-1] ENDLOOP
ELSE FOR i ← 1, i+1 WHILE i <= nSons
DO tb[node].son[i] ← stack[sI←sI-1] ENDLOOP;
IF sI >= LENGTH[stack] THEN ExpandStack[];
stack[sI] ← [subtree[index: node]]; sI ← sI+1}};
PushProperList: PROC [size: INTEGER] = {
IF ~(size IN [-1..1])
THEN PushList[size]
ELSE {
node: Tree.Index = table.GetChunk[SIZE[Tree.Node] + 1];
tb[node].name ← list;
tb[node].info ← 0; tb[node].shared ← FALSE;
tb[node].attr1 ← tb[node].attr2 ← tb[node].attr3 ← FALSE;
tb[node].nSons ← ABS[size];
tb[node].son[1] ← IF size = 0 THEN EndMark ELSE PopTree[];
PushTree[[subtree[index: node]]]}};
PushHash: PROC [hti: SymTabDefs.HTIndex] = {PushTree[[hash[index: hti]]]};
SetInfo: PROC [info: UNSPECIFIED] = {
v: Tree.Link = stack[sI-1];
IF v # Tree.Null THEN
WITH v SELECT FROM subtree => tb[index].info ← info ENDCASE};
SetAttr: PROC [attr: [1..3], value: BOOLEAN] = {
v: Tree.Link = stack[sI-1];
IF v = Tree.Null
THEN ERROR
ELSE
WITH v SELECT FROM
subtree => {
node: Tree.Index = index;
SELECT attr FROM
1 => tb[node].attr1 ← value;
2 => tb[node].attr2 ← value;
3 => tb[node].attr3 ← value;
ENDCASE};
ENDCASE => ERROR};
FreeNode: PROC [node: Tree.Index] = {
IF node # Tree.NullIndex AND ~tb[node].shared
THEN {
i, n: CARDINAL;
t: Tree.Link;
n ← tb[node].nSons;
IF tb[node].name # list OR n # 0
THEN
FOR i ← 1, i+1 WHILE i <= n
DO
t ← tb[node].son[i];
WITH t SELECT FROM subtree => FreeNode[index] ENDCASE;
ENDLOOP
ELSE {
n ← 1;
FOR i ← 1, i+1 UNTIL (t←tb[node].son[i]) = EndMark
DO
WITH t SELECT FROM subtree => FreeNode[index] ENDCASE;
n ← n+1;
ENDLOOP};
table.FreeChunk[node, SIZE[Tree.Node]+n]}};
FreeTree: PROC [t: Tree.Link] RETURNS [Tree.Link] = {
WITH t SELECT FROM subtree => FreeNode[index]; ENDCASE;
RETURN [Tree.Null]};
-- procedures for tree testing
GetHash: PROC [t: Tree.Link] RETURNS [SymTabDefs.HTIndex] = {
RETURN [WITH t SELECT FROM hash => index, ENDCASE => ERROR]};
GetNode: PROC [t: Tree.Link] RETURNS [Tree.Index] = {
RETURN [WITH t SELECT FROM subtree => index, ENDCASE => ERROR]};
NthSon: PROC [t: Tree.Link, n: CARDINAL] RETURNS [Tree.Link] = {
RETURN [IF t = Tree.Null
THEN ERROR
ELSE WITH t SELECT FROM subtree => tb[index].son[n], ENDCASE => ERROR]};
OpName: PROC [t: Tree.Link] RETURNS [Tree.NodeName] = {
RETURN [IF t = Tree.Null
THEN none
ELSE WITH t SELECT FROM subtree => tb[index].name, ENDCASE => none]};
Shared: PROC [t: Tree.Link] RETURNS [BOOLEAN] = {
RETURN [WITH t SELECT FROM
subtree => IF index = Tree.NullIndex THEN FALSE ELSE tb[index].shared,
ENDCASE => FALSE]};
SetShared: PROC [t: Tree.Link, shared: BOOLEAN] = {
WITH t SELECT FROM
subtree => IF index # Tree.NullIndex THEN tb[index].shared ← shared;
ENDCASE};
SonCount: PRIVATE PROC [node: Tree.Index] RETURNS [CARDINAL] = {
RETURN [SELECT node FROM
Tree.NullIndex, EndIndex => 0,
ENDCASE => IF tb[node].name = list AND tb[node].nSons = 0
THEN ListLength[[subtree[index: node]]] + 1
ELSE tb[node].nSons]};
-- procedures for tree traversal
UpdateTree: PROC [root: Tree.Link, map: Tree.Map] RETURNS [v: Tree.Link] = {
IF root = Tree.Null
THEN v ← Tree.Null
ELSE
WITH root SELECT FROM
subtree => {
node: Tree.Index = index;
t: Tree.Link;
FOR i: CARDINAL IN [1 .. SonCount[node]]
DO
IF (t←tb[node].son[i]) # EndMark THEN tb[node].son[i] ← map[t];
ENDLOOP;
v ← root};
ENDCASE => v ← map[root];
RETURN};
-- procedures for list testing
ListLength: PROC [t: Tree.Link] RETURNS [CARDINAL] = {
IF t = Tree.Null THEN RETURN [0];
WITH t SELECT FROM
subtree => {
node: Tree.Index = index;
n: CARDINAL;
IF tb[node].name # list THEN RETURN [1];
n ← tb[node].nSons;
IF n # 0 THEN RETURN [n];
FOR i: CARDINAL ← 1, i+1 UNTIL tb[node].son[i] = EndMark
DO n ← n+1 ENDLOOP;
RETURN [n]};
ENDCASE => RETURN [1]};
ListHead: PROC [t: Tree.Link] RETURNS [Tree.Link] = {
IF t = Tree.Null THEN RETURN [Tree.Null];
WITH t SELECT FROM
subtree => {
node: Tree.Index = index;
RETURN [SELECT TRUE FROM
(tb[node].name # list) => t,
(tb[node].son[1] # EndMark) => tb[node].son[1],
ENDCASE => Tree.Null]};
ENDCASE => RETURN [t]};
ListTail: PROC [t: Tree.Link] RETURNS [Tree.Link] = {
IF t = Tree.Null THEN RETURN [Tree.Null];
WITH t SELECT FROM
subtree => {
node: Tree.Index = index;
RETURN [SELECT TRUE FROM
(tb[node].name # list) => t,
(tb[node].son[1] # EndMark) => tb[node].son[ListLength[t]],
ENDCASE => Tree.Null]};
ENDCASE => RETURN [t]};
-- procedures for list traversal
ScanList: PROC [root: Tree.Link, action: Tree.Scan] = {
IF root # Tree.Null
THEN
WITH root SELECT FROM
subtree => {
node: Tree.Index = index;
i, n: CARDINAL;
t: Tree.Link;
IF tb[node].name # list
THEN action[root]
ELSE
IF (n ← tb[node].nSons) # 0
THEN FOR i ← 1, i+1 WHILE i <= n
DO action[tb[node].son[i]] ENDLOOP
ELSE FOR i ← 1, i+1 UNTIL (t←tb[node].son[i]) = EndMark
DO action[t] ENDLOOP};
ENDCASE => action[root]};
ReverseScanList: PROC [root: Tree.Link, action: Tree.Scan] = {
IF root # Tree.Null
THEN
WITH root SELECT FROM
subtree => {
node: Tree.Index = index;
IF tb[node].name # list
THEN action[root]
ELSE
FOR i: CARDINAL DECREASING IN [1 .. ListLength[root]]
DO action[tb[node].son[i]] ENDLOOP};
ENDCASE => action[root]};
SearchList: PROC [root: Tree.Link, test: Tree.Test] = {
IF root # Tree.Null
THEN
WITH root SELECT FROM
subtree => {
node: Tree.Index = index;
i, n: CARDINAL;
t: Tree.Link;
IF tb[node].name # list
THEN [] ← test[root]
ELSE
IF (n ← tb[node].nSons) # 0
THEN FOR i ← 1, i+1 WHILE i <= n
DO IF test[tb[node].son[i]] THEN EXIT ENDLOOP
ELSE FOR i ← 1, i+1 UNTIL (t←tb[node].son[i]) = EndMark
DO IF test[t] THEN EXIT ENDLOOP};
ENDCASE => [] ← test[root]};
UpdateList: PROC [root: Tree.Link, map: Tree.Map] RETURNS [Tree.Link] = {
IF root = Tree.Null THEN RETURN [Tree.Null];
WITH root SELECT FROM
subtree => {
node: Tree.Index = index;
i, n: CARDINAL;
t: Tree.Link;
IF tb[node].name # list THEN RETURN [map[root]];
IF (n ← tb[node].nSons) # 0
THEN FOR i ← 1, i+1 WHILE i <= n
DO tb[node].son[i] ← map[tb[node].son[i]] ENDLOOP
ELSE FOR i ← 1, i+1 UNTIL (t←tb[node].son[i]) = EndMark
DO tb[node].son[i] ← map[t] ENDLOOP;
RETURN [root]};
ENDCASE => RETURN [map[root]]};
ReverseUpdateList: PROC [root: Tree.Link, map: Tree.Map] RETURNS [Tree.Link] = {
IF root = Tree.Null THEN RETURN [Tree.Null];
WITH root SELECT FROM
subtree => {
node: Tree.Index = index;
IF tb[node].name # list THEN RETURN [map[root]];
FOR i: CARDINAL DECREASING IN [1..ListLength[root]]
DO tb[node].son[i] ← map[tb[node].son[i]] ENDLOOP;
RETURN [root]};
ENDCASE => RETURN [map[root]]};
-- cross-table tree manipulation
CopyTree: PROC [root: Tree.Id, map: Tree.Map] RETURNS [v: Tree.Link] = {
WITH root.link SELECT FROM
subtree => {
sNode: Tree.Index = index;
IF sNode = Tree.NullIndex
THEN v ← Tree.Null
ELSE {
size: CARDINAL = NodeSize[root.baseP, sNode];
dNode: Tree.Index = table.GetChunk[size];
t: Tree.Link;
tb[dNode].name ← root.baseP↑[sNode].name;
tb[dNode].shared ← FALSE;
tb[dNode].nSons ← root.baseP↑[sNode].nSons;
tb[dNode].info ← root.baseP↑[sNode].info;
tb[dNode].attr1 ← root.baseP↑[sNode].attr1;
tb[dNode].attr2 ← root.baseP↑[sNode].attr2;
tb[dNode].attr3 ← root.baseP↑[sNode].attr3;
FOR i: CARDINAL IN [1..size-SIZE[Tree.Node]]
DO
tb[dNode].son[i] ← IF (t←root.baseP↑[sNode].son[i]) = EndMark
THEN EndMark
ELSE map[t];
ENDLOOP;
v ← [subtree[index: dNode]]}};
ENDCASE => v ← map[root.link];
RETURN};
IdentityMap: Tree.Map = {
RETURN [IF t.tag = subtree AND ~Shared[t]
THEN CopyTree[[baseP:@tb, link:t], IdentityMap]
ELSE t]};
NodeSize: PROC [baseP: Table.Finger, node: Tree.Index] RETURNS [size: CARDINAL] = {
IF node = Tree.NullIndex
THEN size ← 0
ELSE
IF baseP↑[node].name # list OR baseP↑[node].nSons # 0
THEN size ← SIZE[Tree.Node] + baseP↑[node].nSons
ELSE {
size ← SIZE[Tree.Node] + 1;
FOR i: CARDINAL ← 1, i+1 UNTIL baseP↑[node].son[i] = EndMark
DO size ← size + 1 ENDLOOP};
RETURN};
END.