home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / aijournl / 1986_10 / vtprolog.pas < prev   
Pascal/Delphi Source File  |  1986-07-14  |  11KB  |  338 lines

  1. (*$V-,R+,B- *)
  2. PROGRAM very_tiny_prolog ;
  3.  
  4. (* Copyright 1986 - MicroExpert Systems
  5.                     Box 430 R.D. 2
  6.                     Nassau, NY 12123       *)
  7.  
  8. (* VTPROLOG implements the data base searching and pattern matching of
  9.    PROLOG. It is described in "PROLOG from the Bottom Up" in issues
  10.    1 and 2 of AI Expert.
  11.  
  12.    This program has been tested using Turbo ver 3.01A on an IBM PC. It has
  13.    been run under both DOS 2.1 and Concurrent 4.1 .
  14.  
  15.    We would be pleased to hear your comments, good or bad, or any applications
  16.    and modifications of the program. Contact us at:
  17.  
  18.      AI Expert
  19.      CL Publications Inc.
  20.      650 Fifth St.
  21.      Suite 311
  22.      San Francisco, CA 94107
  23.  
  24.    or on the AI Expert BBS. Our id is BillandBev Thompson. You can also
  25.    contact us on BIX, our id is bbt.
  26.  
  27.    Bill and Bev Thompson    *)
  28.  
  29.  CONST
  30.   debug = false ;
  31.   back_space = ^H ;
  32.   tab = ^I ;
  33.   eof_mark = ^Z ;
  34.   esc = #27 ;
  35.   quote_char = #39 ;
  36.   left_arrow = #75 ;
  37.   end_key = #79 ;
  38.   del_line = ^X ;
  39.   return = ^M ;
  40.   bell = ^G ;
  41.  
  42.  TYPE
  43.   counter = 0 .. maxint ;
  44.   string80 = string[80] ;
  45.   string132 = string[132] ;
  46.   string255 = string[255] ;
  47.   text_file = text ;
  48.   char_set = SET OF char ;
  49.   node_type = (cons_node,func,variable,constant,free_node) ;
  50.   node_ptr = ^node ;
  51.   node = RECORD
  52.           in_use : boolean ;
  53.           CASE tag : node_type OF
  54.            cons_node : (tail_ptr : node_ptr ;
  55.                         head_ptr : node_ptr) ;
  56.            func,
  57.            constant,
  58.            variable  : (string_data : string80) ;
  59.            free_node : (next_free : node_ptr ;
  60.                         block_cnt : counter) ;
  61.           END ;
  62.  
  63. (* node is the basic allocation unit for lists. The fields are used as
  64.    follows:
  65.  
  66.     in_use     - in_use = false tells the garbage collector that this node
  67.                  is available for re-use.
  68.     tag        - which kind of node this is.
  69.     cons_node  - cons_nodes consist of two pointers. one to the head (first item)
  70.                  the other to the rest of the list. They are the "glue" which
  71.                  holds the list together. The list (A B C) would be stored as
  72.                    -------         --------          --------
  73.                    | .| . |----->  |  .| . |------> |  .| . |---> NIL
  74.                    --|-----         --|------        --|-----
  75.                      |                |                |
  76.                      V                V                V
  77.                      A                B                C
  78.  
  79.                  The boxes are the cons nodes, the first part of the box
  80.                  holds the head pointer, then second contains the tail.
  81.     constant   - holds string values, we don't actually use the entire 80
  82.                  characters in most cases.
  83.     variable   - also conatins a string value, these nodes will be treated as
  84.                  PROLOG variables rather than constants.
  85.     free_node  - the garbage collector gathers all unused nodes and puts
  86.                  them on a free list. It also compacts the free space into
  87.                  contiguous blocks. next_free points to the next free block.
  88.                  block_cnt contains a count of the number of contiguous 8 byte free
  89.                  blocks which follow this one.    *)
  90.  
  91.  
  92.  VAR
  93.   line,saved_line : string132 ;
  94.   token : string80 ;
  95.   source_file : text_file ;
  96.   error_flag,in_comment : boolean ;
  97.   delim_set,text_chars : char_set ;
  98.   data_base,initial_heap,free,saved_list : node_ptr ;
  99.   total_free : real ;
  100.  
  101. (* The important globals are:
  102.    source_file  - text file containing PROLOG statements.
  103.    line         - line buffer for reading in the text file
  104.    saved_list   - list of all items that absolutely must be saved if garbage
  105.                   collection occurs. Usually has at least the data_base and
  106.                   the currents query attached to it.
  107.    initial_heap - the value of the heap pointer at the start of the program.
  108.                   used by the garbage collector
  109.    free         - the list of free nodes.
  110.    total_free   - total number of free blocks on the free list.
  111.    data_base    - a pointer to the start of the data base. It points to a
  112.                   node pointing to the first sentence in the data base. Nodes
  113.                   pointing to sentences are linked together to form the data
  114.                   base.
  115.    delim_set    - set of characters which delimit tokens. *)
  116.  
  117.  
  118. (* ----------------------------------------------------------------------
  119.         Utility Routines
  120.    ---------------------------------------------------------------------- *)
  121.  
  122.  PROCEDURE noise ;
  123.   (* Make a noise on the terminal - used for warnings. *)
  124.   BEGIN
  125.    write(bell) ;
  126.   END ; (* noise *)
  127.  
  128.  FUNCTION open(VAR f : text_file ; f_name : string80) : boolean ;
  129.   (* open a file - returns true if the file exists and was opened properly
  130.      f      - file pointer
  131.      f_name - external name of the file *)
  132.   BEGIN
  133.    assign(f,f_name) ;
  134.    (*$I- *)
  135.    reset(f) ;
  136.    (*$I+ *)
  137.    open := (ioresult = 0) ;
  138.   END ; (* open *)
  139.  
  140.  
  141.  FUNCTION is_console(VAR f : text_file) : boolean ;
  142.   (* return true if f is open on the system console
  143.      for details of fibs and fib_ptrs see the Turbo Pascal ver 3.0 reference
  144.      manual chapter 20. This should work under CP/M-86 or 80, but we haven't
  145.      tried it. *)
  146.   TYPE
  147.    fib = ARRAY [0 .. 75] OF byte ;
  148.   VAR
  149.    fib_ptr : ^fib ;
  150.    dev_type : byte ;
  151.   BEGIN
  152.    fib_ptr := addr(f) ;
  153.    dev_type := fib_ptr^[2] AND $07 ;
  154.    is_console := (dev_type = 1) OR (dev_type = 2) ;
  155.   END ; (* is_console *)
  156.  
  157.  
  158.  PROCEDURE strip_leading_blanks(VAR s : string80) ;
  159.   BEGIN
  160.    IF length(s) > 0
  161.     THEN
  162.      IF (s[1] = ' ') OR (s[1] = tab)
  163.       THEN
  164.        BEGIN
  165.         delete(s,1,1) ;
  166.         strip_leading_blanks(s) ;
  167.        END ;
  168.   END ; (* strip_leading_blanks *)
  169.  
  170.  
  171.  PROCEDURE strip_trailing_blanks(VAR s : string80) ;
  172.   BEGIN
  173.    IF length(s) > 0
  174.     THEN
  175.      IF (s[length(s)] = ' ') OR (s[length(s)] = tab)
  176.       THEN
  177.        BEGIN
  178.         delete(s,length(s),1) ;
  179.         strip_trailing_blanks(s) ;
  180.        END ;
  181.   END ; (* strip_trailing_blanks *)
  182.  
  183.  
  184.  
  185.  FUNCTION toupper(s : string80) : string80 ;
  186.   (* returns s converted to upper case *)
  187.   VAR
  188.    i : byte ;
  189.   BEGIN
  190.    IF length(s) > 0
  191.     THEN
  192.      FOR i := 1 TO length(s) DO
  193.       s[i] := upcase(s[i]) ;
  194.    toupper := s ;
  195.   END ; (* toupper *)
  196.  
  197.  
  198.  FUNCTION is_number(s : string80) : boolean ;
  199.   (* checks to see if s contains a legitimate numerical string.
  200.      It ignores leading and trailing blanks *)
  201.   VAR
  202.    num : real ;
  203.    code : integer ;
  204.   BEGIN
  205.    strip_trailing_blanks(s) ;
  206.    strip_leading_blanks(s) ;
  207.    IF s <> ''
  208.     THEN val(s,num,code)
  209.     ELSE code := -1 ;
  210.    is_number := (code = 0) ;
  211.   END ; (* is_number *)
  212.  
  213.  
  214.  FUNCTION head(list : node_ptr) : node_ptr ;
  215.   (* returns a pointer to the first item in the list.
  216.      If the list is empty, it returns NIL.  *)
  217.   BEGIN
  218.    IF list = NIL
  219.     THEN head := NIL
  220.     ELSE head := list^.head_ptr ;
  221.   END ; (* head *)
  222.  
  223.  
  224.  FUNCTION tail(list : node_ptr) : node_ptr ;
  225.   (* returns a pointer to a list starting at the second item in the list.
  226.      Note - tail( (a b c) ) points to the list (b c), but
  227.             tail( ((a b) c d) ) points to the list (c d) .  *)
  228.   BEGIN
  229.    IF list = NIL
  230.     THEN tail := NIL
  231.    ELSE
  232.     CASE list^.tag OF
  233.      cons_node : tail := list^.tail_ptr ;
  234.      free_node : tail := list^.next_free ;
  235.      ELSE        tail := NIL ;
  236.     END ;
  237.   END ; (* tail *)
  238.  
  239.  
  240.  FUNCTION allocation_size(x : counter) : counter ;
  241.   (* Turbo 3.0 allocates memory in 8 byte blocks, this routine calculates the
  242.      actual number of bytes returned for a request of x bytes.  *)
  243.   BEGIN
  244.    allocation_size := (((x - 1) DIV 8) + 1) * 8 ;
  245.   END ; (* allocation_size *)
  246.  
  247.  
  248.  FUNCTION node_size : counter ;
  249.   (* calculates the base size of a node. Add the rest of the node to this
  250.      to get the actual size of a node *)
  251.   BEGIN
  252.    node_size := 2 * sizeof(node_ptr) + sizeof(boolean) + sizeof(node_type) ;
  253.   END ; (* node_size *)
  254.  
  255.  
  256.  FUNCTION normalize(pt : node_ptr) : node_ptr ;
  257.   (* returns a normalized pointer. Pointers are 32 bit addresses. The first
  258.      16 bits contain the segment number and the second 16 bits contain the
  259.      offset within the segment. Normalized pointers have offsets in the range
  260.      $0 to $F (0 .. 15)    *)
  261.   VAR
  262.    pt_seg,pt_ofs : integer ;
  263.   BEGIN
  264.    pt_seg := seg(pt^) + (ofs(pt^) DIV 16) ;
  265.    pt_ofs := ofs(pt^) MOD 16 ;
  266.    normalize := ptr(pt_seg,pt_ofs) ;
  267.   END ; (* normalize *)
  268.  
  269.  
  270.  FUNCTION string_val(list : node_ptr) : string80 ;
  271.   (* returns the string pointed to by list. If list points to a number
  272.      node, it returns a string representing that number *)
  273.   VAR
  274.    s : string[15] ;
  275.   BEGIN
  276.    IF list = NIL
  277.     THEN string_val := ''
  278.    ELSE IF list^.tag IN [constant,variable,func]
  279.     THEN string_val := list^.string_data
  280.    ELSE string_val := '' ;
  281.   END ; (* string_val *)
  282.  
  283.  
  284.  FUNCTION tag_value(list : node_ptr) : node_type ;
  285.   (* returns the value of the tag for a node.     *)
  286.   BEGIN
  287.    IF list = NIL
  288.     THEN tag_value := free_node
  289.     ELSE tag_value := list^.tag ;
  290.   END ; (* tag_value *)
  291.  
  292.  
  293.  PROCEDURE print_list(list : node_ptr) ;
  294.   (* recursively traverses the list and prints its elements. This is
  295.      not a pretty printer, so the lists may look a bit messy.  *)
  296.   VAR
  297.    p : node_ptr ;
  298.   BEGIN
  299.    IF list <> NIL
  300.     THEN
  301.      CASE list^.tag OF
  302.       constant,
  303.       func,
  304.       variable  : write(string_val(list),' ') ;
  305.       cons_node : BEGIN
  306.                    write('(') ;
  307.                    p := list ;
  308.                    WHILE p <> NIL DO
  309.                     BEGIN
  310.                      print_list(head(p)) ;
  311.                      p := tail(p) ;
  312.                     END ;
  313.                    write(') ') ;
  314.                   END ;
  315.      END ;
  316.   END ; (* print_list *)
  317.  
  318.  
  319.  PROCEDURE get_memory(VAR p : node_ptr ; size : counter) ;
  320.   (* On exit p contains a pointer to a block of allocation_size(size) bytes.
  321.      If possible this routine tries to get memory from the free list before
  322.      requesting it from the heap *)
  323.   VAR
  324.    blks : counter ;
  325.    allocated : boolean ;
  326.  
  327.   PROCEDURE get_from_free(VAR list : node_ptr) ;
  328.    (* Try and get need memory from the free list. This routine uses a
  329.       first-fit algorithm to get the space. It takes the first free block it
  330.       finds with enough storage. If the free block has more storage than was
  331.       requested, the block is shrunk by the requested amount.  *)
  332.    BEGIN
  333.     IF list <> NIL
  334.      THEN
  335.       IF list^.block_cnt >= (blks - 1)
  336.        THEN
  337.         BEGIN
  338.          p :=