home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / INDUCE.ZIP / INDUCE.INC < prev    next >
Encoding:
Text File  |  1986-06-04  |  20.0 KB  |  621 lines

  1.  
  2. (* ----------------------------------------------------------------------
  3.         Utility Routines
  4.    ---------------------------------------------------------------------- *)
  5.  
  6.  FUNCTION open(VAR f : text_file ; f_name : string80) : boolean ;
  7.   (* open a file - returns true if the file exists and was opened properly
  8.      f      - file pointer
  9.      f_name - external name of the file *)
  10.   BEGIN
  11.    assign(f,f_name) ;
  12.    (*$I- *)
  13.    reset(f) ;
  14.    (*$I+ *)
  15.    open := (ioresult = 0) ;
  16.   END ; (* open *)
  17.  
  18.  
  19.  FUNCTION is_console(VAR f : text_file) : boolean ;
  20.   (* return true if f is open on the system console
  21.      for details of fibs and fib_ptrs see the Turbo Pascal ver 3.0 reference
  22.      manual chapter 20. This should work under CP/M-86 or 80, but we haven't
  23.      tried it. *)
  24.   TYPE
  25.    fib = ARRAY [0 .. 75] OF byte ;
  26.   VAR
  27.    fib_ptr : ^fib ;
  28.    dev_type : byte ;
  29.   BEGIN
  30.    fib_ptr := addr(f) ;
  31.    dev_type := fib_ptr^[2] AND $07 ;
  32.    is_console := (dev_type = 1) OR (dev_type = 2) ;
  33.   END ; (* is_console *)
  34.  
  35.  
  36.  PROCEDURE strip_leading_blanks(VAR s : string80) ;
  37.   BEGIN
  38.    IF length(s) > 0
  39.     THEN
  40.      IF (s[1] = ' ') OR (s[1] = tab)
  41.       THEN
  42.        BEGIN
  43.         delete(s,1,1) ;
  44.         strip_leading_blanks(s) ;
  45.        END ;
  46.   END ; (* strip_leading_blanks *)
  47.  
  48.  
  49.  PROCEDURE strip_trailing_blanks(VAR s : string80) ;
  50.   BEGIN
  51.    IF length(s) > 0
  52.     THEN
  53.      IF (s[length(s)] = ' ') OR (s[length(s)] = tab)
  54.       THEN
  55.        BEGIN
  56.         delete(s,length(s),1) ;
  57.         strip_trailing_blanks(s) ;
  58.        END ;
  59.   END ; (* strip_trailing_blanks *)
  60.  
  61.  
  62.  
  63.  FUNCTION toupper(s : string80) : string80 ;
  64.   (* returns s converted to upper case *)
  65.   VAR
  66.    i : byte ;
  67.   BEGIN
  68.    IF length(s) > 0
  69.     THEN
  70.      FOR i := 1 TO length(s) DO
  71.       s[i] := upcase(s[i]) ;
  72.    toupper := s ;
  73.   END ; (* toupper *)
  74.  
  75.  
  76.  FUNCTION toreal(s : string80) : real ;
  77.   (* converts s to a real number
  78.      This routine uses the Turbo intrinsic val to do the conversion.
  79.      If s does not contain a legal representation of a number, it returns
  80.      0.0  *)
  81.   VAR
  82.    num : real ;
  83.    code : integer ;
  84.   BEGIN
  85.    strip_trailing_blanks(s) ;
  86.    strip_leading_blanks(s) ;
  87.    val(s,num,code) ;
  88.    IF code = 0
  89.     THEN toreal := num
  90.     ELSE toreal := 0 ;
  91.   END ; (* toreal *)
  92.  
  93.  
  94.  FUNCTION is_number(s : string80) : boolean ;
  95.   (* checks to see if s contains a legitimate numerical string.
  96.      It ignores leading and trailing blanks *)
  97.   VAR
  98.    num : real ;
  99.    code : integer ;
  100.   BEGIN
  101.    strip_trailing_blanks(s) ;
  102.    strip_leading_blanks(s) ;
  103.    IF s <> ''
  104.     THEN val(s,num,code)
  105.     ELSE code := -1 ;
  106.    is_number := (code = 0) ;
  107.   END ; (* is_number *)
  108.  
  109.  
  110.  FUNCTION head(list : node_ptr) : node_ptr ;
  111.   (* returns a pointer to the first item in the list.
  112.      If the list is empty, it returns NIL.  *)
  113.   BEGIN
  114.    IF list = NIL
  115.     THEN head := NIL
  116.     ELSE head := list^.head_ptr ;
  117.   END ; (* head *)
  118.  
  119.  
  120.  FUNCTION tail(list : node_ptr) : node_ptr ;
  121.   (* returns a pointer to a list starting at the second item in the list.
  122.      Note - tail( (a b c) ) points to the list (b c), but
  123.             tail( ((a b) c d) ) points to the list (c d) .  *)
  124.   BEGIN
  125.    IF list = NIL
  126.     THEN tail := NIL
  127.    ELSE
  128.     CASE list^.tag OF
  129.      cons_node : tail := list^.tail_ptr ;
  130.      free_node : tail := list^.next_free ;
  131.      ELSE        tail := NIL ;
  132.     END ;
  133.   END ; (* tail *)
  134.  
  135.  
  136.  FUNCTION element(list : node_ptr ; elem_no : counter) : node_ptr ;
  137.   (* returns a pointer to the element number elem_no in the list.
  138.      element(list,1) points to list.
  139.      element(list,2) is the same as tail(list).    *)
  140.   VAR
  141.    i : counter ;
  142.   BEGIN
  143.    FOR i := 1 TO elem_no - 1 DO
  144.     list := tail(list) ;
  145.    element := list ;
  146.   END ; (* element *)
  147.  
  148.  
  149.  FUNCTION allocation_size(x : counter) : counter ;
  150.   (* Turbo 3.0 allocates memory in 8 byte blocks, this routine calculates the
  151.      actual number of bytes returned for a request of x bytes.  *)
  152.   BEGIN
  153.    allocation_size := (((x - 1) DIV 8) + 1) * 8 ;
  154.   END ; (* allocation_size *)
  155.  
  156.  
  157.  FUNCTION node_size : counter ;
  158.   (* calculates the base size of a node. Add the rest of the node to this
  159.      to get the actual size of a node *)
  160.   BEGIN
  161.    node_size := 2 * sizeof(node_ptr) + sizeof(boolean) + sizeof(node_type) ;
  162.   END ; (* node_size *)
  163.  
  164.  
  165.  FUNCTION normalize(pt : node_ptr) : node_ptr ;
  166.   (* returns a normalized pointer. Pointers are 32 bit addresses. The first
  167.      16 bits contain the segment number and the second 16 bits contain the
  168.      offset within the segment. Normalized pointers have offsets in the range
  169.      $0 to $F (0 .. 15)    *)
  170.   VAR
  171.    pt_seg,pt_ofs : integer ;
  172.   BEGIN
  173.    pt_seg := seg(pt^) + (ofs(pt^) DIV 16) ;
  174.    pt_ofs := ofs(pt^) MOD 16 ;
  175.    normalize := ptr(pt_seg,pt_ofs) ;
  176.   END ; (* normalize *)
  177.  
  178.  
  179.  FUNCTION string_val(list : node_ptr) : string80 ;
  180.   (* returns the string pointed to by list. If list points to a number
  181.      node, it returns a string representing that number *)
  182.   VAR
  183.    s : string[15] ;
  184.   BEGIN
  185.    IF list = NIL
  186.     THEN string_val := ''
  187.    ELSE IF list^.tag = symbol
  188.     THEN string_val := list^.string_data
  189.    ELSE IF list^.tag = number
  190.     THEN
  191.      BEGIN
  192.       str(list^.num_data : 14,s) ;
  193.       string_val := s ;
  194.      END
  195.    ELSE string_val := '' ;
  196.   END ; (* string_val *)
  197.  
  198.  
  199.  FUNCTION num_val(list : node_ptr) : real ;
  200.   (* returns the number pointed to by list. If list points to a string,
  201.      it returns the numerical value of the string.   *)
  202.   VAR
  203.    s : string80 ;
  204.    code : integer ;
  205.    r : real ;
  206.   BEGIN
  207.    IF list = NIL
  208.     THEN num_val := 0.0
  209.    ELSE IF list^.tag = number
  210.     THEN num_val := list^.num_data
  211.    ELSE IF list^.tag = symbol
  212.     THEN num_val := toreal(list^.string_data)
  213.    ELSE num_val := 0.0 ;
  214.   END ; (* num_val *)
  215.  
  216.  
  217.  FUNCTION attrib_value(p : node_ptr) : string80 ;
  218.   (* This routine is used by print_rule and print_tree to strip off
  219.      ':number' from an attribute name.   *)
  220.   BEGIN
  221.    IF pos(':NUMBER',toupper(string_val(p))) > 0
  222.     THEN attrib_value := copy(string_val(p),
  223.                  1,pos(':NUMBER',toupper(string_val(p))) - 1)
  224.     ELSE attrib_value := string_val(p) ;
  225.   END ; (* attrib_value *)
  226.  
  227.  
  228.  FUNCTION tag_value(list : node_ptr) : node_type ;
  229.   (* returns the value of the tag for a node.     *)
  230.   BEGIN
  231.    IF list = NIL
  232.     THEN tag_value := free_node
  233.     ELSE tag_value := list^.tag ;
  234.   END ; (* tag_value *)
  235.  
  236.  
  237.  FUNCTION match_lists(list1,list2 : node_ptr) : boolean ;
  238.   (* returns true if list1 and list2 are identical.
  239.      Two lists are identical if they are both NIL or if their heads match
  240.      and match_lists returns true for thier tails. *)
  241.   BEGIN
  242.    IF (list1 = NIL) AND (list2 = NIL)
  243.     THEN match_lists := true
  244.    ELSE IF (list1 = NIL) OR (list2 = NIL)
  245.     THEN match_lists := false
  246.    ELSE IF tag_value(head(list1)) <> tag_value(head(list2))
  247.     THEN match_lists := false
  248.    ELSE
  249.     CASE tag_value(head(list1)) OF
  250.      symbol    : IF string_val(head(list1)) = string_val(head(list2))
  251.                   THEN match_lists := match_lists(tail(list1),tail(list2))
  252.                   ELSE match_lists := false ;
  253.      number    : IF num_val(head(list1)) = num_val(head(list2))
  254.                   THEN match_lists := match_lists(tail(list1),tail(list2))
  255.                   ELSE match_lists := false ;
  256.      cons_node : IF match_lists(head(list1),head(list2))
  257.                   THEN match_lists := match_lists(tail(list1),tail(list2))
  258.                   ELSE match_lists := false ;
  259.     END ;
  260.   END ; (* match_lists *)
  261.  
  262.  
  263.  FUNCTION on_list(s : string80 ; list : node_ptr) : boolean ;
  264.   (* checks to see if s is on the list, list. s is on the list if it
  265.      matches the head of the list or if on_list(tail(list)) returns true.  *)
  266.   BEGIN
  267.    IF list = NIL
  268.     THEN on_list := false
  269.    ELSE IF s = string_val(head(list))
  270.     THEN on_list := true
  271.    ELSE on_list := on_list(s,tail(list)) ;
  272.   END ; (* on_list *)
  273.  
  274.  
  275.  PROCEDURE print_list(list : node_ptr) ;
  276.   (* recursively traverses the list and prints its elements. This is
  277.      not a pretty printer, so the lists may look a bit messy.  *)
  278.   VAR
  279.    p : node_ptr ;
  280.   BEGIN
  281.    IF list <> NIL
  282.     THEN
  283.      CASE list^.tag OF
  284.       symbol    : write(string_val(list),' ') ;
  285.       number    : write(num_val(list) : 6,' ') ;
  286.       cons_node : BEGIN
  287.                    write('(') ;
  288.                    p := list ;
  289.                    WHILE p <> NIL DO
  290.                     BEGIN
  291.                      print_list(head(p)) ;
  292.                      p := tail(p) ;
  293.                     END ;
  294.                    write(') ') ;
  295.                   END ;
  296.      END ;
  297.   END ; (* print_list *)
  298.  
  299.  
  300.  PROCEDURE get_memory(VAR p : node_ptr ; size : counter) ;
  301.   (* On exit p contains a pointer to a block of allocation_size(size) bytes.
  302.      If possible this routine tries to get memory from the free list before
  303.      requesting it from the heap *)
  304.   VAR
  305.    blks : counter ;
  306.    allocated : boolean ;
  307.  
  308.   PROCEDURE get_from_free(VAR list : node_ptr) ;
  309.    (* Try and get need memory from the free list. This routine uses a
  310.       first-fit algorithm to get the space. It takes the first free block it
  311.       finds with enough storage. If the free block has more storage than was
  312.       requested, the block is shrunk by the requested amount.  *)
  313.    BEGIN
  314.     IF list <> NIL
  315.      THEN
  316.       IF list^.block_cnt >= (blks - 1)
  317.        THEN
  318.         BEGIN
  319.          p := normalize(ptr(seg(list^),ofs(list^) +
  320.                                        (list^.block_cnt - blks + 1) * 8)) ;
  321.          IF list^.block_cnt = blks - 1
  322.           THEN list := list^.next_free
  323.           ELSE list^.block_cnt := list^.block_cnt - blks ;
  324.          allocated := true ;
  325.          total_free := total_free - (blks * 8.0) ;
  326.         END
  327.        ELSE get_from_free(list^.next_free) ;
  328.    END ; (* get_from_free *)
  329.  
  330.   BEGIN
  331.    blks := ((size - 1) DIV 8) + 1 ;
  332.    allocated := false ;
  333.    get_from_free(free) ;
  334.    IF NOT allocated
  335.     THEN getmem(p,blks * 8) ;
  336.   END ; (* get_memory *)
  337.  
  338.  
  339.  FUNCTION alloc_str(s : string80) : node_ptr ;
  340.   (* Allocate storage for a string and return a pointer to the new node.
  341.      This routine only allocates enough storage for the actual number of
  342.      characters in the string plus one for the length. Because of this,
  343.      concatenating anything to the end of a string stored in a symbol node
  344.      will lead to disaster. Copy the string to a new string do the
  345.      concatenation and then allocate a new node.  *)
  346.   VAR
  347.    pt : node_ptr ;
  348.   BEGIN
  349.    get_memory(pt,allocation_size(sizeof(node_type) + sizeof(boolean) +
  350.                                  length(s) + 1)) ;
  351.    pt^.tag := symbol   ;
  352.    pt^.string_data := s ;
  353.    alloc_str := pt ;
  354.   END ; (* alloc_str *)
  355.  
  356.  
  357.  FUNCTION alloc_num(r : real) : node_ptr ;
  358.   (* Allocate storage for a real number and return a pointer to the new node. *)
  359.   VAR
  360.    pt : node_ptr ;
  361.   BEGIN
  362.    get_memory(pt,allocation_size(sizeof(node_type) + sizeof(boolean) +
  363.                                  sizeof(real))) ;
  364.    pt^.tag := number ;
  365.    pt^.num_data := r ;
  366.    alloc_num := pt ;
  367.   END ; (* alloc_num *)
  368.  
  369.  
  370.  FUNCTION cons(new_node,list : node_ptr) : node_ptr ;
  371.   (* Construct a list. This routine allocates storage for a new cons node.
  372.      new_node points to the new head of the list. The tail pointer of the
  373.      new node points to list. This routine adds the new cons node to the
  374.      beginning of the list and returns a pointer to it. The list described
  375.      in the comments at the beginning of the program could be constructed
  376.      as cons(alloc_str('A'),cons(alloc_str('B'),cons(alloc_str('C'),NIL))). *)
  377.   VAR
  378.    p : node_ptr ;
  379.   BEGIN
  380.    get_memory(p,allocation_size(node_size)) ;
  381.    p^.tag := cons_node ;
  382.    p^.head_ptr := new_node ;
  383.    p^.tail_ptr := list ;
  384.    cons := p ;
  385.   END ; (* cons *)
  386.  
  387.  
  388.  FUNCTION append_list(list1,list2 : node_ptr) : node_ptr ;
  389.   (* Append list2 to list1. This routine returns a pointer to the
  390.      combined list. Appending is done by consing each item on the first
  391.      list to the second list. This routine is one of the major sources of
  392.      garbage so if garbage collection becomes a problem, you may want to
  393.      rewrite it. *)
  394.   BEGIN
  395.    IF list1 = NIL
  396.     THEN append_list := list2
  397.     ELSE append_list := cons(head(list1),append_list(tail(list1),list2)) ;
  398.   END ; (* append_list *)
  399.  
  400.  
  401.  FUNCTION list_length(list : node_ptr) : counter ;
  402.   (* returns the length of a list.
  403.      Note - both (A B C) and ( (A B) C D) have length 3.   *)
  404.   BEGIN
  405.    IF list = NIL
  406.     THEN list_length := 0
  407.     ELSE list_length := 1 + list_length(list^.tail_ptr) ;
  408.   END ; (* list_length *)
  409.  
  410.  
  411.  FUNCTION copy_list(list : node_ptr) : node_ptr ;
  412.   (* Returns a pointer to a copy of list. This routine allocates new nodes
  413.      for each item in the original list *)
  414.   BEGIN
  415.    IF list = NIL
  416.     THEN copy_list := NIL
  417.     ELSE
  418.      CASE tag_value(list) OF
  419.       cons_node : copy_list := cons(copy_list(head(list)),copy_list(tail(list))) ;
  420.       number    : copy_list := alloc_num(num_val(list)) ;
  421.       symbol    : copy_list := alloc_str(string_val(list)) ;
  422.      END ;
  423.   END ; (* copy_list *)
  424.  
  425.  
  426.  PROCEDURE collect_garbage ;
  427.   (* This routine is specific to Turbo Pascal Ver 3.01
  428.      It depends upon the fact that Turbo allocates memory in 8 byte blocks
  429.      on the PC. If you recompile this program on another system be very
  430.      careful with this routine.
  431.      Garbage collection proceeds in three phases:
  432.       unmark  - free all memory between the initial_heap^ and the current
  433.                 top of the heap.
  434.       mark    - mark everything on the saved_list as being in ues.
  435.       release - gather all unmarked blocks and put them on the free list.
  436.      The collector displays a '*' on the screen to let you know it is
  437.       operating.  *)
  438.  
  439.   FUNCTION lower(p1,p2 : node_ptr) : boolean ;
  440.    (* returns true if p1 points to a lower memory address than p2 *)
  441.    BEGIN
  442.     p1 := normalize(p1) ;
  443.     p2 := normalize(p2) ;
  444.     lower := (seg(p1^) < seg(p2^)) OR
  445.               ((seg(p1^) = seg(p2^)) AND (ofs(p1^) < ofs(p2^))) ;
  446.    END ; (* lower *)
  447.  
  448.   PROCEDURE mark(list : node_ptr) ;
  449.    (* Mark the blocks on list as being in use. Since a node may be on several
  450.       lists at one time, if it is already marked we don't continue processing
  451.       the tail of the list. *)
  452.    BEGIN
  453.     IF list <> NIL
  454.      THEN
  455.       BEGIN
  456.        IF NOT list^.in_use
  457.         THEN
  458.          BEGIN
  459.           list^.in_use := true ;
  460.           IF list^.tag = cons_node
  461.            THEN
  462.             BEGIN
  463.              mark(head(list)) ;
  464.              mark(tail(list)) ;
  465.             END ;
  466.          END ;
  467.       END ;
  468.    END ; (* mark *)
  469.  
  470.   PROCEDURE unmark_mem ;
  471.    (* Go through memory from initial_heap^ to HeapPtr^ and mark each node
  472.       as not in use. The tricky part here is updating the pointer p to point
  473.       to the next cell. *)
  474.    VAR
  475.     p : node_ptr ;
  476.     string_base,node_allocation : counter ;
  477.    BEGIN
  478.     string_base := sizeof(node_type) + sizeof(boolean) ;
  479.     p := normalize(initial_heap) ;
  480.     node_allocation := allocation_size(node_size) ;
  481.     WHILE lower(p,HeapPtr) DO
  482.      BEGIN
  483.       p^.in_use := false ;
  484.       CASE p^.tag OF
  485.        cons_node : p := normalize(ptr(seg(p^),ofs(p^) + node_allocation)) ;
  486.        free_node : p := normalize(ptr(seg(p^),ofs(p^) + (p^.block_cnt + 1) * 8)) ;
  487.        number    : p := normalize(ptr(seg(p^),
  488.                                   ofs(p^) +
  489.                                   allocation_size(string_base + sizeof(real)))) ;
  490.        symbol    : p := normalize(ptr(seg(p^),
  491.                                   ofs(p^) +
  492.                                   allocation_size(string_base +
  493.                                                   length(p^.string_data) + 1))) ;
  494.       END ;
  495.      END ;
  496.    END ; (* unmark_mem *)
  497.  
  498.   PROCEDURE release_mem ;
  499.    (* This procedure does the actual collection and compaction of nodes.
  500.       This is the slow phase of garbage collection because of all the pointer
  501.       manipulation.  *)
  502.    VAR
  503.     heap_top : node_ptr ;
  504.     string_base,node_allocation,string_allocation,block_allocation : counter ;
  505.  
  506.    PROCEDURE free_memory(pt : node_ptr ; size : counter) ;
  507.     (* return size bytes pointed to by pt to the free list. If pt points to
  508.        a block next to the head of the free list combine it with the top
  509.        free node. total_free keeps track of the total number of free bytes. *)
  510.     VAR
  511.      blks : counter ;
  512.     BEGIN
  513.      blks := ((size - 1) DIV 8) + 1 ;
  514.      pt^.tag := free_node ;
  515.      IF normalize(ptr(seg(pt^),ofs(pt^) + 8 * blks)) = free
  516.       THEN
  517.        BEGIN
  518.         pt^.next_free := free^.next_free ;
  519.         pt^.block_cnt := free^.block_cnt + blks ;
  520.         free := pt ;
  521.        END
  522.      ELSE IF normalize(ptr(seg(free^),ofs(free^) + 8 * (free^.block_cnt + 1))) =
  523.              normalize(pt)
  524.       THEN free^.block_cnt := free^.block_cnt + blks
  525.      ELSE
  526.       BEGIN
  527.        pt^.next_free := free ;
  528.        pt^.block_cnt := blks - 1 ;
  529.        free := pt ;
  530.       END ;
  531.      total_free := total_free + (blks * 8.0) ;
  532.     END ; (* free_memory *)
  533.  
  534.    PROCEDURE do_release ;
  535.     (* This routine sweeps through memory and checks for nodes with
  536.        in_use = false. *)
  537.     VAR
  538.      p : node_ptr ;
  539.     BEGIN
  540.      p := normalize(initial_heap) ;
  541.      WHILE lower(p,heap_top) DO
  542.       CASE p^.tag OF
  543.        cons_node : BEGIN
  544.                     IF NOT p^.in_use
  545.                      THEN free_memory(p,node_size) ;
  546.                     p := normalize(ptr(seg(p^),ofs(p^) + node_allocation)) ;
  547.                    END ;
  548.        free_node : BEGIN
  549.                     block_allocation := (p^.block_cnt + 1) * 8 ;
  550.                     free_memory(p,block_allocation) ;
  551.                     p := normalize(ptr(seg(p^),ofs(p^) + block_allocation)) ;
  552.                    END ;
  553.        number    : BEGIN
  554.                     block_allocation := allocation_size(string_base +
  555.                                                         sizeof(real)) ;
  556.                     IF NOT p^.in_use
  557.                      THEN free_memory(p,block_allocation) ;
  558.                     p := normalize(ptr(seg(p^),ofs(p^) + block_allocation)) ;
  559.                    END ;
  560.        symbol    : BEGIN
  561.                     string_allocation := allocation_size(string_base +
  562.                                                 length(p^.string_data) + 1) ;
  563.                     IF NOT p^.in_use
  564.                      THEN free_memory(p,string_base + length(p^.string_data)
  565.                                       + 1) ;
  566.                     p := normalize(ptr(seg(p^),ofs(p^) + string_allocation)) ;
  567.                    END ;
  568.       END ;
  569.     END ; (* do_release *)
  570.  
  571.    BEGIN
  572.     free := NIL ;
  573.     total_free := 0.0 ;
  574.     heap_top := HeapPtr ;
  575.     string_base := sizeof(node_type) + sizeof(boolean) ;
  576.     node_allocation := allocation_size(node_size) ;
  577.     do_release ;
  578.    END ; (* release_mem *)
  579.  
  580.   BEGIN
  581.    write('*') ;
  582.    unmark_mem ;
  583.    mark(saved_list) ;
  584.    release_mem ;
  585.    write(back_space) ;
  586.    clreol ;
  587.   END ; (* collect_garbage *)
  588.  
  589.  
  590.  PROCEDURE test_memory ;
  591.   (* This routine activates the garbage collector, if the the total available
  592.      memory (free_list + heap) is less than a specified amount. Lowering the
  593.      minimum causes garbage collection to be called less often, but if you
  594.      make it too small you may not have enough room left for recursion or any
  595.      temporary lists you need. Using 10000 is probably being overly
  596.      cautious.   *)
  597.   BEGIN
  598.    IF (memavail * 16.0) + total_free < 10000
  599.     THEN collect_garbage ;
  600.   END ; (* test_memory *)
  601.  
  602.  
  603.  PROCEDURE wait ;
  604.   (* Just like it says. It waits for the user to press a key before
  605.      continuing. *)
  606.   VAR
  607.    ch : char ;
  608.   BEGIN
  609.    writeln ;
  610.    writeln ;
  611.    write('Press any key to continue. ') ;
  612.    read(kbd,ch) ;
  613.    write(return) ;
  614.    clreol ;
  615.   END ; (* wait *)
  616.  
  617.  
  618. (* ------------------------------------------------------------------------
  619.         End of utility routines
  620.    ------------------------------------------------------------------------ *)
  621.