-- 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.