home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume3 / trc / part4 < prev    next >
Encoding:
Text File  |  1986-11-30  |  42.6 KB  |  1,532 lines

  1. Newsgroups: mod.sources
  2. Subject: TRC - expert system building tool (part 4 of 8)
  3. Approved: jpn@panda.UUCP
  4.  
  5. Mod.sources:  Volume 3, Issue 112
  6. Submitted by: ihnp4!dicomed!ndsuvax!nckary (Daniel D. Kary)
  7.  
  8. This is NOT a shell archive.  Simply delete everything up to and including
  9. the cut mark and save the result as reference.2.doc.
  10.  
  11. Dan Kary
  12. ihnp4!dicomed!ndsuvax!nckary
  13.  
  14. -------------- cut here ---------------
  15.  
  16.  
  17.                            - 23 -
  18.  
  19.  
  20.                      PART TWO - OUTPUT
  21.  
  22.  
  23. _8.  _O_V_E_R_V_I_E_W
  24.  
  25.      The output of TRC consists of several procedures  writ-
  26. ten  on  several  different  files.   The  files contain the
  27. definitions and declarations of the data objects to be mani-
  28. pulated by the TRC generated inference engine, procedures to
  29. manipulate those data objects and a procedure which embodies
  30. the rules.
  31.  
  32.      The output of TRC is written on several  files  in  the
  33. current  directory.   The  file  names generated are; add.c,
  34. dump.c,   free.c,   loop.c,   misc.c,   search.c   relink.c,
  35. backtrack.c,  profile.c,  zero.c and save.c.  In addition to
  36. these files, the user must provide at least a main procedure
  37. which will invoke the inference engine, e.g.:
  38.  
  39.      main()
  40.      {
  41.           /* 'loop' is the name of the
  42.               procedure that embodies
  43.               the rules and controls
  44.               testing the rules */
  45.           loop();
  46.      }
  47.  
  48.  
  49.      A sample Makefile is given here,  the  file  main.c  is
  50. user supplied code.
  51.  
  52.      # Makefile for expert systems generated by TRC
  53.  
  54.      PROG =    loop
  55.      OBJS =    add.o backtrack.o dump.o free.o loop.o \
  56.                misc.o profile.o relink.o save.o \
  57.                search.o zero.o main.o
  58.      INCS =    loop.h
  59.      CC   =    cc
  60.  
  61.      all:      $(PROG)
  62.                $(CC) -c -O $<
  63.  
  64.      $(OBJS):  $(INCS)
  65.  
  66.      $(PROG):  $(OBJS)
  67.                $(CC) -o $@ $(OBJS) $(LIBS)
  68.  
  69.  
  70. _9.  _C_O_M_M_O_N _P_R_O_C_E_D_U_R_E_S
  71.  
  72.      There are several utility procedures that are generated
  73. for  each  input  file which are not dependent on the input.
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80.  
  81.  
  82.  
  83.                            - 24 -
  84.  
  85.  
  86. These procedures, written on the file 'misc.c' perform rela-
  87. tional testing.
  88.  
  89.      test_int (a,b)
  90.      int  a, b;
  91.      {
  92.           if(a < b) return(4);
  93.           if(a == b) return(2);
  94.           return(1);
  95.      }
  96.  
  97.      test_double (a,b)
  98.      double  a, b;
  99.      {
  100.           if(a < b) return(4);
  101.           if(a == b) return(2);
  102.           return(1);
  103.      }
  104.  
  105.      test_string(a,b)
  106.      char *a, *b;
  107.      {
  108.           int i;
  109.  
  110.           i = strcmp(a, b);
  111.           if(i < 0) return(4);
  112.           if(i == 0) return(2);
  113.           return(1);
  114.      }
  115.  
  116.  
  117.      The relational operators are bit encoded in an integer;
  118. 'less-than'  occupies  bit  two, 'equality' occupies bit one
  119. and 'greater-than' occupies bit zero.  Each of these  'test'
  120. procedures  returns  an integer which indicates the relation
  121. between the operands.  Examples of calls to these procedures
  122. are  included  in  section  X.X.X.  There are eight possible
  123. values for a bit encoded relational operator; the  generated
  124. code:
  125.  
  126.      < = >
  127.      0 0 0     /* never match */
  128.      0 0 1   /* greater-than */
  129.      0 1 0   /* equal */
  130.      0 1 1   /* greater-than-or-equal */
  131.      1 0 0   /* less-than */
  132.      1 0 1   /* not-equal */
  133.      1 1 0   /* less-than-or-equal */
  134.      1 1 1   /* don't care */
  135.  
  136.  
  137.      In addition to the testing procedures, a procedure  for
  138. dynamically   allocating  memory  is  written  on  the  file
  139. 'misc.c'.  This procedure  checks  for  the  out  of  memory
  140.  
  141.  
  142.  
  143.  
  144.  
  145.  
  146.  
  147.  
  148.  
  149.                            - 25 -
  150.  
  151.  
  152. condition.  Using this procedure to allocate memory obviates
  153. the need to check for the out of memory condition  elsewhere
  154. in the code.
  155.  
  156.      char *myalloc(n)
  157.      int n;
  158.      {
  159.          char *r;
  160.  
  161.          r = (char *) malloc(n);
  162.          if(r == NULL){
  163.           fprintf(stderr,"OUT OF MEMORY");
  164.           fprintf(stderr,"TRC IS EXITING");
  165.           exit(0);
  166.          }
  167.          return(r);
  168.      }
  169.  
  170.  
  171. _1_0.  _D_A_T_A _O_B_J_E_C_T_S
  172.  
  173.      At several points in PART ONE, it was mentioned that  a
  174. list  is  maintained  for  each object that has at least one
  175. element.  Objects that do not contain elements  can  not  be
  176. distinguished  from  one  another, so no list is maintained,
  177. only a count of how many there are is  needed.   The  struc-
  178. tures  those  lists  are  built from are defined in the file
  179. 'loop.h'.  The example below gives a sample  TRC  definition
  180. part and the output that might be generated with that input:
  181.  
  182. Input:
  183.      %%
  184.      A
  185.      B (B1 : INT
  186.         B2 : FLOAT
  187.         B3 : STRING
  188.         B4 : POINTER)
  189.      %%
  190.  
  191. Output:
  192.      #define A 0
  193.      #define A_max 2
  194.      #define B 1
  195.      #define B_max 2
  196.  
  197.      struct B_struct {
  198.                int B1;
  199.                double B2;
  200.                char *B3;
  201.                struct B_struct *B4;
  202.                int MARK;
  203.                struct B_struct *prev;
  204.                struct B_struct *next;
  205.      } *B_list[B_max], B_empty[2], *B_temp[B_max];
  206.  
  207.  
  208.  
  209.  
  210.  
  211.  
  212.  
  213.  
  214.  
  215.                            - 26 -
  216.  
  217.  
  218.      There are two '#define'  statements  for  each  object.
  219. The  first  defines  the object name to be an integer.  This
  220. name is used for indexing arrays.  The intention is to  make
  221. code  more  readable by using the name of the object that is
  222. being referred to rather than a literal  index  number.   At
  223. the  points  in  the output code where these names are used,
  224. their meaning will be explained.  The second '#define' asso-
  225. ciated with each object is used for specifying the number of
  226. pointers that are needed for each object.  Since  each  rule
  227. is  completely  independent  of  each  other  rule, the same
  228. pointers may be reused in each rule.  The maximum number  of
  229. pointers needed is the maximum used by any single rule.
  230.  
  231.      Each object with at least one element has an associated
  232. structure.  In this example the object A has no elements and
  233. therefore no structure.  The object  B  contains  four  ele-
  234. ments, one of each type.  The structure is named 'B_struct',
  235. each  structure  will  be  similarly  named   by   appending
  236. '_struct'  to  the  object  name.   A  data  object  will be
  237. included in the structure for each element that was  defined
  238. for  the object.  The element names defined in the input are
  239. used in the output, again to keep the output  code  readable
  240. and  meaningful.   The correspondence of input data types to
  241. output data types is straight  forward;  INT  translates  to
  242. int,  FLOAT  to  double,  STRING to char *, and POINTER to a
  243. pointer to  a  structure  of  the  type  that  contains  the
  244. POINTER.  The POINTER type is included for users who wish to
  245. extend STM with user supplied code.  There is no support for
  246. testing or searching POINTERs or anything they may point to.
  247.  
  248.      The 'B_list' and 'B_temp' pointers  are  used  as  free
  249. variables  and  place  holders in the inference engine.  The
  250. 'B_list[0]' pointer points to the list of  B  objects.   STM
  251. consists of the various '*_list[0]' pointers, the lists they
  252. point to and the count of how  many  objects  of  each  type
  253. exist at any given moment.
  254.  
  255. _1_1.  _M_A_N_I_P_U_L_A_T_I_N_G _T_H_E _D_A_T_A
  256.  
  257.      There are three basic manipulations that  can  be  per-
  258. formed on the data in STM, an object can be added to STM, an
  259. object can be deleted from STM and STM can be  searched  for
  260. the  existence of an object with given elements.  Since each
  261. of the object types is  defined  by  a  separate  structure,
  262. separate  add,  delete and search procedures must be created
  263. for each object type.  The following sections give an  exam-
  264. ple and an explanation of how each procedure is generated.
  265.  
  266. _1_1._1.  _A_D_D _P_R_O_C_E_D_U_R_E_S
  267.  
  268.      For each object that is defined, a  procedure  is  gen-
  269. erated  for  inserting  structures  into the list associated
  270. with the object.  These procedures are written on  the  file
  271. 'add.c'.   The  parameters that are passed to this procedure
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278.  
  279.  
  280.  
  281.                            - 27 -
  282.  
  283.  
  284. are the values that are to be assigned to  the  elements  of
  285. the object.  The parameters are listed in the order that the
  286. elements were declared, e.g.:
  287.  
  288. INPUT:
  289.      A
  290.      B (B1 : INT
  291.         B2 : FLOAT
  292.         B3 : STRING
  293.         B4 : POINTER)
  294.  
  295. OUTPUT:
  296.      add_A_struct()
  297.      {
  298.           token[A]++;
  299.      }
  300.  
  301.      add_B_struct(B1, B2, B3)
  302.      int B1;
  303.      double B2;
  304.      char *B3;
  305.      {
  306.           struct B_struct *temp;
  307.  
  308.           temp = (struct B_struct *)
  309.           myalloc(sizeof(struct B_struct));
  310.           temp->B1 = B1;
  311.           temp->B2 = B2;
  312.           temp->B3 = (char *) myalloc((strlen(B3)) + 1);
  313.           strcpy(temp->B3, B3);
  314.           temp->MARK = 0;
  315.           temp->next = B_list[0];
  316.           temp->prev = NULL;
  317.           if(B_list[0])
  318.                B_list[0]->prev = temp;
  319.           B_list[0] = temp;
  320.           token[B]++;
  321.      }
  322.  
  323.  
  324.      Since the A object contains no elements,  adding  an  A
  325. object  is  trivial;  the  count is simply incremented.  The
  326. variable 'token' is an  integer  array  sized  to  have  one
  327. integer  for  each object type.  If there are N object types
  328. token is an array of N integers, indexed 0 through N-1.   In
  329. 'add_A_struct'  the  array  'token' is indexed by A.  Recall
  330. that A, the name of a type of object, was defined to  be  an
  331. integer,  in  this  case  0.   The  integer  'token[0]'   or
  332. 'token[A]' is the count of how many objects of type A exist.
  333.  
  334.      The procedure 'add_B_struct' is  typical  of  add  pro-
  335. cedures for objects with elements.  The parameters passed in
  336. are the values that are to be assigned to  the  elements  of
  337. the  new  object.   Even  though B_struct includes a POINTER
  338.  
  339.  
  340.  
  341.  
  342.  
  343.  
  344.  
  345.  
  346.  
  347.                            - 28 -
  348.  
  349.  
  350. object, no value is assigned to that pointer.  As  has  been
  351. mentioned  earler,  there is no support for the pointer type
  352. in TRC generated code.  The code is very  straight  forward;
  353. allocate  a  structure,  initialize it's elements (note that
  354. space is allocated for strings in the add procedure), insert
  355. it  at  the head of the doubly linked list and increment the
  356. count (token[B]++).
  357.  
  358.      The file also contains the  procedure  'init()'.   This
  359. procedure  is  based  on  the contents of the STM section of
  360. code.  For each statement in STM,  a  statement  appears  in
  361. init.   The  statements  are simply calls to the various add
  362. procedures.  The calls are made in  an  order  opposite  the
  363. order the STM statements are listed.  Since all additions to
  364. lists are made as  insertions  at  the  head  of  the  list,
  365. inverting  the  order  causes  the final list to contain the
  366. objects in the order they were actually listed, e.g:
  367.  
  368. INPUT:
  369.      %%
  370.      A
  371.      B
  372.      5A
  373.      B (B1 => 7
  374.         B2 => 6.6
  375.         B3 => "THIS")
  376.      5 B (B1 = 2)
  377.      %%
  378.  
  379. OUTPUT:
  380.      init()
  381.      {
  382.           int i;
  383.  
  384.           for(i = 0; i < 5; i++)
  385.                add_A_struct(2, 0.0, "");
  386.           add_B_struct(7, 6.6, "THIS");
  387.           for(i = 0; i < 5; i++)
  388.                add_A_struct();
  389.           add_B_struct(0, 0.0, "");
  390.           add_A_struct();
  391.      }
  392.  
  393.  
  394.      As you can see, this facility  is  pretty  crude,  each
  395. element that is listed in STM becomes a literal value in the
  396. code.  These literal values are then copied into dynamically
  397. allocated  memory,  so  there are actually two copies of all
  398. the data in memory.  The intention is that the  STM  section
  399. and  the  init  procedure  will  be  used  primarily  during
  400. development and testing and  will  be  replaced  by  a  user
  401. developed  front  end  that will collect the data and create
  402. the dynamic structures for the TRC  code.   It  is  possible
  403. that  there is some small amount of data that must always be
  404.  
  405.  
  406.  
  407.  
  408.  
  409.  
  410.  
  411.  
  412.  
  413.                            - 29 -
  414.  
  415.  
  416. loaded into STM for a given set of problems, it may be  con-
  417. venient to have the init procedure load this data into STM.
  418.  
  419. _1_1._2.  _M_A_R_K _P_R_O_C_E_D_U_R_E_S
  420.  
  421.      For each object that is defined, a  procedure  is  gen-
  422. erated for deleting structures from the list associated with
  423. the  object.  These  procedures  are  written  on  the  file
  424. 'free.c'.   The parameter passed to this procedure indicates
  425. which object is to be deleted from the list, e.g.:
  426.  
  427. INPUT:
  428.      A
  429.      B (B1 : INT
  430.         B2 : FLOAT
  431.         B3 : STRING
  432.         B4 : POINTER)
  433.  
  434. OUTPUT
  435.      free_A_struct()
  436.      {
  437.          token[A]--;
  438.      }
  439.  
  440.      free_B_struct(start)
  441.      int start;
  442.      {
  443.          int i;
  444.  
  445.          for(i = start; i < B_max; i++)
  446.           if(B_list[i]){
  447.               if(B_list[i]->prev == NULL)
  448.                B_list[0] = B_list[0]->next;
  449.               else
  450.                B_list[i]->prev->next = B_list[i]->next;
  451.               if(B_list[i]->next)
  452.                B_list[i]->next->prev = B_list[i]->prev;
  453.               free(B_list[i]->B3);
  454.               free(B_list[i]);
  455.               B_list[i] = NULL;
  456.               i = B_max;
  457.               token[B]--;
  458.              }
  459.      }
  460.  
  461.  
  462.      As in the add procedures, the procedure  to  delete  an
  463. object  with  no elements is trivial; decrement the count of
  464. objects of that type.  The procedure 'free_B_struct' is typ-
  465. ical of procedures for deleting an object from a list.
  466.  
  467.      Recall that 'B_list[0]' points to the list of B objects
  468. in STM and that the other 'B_list' pointers are used as free
  469. variables.  Each  match  statement  in  the  situation  part
  470.  
  471.  
  472.  
  473.  
  474.  
  475.  
  476.  
  477.  
  478.  
  479.                            - 30 -
  480.  
  481.  
  482. causes  STM to be searched for an object.  If an object that
  483. matches the test exists in STM, a pointer to that object  is
  484. returned and assigned to one of the pointers in the 'B_list'
  485. array.  Recall that only objects  that  were  found  in  the
  486. situation  part  can  be  MARKed in the action part and that
  487. objects may be MARKed by name or the order in which they are
  488. found.
  489.  
  490.      There are two cases, the case where a named  object  is
  491. to be MARKed and the case where the first object found is to
  492. be MARKed.  In the case  where  a  named  object  is  to  be
  493. MARKed,  the  name  of the object is translated to the index
  494. number of the pointer that  points  to  that  object.   This
  495. index  number  is  passed  to  the free procedure.  In cases
  496. where the object is being MARKed based on the order  it  was
  497. found, the index 1 (the first free variable used) is passed.
  498. Examples of calls to 'free_B_struct' are  given  in  section
  499. X.X.X.
  500.  
  501.      The array of pointers to  objects  of  the  given  type
  502. (B_list  in  this  case)  is searched for the first one that
  503. points to an object.  This object is unlinked from the list,
  504. any  space allocated for strings in the object being deleted
  505. is freed and finally the space  occupied  by  the  structure
  506. itself is freed.  The count of objects in the list is decre-
  507. mented and the 'for' loop counting variable is  set  to  the
  508. exit condition.
  509.  
  510.  
  511. _1_1._3.  _S_E_A_R_C_H _P_R_O_C_E_D_U_R_E_S
  512.  
  513.      For each object that is defined, a  procedure  is  gen-
  514. erated  for  searching list associated with the object.  The
  515. procedure simply performs a linear search  on  the  list  in
  516. question.   The  RECURSIVE search strategy is implemented as
  517. multiple calls to the LINEAR search procedure.   These  pro-
  518. cedures  are  written on the file 'search.c'.  The parameter
  519. passed to this procedure indicates  where  in  the  list  to
  520. begin searching, e.g.: INPUT:
  521.      A
  522.      B (B1 : INT
  523.         B2 : FLOAT
  524.         B3 : STRING
  525.         B4 : POINTER)
  526.  
  527. OUTPUT:
  528.      struct B_list *search_B_list(index,
  529.                     B1, B1_relop,
  530.                     B2, B2_relop,
  531.                     B3, B3_relop)
  532.      int index, B1_relop, B2_relop, B3_relop;
  533.      int B1;
  534.      double B2;
  535.      char *B3;
  536.  
  537.  
  538.  
  539.  
  540.  
  541.  
  542.  
  543.  
  544.  
  545.                            - 31 -
  546.  
  547.  
  548.      {
  549.          int flag
  550.          struct B_struct *temp;
  551.  
  552.          temp = B_temp[index];
  553.          while(temp){
  554.           if(temp->MARK == 0){
  555.               flag = 7;
  556.               if(flag & test_int(temp->B1, B1) & B1_relop);
  557.                else flag = 0;
  558.               if(flag & test_double(temp->B2, B2) & B2_relop);
  559.                else flag = 0;
  560.               if(flag & test_string(temp->B3, B3) & B3_relop);
  561.                else flag = 0;
  562.               if(flag){
  563.                temp->MARK = 1;
  564.                return(temp);
  565.               }
  566.           }
  567.           temp = temp->next;
  568.          }
  569.          return(NULL);
  570.      }
  571.  
  572.  
  573.      Since the object A has no elements, there  is  no  list
  574. and  no search procedure for A objects.  In the procedure to
  575. search the B list the first parameter, 'index', is the index
  576. of  the  pointer  that points to the point in the list where
  577. the search is to begin.  The remaining  parameters  are  the
  578. value  that  each  element is to be compared against and the
  579. bit encoded relational operator for the comparison.
  580.  
  581.      The first test (temp->MARK==0) checks  to  see  if  the
  582. object  is  already  'in use'.  Each object mentioned in the
  583. situation part must match a unique object, the  same  object
  584. can  not  match two situation part statements.  An object is
  585. marked as 'in use' by setting  MARK  to  non-zero.   If  the
  586. object  is  not 'in use', it's elements are tested, one at a
  587. time, against the required values with  the  required  rela-
  588. tional  operator.   The  procedures test_int, test_float and
  589. test_string return the  bit  encoded  relation  of  the  two
  590. values.  This relation is bitwise ANDed with the bit encoded
  591. relational operator that was passed in.  If  the  result  of
  592. the  bitwise AND is non-zero, the relation is true for those
  593. two values.  The 'flag' variable ensures that  if  one  test
  594. fails, all subsequent tests will fail.
  595.  
  596.      If an object is found  where  all  elements  match  the
  597. desired  values, it's MARK integer is set to one to indicate
  598. that it is 'in use' and a pointer to that object is returned
  599. to  the  calling  procedure.   If one or more elements of an
  600. object fail a test, the next object in the list  is  tested.
  601. If  all objects are tested and none match, a NULL pointer is
  602.  
  603.  
  604.  
  605.  
  606.  
  607.  
  608.  
  609.  
  610.  
  611.                            - 32 -
  612.  
  613.  
  614. returned.
  615.  
  616.      This search procedure will work only for searches where
  617. the  value  that  is  being searched for is known before the
  618. call.  In cases where an element is being compared  to  some
  619. other  element of the same object, a slightly different ver-
  620. sion of the search procedure is generated, e.g.:
  621.  
  622. INPUT:
  623.      %%
  624.      B (B1:INT
  625.         B2:INT
  626.         B3:INT)
  627.      %%
  628.      %%
  629.      R1:
  630.           (B.B1 == B.B2)
  631.           (B.B3 < B.B1
  632.            B.B1 > B.B2)
  633.           (B.B3 < B.B2)
  634.           =>
  635.           MARK B B B
  636.           ;
  637.      %%
  638.  
  639. OUTPUT:
  640.      struct B_struct *search_B_struct(index,
  641.                     B1, B1_relop, B1_case,
  642.                     B2, B2_relop,
  643.                     B3, B3_relop, B3_case)
  644.      int  index, B1_relop, B2_relop, B3_relop;
  645.      int B1;
  646.      int B2;
  647.      int B3;
  648.      {
  649.          int   flag;
  650.          struct B_struct *temp;
  651.  
  652.          temp = B_temp[index];
  653.          while(temp){
  654.            if(temp->MARK == 0){
  655.           flag = 7;
  656.           switch(B1_case){
  657.               case 0:
  658.                if(flag & test_int(temp->B1, B1)
  659.                   & B1_relop);
  660.                else flag = 0;
  661.                break;
  662.               case 1:
  663.                if(flag & test_int(temp->B1, temp->B2)
  664.                   & B1_relop);
  665.                else flag = 0;
  666.                break;
  667.               default: flag = 0;
  668.  
  669.  
  670.  
  671.  
  672.  
  673.  
  674.  
  675.  
  676.  
  677.                            - 33 -
  678.  
  679.  
  680.           }
  681.           if(flag & test_int(temp->B2, B2)
  682.              & B2_relop);
  683.                else flag = 0;
  684.           switch(B3_case){
  685.               case 0:
  686.                if(flag & test_int(temp->B3, B3)
  687.                   & B3_relop);
  688.                else flag = 0;
  689.                break;
  690.               case 1:
  691.                if(flag & test_int(temp->B3, temp->B1)
  692.                   & B3_relop);
  693.                else flag = 0;
  694.                break;
  695.               case 2:
  696.                if(flag & test_int(temp->B3, temp->B2)
  697.                   & B3_relop);
  698.                else flag = 0;
  699.                break;
  700.               default: flag = 0;
  701.           }
  702.           if(flag){
  703.                temp->MARK = 1;
  704.                return(temp);
  705.           }
  706.            }
  707.            temp = temp->next;
  708.          }
  709.          return(NULL);
  710.      }
  711.  
  712.  
  713.      As can be seen in the example, the procedure  is  quite
  714. similar.   A 'case' variable has been added to the parameter
  715. list for each element which might  be  compared  to  another
  716. element  of  the same object.  Case 0 is the situation where
  717. an element is being compared to a value, all other cases are
  718. comparisons  of  an  element  to another element of the same
  719. object.  Only the cases that  are  actually  used  are  gen-
  720. erated, not all possible cases.
  721.  
  722.      There is an obvious code overhead  for  comparing  ele-
  723. ments  within  an object, but this overhead occurs only once
  724. for each type of comparison.  Subsequent rules could include
  725. similar  element  to  element comparisons without adding any
  726. additional code overhead.
  727.  
  728. _1_2.  _T_R_A_N_S_L_A_T_I_N_G _R_U_L_E_S
  729.  
  730.      The LTM section is translated  to  a  single  procedure
  731. named  'loop'  which  is  written  on the file 'loop.c'.  An
  732. inference  engine  is  executed  by  calling  the  procedure
  733. 'init',  which  is written on the file 'add.c' followed by a
  734.  
  735.  
  736.  
  737.  
  738.  
  739.  
  740.  
  741.  
  742.  
  743.                            - 34 -
  744.  
  745.  
  746. call to 'loop'.  The loop procedure will test rules  in  the
  747. order  they  were  listed  until no rule's situation part is
  748. true or until the user code executes a return  or  exit.   A
  749. simple two rule system will be used to illustrate the trans-
  750. lation:
  751.  
  752. INPUT:
  753.      %%
  754.      B (B1:INT
  755.         B2:INT
  756.         B3:INT)
  757.      %%
  758.      %%
  759.      R1:
  760.      EMPTY B NAMED
  761.           (B.B1 == B.B2)
  762.           {
  763.              $NAMED.B1 = some_procedure();
  764.              if(some_other_procedure($NAMED.B1))
  765.                $FAIL.
  766.           }
  767.           (B.B1 != NAMED.B1)
  768.           =>
  769.           MARK B B
  770.           {
  771.               printf("Rule R1 fired0);
  772.           }
  773.           ;
  774.      R2:
  775.      RECURS
  776.           (B.B1 != 7)
  777.           (^B FIRST
  778.             B.B1 == 7)
  779.           (B.B2 <= FIRST.B3)
  780.           =>
  781.           MARK FIRST
  782.           ADD B (B1 => 0
  783.                  B2 => FIRST.B3
  784.                  B3 => FIRST.B2)
  785.           ;
  786.      %%
  787.  
  788. OUTPUT:
  789.      loop()
  790.      {
  791.          int i;
  792.      Start:
  793.      R1:
  794.              if((token[B] >= 2) &&
  795.                  1){
  796.                  B_temp[1] = B_list[0];
  797.                  if((B_list[1] = search_B_struct
  798.               (1, 0, 2, 1, 0, 7, 0, 7)) == NULL){
  799.                      restore();
  800.  
  801.  
  802.  
  803.  
  804.  
  805.  
  806.  
  807.  
  808.  
  809.                            - 35 -
  810.  
  811.  
  812.                      goto R2;
  813.                  }
  814.                  B_empty[0].B1 = some_procedure();
  815.                  if(some_other_procedure(B_empty[0].B1))
  816.                  {
  817.                      restore();
  818.                      goto R2;
  819.                  }
  820.                  B_temp[2] = B_list[0];
  821.                  if((B_list[2] = search_B_struct
  822.               (2, B_empty[0].B1, 5, 0, 0, 7, 0, 7)) == NULL){
  823.                      restore();
  824.                      goto R2;
  825.                  }
  826.               for(i = 0; i < 2; i++)
  827.                      free_B_struct(1);
  828.                  restore();
  829.                  printf("Rule R1 fired0);
  830.                  goto Start;
  831.              }
  832.      R2:
  833.              if((token[B] >= 3) &&
  834.                  1){
  835.                  B_temp[1] = B_list[0];
  836.      R2_B_1:
  837.                  if(B_list[1])
  838.                      B_list[1]->MARK = 0;
  839.                  if((B_list[1] = search_B_struct
  840.               (1, 7, 5, 0, 0, 7, 0, 7)) == NULL){
  841.                      restore();
  842.                      goto End;
  843.                  }
  844.                  B_temp[1] = B_list[1]->next;
  845.                  B_temp[2] = B_list[0];
  846.      R2_B_2:
  847.                  if(B_list[2])
  848.                      B_list[2]->MARK = 0;
  849.                  if((B_list[2] = search_B_struct
  850.               (2, 7, 2, 0, 0, 7, 0, 7)) == NULL){
  851.                      goto R2_B_1;
  852.                  }
  853.                  B_temp[2] = B_list[2]->next;
  854.                  B_temp[3] = B_list[0];
  855.      R2_B_3:
  856.                  if(B_list[3])
  857.                      B_list[3]->MARK = 0;
  858.                  if((B_list[3] = search_B_struct
  859.               (3, 0, 7, 0, B_list[2]->B3, 6, 0, 7)) == NULL){
  860.                      goto R2_B_2;
  861.                  }
  862.                  B_temp[3] = B_list[3]->next;
  863.                  add_B_struct(0, B_list[2]->B3, B_list[2]->B2);
  864.                  free_B_struct(2);
  865.                  restore();
  866.  
  867.  
  868.  
  869.  
  870.  
  871.  
  872.  
  873.  
  874.  
  875.                            - 36 -
  876.  
  877.  
  878.                  goto Start;
  879.              }
  880.      End:
  881.              return(1);
  882.      }
  883.  
  884.  
  885.      A rule is translated to  an  extended  'if'  statement.
  886. Basically,  "if  situation  then  action".  Each rule begins
  887. with a label that repeats the rule  label  from  the  input.
  888. The  label  'Start' marks the beginning of the rules and the
  889. label are included as a convenient way to exit  (goto  End;)
  890. or restart (goto Start;).
  891.  
  892.      The code for rule 'R1' begins at  the  label  'R1'  and
  893. ends  at the label 'R2'.  The first statement, "if((token[B]
  894. >= 2))", is a pre-test.   The  array  'token[]'  contains  a
  895. count of how many objects are in each list.  Token[B] is the
  896. count of how many objects are in the  B  list.   Since  rule
  897. 'R1'  specifys two objects of type B in it's situation part,
  898. it is pointless to search the B list if  it  contains  fewer
  899. than 2 objects.  A statement similar to this is the first in
  900. every rule.  STM is never searched unless there  are  enough
  901. objects  that  it is possible for the rule to fire.  If this
  902. initial test fails, testing will continue at label 'R2'.
  903.  
  904.      The next statement, 'B_temp[1] =  B_list[0];'  initial-
  905. izes a pointer to point to the beginning of the B list.  The
  906. index of this pointer is passed  to  the  search  procedure.
  907. This use of indirection is not necessary in LINEAR rules but
  908. it is convenient in RECURSIVE rules, the same calling  tech-
  909. nique is used by both search strategies to simplify the code
  910. generation.
  911.  
  912.      The call to the search procedure is embedded in an 'if'
  913. statement  along  with  an  assignment  to the free variable
  914. pointer that will point to the object if it exists  in  STM.
  915. The  parameter  list in this call consists first of '1', the
  916. index of the temp pointer that indicates the  start  of  the
  917. search.   The  next  value  '0', is the value that the first
  918. declared element, 'B1', is to be compared against.  The next
  919. parameter,  '2',  is  the  bit  encoded relational operator,
  920. equality. The next parameter, '1', is  the  'case'  of  this
  921. test.   Since  it is not zero, 'B1' is not being compared to
  922. the value but rather is being compared in this case  to  the
  923. element  'B2'  of the same object.  Values for elements 'B2'
  924. and 'B3' were not specified, so those parameters are  filled
  925. in with the default value of '0' and relational operator '7'
  926. which is the bit encoded 'don't care' operator.   If  'NULL'
  927. is  returned,  the object does not exist in STM and the rule
  928. fails.  A linear rule is made to fail by clearing  all  free
  929. variables  (restore();)  and  continuing  with the next rule
  930. (goto R2;).
  931.  
  932.  
  933.  
  934.  
  935.  
  936.  
  937.  
  938.  
  939.  
  940.  
  941.                            - 37 -
  942.  
  943.  
  944.      If the first test does not  fail,  execution  continues
  945. with  the next statement, which is the translated version of
  946. the embedded c-code from rule 'R1'.  The string '$NAMED'  is
  947. translated  to  'B_empty[1]' which is the name of the struc-
  948. ture that was named by  the  EMPTY  statement.   The  string
  949. '$FAIL.'  is  translated  to the statements "restore(); goto
  950. R2;", which cause the rule to fail in the standard manner.
  951.  
  952.      The next 'if' statement is identical  in  form  to  the
  953. previous one, only the values of the elements are different.
  954. In this case element 'B1' is being  compared  to  'NAMED.B1'
  955. which,  again, is translated to B_empty[0].B1.  If this test
  956. fails, the pointers will be cleared and execution will  con-
  957. tinue at 'R2'.  If it does not fail, the action part is exe-
  958. cuted.
  959.  
  960.      The action part of rule 'R1' consists of a MARK  state-
  961. ment  and  c-code  which contains a 'printf' call.  The MARK
  962. statement is translated to a 'for' loop  which  deletes  the
  963. first  object  that  was  found  in  each  of  it's calls to
  964. 'free_B_struct'.  The 'restore();' statement follows all ADD
  965. and  MARK  statements in the action part to clear any active
  966. free variables.  The c-code 'printf' comes next followed  by
  967. 'goto  Start;'  which  causes  the  rule list to be searched
  968. again for the first rule whose situation part is true.
  969.  
  970.      The form of the second rule is  quite  similar  to  the
  971. first  rule.   Since  rule  'R2'  is  RECURSIVE,  some minor
  972. differences are evident.  The first difference is  that  the
  973. start  of  each  test  for  the  existence  of  an object is
  974. labelled.  This is to permit  backing  up  to  the  previous
  975. test.   The  second  difference  is that only the first test
  976. contains the 'restore' and  'goto'  statements.   All  other
  977. tests  simply  back  up  one  position  if  they  fail.  The
  978. 'B_temp' variables now store the location where  the  search
  979. is to be restarted if some test fails.
  980.  
  981.      In  the  action  part  of  rule  'R2',  the   call   to
  982. 'free_B_struct'  passes  the  value '2', indicating that the
  983. second object that was found is the one to delete.  This was
  984. specified  with the statement 'MARK FIRST', where the object
  985. named 'FIRST' was the second object of type B  specified  in
  986. the situation part.
  987.  
  988.  
  989.  
  990. _1_3.  _O_P_T_I_O_N_S
  991.  
  992.      Options may cause additional procedures to be generated
  993. and  sometimes  cause  standard  procedures  to be modified.
  994. This section will detail the effects each option has on  the
  995. output.
  996.  
  997.  
  998.  
  999.  
  1000.  
  1001.  
  1002.  
  1003.  
  1004.  
  1005.  
  1006.  
  1007.                            - 38 -
  1008.  
  1009.  
  1010. _1_3._1.  _P_R_O_F_I_L_E
  1011.  
  1012.      The intention of the profile option  is  to  provide  a
  1013. summary  of the execution of the inference engine.  The pro-
  1014. file option causes the procedure 'loop' to be  modified  and
  1015. an  additional procedure is written on the file 'profile.c',
  1016. e.g.:
  1017.  
  1018. INPUT:
  1019.      %%
  1020.      B (B1:INT
  1021.         B2:INT
  1022.         B3:INT)
  1023.      %%
  1024.      %%
  1025.      PROFILE
  1026.      R1:
  1027.           (B.B1 == B.B2)
  1028.           (B.B1 != B.B2)
  1029.           =>
  1030.           MARK B B
  1031.           ;
  1032.      R2:
  1033.           (B.B1 != 7)
  1034.           (^B FIRST
  1035.             B.B1 == 7)
  1036.           (B.B2 <= FIRST.B3)
  1037.           =>
  1038.           MARK FIRST
  1039.           ;
  1040.      %%
  1041.  
  1042. OUTPUT:
  1043.      loop()
  1044.      {
  1045.          int i;
  1046.      Start:
  1047.              test_profile[0]++;
  1048.      R1:
  1049.              test_profile[1]++;
  1050.              if((token[B] >= 2) &&
  1051.                  1){
  1052.                  B_temp[1] = B_list[0];
  1053.      R1_B_1:
  1054.                  test_profile[2]++;
  1055.                  if((B_list[1] = search_B_struct
  1056.               (1, B_list[1]->B2, 2, 0, 7, 0, 7)) == NULL){
  1057.                      restore();
  1058.                      goto R2;
  1059.                  }
  1060.                  B_temp[2] = B_list[0];
  1061.      R1_B_2:
  1062.                  test_profile[3]++;
  1063.                  if((B_list[2] = search_B_struct
  1064.  
  1065.  
  1066.  
  1067.  
  1068.  
  1069.  
  1070.  
  1071.  
  1072.  
  1073.                            - 39 -
  1074.  
  1075.  
  1076.               (2, B_list[1]->B2, 5, 0, 7, 0, 7)) == NULL){
  1077.                      restore();
  1078.                      goto R2;
  1079.                  }
  1080.                  fire_profile[1]++;
  1081.                  for(i = 0; i < 2; i++)
  1082.                      free_B_struct(1);
  1083.                  restore();
  1084.                  goto Start;
  1085.              }
  1086.      R2:
  1087.              test_profile[4]++;
  1088.              if((token[B] >= 3) &&
  1089.                  1){
  1090.                  B_temp[1] = B_list[0];
  1091.      R2_B_1:
  1092.                  test_profile[5]++;
  1093.                  if((B_list[1] = search_B_struct
  1094.               (1, 7, 5, 0, 7, 0, 7)) == NULL){
  1095.                      restore();
  1096.                      goto End;
  1097.                  }
  1098.                  B_temp[2] = B_list[0];
  1099.      R2_B_2:
  1100.                  test_profile[6]++;
  1101.                  if((B_list[2] = search_B_struct
  1102.               (2, 7, 2, 0, 7, 0, 7)) == NULL){
  1103.                      restore();
  1104.                      goto End;
  1105.                  }
  1106.                  B_temp[3] = B_list[0];
  1107.      R2_B_3:
  1108.                  test_profile[7]++;
  1109.                  if((B_list[3] = search_B_struct
  1110.               (3, 0, 7, B_list[2]->B3, 6, 0, 7)) == NULL){
  1111.                      restore();
  1112.                      goto End;
  1113.                  }
  1114.                  fire_profile[2]++;
  1115.                  free_B_struct(2);
  1116.                  restore();
  1117.                  goto Start;
  1118.              }
  1119.      End:
  1120.              test_profile[8]++;
  1121.              return(1);
  1122.          }
  1123.      }
  1124.  
  1125.  
  1126.      The 'loop' procedure that is generated with the PROFILE
  1127. option  turned  on is differs from the standard procedure in
  1128. several ways.  Each test in each rule is labeled whether  it
  1129. is  RECURSIVE or not.  Each label is followed by a statement
  1130.  
  1131.  
  1132.  
  1133.  
  1134.  
  1135.  
  1136.  
  1137.  
  1138.  
  1139.                            - 40 -
  1140.  
  1141.  
  1142. of form 'test_profile[N]++', causing the array  test_profile
  1143. to maintain a count of how many times the following code was
  1144. executed.  The action part of each rule begins with a state-
  1145. ment   of   form   'fire_profile[N]++',  causing  the  array
  1146. fire_profile to maintain a count of how many times each rule
  1147. fired.
  1148.  
  1149.      The PROFILE option causes the arrays  test_profile  and
  1150. fire_profile  to  be  defined  and  properly sized.  It also
  1151. defines two character arrays, label_names[] and  rules[]  to
  1152. be  defined.   These  character  arrays contain the names of
  1153. each  label  and  each  rule  respectively.   The  procedure
  1154. print_profile  is also generated.  This procedure will print
  1155. the names of each label and it's  associated  count  on  the
  1156. standard output, e.g.:
  1157.  
  1158. OUTPUT:
  1159.      print_profile()
  1160.      {
  1161.          int i, t;
  1162.          t = 0;
  1163.          printf("0ules Tested0);
  1164.          for(i = 0; i < 9; i++){
  1165.              printf("%d%s0,test_profile[i], label_names[i]);
  1166.              t += test_profile[i];
  1167.          }
  1168.          printf("%d0, t);
  1169.          t = 0;
  1170.          printf("0ules Fired0);
  1171.          for(i = 1; i < 3; i++){
  1172.              printf("%d%s0,fire_profile[i], rules[i]);
  1173.              t += fire_profile[i];
  1174.          }
  1175.          printf("%d0, t);
  1176.      }
  1177.  
  1178.  
  1179. _1_3._2.  _T_R_A_C_E
  1180.  
  1181.      The TRACE option causes  the  'loop'  procedure  to  be
  1182. modified  and an additional procedure is written on the file
  1183. 'misc.c'.  The modification to 'loop' is simply  the  inclu-
  1184. sion of a procedure call of form 'append_trace(N);' (where N
  1185. is an integer literal) in the action part of the rule.   The
  1186. parameter  is the index of the name of the rule in the char-
  1187. acter array 'rules' that is generated by the PROFILE option.
  1188. The PROFILE option only keeps a count of the number of times
  1189. a rule fires, the TRACE option records the  ORDER  that  the
  1190. rules were fired.
  1191.  
  1192.      struct trace {
  1193.                int rule;
  1194.                struct trace *next;
  1195.      } *trace_front, *trace_back;
  1196.  
  1197.  
  1198.  
  1199.  
  1200.  
  1201.  
  1202.  
  1203.  
  1204.  
  1205.                            - 41 -
  1206.  
  1207.  
  1208.      append_trace(i)
  1209.      int i;
  1210.      {
  1211.          struct trace *temp;
  1212.  
  1213.          temp = (struct trace *) myalloc (sizeof(struct trace));
  1214.          temp->rule = i;
  1215.          temp->next = NULL;
  1216.          if(trace_front){
  1217.           trace_back->next = temp;
  1218.           trace_back = trace_back->next;
  1219.          }
  1220.          else trace_front = trace_back = temp;
  1221.      }
  1222.  
  1223.  
  1224. _1_3._3.  _D_U_M_P
  1225.  
  1226.      The DUMP option generates a set of  procedures  written
  1227. on    the    file    'dump.c'.     A   procedure   of   form
  1228. 'dump_NAME_struct()' (where NAME is the name of the  object)
  1229. is generated for each object declared in the definition sec-
  1230. tion.  There is also a procedure 'dump_stm()'  which  simply
  1231. calls  the  other  dump  procedures  in  the  order that the
  1232. objects were defined.  Each procedure prints the  number  of
  1233. objects  in that list and the current values of the elements
  1234. of each object in tabular form on the standard output.
  1235.  
  1236. INPUT:
  1237.      %%
  1238.      A
  1239.      B (B1:INT
  1240.         B2:INT
  1241.         B3:INT)
  1242.      %%
  1243.  
  1244. OUTPUT:
  1245.      dump_stm()
  1246.      {
  1247.          dump_A_struct();
  1248.          dump_B_struct();
  1249.      }
  1250.  
  1251.      dump_A_struct()
  1252.      {
  1253.          printf("0umping A list (%d)0,token[A]);
  1254.      }
  1255.  
  1256.      dump_B_struct()
  1257.      {
  1258.          int   i;
  1259.          struct B_struct *temp;
  1260.  
  1261.          i = 1;
  1262.  
  1263.  
  1264.  
  1265.  
  1266.  
  1267.  
  1268.  
  1269.  
  1270.  
  1271.                            - 42 -
  1272.  
  1273.  
  1274.          printf("0umping B list (%d)0,token[B]);
  1275.          temp = B_list[0];
  1276.          while(temp){
  1277.           printf("%d.%d%d%d0, i
  1278.                , temp->B1
  1279.                , temp->B2
  1280.                , temp->B3);
  1281.           temp = temp->next;
  1282.           i++;
  1283.          }
  1284.      }
  1285.  
  1286.  
  1287. _1_3._4.  _B_A_C_K_T_R_A_C_K
  1288.  
  1289.      The BACKTRACKing option is  easily  the  most  complex.
  1290. While  other options usually have a minor effect on the out-
  1291. put, BACKTRACKing will often double the  size  of  the  code
  1292. generated  by TRC.  BACKTRACK modifies the add and loop pro-
  1293. cedures and generates two new  procedures,  insert_backtrack
  1294. and backup, on the file 'backtrack.c'.
  1295.  
  1296.      The intent of backtracking is to make  it  possible  to
  1297. undo  the  action part of a rule and continue as if the rule
  1298. had never fired.  This facility is useful in  systems  where
  1299. the  first  possible  path through the problem space may not
  1300. lead to a solution or may not lead to  the  preferred  solu-
  1301. tion.   In the code produced by TRC, backtracking will occur
  1302. whenever no rule's situation part is true  and  there  is  a
  1303. rule which can be undone.
  1304.  
  1305.      A rule is undone by restoring STM to the state  it  was
  1306. in  before the rule fired and continuing testing at the rule
  1307. following the rule being undone.  There are two obvious ways
  1308. to restore STM.  The first is to save all of STM each time a
  1309. rule fires.  To undo a rule, simply  replace  STM  with  the
  1310. previously saved version.  This strategy can be expensive in
  1311. time and space if STM is large and/or many rules fire.   The
  1312. second strategy is to save only the modifications to STM, to
  1313. restore STM simply reverse the  modifications.   The  second
  1314. strategy is employed by TRC.
  1315.  
  1316.      The backtracking strategy is implemented by building  a
  1317. stack  in memory which contains all known modifications made
  1318. to STM by a rule which fires.  The only  modifications  that
  1319. the  backtracking  code  is aware of are those modifications
  1320. made by ADD and MARK statements in the  action  part  or  by
  1321. calls  to  add  and  relink  (discussed below) procedures in
  1322. embedded c-code in the action part.  Modifications  made  by
  1323. embedded c-code that do not use the add or relink procedures
  1324. will not be known to the TRC code.  It is the responsibility
  1325. of  the  knowledge engineer to insure that any modifications
  1326. that must be undone are known to TRC.
  1327.  
  1328.  
  1329.  
  1330.  
  1331.  
  1332.  
  1333.  
  1334.  
  1335.  
  1336.  
  1337.                            - 43 -
  1338.  
  1339.  
  1340.      The  backtracking  stack  is  built  in  the  following
  1341. manner;  whenever  a rule fires a new structure is placed on
  1342. the backtrack stack.  This structure contains a count of how
  1343. many  of each object are added by this rule.  Since all adds
  1344. are insertions to  the  front  of  the  list,  the  specific
  1345. objects  that  were  added  are  implicitly  known.   MARKed
  1346. objects are unlinked from their STM lists and relinked  into
  1347. the  backtrack  structure  along with an indication of where
  1348. they were in the STM list.   STM  can  now  be  restored  by
  1349. relinking  the  MARKed  objects into their previous position
  1350. and deleting objects that were added to the front of the STM
  1351. lists.  An example follows:
  1352.  
  1353. INPUT:
  1354.      %%
  1355.      A
  1356.      B (B1:INT
  1357.         B2:INT
  1358.         B3:INT)
  1359.      %%
  1360.      %%
  1361.      BACKTRACK
  1362.      R1:
  1363.           (B.B1 == B.B2)
  1364.           (B.B1 != B.B2)
  1365.           =>
  1366.           MARK B B
  1367.           ;
  1368.      R2:
  1369.           (B.B1 != 7)
  1370.           (^B FIRST
  1371.             B.B1 == 7)
  1372.           (B.B2 <= FIRST.B3)
  1373.           =>
  1374.           MARK FIRST
  1375.           ;
  1376.      %%
  1377.  
  1378. OUTPUT:
  1379.      struct back_track_stack {
  1380.           int Add_A;
  1381.           int mark_A;
  1382.           int Add_B;
  1383.           struct B_struct *mark_B;
  1384.           int next_rule;
  1385.           struct back_track_stack *next;
  1386.      } *backtrack;
  1387.  
  1388.      insert_backtrack(rule)
  1389.      int rule;
  1390.      {
  1391.          struct back_track_stack *temp;
  1392.  
  1393.          temp = (struct back_track_stack *)
  1394.  
  1395.  
  1396.  
  1397.  
  1398.  
  1399.  
  1400.  
  1401.  
  1402.  
  1403.                            - 44 -
  1404.  
  1405.  
  1406.          myalloc(sizeof(struct back_track_stack));
  1407.          temp->next_rule = rule;
  1408.          temp->Add_A = 0;
  1409.          temp->mark_A = 0;
  1410.          temp->Add_B = 0;
  1411.          temp->mark_B = NULL;
  1412.          temp->next = backtrack;
  1413.          backtrack = temp;
  1414.      }
  1415.  
  1416.  
  1417.      The struct back_track_stack, pointed to by 'backtrack',
  1418. is  where  the  backtracking data is maintained.  The struct
  1419. back_track_stack contains two variables for each object that
  1420. is  defined.   The  variables  are  of  form  'Add_NAME' and
  1421. 'mark_NAME', where 'NAME' is the name of  the  object.   The
  1422. variable  of  form 'Add_name' is always an integer, it indi-
  1423. cates how many objects of the named type were added  to  STM
  1424. by  this  rule.   The  variable  of  form  'mark_NAME' is an
  1425. integer for objects that do not contain elements (and there-
  1426. fore have no associated list) and a pointer for objects that
  1427. do contain elements.  The procedure 'insert_backtrack' allo-
  1428. cates a structure, places it at the head of the list pointed
  1429. to by 'backtrack' and initializes it's variables.
  1430.  
  1431.      loop()
  1432.      {
  1433.          int i;
  1434.      Start:
  1435.      R1:
  1436.              if((token[B] >= 2) &&
  1437.                  1){
  1438.                  B_temp[1] = B_list[0];
  1439.                  if((B_list[1] = search_B_struct
  1440.               (1, B_list[1]->B2, 2, 0, 7, 0, 7)) == NULL){
  1441.                      restore();
  1442.                      goto R2;
  1443.                  }
  1444.                  B_temp[2] = B_list[0];
  1445.                  if((B_list[2] = search_B_struct
  1446.               (2, B_list[1]->B2, 5, 0, 7, 0, 7)) == NULL){
  1447.                      restore();
  1448.                      goto R2;
  1449.                  }
  1450.                  insert_backtrack(1);
  1451.                  for(i = 0; i < 2; i++)
  1452.                      relink_B_struct(1);
  1453.                  restore();
  1454.                  goto Start;
  1455.              }
  1456.      R2:
  1457.              if((token[B] >= 3) &&
  1458.                  1){
  1459.                  B_temp[1] = B_list[0];
  1460.  
  1461.  
  1462.  
  1463.  
  1464.  
  1465.  
  1466.  
  1467.  
  1468.  
  1469.                            - 45 -
  1470.  
  1471.  
  1472.                  if((B_list[1] = search_B_struct
  1473.               (1, 7, 5, 0, 7, 0, 7)) == NULL){
  1474.                      restore();
  1475.                      goto End;
  1476.                  }
  1477.                  B_temp[2] = B_list[0];
  1478.                  if((B_list[2] = search_B_struct
  1479.               (2, 7, 2, 0, 7, 0, 7)) == NULL){
  1480.                      restore();
  1481.                      goto End;
  1482.                  }
  1483.                  B_temp[3] = B_list[0];
  1484.                  if((B_list[3] = search_B_struct
  1485.               (3, 0, 7, B_list[2]->B3, 6, 0, 7)) == NULL){
  1486.                      restore();
  1487.                      goto End;
  1488.                  }
  1489.                  insert_backtrack(2);
  1490.                  relink_B_struct(2);
  1491.                  restore();
  1492.                  goto Start;
  1493.              }
  1494.      End:
  1495.              if(backtrack){
  1496.                  i = backtrack->next_rule;
  1497.                  backup();
  1498.                  switch(i){
  1499.                  case 1:
  1500.                      goto R2;
  1501.                  case 2:
  1502.                      goto End;
  1503.                  default:
  1504.                      goto End;
  1505.                  }
  1506.              }
  1507.              return(1);
  1508.      }
  1509.  
  1510.  
  1511.      Minor changes are made in the action part of each rule.
  1512. The  action  part  begins with a call to 'insert_backtrack',
  1513. which places a structure on top of the backtrack stack.  The
  1514. integer  literal  that is passed by this procedure indicates
  1515. which rule is firing.  This information is used to determine
  1516. which rule to test next when this rule is undone.
  1517.  
  1518.      Objects that are to be deleted are deleted  with  calls
  1519. to  procedures  of form 'relink_NAME_struct' where 'NAME' is
  1520. the name of the affected object.  The relink procedures  are
  1521. similar  to the free procedures, except they link the object
  1522. to the backtrack stack instead of freeing  it.   The  relink
  1523. procedures  store  a  value in the object's variable MARK to
  1524. indicate the former position of the  object  in  it's  list.
  1525. Recall  that  the  MARK variable is usually used to indicate
  1526.  
  1527.  
  1528.  
  1529.  
  1530.  
  1531.  
  1532.