{ TREE AND LIST OPERATIONS - VERSION CVF01A
                            RANDALL VENHOLA JUNE 30, 1987
                            DOCUMENTED IN MODULEAREA:TREEOPS.TEX }



[INHERIT('SCREENHANDLERS','ARGOPS','UTILITYOPS'), environment('treeandlistops')]
MODULE TREEANDLISTOPS;

{ list and tree operations - requires the external declarations for
 data structures

               ARGUMENT           - the item of the list
               COMPARISONS        - possible results of comparisons
               SETOFCOMPARISONS - set of above ordinal type

 the package is to be copied to the area of the source code and
 recompiled.  It expects to find the environment file for the above
 data structures and at least the following routines :

   function compareargs( leftarg, rightarg : argument ) : comparisons;
   function argtexindex( arg : argument ) : integer;
                                                           }

CONST

   nulllist = NIL;
   nulltree = NIL;

TYPE

arglist     = ^listnode;

argtree     = ^treenode;

         treenode   = record
                          parentnode : argtree;
                          contents    : arglist
                       end;

        listnode    = record
                          field       : argument;
                          next        : arglist;
                          subtree     : argtree
                       end;




[GLOBAL] FUNCTION nextinlist( list : arglist ) : arglist;
begin
  if list = nulllist then
     errorexit('nextinlist','empty list')
  else
    nextinlist := list^.next
end;




[GLOBAL] FUNCTION firstarg( list : arglist ) : argument;
begin
  if list = nulllist then
     errorexit('firstlistpointer','empty list')
  else
    firstarg := list^.field
end;


[GLOBAL] FUNCTION arglistlength( list : arglist ) : integer;
begin
   if list = nulllist then
       arglistlength := 0
   else
       arglistlength := arglistlength(nextinlist(list)) + 1
end;





[GLOBAL] FUNCTION leadingnodesubtree( list : arglist ) : argtree;
begin
  if list = nulllist then
     errorexit('listsubtree','empty list')
  else
     leadingnodesubtree := list^.subtree
end;





[GLOBAL] FUNCTION listofargsattree( tree : argtree ) : arglist;
begin
 if tree = nulltree then
    errorexit('listofargsattree','empty tree')
 else
    listofargsattree := tree^.contents
end;



[GLOBAL] FUNCTION treeisroot( tree : argtree ) : boolean;
begin
  if tree = nulltree then
    treeisroot := TRUE
  else
    treeisroot := tree^.parentnode = nulltree
end;




[GLOBAL] FUNCTION parenttree( tree : argtree ) : argtree;
begin
  if treeisroot( tree ) then
     errorexit('parenttree','tree is root')
  else
     parenttree := tree^.parentnode
end;




[GLOBAL] PROCEDURE insertarginsortedlist( var list : arglist;
        arg : argument; var pointertoarg : arglist );

type
  scanstates = (searching, atfrontoflist, positionfound, endoflist);
var
  state : scanstates;
  p, prevp, newp : arglist;
  comp : comparisons;

  procedure allocatenewp;
  begin
     new( newp );
     newp^.next := nulllist;
     newp^.subtree := nulltree;
     newp^.field := arg;
     pointertoarg := newp
  end;

begin
   if list = nulllist then
   begin
      allocatenewp;
      list := newp
   end
   else
   begin
      p := list;
      comp := compareargs(arg, firstarg(list));
      if (comp = lessthan) or (comp = equal) then
        state := atfrontoflist
      else
      begin
        state := searching;
        repeat
          prevp := p;
          p := nextinlist(p);
          if p = nulllist then
             state := endoflist
          else
          begin
            comp := compareargs(arg, firstarg(p));
            if (comp = lessthan) or (comp = equal) then
               state := positionfound
          end
        until state <> searching
      end;
      if comp = equal then
         warningmessage('insertarginsortedlist','already in list')
      else
        case state of
        atfrontoflist : begin
                              allocatenewp;
                              newp^.next := list;
                              list := newp
                           end;
        positionfound   : begin
                             allocatenewp;
                             newp^.next := p;
                             prevp^.next := newp
                           end;
        endoflist      : begin
                             allocatenewp;
                             prevp^.next := newp
                           end
      end {case}
   end {else}
end;




[GLOBAL] PROCEDURE appendargonlist( var list : arglist; arg : argument );
var
 p, prevp, newp : arglist;
begin
   if list = nulllist then
   begin
       new( newp );
       newp^.subtree := nulltree;
       newp^.field := arg;
       newp^.next := nulllist;
       list := newp
   end
   else
   begin
       p := list;
       repeat
          prevp := p;
          p := nextinlist(p)
       until p = nulllist;
       new( newp );
       newp^.subtree := nulltree;
       newp^.field := arg;
       newp^.next := nulllist;
       prevp^.next := newp
    end
end;




[GLOBAL] PROCEDURE preceedargonlist( var list : arglist; arg : argument );
var
 newl : arglist;
begin
  new(newl);
  newl^.subtree := nulltree;
  newl^.field := arg;
  newl^.next := list;
  list := newl
end;



[GLOBAL] FUNCTION listcopy( list: arglist ) : arglist;
var
  l : arglist;

  procedure prec( list : arglist );
  begin
    if list = nulllist then
      l := nulllist
    else
    begin
        prec( nextinlist(l) );
        preceedargonlist( l, firstarg(l))
    end
  end;

begin
  if list = nulllist then
    listcopy := nulllist
  else
  begin
    prec( list );
    listcopy := l
  end
end;



[GLOBAL] FUNCTION reverseoflist( list: arglist ) : arglist;
var
  l : arglist;

  procedure app( list : arglist );
  begin
    if list = nulllist then
      l := nulllist
    else
    begin
        app( nextinlist(l) );
        appendargonlist( l, firstarg(l))
    end
  end;

begin
  if list = nulllist then
    reverseoflist := nulllist
  else
  begin
    app( list );
    reverseoflist := l
  end
end;




[GLOBAL] FUNCTION leadingnodehassubtree( list : arglist ) : boolean;
begin
  if list = nulllist then
    leadingnodehassubtree := false
  else
    leadingnodehassubtree := list^.subtree <> nulltree
end;






[GLOBAL] PROCEDURE findarginsortedlist( list : arglist; arg : argument;
                                  var found : boolean;
                                  var pointertoarg : arglist );

type
 searchstates = (searching, positionfound, foundlessthanlocation, endoflist);
var
 p : arglist;
 state : searchstates;
 currentarg : argument;
 comp : comparisons;
begin
  found := false;
  if list <> nulllist then
  begin
     p := list;
     state:= searching;
     repeat
        currentarg := firstarg(p);
        comp := compareargs(arg, currentarg);
        case comp of
        notvalid     : errorexit('findarginsortedlist','invalid-comparison');
        lessthan     : state := foundlessthanlocation;
        equal         : begin
                          state := positionfound;
                          pointertoarg := p;
                          found := true
                        end;
        greaterthan  : nullstatement
        end; {case}
        if not found then
        begin
           p := nextinlist(p);
           if p = nulllist then
             state := endoflist
        end
     until state <> searching
  end
end;




[GLOBAL] PROCEDURE findarginlist( list : arglist; arg : argument;
                           var found : boolean;
                           var pointertoarg : arglist );
var
  p : arglist;
  compare : comparisons;
begin
   found := false;
   if list <> nulllist then
   begin
     p := list;
     repeat
        compare := compareargs( arg, firstarg(p) );
        if compare = equal then
        begin
           found := true;
           pointertoarg := p
        end
        else
           p := nextinlist(p)
      until (p = nulllist) or (found)
    end
end;





[GLOBAL] FUNCTION nargsattreenode( tree : argtree ) : integer;
begin
  if tree = nulltree then
    nargsattreenode := 0
  else
    nargsattreenode := arglistlength( tree^.contents )
end;









[GLOBAL] PROCEDURE insertlistintotree( list : arglist; var tree : argtree);

procedure subinsert( list : arglist; var tree : argtree;
                     parentpointer : arglist );
label
 routineexit;
var
  newtree : argtree;
  found : boolean;
  arg : argument;
  pointertoarg : arglist;
begin
  if list = nulllist then
     goto routineexit;
  arg := firstarg(list);
  if tree = nulltree then
  begin
     new( newtree );
     newtree^.contents := nulllist;
     appendargonlist(newtree^.contents, arg);
     if parentpointer = nulllist then
       newtree^.parentnode := nulltree
     else
       newtree^.parentnode := parentpointer^.subtree;
     subinsert(nextinlist(list), newtree^.contents^.subtree, newtree^.contents);
     if parentpointer = nulllist then
       tree := newtree
     else
       parentpointer^.subtree := newtree;
     goto routineexit
  end;
  findarginsortedlist( tree^.contents, arg, found, pointertoarg);
  if not found then
     insertarginsortedlist(tree^.contents, arg, pointertoarg);
  subinsert( nextinlist(list), pointertoarg^.subtree, pointertoarg);
  routineexit : nullstatement
end;

begin
  subinsert( list, tree, nulllist)
end;



[GLOBAL] PROCEDURE searchtreeforlist( tree : argtree; list : arglist;
         var found : boolean; var indexfound, depthfoundat : integer);

  procedure subsearch( tree : argtree; list : arglist );
  label
     routineexit;
  var
     findsuccessful : boolean;
     arg: argument;
     pointertoarg : arglist;
  begin
     if tree = nulltree then
        goto routineexit;
     if list = nulllist then
        goto routineexit;
     arg := firstarg(list);
     depthfoundat := depthfoundat + 1;
     findarginsortedlist(listofargsattree(tree), arg, findsuccessful, pointertoarg);
     if findsuccessful then
     begin
       found := true;
       indexfound := argtexindex(firstarg(pointertoarg));
       if leadingnodehassubtree(pointertoarg) then
         subsearch(leadingnodesubtree(pointertoarg), nextinlist(list))
     end;
     routineexit : nullstatement
  end;

begin {searchtree}
  found := false;
  indexfound := indexofunknowntexcommand;
  if list = nulllist then
    warningmessage('searchtree','given empty list')
  else
     subsearch(tree, list)
end;






[GLOBAL] PROCEDURE padwithnullarguments( var list : arglist; index : integer;
                                   requiredlength : integer );
var
 arg : argument;
 i, ntoappend : integer;
begin
  initarg(arg, [nulltype], blank, index, TRUE);
  ntoappend := requiredlength - arglistlength(list);
  for i := 1 to ntoappend do
     appendargonlist(list, arg)
end;




[GLOBAL] PROCEDURE listtoarray(var list : arglist; index : integer;
                                var arr  : argarray; requiredlength :integer );
var
 l : arglist;
 i : integer;
begin
 if requiredlength > maxargsinarray then
   errorexit('listtoarray','array size exceeded');
 padwithnullarguments( list, index, requiredlength);
 l := list;
 for i := 1 to requiredlength do
 begin
  arr[i] := firstarg(l);
  l := nextinlist(l)
 end
end;




[GLOBAL] PROCEDURE dlist( var f : text; l : arglist );
const
 linelength = 75;
var
 nchars : integer;

procedure dl( l : arglist );
var
  s : pckstr;
begin
  if l = nulllist then
    writeln(f)
  else
  begin
     s := argliteral(firstarg(l), true);
     if (length(s) + nchars + 1) > linelength then
     begin
        writeln(f);
        nchars := 0
     end;
     nchars := nchars + length(s) + 1;
     write(f, s, blank);
     dl( nextinlist(l))
  end
end;

begin
 nchars := 0;
 dl( l )
end;


[GLOBAL] PROCEDURE dtree( var f : text; tree : argtree);

 procedure dt( name : pckstr; tree : argtree );
 var
   l : arglist;
   s : pckstr;
 begin
   if tree <> nulltree then
   begin
     writeln(f);
     writeln(f,'**** "',name,'" NODE HAS ****');
     l := listofargsattree(tree);
     dlist(f,l);
     writeln(f,'**** ',name,' *************');
     while l <> nulllist do
     begin
       if leadingnodehassubtree(l) then
       begin
         s := argliteral(firstarg(l), true);
         dt(s, leadingnodesubtree(l))
       end;
       l := nextinlist(l)
     end
   end
 end;

begin
  dt('<ROOT>', tree)
end;



[HIDDEN] PROCEDURE texwritearg( var f : text; arg : argument);
EXTERN;



[GLOBAL] PROCEDURE writeargarray( var f : text; arr : argarray );
var
 i : integer;
begin
 for i := 1 to maxargsinarray do
   if argclass(arr[i]) <> [nulltype] then
      texwritearg(f, arr[i])
end;




[GLOBAL] PROCEDURE makenullarray( var arr : argarray );
var
 templist : arglist;
begin
 templist := nulllist;
 padwithnullarguments(templist, indexofunknowntexcommand, maxargsinarray);
 listtoarray( templist, indexofunknowntexcommand, arr, maxargsinarray)
end;



END.