-- file SMTreeImpl.mesa -- last modified by Satterthwaite, June 27, 1983 9:47 am -- last edit by Schmidt, 16-Mar-82 16:02:03 DIRECTORY SMTree: TYPE Tree USING [ AttrId, Ext, Handle, Id, Info, Link, Name, Node, NodeName, SonId, null, nullHandle], SMTreeOps: TYPE --TreeOps-- USING [Map, Scan, Test]; SMTreeImpl: CEDAR PROGRAM EXPORTS SMTreeOps ~ { OPEN Tree~~SMTree, TreeOps~~SMTreeOps; LinkStack: TYPE~RECORD[SEQUENCE size: NAT OF Tree.Link]; stackIncr: NAT~128; TreeManager: PUBLIC TYPE~RECORD[ z: ZONE←, stack: REF LinkStack←NIL, sI: NAT←0, initialized: BOOL←FALSE, visitParity: BOOL←FALSE, visitInProgress: BOOL←FALSE]; TM: TYPE~REF TreeManager; Create: PUBLIC PROC[zone: ZONE] RETURNS[TM]~{ tm: TM ~ zone.NEW[TreeManager ← [z~zone]]; RETURN [tm]}; Initialize: PUBLIC PROC[tm: TM] ~{ IF tm.initialized THEN Finalize[tm]; tm.stack ← AllocStack[tm, stackIncr]; tm.sI ← 0; tm.initialized ← TRUE}; Reset: PUBLIC PROC[tm: TM]~{ IF tm.initialized AND tm.stack.size > 2*stackIncr THEN tm.stack ← AllocStack[tm, stackIncr]}; Finalize: PUBLIC PROC[tm: TM]~{ tm.initialized ← FALSE; tm.stack ← NIL}; AllocStack: PROC[tm: TM, size: NAT, forceNew: BOOL←FALSE] RETURNS[st: REF LinkStack] ~ { st ← IF forceNew THEN NIL ELSE tm.stack; IF st = NIL OR st.size < size THEN st ← (tm.z).NEW[LinkStack[size]]}; ExpandStack: PROC[tm: TM]~{ newStack: REF LinkStack ~ AllocStack[tm, tm.stack.size+stackIncr, TRUE]; FOR i: NAT IN [0..tm.stack.size) DO newStack[i] ← tm.stack[i] ENDLOOP; tm.stack ← newStack}; Zone: PUBLIC PROC[tm: TM] RETURNS[ZONE]~{RETURN [tm.z]}; PushTree: PUBLIC PROC[tm: TM, v: Tree.Link]~{ IF tm.sI >= tm.stack.size THEN ExpandStack[tm]; tm.stack[tm.sI] ← v; tm.sI ← tm.sI+1}; PopTree: PUBLIC PROC[tm: TM] RETURNS[Tree.Link]~{ RETURN [tm.stack[tm.sI←tm.sI-1]]}; InsertTree: PUBLIC PROC[tm: TM, v: Tree.Link, n: NAT]~{ i: NAT ← tm.sI; IF tm.sI >= tm.stack.size THEN ExpandStack[tm]; tm.sI ← tm.sI+1; THROUGH [1 .. n) DO tm.stack[i] ← tm.stack[i-1]; i ← i-1 ENDLOOP; tm.stack[i] ← v}; ExtractTree: PUBLIC PROC[tm: TM, n: NAT] RETURNS[v: Tree.Link] ~ { i: NAT ← tm.sI - n; v ← tm.stack[i]; THROUGH [1 .. n) DO tm.stack[i] ← tm.stack[i+1]; i ← i+1 ENDLOOP; tm.sI ← tm.sI - 1; RETURN}; MakeNode: PUBLIC PROC[tm: TM, name: Tree.NodeName, count: INTEGER] RETURNS[Tree.Link] ~ { PushNode[tm, name, count]; RETURN [PopTree[tm]]}; PushNode: PUBLIC PROC[tm: TM, name: Tree.NodeName, count: INTEGER] = { nSons: NAT~count.ABS; node: Tree.Handle~(tm.z).NEW[Tree.Node[nSons]←[name~name, visited~tm.visitParity, son~]]; IF count >= 0 THEN FOR i: Tree.SonId DECREASING IN [1..nSons] DO node.son[i] ← tm.stack[tm.sI←tm.sI-1] ENDLOOP ELSE FOR i: Tree.SonId IN [1..nSons] DO node.son[i] ← tm.stack[tm.sI←tm.sI-1] ENDLOOP; IF tm.sI >= tm.stack.size THEN ExpandStack[tm]; tm.stack[tm.sI] ← node; tm.sI ← tm.sI+1}; SetInfo: PUBLIC PROC[tm: TM, info: Tree.Info]~{ WITH tm.stack[tm.sI-1] SELECT FROM node: Tree.Handle => node.info ← info; ENDCASE => ERROR}; SetAttr: PUBLIC PROC[tm: TM, attr: Tree.AttrId, value: BOOL]~{ FOR node: Tree.Handle ← NARROW[tm.stack[tm.sI-1]], NARROW[node.son[1]] DO IF node.name # $locator THEN { node.attrs[attr] ← value; EXIT}; ENDLOOP}; SetExt: PUBLIC PROC[tm: TM, ext: Tree.Ext]~{ FOR node: Tree.Handle ← NARROW[tm.stack[tm.sI-1]], NARROW[node.son[1]] DO IF node.name # $locator THEN { node.ext ← ext; EXIT}; ENDLOOP}; -- structure extraction GetName: PUBLIC PROC[t: Tree.Link] RETURNS[Tree.Name] ~ { RETURN [WITH t SELECT FROM name: Tree.Name => name, node: Tree.Handle => IF node.name # $locator THEN ERROR ELSE GetName[node.son[1]], ENDCASE => ERROR]}; GetNode: PUBLIC PROC[t: Tree.Link] RETURNS[Tree.Handle] ~ { RETURN [WITH t SELECT FROM node: Tree.Handle => IF node.name # $locator THEN node ELSE GetNode[node.son[1]], ENDCASE => ERROR]}; GetId: PUBLIC PROC[t: Tree.Link] RETURNS[Tree.Id] ~ { RETURN [WITH t SELECT FROM id: Tree.Id => id, node: Tree.Handle => IF node.name # $locator THEN ERROR ELSE GetId[node.son[1]], ENDCASE => ERROR]}; -- procedures for tree testing NSons: PUBLIC PROC[t: Tree.Link] RETURNS[NAT] ~ { RETURN [WITH t SELECT FROM node: Tree.Handle => IF node.name # $locator THEN node.sonLimit-1 ELSE NSons[node.son[1]], ENDCASE => 0]}; NthSon: PUBLIC PROC[t: Tree.Link, n: Tree.SonId] RETURNS[Tree.Link] ~ { RETURN [WITH t SELECT FROM node: Tree.Handle => IF node.name # $locator THEN node.son[n] ELSE NthSon[node.son[1], n], ENDCASE => ERROR]}; PutNthSon: PUBLIC PROC[t: Tree.Link, n: Tree.SonId, v: Tree.Link] ~ { WITH t SELECT FROM node: Tree.Handle => IF node.name # $locator THEN node.son[n] ← v ELSE PutNthSon[node.son[1], n, v]; ENDCASE => ERROR}; OpName: PUBLIC PROC[t: Tree.Link] RETURNS[Tree.NodeName] ~ { RETURN [WITH t SELECT FROM node: Tree.Handle => IF node.name # $locator THEN node.name ELSE OpName[node.son[1]], ENDCASE => $none]}; GetAttr: PUBLIC PROC[t: Tree.Link, attr: Tree.AttrId] RETURNS[BOOL] ~ { RETURN [WITH t SELECT FROM node: Tree.Handle => IF node.name # $locator THEN node.attrs[attr] ELSE GetAttr[node.son[1], attr], ENDCASE => ERROR]}; PutAttr: PUBLIC PROC[t: Tree.Link, attr: Tree.AttrId, value: BOOL] ~ { WITH t SELECT FROM node: Tree.Handle => IF node.name # $locator THEN node.attrs[attr] ← value ELSE PutAttr[node.son[1], attr, value]; ENDCASE => ERROR}; GetInfo: PUBLIC PROC[t: Tree.Link] RETURNS[Tree.Info] ~ { RETURN [WITH t SELECT FROM node: Tree.Handle => node.info, ENDCASE => ERROR]}; PutInfo: PUBLIC PROC[t: Tree.Link, value: Tree.Info] ~ { WITH t SELECT FROM node: Tree.Handle => node.info ← value; ENDCASE => ERROR}; GetExt: PUBLIC PROC[t: Tree.Link] RETURNS[Tree.Ext] ~ { RETURN [WITH t SELECT FROM node: Tree.Handle => IF node.name # $locator THEN node.ext ELSE GetExt[node.son[1]], ENDCASE => ERROR]}; PutExt: PUBLIC PROC[t: Tree.Link, ext: Tree.Ext] ~ { WITH t SELECT FROM node: Tree.Handle => IF node.name # $locator THEN node.ext ← ext ELSE PutExt[node.son[1], ext]; ENDCASE => ERROR}; -- procedures for tree traversal ScanSons: PUBLIC PROC[root: Tree.Link, action: TreeOps.Scan]~{ WITH root SELECT FROM node: Tree.Handle => FOR i: Tree.SonId IN [1 .. node.sonLimit) DO action[node.son[i]] ENDLOOP; ENDCASE}; SearchSons: PUBLIC PROC[root: Tree.Link, test: TreeOps.Test]~{ WITH root SELECT FROM node: Tree.Handle => FOR i: Tree.SonId IN [1 .. node.sonLimit) DO IF test[node.son[i]] THEN EXIT ENDLOOP; ENDCASE}; UpdateSons: PUBLIC PROC[tm: TM, root: Tree.Link, map: TreeOps.Map]~{ WITH root SELECT FROM node: Tree.Handle => FOR i: Tree.SonId IN [1 .. node.sonLimit) DO node.son[i] ← map[tm, node.son[i]] ENDLOOP; ENDCASE}; UpdateLeaves: PUBLIC PROC[tm: TM, root: Tree.Link, map: TreeOps.Map] RETURNS[v: Tree.Link] ~ { IF root = Tree.null THEN v ← Tree.null ELSE WITH root SELECT FROM node: Tree.Handle => { FOR i: Tree.SonId IN [1 .. node.sonLimit) DO node.son[i] ← map[tm, node.son[i]] ENDLOOP; v ← root}; ENDCASE => v ← map[tm, root]; RETURN}; -- procedures for synchronizing external tree walks StartVisit: PUBLIC PROC[tm: TM] RETURNS[mark: BOOL] ~ { IF tm.visitInProgress THEN ERROR; -- attempted recursion RETURN [~tm.visitParity]}; EndVisit: PUBLIC PROC[tm: TM] ~ { tm.visitInProgress ← FALSE; tm.visitParity ← ~tm.visitParity}; -- cross-table tree manipulation CopyTree: PUBLIC PROC[tm: TM, root: Tree.Link, map: TreeOps.Map] RETURNS[v: Tree.Link] ~ { WITH root SELECT FROM sNode: Tree.Handle => { IF sNode = Tree.nullHandle THEN v ← Tree.null ELSE { dNode: Tree.Handle ~ (tm.z).NEW[Tree.Node[NSons[sNode]] ← [ name~sNode.name, attrs~sNode.attrs, visited~tm.visitParity, info~sNode.info, son~]]; FOR i: Tree.SonId IN [1..sNode.sonLimit) DO dNode.son[i] ← map[tm, sNode.son[i]] ENDLOOP; v ← dNode}}; ENDCASE => v ← map[tm, root]; RETURN}; IdentityMap: PUBLIC TreeOps.Map~{ RETURN [IF ISTYPE[t, Tree.Handle] THEN CopyTree[tm, t, IdentityMap] ELSE t]}; }.