-- file TreePack.Mesa -- last modified by Satterthwaite, May 21, 1982 1:34 pm DIRECTORY Alloc: TYPE USING [ Handle, Notifier, AddNotify, DropNotify, FreeChunk, GetChunk], Literals: TYPE USING [LTIndex, STIndex], Symbols: TYPE USING [HTIndex, ISEIndex], Tree: TYPE USING [ AttrId, Base, Finger, Id, Index, Link, Map, Node, NodeName, Scan, Test, MaxNSons, Null, NullIndex, treeType], TreeOps: TYPE USING []; TreePack: PROGRAM IMPORTS Alloc EXPORTS TreeOps = PUBLIC { EndIndex: Tree.Index = LAST[Tree.Index]; EndMark: Tree.Link = [subtree[index: EndIndex]]; initialized: PRIVATE BOOLEAN ← FALSE; table: PRIVATE Alloc.Handle; zone: PRIVATE UNCOUNTED ZONE ← NIL; LinkSeq: PRIVATE TYPE = RECORD [SEQUENCE length: CARDINAL OF Tree.Link]; LinkStack: PRIVATE TYPE = LONG POINTER TO LinkSeq; stack: PRIVATE LinkStack; sI: PRIVATE CARDINAL; tb: PRIVATE Tree.Base; -- tree base UpdateBase: PRIVATE Alloc.Notifier = {tb ← base[Tree.treeType]}; Initialize: PROC [ownTable: Alloc.Handle, scratchZone: UNCOUNTED ZONE] = { IF initialized THEN Finalize[]; zone ← scratchZone; stack ← zone.NEW[LinkSeq[250]]; sI ← 0; table ← ownTable; table.AddNotify[UpdateBase]; IF MakeNode[none,0] # Tree.Null THEN ERROR; -- reserve null initialized ← TRUE}; Reset: PROC = { IF initialized AND stack.length > 250 THEN { zone.FREE[@stack]; stack ← zone.NEW[LinkSeq[250]]}}; Finalize: PROC = { table.DropNotify[UpdateBase]; table ← NIL; zone.FREE[@stack]; zone ← NIL; initialized ← FALSE}; ExpandStack: PRIVATE PROC = { newStack: LinkStack = zone.NEW[LinkSeq[stack.length + 256]]; FOR i: CARDINAL IN [0 .. stack.length) DO newStack[i] ← stack[i] ENDLOOP; zone.FREE[@stack]; stack ← newStack}; PushTree: PROC [v: Tree.Link] = { IF sI >= stack.length 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 >= stack.length 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, Tree.treeType]; 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 >= stack.length 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, Tree.treeType] ELSE { node ← table.GetChunk[SIZE[Tree.Node]+(nSons+1), Tree.treeType]; 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 >= stack.length THEN ExpandStack[]; stack[sI] ← [subtree[index: node]]; sI ← sI+1}}; PushProperList: PROC [size: INTEGER] = { IF size IN [-1..1] THEN { node: Tree.Index = table.GetChunk[SIZE[Tree.Node] + 1, Tree.treeType]; 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]]]} ELSE PushList[size]}; PushHash: PROC [hti: Symbols.HTIndex] = {PushTree[[hash[index: hti]]]}; PushSe: PROC [sei: Symbols.ISEIndex] = {PushTree[[symbol[index: sei]]]}; PushLit: PROC [lti: Literals.LTIndex] = {PushTree[[literal[info: [word[lti]]]]]}; PushStringLit: PROC [sti: Literals.STIndex] = {PushTree[[literal[info: [string[sti]]]]]}; 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: Tree.AttrId, 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, Tree.treeType]}}; 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 [Symbols.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]}; GetSe: PROC [t: Tree.Link] RETURNS [Symbols.ISEIndex] = { RETURN [WITH t SELECT FROM symbol => 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] = INLINE { RETURN [SELECT node FROM Tree.NullIndex, EndIndex => 0, ENDCASE => IF tb[node].name = list AND tb[node].nSons = 0 THEN ListLength[[subtree[index: node]]] ELSE tb[node].nSons]}; -- procedures for tree traversal ScanSons: PROC [root: Tree.Link, action: Tree.Scan] = { IF root # Tree.Null THEN WITH root SELECT FROM subtree => { node: Tree.Index = index; FOR i: CARDINAL IN [1 .. SonCount[node]] DO action[tb[node].son[i]] ENDLOOP}; ENDCASE; RETURN}; UpdateLeaves: 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; FOR i: CARDINAL IN [1 .. SonCount[node]] DO tb[node].son[i] ← map[tb[node].son[i]]; 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, Tree.treeType]; 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: Tree.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}; }.