home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / snobol / aisnobol / report1.doc < prev    next >
Text File  |  1987-10-18  |  224KB  |  5,677 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.                                           
  8.  
  9.                                           
  10.  
  11.                                           
  12.  
  13.                                           
  14.  
  15.                Technical Report # 47                      August, 1982
  16.  
  17.                                           
  18.  
  19.  
  20.                          ARTIFICIAL INTELLIGENCE PROGRAMMING
  21.                                      IN SNOBOL4
  22.  
  23.  
  24.                                   MICHAEL G. SHAFTO
  25.                               GUSTAVUS ADOLPHUS COLLEGE
  26.  
  27.  
  28.                                           
  29.  
  30.  
  31.           Prepared for distribution on diskette by:
  32.           
  33.                          Catspaw, Inc.
  34.                          P.O. Box 1123
  35.                          Salida, Colorado 81201 U.S.A.
  36.                          303/539-3884
  37.           
  38.           with assistance from Mark Olsen of Arizona State University and
  39.           Martin Rice of the University of Tennessee.  The cooperation of
  40.           Michael Shafto is also appreciated.
  41.  
  42.  
  43.           Preparer's Note:  The programming examples in this paper were
  44.           prepared and run under 370/Spitbol.  Some differences from
  45.           SNOBOL4+ and Macro Spitbol required coding changes for the
  46.           program files included.  Specifically, the latter two systems do
  47.           not support the DREAL datatype, and SNOBOL4+ does not support
  48.           "NUMERIC" as a second argument to the DATATYPE function.  Also,
  49.           the 16-bit integers of SNOBOL4+ were inadequate for the TEST
  50.           program, requiring conversion to real numbers in some places.
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.                                       ABSTRACT
  74.  
  75.             The SNOBOL4 programming language has not been used extensively
  76.           for artificial intelligence (AI) programming.  There are several
  77.           reasons for this.  SNOBOL4 has no built-in list processing func-
  78.           tions.  Therefore, it is widely considered to be strictly a
  79.           string processing language.  Only the most recent version of
  80.           SNOBOL (SNOBOL4) could easily accommodate list processing.  More-
  81.           over, most AI programmers have been trained in LISP programming,
  82.           while most SNOBOL4 experts have shown little interest in AI.  The
  83.           standard texts on SNOBOL4 concentrate on pattern matching, text
  84.           formatting, compiler construction, and software development.
  85.  
  86.             This paper describes SNOBOL4 from the perspective of AI pro-
  87.           gramming.  SNOBOL4 has several features which make it an excel-
  88.           lent AI language.  It is well standardized, so that programs are
  89.           highly portable.  Efficient optimizing compilers are available
  90.           (SPITBOL, FASBOL).  It supports string, list, and numerical pro-
  91.           cessing, all of which are potentially important in AI.  It also
  92.           supports recursion, associative data structures, backtrack pro-
  93.           gramming, pattern-matching, run-time code construction, and
  94.           "self-modifying" code.  Furthermore, SNOBOL4 is a good language
  95.           in which to write special-purpose compilers, and it is a good
  96.           target language for such compilers.
  97.  
  98.  
  99.  
  100.  
  101.  
  102.  
  103.  
  104.  
  105.  
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115.  
  116.  
  117.  
  118.  
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.        AI Programming in SNOBOL4       - 2 -                     August, 1982
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.                          ARTIFICIAL INTELLIGENCE PROGRAMMING
  140.                                      IN SNOBOL4
  141.  
  142.             Many computer programmers have only the most casual acquain-
  143.           tance with SNOBOL4.  A typical conception of the language seems
  144.           to be along the lines of this description (TRS-80 Microcomputer
  145.           News, 1982):
  146.  
  147.              "SNOBOL: Text manipulation language."
  148.  
  149.             For example, a computer science graduate student (an AI spe-
  150.           cialist) recently asked me, "But SNOBOL doesn't give you recur-
  151.           sion, does it?"  (All programmer-defined functions in SNOBOL4 are
  152.           recursive.)  Considering the restricted audience within computer
  153.           science itself, it is not surprising that psychologists' knowl-
  154.           edge of SNOBOL4 rarely extends beyond a chuckle at the name.
  155.  
  156.             (I will use "SNOBOL4" as a generic term covering BTL SNOBOL4,
  157.           SPITBOL, FASBOL, SITBOL, CISBOL, ICEBOL, SNOFLAKE, SNOFLEX,
  158.           ELFBOL, SLOBOL, SNOBAT, SIXBOL, and so on.  The programs
  159.           described below were all implemented in SPITBOL, Version 2.2.6,
  160.           under MTS at the University of Michigan.)
  161.  
  162.             In this paper, I want to present SNOBOL4 to the psychologist
  163.           who is interested in AI programming, and who has some programming
  164.           experience.  I assume that the reader is not a professional com-
  165.           puter scientist, but is acquainted  with AI programming in LISP,
  166.           as presented in Friedman (1974), Siklossy (1976), Winston and
  167.           Horn (1981), Bundy et al. (1978), Maurer (1973), University of
  168.           Michigan Computing Center (1976), McCarthy et al. (1963), Char-
  169.           niak et al. (1980), Schank and Riesbeck (1981), Shapiro (1979),
  170.           and Winograd (1972).
  171.  
  172.             I want to present SNOBOL4 as an AI programming language for
  173.           several reasons.  As a practical matter, the reader might have
  174.           access to a good SNOBOL4 system, but not to a good LISP system.
  175.           In fact, the LISP system at the University of Michigan is not a
  176.           top-of-the-line model, but the SNOBOL4 system is.  It was this
  177.           simple, practical consideration that provided my own initial
  178.           motive to explore AI programming in SNOBOL4.
  179.  
  180.             Beyond pragmatics, there is an aesthetic motive.  I have found
  181.           SNOBOL4 to be an interesting programming language, full of unex-
  182.           pected subtlety and expressive nuance.  I therefore suggest it to
  183.           the reader as a source of intellectual stimulation.
  184.  
  185.             As Maurer (1976, pp. ix-x) writes,
  186.  
  187.             "From a theoretical viewpoint, SNOBOL4 addresses all of the
  188.           major issues in programming languages, and provides fresh new
  189.           insights into many of them.  ... [M]uch of the programming world
  190.           is now convinced that SNOBOL4 is not just a good programming lan-
  191.           guage [but] is an important advance in the state of the art of
  192.           programming languages.
  193.  
  194.  
  195.  
  196.        AI Programming in SNOBOL4       - 3 -                     August, 1982
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204.  
  205.             "SNOBOL4 is more than a string-processing language; it is an
  206.           algebraic language, a pattern-matching language, and an associa-
  207.           tive language, as well."
  208.  
  209.             Indeed, SNOBOL4 is the Alice's Restaurant of programming
  210.           languages: You can get anything you want.
  211.  
  212.             Besides proselytizing, I intend simply to provide information.
  213.           SNOBOL4 is not a "new AI language," but rather an old, well-
  214.           established programming language with unexploited potential.  It
  215.           is impossible to anticipate how the special features of SNOBOL4
  216.           might serve the purposes of a creative AI programmer (see, for
  217.           example, Shapiro, 1979, pp. 79-85).  But more psychologists
  218.           should at least be aware of SNOBOL4 as a language worth
  219.           considering for AI applications.
  220.  
  221.             Finally, along with information comes clarification.  There
  222.           sometimes seems to be a mystique surrounding LISP and AI.  Under-
  223.           lying this mystique seems to be something like the following
  224.           argument: "AI programming is list processing.  LISP is the list
  225.           processing language.  Therefore, LISP is the AI language."
  226.  
  227.             In fact, LISP does have certain features which make it espe-
  228.           cially suited to AI programming.  By comparative programming in
  229.           LISP and SNOBOL4, we can appreciate more clearly what these spe-
  230.           cial features are.  Comparative programming may help to demystify
  231.           AI.  Problems can be separated from methods, and methods from
  232.           implementations.
  233.  
  234.             List processing is one important programming technique; but
  235.           string processing, backtrack programming, and even numerical com-
  236.           putation are also important.  Powerful programs depend on a
  237.           "combination of ingredients."
  238.  
  239.             I will argue that the list vs. string distinction, which
  240.           allegedly separates LISP and SNOBOL4, is a red herring.  The most
  241.           important language characteristics for AI programming are those
  242.           characteristics which are SHARED by LISP and SNOBOL4, and which
  243.           distinguish them from other programming languages.
  244.  
  245.  
  246.                         STRING PROCESSING AND LIST PROCESSING
  247.  
  248.  
  249.           Nonnumerical Computing
  250.           ----------------------
  251.  
  252.             In nonnumerical computing the computer is treated as a symbol-
  253.           manipulator rather than a number-cruncher.  Non-numerical pro-
  254.           gramming problems often originate within computer science itself.
  255.           The programming techniques, which involve string and list pro-
  256.           cessing, seem remote from those needed in science and engineer-
  257.           ing.
  258.  
  259.  
  260.  
  261.  
  262.        AI Programming in SNOBOL4       - 4 -                     August, 1982
  263.  
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271.             The main topics of nonnumerical computing include the follow-
  272.           ing: graph theory; algebra of strings; construction and searching
  273.           of trees and other list structures; scatter storage techniques;
  274.           database management; sorting; grammatical analysis and compiler
  275.           construction; symbolic mathematics; and artificial intelligence.
  276.           The specialist in nonnumerical computing needs a strong back-
  277.           ground in logic and algebra, but may know little or nothing about
  278.           physics, chemistry or engineering.
  279.  
  280.             As might be expected, the major nonnumerical programming lan-
  281.           guages (LISP and SNOBOL4) are quite different from the languages
  282.           familiar to scientists and engineers (FORTRAN, ALGOL, BASIC,
  283.           APL).  It is not obvious, however, why LISP and SNOBOL4 are often
  284.           considered quite different from each other.
  285.  
  286.             Folklore has it that SNOBOL4 is for strings, and LISP is for
  287.           lists; and that this constitutes a significant difference between
  288.           the two languages.  In fact, however, the specialization for
  289.           strings or for lists is only a matter of degree.  LISP can pro-
  290.           cess strings and SNOBOL4 can process lists.  Too much emphasis on
  291.           the string-list distinction has made LISP and SNOBOL4 appear more
  292.           dissimilar than they really are.
  293.  
  294.  
  295.           Strings and Lists
  296.           -----------------
  297.  
  298.             STRINGS.  A string is an ordered set of zero or more charac-
  299.           ters, each character typically occupying one byte of the com-
  300.           puter's memory.  A particular string can be defined by two inte-
  301.           gers, its start address and its length (measured in characters).
  302.  
  303.             LISTS.  There are many ways to define lists and list-like
  304.           structures.  I will follow LISP conventions, according to which
  305.           lists are linked together by CONS cells.
  306.  
  307.             (Since I am discussing list processing in general, and not LISP
  308.           in particular, I will use the word "string" where "atom" would be
  309.           more accurate in a discussion of LISP.  Although this oversimpli-
  310.           fies both LISP and SNOBOL4, I don't think that it is misleading
  311.           in the present context.  The reader, however, should not take
  312.           this section to be descriptive of any actual implementation of
  313.           LISP or SNOBOL4.)
  314.  
  315.             A CONS cell consists of two integers which are pointers to (or
  316.           addresses of) other CONS cells or strings.  The first ("left
  317.           hand") pointer, called the CAR in LISP, points to the first ele-
  318.           ment of the list (the head), which is usually a string.  The sec-
  319.           ond ("right hand") pointer, called the CDR in LISP, points to the
  320.           rest of the list (the tail), which is usually another list.
  321.  
  322.             In the simplest case, the CAR (head) of a list is a string, and
  323.           the CDR (tail) is another list.  Obviously, this can't go on for-
  324.           ever because we come to the end of the list.  The end of a list
  325.  
  326.  
  327.  
  328.        AI Programming in SNOBOL4       - 5 -                     August, 1982
  329.  
  330.  
  331.  
  332.  
  333.  
  334.  
  335.  
  336.  
  337.           is conventionally marked by the list NIL, which is the list of
  338.           zero elements.  (Again, it is beside the point whether in some
  339.           particular version of LISP NIL is an atom, a list, both, or
  340.           neither.)
  341.  
  342.             The characters of a string are located in adjacent, memory
  343.           locations.  If one character is at memory location n, the next
  344.           character is at location n + 1.  In a list, however, there is no
  345.           natural relation between the addresses of successive elements.
  346.           The links between successive elements must be stored explicitly.
  347.           Thus, there is more to a list than meets the eye: The pointers
  348.           that organize a list -- that enable the program to find the suc-
  349.           cessive elements in the correct order -- do not appear in the
  350.           printed representation of the list.
  351.  
  352.             In the course of explaining list processing, it is sometimes
  353.           necessary to refer to the complete set of information which is
  354.           stored in the computer's memory, pointers and all.  Suppose we
  355.           consider just two datatypes, "string" and "cons."  Let each
  356.           string identifier have two pointers, labeled "name>" and
  357.           "value>."  The name> pointer is the address of the string of
  358.           characters which would be used to print the name of the identi-
  359.           fier.  (This is called the "print-name" in LISP.)  The value>
  360.           pointer is the address of the current value of the identifier.
  361.  
  362.             Suppose each cons-cell has two pointers, labeled "car>" and
  363.           "cdr>."  Now any identifier can be represented by its datatype
  364.           (string or cons) and the items pointed to by each of its point-
  365.           ers.  For example, if the identifier L has the value FISH, we can
  366.           show this as
  367.  
  368.                string:[name> L, value> string:[name> FISH, value>
  369.                    string:[*].
  370.  
  371.             The notation string:[*] will stand for the null string, i.e.,
  372.           the string of length zero.  Similarly, cons:[x] will represent
  373.           the null list, i.e., the list of zero elements.  I will use the
  374.           period (.) in this context as a right-hand super-bracket, closing
  375.           all the pending left-hand brackets.
  376.  
  377.             A simple, standard list structure would be stored as
  378.  
  379.                string:[name> L, value> cons:[car> string:[name> CAT,
  380.                   value> string:[*]], cdr> cons:[car> string:[name> DOG,
  381.                      value> string:[*]], cdr> cons:[*].
  382.  
  383.             This list would be created in LISP by the expression
  384.  
  385.                (SETQ L '(CAT DOG)).
  386.  
  387.             The power and flexibility of list processing comes from the
  388.           simple idea of connecting strings by means of labeled pointers.
  389.           The convenience of programming in a list oriented language
  390.           depends upon creating expressions for lists, in which the labeled
  391.  
  392.  
  393.  
  394.        AI Programming in SNOBOL4       - 6 -                     August, 1982
  395.  
  396.  
  397.  
  398.  
  399.  
  400.  
  401.  
  402.  
  403.           pointers do not appear explicitly.  Notice the compactness of the
  404.           LISP expression above in comparison with the more explicit ver-
  405.           sion of the created list.
  406.  
  407.             LISTS vs. STRINGS. A list has the same structure as a string
  408.           but on a "higher level."  A string is an ordered set of charac-
  409.           ters, and a list is an ordered set of strings.
  410.  
  411.             List structures, however, can be extended to tree structures
  412.           and even to arbitrary network structures.  Then the list-string
  413.           analogy breaks down.  The greater flexibility of lists comes from
  414.           the fact that their elements are not single characters, but
  415.           rather CONS cells whose pointers can point to other lists.  Hence
  416.           lists can be embedded in other lists indefinitely: Any single
  417.           element of a list can be an arbitrarily complex list structure.
  418.  
  419.             More importantly, small changes in a complex list structure
  420.           have only local impact.  If the same complex structure were en-
  421.           coded as a string, then almost any change would require repacking
  422.           the string, a costly and inefficient process.  Thus, lists allow
  423.           flexible encoding of complex data structures because lists are
  424.           organized into meaningful subunits and can, therefore, be modi-
  425.           fied efficiently.
  426.  
  427.  
  428.           Typical Operations on Strings and Lists
  429.           ---------------------------------------
  430.  
  431.             The built-in functions of SNOBOL4 are specialized for string
  432.           processing.  Those of LISP are specialized for list processing.
  433.           String processing functions can read or write strings, build new
  434.           strings, decompose old strings, or search existing strings for
  435.           target characters or substrings.  List processing functions per-
  436.           form the analogous operations on lists.
  437.  
  438.             STRING PROCESSING FUNCTIONS.  The following (typical) string
  439.           processing functions are among the commonly used built-in func-
  440.           tions in SNOBOL4.  They are used here simply as examples of typi-
  441.           cal string processing functions.  The SNOBOL4 language will be
  442.           described in more detail in a later section of the paper.
  443.  
  444.             1. RECORD = INPUT
  445.  
  446.                The next logical record is read from the input file.  It is
  447.                stored in memory as a string of adjacent characters.  Its
  448.                address becomes the value> pointer of the identifier RECORD.
  449.                (By "the identifier RECORD," I mean the identifier named
  450.                RECORD, i.e., the identifier whose name> pointer is the
  451.                address of the string RECORD.)
  452.  
  453.             2. OUTPUT = RECORD
  454.  
  455.                The string whose address is in the value> field of RECORD is
  456.                written to the output file.
  457.  
  458.  
  459.  
  460.        AI Programming in SNOBOL4       - 7 -                     August, 1982
  461.  
  462.  
  463.  
  464.  
  465.  
  466.  
  467.  
  468.  
  469.             3. NEW = OLD1 OLD2
  470.  
  471.                A new string is constructed by concatenating the strings
  472.                accessed via the value> pointers of OLD1 and OLD2.  The
  473.                address of the newly formed string becomes the value>
  474.                pointer of the identifier NEW.
  475.  
  476.             4. T = SUBSTR(FS,5,10)
  477.  
  478.                The string whose address is the value> pointer of FS is
  479.                located in memory.  A new string is defined which has length
  480.                10 and which starts with the fifth character of (the value
  481.                of) FS.  The address of this substring becomes the value>
  482.                pointer of the identifier T.  The value> pointer of PS is
  483.                unchanged.
  484.  
  485.             5. XYZ '  ' = ' '
  486.  
  487.                The string addressed by the value> pointer of XYZ is
  488.                located.  It is scanned "from left to right" for a pair of
  489.                adjacent blanks.  The first such pair is replaced by a sin-
  490.                gle blank.  The value> field of XYZ is changed so as to
  491.                point to the altered string.  If no pair of blanks is found,
  492.                then the statement fails and the value of XYZ is not
  493.                changed.
  494.  
  495.             6. R LEN(10) $ PART1 REM $ PART2 = ''
  496.  
  497.                The string addressed by the value> pointer of R is located.
  498.                The substring consisting of the first 10 characters of R
  499.                becomes the value of PART1; the rest of R becomes the value
  500.                of PART2.  Then the value of R becomes the null string (the
  501.                string of length 0).  If R has fewer than 10 characters,
  502.                however, the statement fails and the values of PART1, PART2,
  503.                and R remain unchanged.
  504.  
  505.             LIST PROCESSING FUNCTIONS.  Just as the structure of lists is
  506.           analogous to that of strings, so operations on lists are similar
  507.           to operations on strings.  We can concatenate two strings to get
  508.           a new string, and append one list to another to get a new list.
  509.           We can break a string down into substrings or individual charac-
  510.           ters, and break a list down into sublists or individual elements.
  511.           We can search a string for a target character or substring, and
  512.           search a list for a target element or sublist.
  513.  
  514.             The following typical LISP commands illustrate some common list
  515.           I/O, composition, decomposition, and search functions:
  516.  
  517.             1. (SETQ RECORD (READ))
  518.  
  519.                The next complete list expression is read from the input
  520.                file.  (It may be a single atom or a complex list.)  It is
  521.                converted from its string representation to LISP's internal
  522.                list representation, and the address of this list becomes
  523.  
  524.  
  525.  
  526.        AI Programming in SNOBOL4       - 8 -                     August, 1982
  527.  
  528.  
  529.  
  530.  
  531.  
  532.  
  533.  
  534.  
  535.                the value> pointer of the identifier named RECORD.  This
  536.                means that the value> pointer of RECORD is the address of
  537.                the first top-level cons-cell of the list.
  538.  
  539.             2. (PRINT RECORD)
  540.  
  541.                The list addressed by the value> pointer of RECORD is con-
  542.                verted to a string, which is written to the output file.
  543.                Note that READing involves string-to-list conversion, and
  544.                PRINTing involves list-to-string conversion.  Lack of aware-
  545.                ness of these conversions, which are handled automatically
  546.                in LISP's top-level READ-EVAL-PRINT loop, can lead to the
  547.                mistaken idea that lists are really strings.  This is incor-
  548.                rect: Lists are not represented internally as strings.  If
  549.                they were (as mentioned above) list processing would be much
  550.                less efficient.
  551.  
  552.             3. (SETQ NEW (APPEND OLD1 OLD2))
  553.  
  554.                The value> pointers of OLD1 and OLD2 both point to lists
  555.                stored in memory.  A new list, consisting of the top-level
  556.                elements of OLD1 followed by the top-level elements of OLD2,
  557.                is constructed somewhere in memory.  A pointer to this new
  558.                list is stored in the value> field of the identifier named
  559.                NEW.
  560.  
  561.             4. (SETQ HEAD (CAR L))
  562.  
  563.                The value> pointer of L points to a CONS cell which is the
  564.                first top-level element of an existing list.  The car>
  565.                pointer of this cons-cell is the address of a string or
  566.                list.  This address is copied into the value> field of the
  567.                identifier named HEAD.
  568.  
  569.             5. (SETQ SUBLIST (MEMBER TARGET L))
  570.  
  571.                The value> pointers of L and TARGET point to existing lists.
  572.                (The value of TARGET may be a string.)  The top-level ele-
  573.                ments of L are examined sequentially.  If one is found which
  574.                is equal to TARGET, then the tail of L starting with that
  575.                element becomes the value of SUBLIST.
  576.  
  577.             Suppose the following data structures are in memory:
  578.  
  579.           string:[name> TARGET, value>
  580.              string:[name> DOG, value> string:[*].
  581.  
  582.           string:[name> L, value>
  583.              cons:[car> string:[name> SHARK, value> string:[*]], cdr>
  584.                cons:[car> string:[name> FISH, value> string:[*]], cdr>
  585.           ***    cons:[car> string:[name> DOG, value> string:[*]],
  586.                    cdr> cons:[car> string:[name> BAD,
  587.                       value> string[*]], cdr> cons:[*].
  588.  
  589.  
  590.  
  591.  
  592.        AI Programming in SNOBOL4       - 9 -                     August, 1982
  593.  
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.  
  601.             In this context, (MEMBER TARGET L) will return the address of
  602.           the first cons-cell in the list starting at the flagged (***)
  603.           line.
  604.  
  605.             6. (SETQ REVISED (SUBST L NOW NEW))
  606.  
  607.                Every sublist of L that matches the value of NOW is replaced
  608.                by the value of NEW.  The address of the revised list is
  609.                stored in the value> field of REVISED, but the value> field
  610.                of L remains unchanged.
  611.  
  612.             These examples are intended simply to illustrate some typical
  613.           list processing functions.  Note that with lists, as with
  614.           strings, most processing consists of building, decomposing, or
  615.           searching.
  616.  
  617.  
  618.                                 THE SNOBOL4 LANGUAGE
  619.  
  620.             The following description of SNOBOL4 is intended to provide a
  621.           quick overview of the language.  It is insufficient as a program-
  622.           mer's guide, but I hope it will serve as adequate background for
  623.           the rest of the paper.
  624.  
  625.  
  626.           Datatypes
  627.           ---------
  628.  
  629.             SNOBOL4 supports a variety of datatypes, including strings,
  630.           integers, floating-point numbers, arrays, tables, expressions,
  631.           patterns, code, and programmer-defined datatypes (Griswold, 1975,
  632.           chapter 3).  Conversion between datatypes, where required, is
  633.           usually automatic.  A concise description of SNOBOL4's structured
  634.           datatypes is given in Lewis and Smith (1976, pp. 248-253).
  635.  
  636.             ARRAYS may be multidimensional, and subscript bounds may be
  637.           negative.  The TABLE datatype is a one-dimensional array
  638.           "subscripted" by items of any datatype (usually by strings).
  639.           NAMES are similar to strings, though there are some technical
  640.           differences.  PATTERNS, EXPRESSIONS, and CODE are procedural, in
  641.           the sense that their evaluation normally involves more processing
  642.           than simply accessing a value.
  643.  
  644.  
  645.           Syntax and Statement Types
  646.           --------------------------
  647.  
  648.             SNOBOL4 has a simple, statement-oriented syntax.  It is a non-
  649.           interactive, compiled language.  The SPITBOL and FASBOL compilers
  650.           produce efficient, optimized code while still supporting the full
  651.           range of features available in the slower, interpreted version of
  652.           the language.
  653.  
  654.  
  655.  
  656.  
  657.  
  658.        AI Programming in SNOBOL4       - 10 -                    August, 1982
  659.  
  660.  
  661.  
  662.  
  663.  
  664.  
  665.  
  666.  
  667.             The advantages of compilation are somewhat undermined by the
  668.           fact that subroutines cannot be compiled separately and indepen-
  669.           dently.  According to Maurer (1976, pp. 90-92), this flaw is cor-
  670.           rected in FASBOL.  As in LISP, structured programming devices are
  671.           limited to the programmer-defined function, the statement label,
  672.           and the goto command.  Also as in LISP, there are modifications
  673.           of SNOBOL4 which encourage structured programming (University of
  674.           Michigan Computing Center, 1975 Sommerville, 1979).
  675.  
  676.             The principal landmarks of the SNOBOL4 statement are the first
  677.           column, the equals (=) sign, and the colon (:). Column 1 is used
  678.           for special control characters which indicate commands to the
  679.           compiler, statement continuation, comments, or statement labels.
  680.           If a statement has a label, then the first character of the label
  681.           must begin in column 1.  A statement may be continued over sev-
  682.           eral input lines by putting a plus (+) in the first column of ev-
  683.           ery line after the first.  Several statements may be placed on a
  684.           single line by using the semicolon (;) as a statement terminator.
  685.  
  686.             The equals (=) sign separates the two major parts of the state-
  687.           ment, the "pattern matching" part (left-hand side) and the
  688.           "object" (right-hand side).  The pattern matching part may be
  689.           further subdivided into the "subject" and the "pattern."
  690.  
  691.             The colon (:), if present, delimits the "goto field," which is
  692.           used to control conditional and unconditional branching.
  693.  
  694.             Thus, the following prototype summarizes the syntax of the
  695.           SNOBOL4 statement:
  696.  
  697.                <label> <subject> <pattern> = <object>  :<goto field>
  698.  
  699.             Since any statement may have a label and/or a goto field, the
  700.           important types of statements consist of variations on
  701.  
  702.                        <subject> <pattern> = <object>
  703.  
  704.             The full (subject-pattern-object) form is called a "replacement
  705.           statement."  The part of the "subject" string which is matched by
  706.           the "pattern" gets replaced by the "object" string.  If the
  707.           object is null (but the = sign is still present), the matched
  708.           part of the subject is simply deleted.
  709.  
  710.             The subject-object statement, without a pattern, is an ordinary
  711.           "assignment statement."  It has the same purpose as the assign-
  712.           ment statement in other languages, viz., to change the value of a
  713.           variable.  Since the value of a variable can be an object of any
  714.           datatype, assignment statements can be used to create data struc-
  715.           tures such as arrays and tables.
  716.  
  717.             The subject-only statement can consist of a single function
  718.           call or several function calls enclosed in parentheses.  In the
  719.           latter case, the function calls will be executed from left to
  720.           right until one of them fails.  It is also possible to have sev-
  721.  
  722.  
  723.  
  724.        AI Programming in SNOBOL4       - 11 -                    August, 1982
  725.  
  726.  
  727.  
  728.  
  729.  
  730.  
  731.  
  732.  
  733.           eral function calls in one statement without enclosing them in
  734.           parentheses.  This is stylistically questionable, since the val-
  735.           ues, if any, returned by the second through the last function
  736.           calls will be concatenated to form a "pattern," which will then
  737.           be applied to the "subject" returned by the first function call.
  738.           In this case, it is possible for all the function calls to suc-
  739.           ceed and yet to have the statement fail because of a spurious and
  740.           unintended pattern matching process.
  741.  
  742.             The subject-pattern statement, without an equals sign, is a
  743.           "pattern matching" statement.  It can control conditional branch-
  744.           ing, depending upon whether or not the pattern "succeeds" in
  745.           matching part of the subject.  Pattern matching statements can
  746.           also control conditional assignment of values to variables.
  747.  
  748.  
  749.           Pattern Matching
  750.           ----------------
  751.  
  752.             Gimpel (1976, p. 11) describes the pattern matching capabili-
  753.           ties of SNOBOL4 as "... so rich as to amount to a language within
  754.           a language."  MTS users can get a sense of SNOBOL4 pattern match-
  755.           ing by looking through the relevant documentation of the MTS sys-
  756.           tem editor (University of Michigan Computing Center, 1979, pp.
  757.           334-345).
  758.  
  759.             As the most distinctive feature of SNOBOL4, pattern matching
  760.           certainly deserves more attention than I can give it here.  The
  761.           reader is urged to consult Griswold and Griswold (1973, chapter
  762.           4), Griswold, Poage, and Polonsky (1971, chapter 2), and Gimpel
  763.           (1976, chapters 6, 7, 8, and 18).
  764.  
  765.             Pattern matching is somewhat related to the theory of languages
  766.           and automata, as described, for example, in Denning, Dennis, and
  767.           Qualitz (1978) or Hopcroft and Ullman (1979).  The pattern-build-
  768.           ing functions and the syntax of SNOBOL4 patterns are especially
  769.           adaptable to the implementation of parsers based on syntax graphs
  770.           (compare Wirth, 1976, chapter 5, with Griswold et al., 1971,
  771.           chapter 3).
  772.  
  773.             A SNOBOL4 pattern can be said to describe a set of strings, in
  774.           the sense that a phrase-structure grammar (for example) describes
  775.           a set of strings.  The pattern
  776.  
  777.                        POS(0) "*"
  778.  
  779.           describes the set of strings which have an asterisk as their
  780.           first character.  The pattern
  781.  
  782.                        RPOS(2) ( LEN(1) $ L ) *L
  783.  
  784.           describes the set of strings which end with two identical charac-
  785.           ters.
  786.  
  787.  
  788.  
  789.  
  790.        AI Programming in SNOBOL4       - 12 -                    August, 1982
  791.  
  792.  
  793.  
  794.  
  795.  
  796.  
  797.  
  798.  
  799.             Complex patterns are usually defined by a sequence of assign-
  800.           ment statements in which larger patterns are built up from sim-
  801.           pler components.  Besides making the structure of the pattern
  802.           clearer, this method of definition makes it easier to save sub-
  803.           strings by conditional or immediate value assignment (explained
  804.           below) during the pattern matching process.  When a complex pat-
  805.           tern definition is examined, reading the statements in reverse
  806.           order, it often resembles a sequence of rewrite rules of the kind
  807.           used in writing formal grammars.
  808.  
  809.             After a pattern is defined and assigned to a variable, it can
  810.           be used in a pattern-matching or replacement statement.  The pat-
  811.           tern matching process is best thought of as a special kind of
  812.           function call:  The pattern is the function and the subject
  813.           string is the argument.
  814.  
  815.             In terms of automata theory: The SNOBOL4 source statements
  816.           which define the pattern give a "grammatical" description of the
  817.           set of strings which that pattern will match.  The SNOBOL4 com-
  818.           piler converts this description into a program (machine, automa-
  819.           ton) which will "accept" just the set of strings described by the
  820.           "grammar."
  821.  
  822.             This interpretation of SNOBOL4 pattern matching is not seri-
  823.           ously misleading, but neither is it complete.  In addition to
  824.           accepting or failing to accept its subject string, a pattern may
  825.           have a number of side effects, most notably assignments and func-
  826.           tion calls.
  827.  
  828.             Pattern matching involves scanning the subject string by moving
  829.           a pointer called the "cursor."  The cursor is initially at posi-
  830.           tion 0, immediately to the left of the first character of the
  831.           subject.  Cursor movement is controlled by various pattern ele-
  832.           ments.  The substring to the immediate right of the cursor can be
  833.           tested to determine whether it matches a literal string or
  834.           whether it is one of a specified set of alternatives.  Also, the
  835.           position of the cursor itself can be tested.  The pattern ele-
  836.           ments POS(N) and RPOS(N) succeed if the cursor is N characters
  837.           from the left or right end of the subject string, respectively.
  838.  
  839.             As these operations -- cursor movement, substring testing, and
  840.           cursor position testing -- are executed, assignments and expres-
  841.           sion evaluations can occur as side effects.  Immediate value
  842.           assignment ($) causes the substring matched by some subpattern to
  843.           be saved as the value of a variable.  Conditional value assign-
  844.           ment (.) is similar, but takes effect only if the entire pattern
  845.           matching process succeeds.  Cursor position assignment (@) saves
  846.           the current cursor position (the number of characters to the left
  847.           of the cursor) as the value of a variable.  Any deferred expres-
  848.           sion (an expression preceded by an asterisk) will be evaluated
  849.           whenever it is encountered during the pattern matching process.
  850.  
  851.  
  852.  
  853.  
  854.  
  855.  
  856.        AI Programming in SNOBOL4       - 13 -                    August, 1982
  857.  
  858.  
  859.  
  860.  
  861.  
  862.  
  863.  
  864.  
  865.             For example, the following pattern will set X equal to the
  866.           three-character substring immediately following the first occur-
  867.           rence of 'DOG' in the subject string.
  868.  
  869.                        'DOG'   ( LEN(3) $ X )
  870.  
  871.             Here are the results of applying this pattern to each of sev-
  872.           eral subject strings.
  873.  
  874.                        subject        result
  875.                        -------        ------
  876.  
  877.                        'DOGMA'        failure
  878.                                       (There are fewer than three
  879.                                       characters after 'DOG'.)
  880.  
  881.                        'DOGMAN'       success, X = 'MAN'
  882.  
  883.                        'ADOGOODER'    success, X = 'OOD'
  884.  
  885.             The following pattern assigns values to P and Q such that the
  886.           first occurrence of 'DOG' in the subject string occurs at loca-
  887.           tions P+1 through Q.
  888.  
  889.                        @P 'DOG' @Q
  890.  
  891.             Here are the results of applying this pattern to each of sev-
  892.           eral subject strings.
  893.  
  894.                        subject        result
  895.                        -------        ------
  896.  
  897.                        'DOGMA'        success, P=0, Q=3
  898.  
  899.                        'ADOGOODER'    success, P=1, Q=4
  900.  
  901.                        'GODOT'        failure
  902.                                       ('DOG' does not occur in the
  903.                                       subject string.)
  904.  
  905.             The following pattern will evaluate ENEMY('CAT') if
  906.           ENEMIES_LIST = 1 and the subject string contains 'DOG':
  907.  
  908.                'DOG' *EQ(ENEMIES_LIST,1) *ENEMY('CAT')
  909.  
  910.             The use of 'DOG' in the examples above shows how a literal
  911.           string can be used as a pattern element.  A string- or pattern-
  912.           valued variable, function call, expression, or deferred expres-
  913.           sion could be used similarly.  Much of the power of pattern
  914.           matching is due to SNOBOL4's built-in pattern elements and pat-
  915.           tern-valued functions.
  916.  
  917.             The built-in pattern elements include the following: ARB
  918.           matches any substring.  BAL matches any substring which is bal-
  919.  
  920.  
  921.  
  922.        AI Programming in SNOBOL4       - 14 -                    August, 1982
  923.  
  924.  
  925.  
  926.  
  927.  
  928.  
  929.  
  930.  
  931.           anced with respect to parentheses.  FAIL causes backtracking.
  932.           FENCE blocks backtracking.  REM matches the substring from the
  933.           current cursor position to the end of the subject.
  934.  
  935.             The built-in pattern-valued functions include the following:
  936.           ANY(S) matches any single character included in the string S.
  937.           NOTANY(S) matches any character not in S.  BREAK(S) matches the
  938.           substring up to the first occurrence of any of the "break" char-
  939.           acters in S.  SPAN(S) matches up to the first occurrence of a
  940.           character not in S.  LEN(N) matches any substring of length N.
  941.           TAB(N) matches the substring from the current cursor position up
  942.           to cursor position N.  ARBNO(P) matches any sequence of consecu-
  943.           tive substrings, each of which is matched by the pattern P.
  944.  
  945.             Pattern elements -- built-in elements, built-in functions,
  946.           string constants, string variables, deferred expressions, and so
  947.           on -- can be combined by "concatenation" and "alternation."  Thus
  948.           far, only concatenation has been illustrated.  Two pattern
  949.           elements are concatenated simply by placing them side by side
  950.           with one or more intervening blanks.  Concatenated pattern ele-
  951.           ments must match adjacent substrings.
  952.  
  953.             The alternation operator (|) separates pattern elements which
  954.           are tried successively.  If the pattern matching process fails at
  955.           some later point, the process backtracks and tries the next al-
  956.           ternative.  If all the alternatives have been tried, then the al-
  957.           ternation expression fails.
  958.  
  959.             Here are three patterns, each of which matches the same set of
  960.           strings, namely, CAR, CDR, CAAR, CADR, etc. up to four A's and
  961.           D's (i.e., the names of the standard LISP CAR-CDR compounds).
  962.  
  963.                        'C' ANY('AD')
  964.                +         ('R' | (ANY('AD')
  965.                +           ('R' | (ANY('AD')
  966.                +             ('R' | ANY('AD') 'R')) )) ))
  967.  
  968.                        'C' ('A' | 'D')
  969.                +         @P ARBNO('A' | 'D') @Q 'R'
  970.                +           *LE(Q - P, 3)
  971.  
  972.                        'C' *P SPAN('AD') @Q 'R' *LE(Q - P, 4)
  973.  
  974.             The side effects of pattern matching often involve conditional
  975.           assignment and conditional branching.  The conditions here are
  976.           the "success" or "failure" of the pattern matching process.
  977.           "Predicates" in SNOBOL4 also succeed or fail (rather than return-
  978.           ing T or NIL, 1 or 0, .TRUE. or .FALSE., etc.).  For example,
  979.           EQ(5,5) succeeds, and GT(1,7) fails.  Programmer-defined func-
  980.           tions succeed by executing :(RETURN) and fail by executing
  981.           :(FRETURN).  Therefore, the success or failure of pattern match-
  982.           ing can be controlled by evaluation of expressions as well as by
  983.           testing substrings. Both methods are used in the CAR/CDR examples
  984.           above.
  985.  
  986.  
  987.  
  988.        AI Programming in SNOBOL4       - 15 -                    August, 1982
  989.  
  990.  
  991.  
  992.  
  993.  
  994.  
  995.  
  996.  
  997.             Success and failure control "assignment" in that, if any part
  998.           of an assignment or replacement statement fails, then the entire
  999.           statement fails, and no values are changed.  Success and failure
  1000.           control "branching" through the selection of "S" or "F" clauses
  1001.           in the goto field.  The "S" branch is taken if the statement suc-
  1002.           ceeds; the "F" branch is taken if it fails.
  1003.  
  1004.             The following program fragment shows a typical SNOBOL4 loop.
  1005.           It illustrates the use of a predicate function and conditional
  1006.           branching.
  1007.  
  1008.                        SUM = 0
  1009.                        N = 10
  1010.                        I = 0
  1011.                SUM.LOOP
  1012.                        I = LT(I,N) I + 1        :F(SUM.LOOP.END)
  1013.                        SUM = SUM + I            :(SUM.LOOP)
  1014.                SUM.LOOP.END
  1015.  
  1016.             The interesting statement here is the one at SUM.LOOP.  This
  1017.           statement tests the loop index I, terminates the loop if N itera-
  1018.           tions have been completed, else increments I and continues.  Syn-
  1019.           tactically, this is an assignment statement.  The right-hand side
  1020.           consists of a binary expression: a function-call, LT(I,N); a con-
  1021.           catenation operator, blank; and a binary arithmetic expression, I
  1022.           + 1.  LT is a built-in predicate function.  If I < N, it suc-
  1023.           ceeds, returning the null string, which is concatenated with the
  1024.           value of I + 1, yielding simply the value of I + 1.  Hence, I is
  1025.           incremented and the entire statement succeeds.  There is no "S"
  1026.           clause in the goto field, so control passes to the next statement
  1027.           in sequence.
  1028.  
  1029.             If I >= N, on the other hand, then LT(I,N) fails, the value of
  1030.           I + 1 is never computed, the whole statement fails, the value of
  1031.           I is unchanged, and the "F" branch is selected.  Control trans-
  1032.           fers to the statement labeled SUM.LOOP.END, and execution contin-
  1033.           ues from there.
  1034.  
  1035.             Patterns may also be used to "drive" programs.  Sometimes side
  1036.           effects become the main purpose of the pattern, evoking vague
  1037.           images of DNA and RNA, or perhaps punched paper tape.
  1038.  
  1039.             As one fairly straightforward example of this technique, con-
  1040.           sider how pattern matching can be used to implement the evalua-
  1041.           tion of arbitrary Boolean expressions. The atomic units of these
  1042.           expressions will be SNOBOL4 predicate functions (possibly pro-
  1043.           grammer-defined).  We can use pattern concatenation for "and",
  1044.           pattern alternation for "or," and the standard negation operator
  1045.           (~) for "not."
  1046.  
  1047.             The trick in this case is to use the null string (or any dummy
  1048.           string) as the subject, simply as a syntactic place-holder.  No
  1049.           "real" pattern matching occurs, yet the pattern gets evaluated,
  1050.  
  1051.  
  1052.  
  1053.  
  1054.        AI Programming in SNOBOL4       - 16 -                    August, 1982
  1055.  
  1056.  
  1057.  
  1058.  
  1059.  
  1060.  
  1061.  
  1062.  
  1063.           and it succeeds or fails depending upon whether the corresponding
  1064.           Boolean expression is true or false.
  1065.  
  1066.             For example, consider the LISP expression
  1067.  
  1068.                        (OR (EQ V 13)
  1069.                            (AND (LESSP V (DIFFERENCE 7 N))
  1070.                                 (ZEROP (VALUE (POTR PL (PLUS N V))))
  1071.                                 (NOT (EMPTY (OPP (POTR PL (PLUS N V))))))
  1072.                        (AND (GREATERP V (DIFFERENCE 13 N))
  1073.                             (LESSP V 13)
  1074.                             (EMPTY (POTR PL (PLUS N-13 V))))
  1075.  
  1076.             This could be translated into SNOBOL4 as follows:
  1077.  
  1078.                        DUMMY.SUBJECT
  1079.                        +     POS(0)    EQ(V,13)
  1080.                        +     | LT(V, 7 - N)
  1081.                        +       EQ(0, VALUE(POTR(PL, N + V)))
  1082.                        +       ~EMPTY(OPP(POTR(PL, N + V)))
  1083.                        +     | GT(V, 13 - N)
  1084.                        +       LT(V,13)
  1085.                        +       EMPTY(POTR(PL, N - 13 + V))
  1086.  
  1087.             This is just one example of a nonstandard use of pattern match-
  1088.           ing.  This particular use seems well motivated: SNOBOL4 has no
  1089.           built-in AND or OR functions, evaluation of Boolean expressions
  1090.           is often useful, and the resulting pattern expressions serve to
  1091.           clarify, not to obscure, what is happening.  The use of nonstan-
  1092.           dard pattern matching, however, must be carefully considered:
  1093.           "Clever" tricks may be quite costly.
  1094.  
  1095.             As Griswold (1975, p.14) remarks: "This kind of programming ...
  1096.           appeals to some programmers and repels others.  It offers the ad-
  1097.           vantages of compactness, efficiency in some situations, and in-
  1098.           tellectual challenge.  On the other hand, such programming meth-
  1099.           ods tend to be difficult, obscure, error-prone, inefficient in
  1100.           some situations, and particularly susceptible to idiosyncrasies
  1101.           [of the implementation]."
  1102.  
  1103.  
  1104.           Input and Output
  1105.           ----------------
  1106.  
  1107.             SNOBOL4 handles input and output by associating variables with
  1108.           files.  The built-in functions INPUT and OUTPUT are used for this
  1109.           purpose.  After the statements
  1110.  
  1111.                        INPUT('XIN',,,'CON:')
  1112.                        OUTPUT('YOUT',,,'CON:')
  1113.  
  1114.           have been executed, the variables XIN and YOUT are input- and
  1115.           output-associated, respectively, with the user's terminal.  This
  1116.           means that any request for assignment from XIN will cause a
  1117.  
  1118.  
  1119.  
  1120.        AI Programming in SNOBOL4       - 17 -                    August, 1982
  1121.  
  1122.  
  1123.  
  1124.  
  1125.  
  1126.  
  1127.  
  1128.  
  1129.           record to be read from the terminal, and any assignment to YOUT
  1130.           will cause the assigned value to be written to the terminal.
  1131.  
  1132.  
  1133.           Programmer-defined Function
  1134.           ---------------------------
  1135.  
  1136.             Functions are defined by calling the built-in function DEFINE.
  1137.           In SNOBOL4, the argument to DEFINE is a string which is the
  1138.           "prototype" of the function.  It specifies the name of the func-
  1139.           tion, the names of the dummy arguments, and the names of any
  1140.           local variables.  An optional second argument to DEFINE gives the
  1141.           label of the entry-point if it differs from the function name.
  1142.           Changes in the values of dummy arguments and local variables have
  1143.           no effect outside the function itself.
  1144.  
  1145.             The function body is not included in the argument to DEFINE.
  1146.           When a defined function is called, control branches to the state-
  1147.           ment whose label matches the name of the function, or the label
  1148.           specified by the second argument to DEFINE.  There must be
  1149.           exactly one such statement in the program.  Returning from a
  1150.           function is accomplished by a quasi-goto, a branch to one of the
  1151.           three reserved labels, RETURN, FRETURN, or NRETURN.
  1152.  
  1153.             As in LISP, every successful function call returns a value.
  1154.           The value returned is the value assigned to the identifier whose
  1155.           name matches the function name.  For example, a function named
  1156.           OTTO returns whatever value is assigned to the variable OTTO at
  1157.           the time the return is executed.
  1158.  
  1159.             All defined functions are recursive.  "Values" are normally
  1160.           passed to functions, but "names" may be passed by using the unary
  1161.           name operator (.) or by enclosing the name in quotes.  The tech-
  1162.           nical details of SNOBOL4 argument-passing are very similar to
  1163.           those of LISP (see Winston & Horn, 1981, 419-420.)
  1164.  
  1165.  
  1166.           Unary and Binary Operators
  1167.           --------------------------
  1168.  
  1169.             SNOBOL4 has a number of built-in functions which are invoked by
  1170.           operators.  The same characters serve as unary and as binary
  1171.           operators, and there is no connection between the two uses of a
  1172.           given character.
  1173.  
  1174.             The predefined unary operators are negation (~), interrogation
  1175.           (?), value ($), name (.), defer (*), unary plus and minus (+ and
  1176.           -), cursor position assignment (@), and keyword (&).
  1177.  
  1178.             The predefined binary operators are immediate value assignment
  1179.           ($), conditional value assignment (.), exponentiation (^, ! or
  1180.           **), the standard arithmetic operators (+, -, *, and /), pattern
  1181.           and string concatenation (blank), and pattern alternation (|).
  1182.  
  1183.  
  1184.  
  1185.  
  1186.        AI Programming in SNOBOL4       - 18 -                    August, 1982
  1187.  
  1188.  
  1189.  
  1190.  
  1191.  
  1192.  
  1193.  
  1194.  
  1195.             The following operators are not predefined: unary !, %, /, #,
  1196.           and |; binary ~, ?, %, #, @, and &. These operators can be
  1197.           defined by the programmer, by using the OPSYN function.  OPSYN
  1198.           makes an undefined operator or function name synonymous with a
  1199.           defined function.
  1200.  
  1201.             For example, in using the technique of Boolean expression eval-
  1202.           uation described above, the programmer might want to introduce
  1203.           the & operator for "and," in order to make the expressions
  1204.           clearer.  This could be accomplished by means of a function and a
  1205.           call to OPSYN:
  1206.  
  1207.                        DEFINE('AND(Pl,P2)')     :(AND_END)
  1208.                AND     AND = Pl P2              :(RETURN)
  1209.                AND_END OPSYN('&','AND',2)
  1210.  
  1211.             This redefinition would introduce one slight complication:
  1212.           Precedence of all operators, even free operators, is predefined.
  1213.           It so happens that & has lower precedence than |.  Therefore,
  1214.           parentheses would be necessary to ensure that conjunctions were
  1215.           performed before disjunctions.
  1216.  
  1217.  
  1218.           Programmer-defined Datatypes
  1219.           ----------------------------
  1220.  
  1221.             Datatypes with labeled fields are convenient for many AI appli-
  1222.           cations (Charniak et al., 1980, chapter 4; Shapiro, 1979, pp.
  1223.           142-143).  These are easily implemented in SNOBOL4 by means of
  1224.           the built-in function DATA.  The CONS datatype, for example,
  1225.           could be defined in SNOBOL4 as follows:
  1226.  
  1227.                        DATA('CONS(CAR,CDR)')
  1228.  
  1229.             After this function-call, T and NIL could be defined as
  1230.           follows:
  1231.  
  1232.                        NIL = CONS() ; T = CONS()
  1233.                        CAR(NIL) = NIL ; CDR(NIL) = NIL
  1234.                        CAR(T) = T ; CDR(T) = T
  1235.  
  1236.  
  1237.           EVAL and CODE
  1238.           -------------
  1239.  
  1240.             EVAL and CODE are built-in SNOBOL4 functions which have special
  1241.           importance for AI applications.  EVAL takes a string or expres-
  1242.           sion argument and interprets it as a SNOBOL4 expression.  It
  1243.           returns the value of the expression.  For example,
  1244.  
  1245.                        EVAL( *REVERSE('ABCDE'))
  1246.  
  1247.           returns 'EDCBA'.
  1248.  
  1249.  
  1250.  
  1251.  
  1252.        AI Programming in SNOBOL4       - 19 -                    August, 1982
  1253.  
  1254.  
  1255.  
  1256.  
  1257.  
  1258.  
  1259.  
  1260.  
  1261.             
  1262.  
  1263.             CODE takes a string argument and interprets it as a series of
  1264.           SNOBOL4 statements.  Each statement but the last must end with a
  1265.           semicolon (;).  CODE compiles the program fragment and returns
  1266.           the object code, which becomes accessible from the current pro-
  1267.           gram.  One way to execute this code would be to branch to a
  1268.           statement label it contains.  For example, the following fragment
  1269.           would print 76 and then continue execution at the statement
  1270.           labeled ZLABEL.
  1271.  
  1272.                        CODE('ALABEL SIX = 4 + 2 ; SEVEN = 9 - 2 ; '
  1273.                +          ' OUTPUT = SEVEN SIX   :(ZLABEL)'  )
  1274.                +               :(ALABEL)
  1275.                ZLABEL
  1276.  
  1277.             It is pointless to call CODE with a literal string argument, as
  1278.           in the example above.  But it can be called with an argument
  1279.           which is a string-valued variable.  The program fragment assigned
  1280.           to that variable could be an entire subroutine constructed under
  1281.           program control and dependent on input of various sorts.  The AI
  1282.           potential is obvious.  CODE also has more mundane applications.
  1283.           For example, it is instrumental to the function DEXTERN (Gimpel,
  1284.           1976), which loads and compiles external functions at run-time.
  1285.  
  1286.  
  1287.           Keywords
  1288.           --------
  1289.  
  1290.             Keywords are special variables preceded by the unary operator
  1291.           &.  They control various aspects of program execution.  For exam-
  1292.           ple, &TRIM = 1 instructs the program to trim trailing blanks off
  1293.           every input record; &DUMP = 1 means  that a dump of all variables
  1294.           and their values should be written to OUTPUT at the end of execu-
  1295.           tion; &STCOUNT is equal to the number of statements executed at
  1296.           any point during a run; &TRACE = 0 turns off all tracing; &FTRACE
  1297.           = 100 means that all defined functions should be traced on call
  1298.           and return until 100 trace messages have been printed.
  1299.  
  1300.  
  1301.           Tracing
  1302.           -------
  1303.  
  1304.             SNOBOL4 has good facilities for tracing program execution.
  1305.           Tracing can be turned on and off selectively, under program con-
  1306.           trol, by means of the TRACE and STOPTR functions and the keywords
  1307.           &TRACE and &FTRACE.
  1308.  
  1309.             The TRACE function allows the programmer to request an informa-
  1310.           tive message for every change of value for each of a specified
  1311.           subset of variables; for every call and/or every return from each
  1312.           of a specified subset of user-defined functions; for every trans-
  1313.           fer of control to any of a specified subset of statement labels;
  1314.  
  1315.  
  1316.  
  1317.  
  1318.        AI Programming in SNOBOL4       - 20 -                    August, 1982
  1319.  
  1320.  
  1321.  
  1322.  
  1323.  
  1324.  
  1325.  
  1326.  
  1327.           and/or for every change of value of any of a specified subset of
  1328.           keywords.
  1329.  
  1330.             As described above, the &FTRACE keyword allows tracing to be
  1331.           turned on for all calls to and returns from each user-defined
  1332.           function; and the &TRACE keyword can be used globally to turn on
  1333.           and off all tracing except that controlled by &FTRACE.
  1334.  
  1335.             Instead of using the default tracing messages, the programmer
  1336.           may use tracing interrupts defined by TRACE to activate any pro-
  1337.           grammer-defined function.  The following tools are available to
  1338.           help the programmer define special-purpose tracing functions:
  1339.  
  1340.           ARG(function-name,N) returns the name of the Nth argument for a
  1341.           given function.
  1342.  
  1343.           LOCAL(function-name,N) returns the name of the N local variable
  1344.           for a given function.
  1345.  
  1346.           FIELD(datatype,N) returns the name of the Nth field of a pro-
  1347.           grammer-defined datatype.
  1348.  
  1349.           DATATYPE(X) returns the datatype of the object X.
  1350.  
  1351.           &STNO is the statement number of the currently active statement.
  1352.  
  1353.           &LASTNO is the statement number of the last completed statement.
  1354.  
  1355.           &RTNTYPE is the type of return (RETURN, FRETURN, or NRETURN) from
  1356.           the last completed function.
  1357.  
  1358.  
  1359.           Features of SNOBOL4 Important for AI Programming
  1360.           ------------------------------------------------
  1361.  
  1362.             The following characteristics of SNOBOL4 are particularly
  1363.           important for AI programming: It is a flexible language because
  1364.           it is associative, that is, based on links or pointers which
  1365.           associate one data object with another.  Data structures do not
  1366.           have to be declared.  Any identifier can have a value of any
  1367.           datatype, and that datatype can change freely during program exe-
  1368.           cution.  Dynamic storage allocation and automatic "garbage col-
  1369.           lection" are transparent to the programmer.
  1370.  
  1371.             The function or subroutine is the natural unit of program
  1372.           design, and all programmer-defined functions are recursive.
  1373.           Arguments are passed to subroutines by reference, although it is
  1374.           also possible to pass by name.  Functions have some degree of
  1375.           isolation from the rest of the program, though not as much as
  1376.           FORTRAN subroutines.  LISP and SNOBOL4 have similar problems with
  1377.           respect to the scope of names, name conflicts, the need for
  1378.           unique names in certain contexts, and so on.
  1379.  
  1380.  
  1381.  
  1382.  
  1383.  
  1384.        AI Programming in SNOBOL4       - 21 -                    August, 1982
  1385.  
  1386.  
  1387.  
  1388.  
  1389.  
  1390.  
  1391.  
  1392.  
  1393.             SNOBOL4 allows expressions to be constructed, coded, and evalu-
  1394.           ated at run-time.  This means that programs can be self-modify-
  1395.           ing, and that the program/data distinction is far less rigid than
  1396.           in other programming languages.
  1397.  
  1398.             The following built-in functions are particularly useful in AI
  1399.           programming: APPLY, CODE, DATA, DATATYPE, EVAL, ITEM, OPSYN,
  1400.           PROTOTYPE, and TABLE.  The name (.), value ($), and unevaluated
  1401.           expression (*) operators are also handy, as are the free
  1402.           operators.
  1403.  
  1404.             Pattern matching also has many different uses, including simple
  1405.           string processing, mapping functions, compilation, and concept
  1406.           learning (see Shapiro, 1979).
  1407.  
  1408.  
  1409.           Programming Style
  1410.           -----------------
  1411.  
  1412.             SNOBOL4 lacks the control structures which have come to be
  1413.           expected in a programming language, structures such as
  1414.           IF...THEN...ELSE, LOOP...WHILE...UNTIL, BEGIN...END, PROCEDURE,
  1415.           CASE, and so on (Dijkstra, 1976; Gries, 1981; Wirth, 1976).  On
  1416.           the other hand, there are extensions of SNOBOL4 which permit the
  1417.           use of such control structures (Sommerville, 1979; University of
  1418.           Michigan Computing Center, 1975).
  1419.  
  1420.             Normally, however, in the hands of a novice, SNOBOL4 tends to
  1421.           encourage (or to fail to discourage) the writing of ugly, hard-
  1422.           to-read, hard-to-debug code.  In developing large programs, the
  1423.           programmer must adopt sensible conventions.  Bad practices which
  1424.           are permitted must be actively avoided.  For a detailed discus-
  1425.           sion of programming style, see Gimpel (1976, pp. 11-21).
  1426.  
  1427.  
  1428.           Comparison of SNOBOL4 with Other Languages
  1429.           ------------------------------------------
  1430.  
  1431.             LISP.  It is clear from the preceding discussion that SNOBOL4
  1432.           and LISP share many features which differentiate them from other
  1433.           programming languages.  Some features which are widely considered
  1434.           unique to LISP turn out to be shared by SNOBOL4, e.g.,
  1435.           serviceability as a target language into which "higher level" or
  1436.           special-purpose languages can be compiled (McCarthy, 1981;
  1437.           Gimpel, 1976, chapter 18; Winston & Horn, 1981; and below).
  1438.  
  1439.             Both LISP and SNOBOL4 are nonnumerical, associative, and
  1440.           flexible.  Both make frequent use of recursive functions.  Both
  1441.           allow the construction and execution of code at run-time.
  1442.  
  1443.             What, then, are the major differences between the two
  1444.           languages?
  1445.  
  1446.  
  1447.  
  1448.  
  1449.  
  1450.        AI Programming in SNOBOL4       - 22 -                    August, 1982
  1451.  
  1452.  
  1453.  
  1454.  
  1455.  
  1456.  
  1457.  
  1458.  
  1459.             LISP is primarily interpreted and interactive, though
  1460.           compilation is sometimes permitted.  SNOBOL4 is primarily
  1461.           compiled and non-interactive, though interpreted, interactive
  1462.           versions exist.  Of course, interactive here refers to the
  1463.           programming phase.  Once written, programs in either language may
  1464.           or may not interact with someone during execution.
  1465.  
  1466.             LISP has a uniform, logical syntax: S-expressions represent
  1467.           programs and data structures alike.  Semantically, everything is
  1468.           done with lists: Data structures are lists, and programs are also
  1469.           lists.  Therefore, a given list may be treated as data at one
  1470.           point in a program and as a subroutine at another point.
  1471.  
  1472.             SNOBOL4 supports a variety of datatypes, including arrays, hash
  1473.           tables, patterns, expressions, code, and programmer-defined
  1474.           datatypes.  Patterns, expressions, and code are all procedural.
  1475.           Each can be defined (that is, constructed) during program
  1476.           execution, and then later used procedurally (evaluated or
  1477.           executed).  From the programmer's point of view, all this is done
  1478.           without list processing. The capacity to build and use procedural
  1479.           datatypes is unrelated to the capacity to manipulate lists.
  1480.  
  1481.             There are other reasons for downplaying the significance of
  1482.           list or string processing per se: Strings can be converted to
  1483.           lists, and lists can be converted to strings.  See, for example,
  1484.           Gimpel (1976, chapter 5); or consider what goes on during the
  1485.           READ and PRINT phases of LISP's READ-EVAL-PRINT loop.
  1486.           Furthermore, strings can be processed by LISP as lists of single-
  1487.           character atoms, while lists can be implemented in SNOBOL4 as
  1488.           programmer-defined datatypes.  In fact, the SNOBOL4 DATA function
  1489.           may be more useful in some cases than general purpose list
  1490.           processing functions (Shapiro, 1979, pp. 141-143; Charniak,
  1491.           Riesbeck, & McDermott, 1980, chapter 4).
  1492.  
  1493.             The decision to use strings or lists is often up to the
  1494.           programmer.  A specific application may not dictate the use of
  1495.           one or the other.  And neither lists nor strings are particularly
  1496.           useful for most numerical applications: LISP and SNOBOL4 both
  1497.           need their complement of specifically numerical functions.
  1498.  
  1499.             Thus, there are two major differences between LISP and SNOBOL4:
  1500.           LISP has a simpler syntax and supports fewer datatypes than
  1501.           SNOBOL4; and LISP is usually interactive/ interpretive while
  1502.           SNOBOL4 is usually compiled.  The practical significance of these
  1503.           differences would seem to be dependent upon the aesthetic
  1504.           preferences of the programmer and the nature of the specific
  1505.           programming task.
  1506.  
  1507.             ICON.  The Icon programming language (Griswold, 1978b; Griswold
  1508.           & Hanson, 1980; Griswold, Hanson & Korb, 1979) is the most recent
  1509.           descendant of SNOBOL4 (see also Griswold & Hanson, 1977b).  It
  1510.           differs from SNOBOL4 in two major respects: First, it has control
  1511.           structures like those of Algol and PASCAL.  Second, it is
  1512.           designed to be small, efficient, and portable (Griswold, Hanson,
  1513.  
  1514.  
  1515.  
  1516.        AI Programming in SNOBOL4       - 23 -                    August, 1982
  1517.  
  1518.  
  1519.  
  1520.  
  1521.  
  1522.  
  1523.  
  1524.  
  1525.           & Wampler, 1980; Hanson, 1979).  "Unlike SNOBOL4 and SL5, Icon is
  1526.           intended to be practical for production applications" (Griswold,
  1527.           Hanson, & Korb, 1979).
  1528.  
  1529.             Icon supports a variety of datatypes and related functions:
  1530.           stacks, lists, programmer-defined records, sets, and tables.
  1531.  
  1532.             Programmer-defined functions are recursive and may be suspended
  1533.           for later reactivation.  Compared with SNOBOL4, backtracking has
  1534.           been refined and is better integrated with the rest of the
  1535.           language (Griswold & Hanson, 1977a).  This has been accomplished
  1536.           through the use of generators (Griswold, 1978a; Griswold &
  1537.           Hanson, 1977; Griswold, Hanson, & Korb, 1981).  Generators are
  1538.           designed to facilitate goal-directed programming.  Their
  1539.           evaluation is oriented toward seeking successful results in the
  1540.           presence of alternative possible values, as do SNOBOL4 pattern
  1541.           alternation expressions.
  1542.  
  1543.             Griswold, Hanson, and Korb (1981) discuss some AI applications
  1544.           of Icon, especially its advantages over CONNIVER (McDermott &
  1545.           Sussman, 1973) and PLANNER (Hewitt, 1969).
  1546.  
  1547.             It would seem, on balance, that Icon is an improvement over
  1548.           SNOBOL4.  Many of the strengths of SNOBOL4 have been retained,
  1549.           and some weaknesses (large size, low efficiency, and lack of
  1550.           control structures) have been eliminated.
  1551.  
  1552.             There are some drawbacks to Icon, however: Code construction
  1553.           and execution at run-time are not possible.  This could be seen
  1554.           as a fatal flaw as far as AI programming is concerned.  Also, it
  1555.           is not clear that Icon would be as suitable as SNOBOL4 for use as
  1556.           a target language into which special purpose languages could be
  1557.           compiled.
  1558.  
  1559.             Although, to the extent that well-structured, goal-directed
  1560.           programs are important in AI, Icon may prove useful, it has not
  1561.           inherited the full power and flexibility of SNOBOL4: A LISP
  1562.           programmer would probably feel restricted by Icon (and liberated
  1563.           by SNOBOL4!).
  1564.  
  1565.  
  1566.                                       EXAMPLES
  1567.  
  1568.             The following five examples of SNOBOL4 programs range from a
  1569.           simple sorting program to a special-purpose compiler.  I have
  1570.           chosen these examples with two purposes in mind: First, to
  1571.           provide some concrete and realistic examples of SNOBOL4 programs;
  1572.           and second, to support my claim that SNOBOL4 deserves wider
  1573.           consideration as an AI programming language.
  1574.  
  1575.             There are three ways in which SNOBOL4 can be used in AI
  1576.           programming.  First, it can be used directly, taking advantage of
  1577.           its recursive functions, its pattern-matching capabilities, and
  1578.           its extendibility by means of user-defined datatypes.  Direct
  1579.  
  1580.  
  1581.  
  1582.        AI Programming in SNOBOL4       - 24 -                    August, 1982
  1583.  
  1584.  
  1585.  
  1586.  
  1587.  
  1588.  
  1589.  
  1590.  
  1591.           applications of SNOBOL4 to AI programming are illustrated in
  1592.           Darlington (1971, 1972), Shapiro (1979), and in the Wang's
  1593.           Algorithm, English Morphology, and Kalah examples below.
  1594.  
  1595.             Second, SNOBOL4 can emulate LISP.  That is, list-processing
  1596.           functions can be written in SNOBOL4 which parallel most of the
  1597.           standard LISP functions.  Of course, it would not be too
  1598.           difficult to go all the way and implement one's own favorite
  1599.           version of LISP in SNOBOL4.  It seems more prudent, however, to
  1600.           embed the LISP-like functions in SNOBOL4, so that SNOBOL4's
  1601.           intrinsic string-processing and pattern-matching strengths can
  1602.           also be exploited.  That is what I have tried to do with the
  1603.           SNOLISPIST routines, which are described below and in Appendix F.
  1604.           The SIR and TEST programs (see below and Appendices G and H)
  1605.           illustrate the use of these routines.
  1606.  
  1607.             Finally, rather than using SNOBOL4 directly or quasi-
  1608.           implementing LISP in SNOBOL4, the programmer may choose to define
  1609.           a special-purpose AI language (e.g., Charniak et al., 1980,
  1610.           chapter 17).  SNOBOL4 and LISP are the only two widely available
  1611.           languages which can easily be used both as target languages and
  1612.           as host languages for compilers (McCarthy, 1981; Uhr, 1973;
  1613.           Winston & Horn, 1981). This aspect of SNOBOL4 programming is
  1614.           extensively illustrated in Uhr (1973), as well as in the modest
  1615.           Augmented Transition Network language described below.
  1616.  
  1617.             Uhr used SNOBOL4 to implement a simple, yet powerful,
  1618.           programming language which he called EASEy (Encoder for
  1619.           Algorithmic Syntactic English).  He then used EASEy to write a
  1620.           large number of illustrative AI programs for his book. These
  1621.           programs are still valuable as easily comprehended examples of AI
  1622.           programming in pattern recognition, visual feature abstraction,
  1623.           game playing, theorem proving, and inductive learning.
  1624.  
  1625.  
  1626.           Quicksort
  1627.           ---------
  1628.  
  1629.             C. A. R. Hoare's Quicksort algorithm (Berztiss, 1975; Gimpel,
  1630.           1976; Wirth, 1976; Gries, 1981) is an efficient method for
  1631.           sorting a one-dimensional array A of N elements with respect to a
  1632.           two-place predicate P.  The array A is completely sorted when
  1633.           P(A<I>,A<J>) is true for all 1 <= I <= J <= N.
  1634.  
  1635.             The program in Appendix A is based on Gimpel's (1976) version
  1636.           of Hoare's algorithm.  It is included as an example of a SNOBOL4
  1637.           program that is short, simple, instructive, useful, and recursive
  1638.           -- though it doesn't have much to do with AI.
  1639.  
  1640.             The program consists of four function definitions (statements
  1641.           1-4), the sorting subroutine (5-20), three one-line functions
  1642.           (21-25), and the main program (27-46).
  1643.  
  1644.  
  1645.  
  1646.  
  1647.  
  1648.        AI Programming in SNOBOL4       - 25 -                    August, 1982
  1649.  
  1650.  
  1651.  
  1652.  
  1653.  
  1654.  
  1655.  
  1656.  
  1657.             The main program establishes I/O associations (statements 27-
  1658.           28), reads the input file once to determine the number of records
  1659.           (29-33), allocates an array of the appropriate size (34), rereads
  1660.           the input file into the array (35-40), calls the sort routine
  1661.           (41), and writes the sorted array to the output file (42-46).
  1662.           The function call in statement 41 is set up to do a lexical sort
  1663.           in ascending order (SNOBOL4 predicate LLE).
  1664.  
  1665.             The sorting routine HSORT calls HSORT.SWAP to interchange two
  1666.           values, and it calls HSORT.OK or HSORT.KO to determine whether or
  1667.           not two values need to be interchanged.
  1668.  
  1669.             After checking for termination (statements 5-9), HSORT picks a
  1670.           value C near the middle of the array (11) and initializes two
  1671.           indices J and K (12-13) used in the loops at HSORT2 and HSORT3.
  1672.  
  1673.             At HSORT2, J is incremented until P(C,A<J>) succeeds, that is,
  1674.           until C and A<J> are in the wrong order (14-15).  At HSORT3, K is
  1675.           decremented until P(A<K>,C) succeeds, that is, until A<K> and C
  1676.           are in the wrong order (16-17).
  1677.  
  1678.             If J < K then A<J> and A<K> are in the wrong order.  They are
  1679.           swapped (18), and the search for an incorrectly ordered pair
  1680.           continues at HSORT2.
  1681.  
  1682.             If J >= K then the array segment from I to N has been
  1683.           partitioned into two segments such that P(X,Y) is true if X is in
  1684.           the lower segment and Y is in the upper segment.  HSORT calls
  1685.           itself recursively to partition each of these two segments
  1686.           (statements 19-20).
  1687.  
  1688.             The recursion terminates when a segment contains two items or
  1689.           fewer (statements 6-8).  If there are exactly two items and they
  1690.           are in the wrong order, then they must be swapped (8).
  1691.  
  1692.             This example illustrates the use of programmer-defined
  1693.           functions, recursion, file I/O, dynamic array allocation
  1694.           (statement 34), formatted output (45), and several typical loops
  1695.           (14-15, 16-17, 32-33, 36-40, and 42-46).  Note that a change in
  1696.           the fourth argument to HSORT (at statement 41) is sufficient to
  1697.           switch to any combination of ascending or descending sort, with
  1698.           string or numeric data.
  1699.  
  1700.  
  1701.           Wang's Algorithm
  1702.           ----------------
  1703.  
  1704.             Wang's Algorithm is a method for proving or disproving formulas
  1705.           in the propositional calculus.  The program in Appendix B is a
  1706.           modified version of a program from Griswold, Poage, and Polonsky
  1707.           (1971).  Shapiro (1979) presents a LISP program for Wang's
  1708.           Algorithm.
  1709.  
  1710.  
  1711.  
  1712.  
  1713.  
  1714.        AI Programming in SNOBOL4       - 26 -                    August, 1982
  1715.  
  1716.  
  1717.  
  1718.  
  1719.  
  1720.  
  1721.  
  1722.  
  1723.             After the definition of the principal function (statement 1),
  1724.           several patterns are defined (2-7).  These patterns are used to
  1725.           analyze formulas into their component parts, and to sort them
  1726.           into two categories, unary or binary.  The body of the function
  1727.           WANG extends from statement 8 to statement 41.
  1728.  
  1729.             The main program consists of some keyword settings (statements
  1730.           42-44), I/O associations (45-46), and one loop (48-54) which
  1731.           reads, evaluates, and reports on formulas until an end-of-file is
  1732.           detected.  The execution of statement 53 is conditional on the
  1733.           success of the call to WANG.  If WANG succeeds, then 'VALID' is
  1734.           printed and the :S(READ) branch is taken.  If WANG fails, then
  1735.           control passes to the next statement, 'INVALID' is printed, and
  1736.           the :(READ) branch is taken.
  1737.  
  1738.             Several features of pattern matching are illustrated in the
  1739.           patterns UNOP through ATOM (statements 2-7).  Recall that a
  1740.           pattern describes a scanning operation on a string (the subject)
  1741.           which either succeeds (matches) or fails.
  1742.  
  1743.             UNOP (with &ANCHOR=O) will succeed for any subject which
  1744.           contains 'NOT' as a substring.  BINOP will succeed for any
  1745.           subject containing 'AND', 'OR', 'IMP', or 'EQU'.
  1746.  
  1747.             UNOP.FORMULA looks for a blank, followed by UNOP ('NOT'),
  1748.           followed by a left parenthesis, followed by a substring that is
  1749.           balanced with respect to parentheses (built-in primitive pattern
  1750.           element BAL), followed by a right parenthesis -- something like '
  1751.           NOT(AND(P,Q))'.  If UNOP.FORMULA succeeds then the substring
  1752.           matched by UNOP becomes the value of OP, and the part matched by
  1753.           BAL becomes the value of PHI.
  1754.  
  1755.             BINOP.FORMULA is similar to UNOP.FORMULA, except that it
  1756.           matches strings like ' AND(OR(P,Q),NOT(R))', with the
  1757.           assignments, in this case, of OP = 'AND', PHI = 'OR(P,Q)', and
  1758.           PSI = 'NOT(R)'.
  1759.  
  1760.             FORMULA matches anything matched by UNOP.FORMULA or
  1761.           BINOP.FORMULA, and performs the appropriate assignments.
  1762.  
  1763.             ATOM matches a substring of one or more non-blanks.  If no
  1764.           blank is found, ATOM will match everything up to the end of the
  1765.           subject string -- that is the effect of REM.  The substring
  1766.           matched by ATOM becomes the value of A.
  1767.  
  1768.             The details of WANG will not be discussed except to say that
  1769.           ANTECEDENT and CONSEQUENT are processed similarly, at statements
  1770.           10 and 25, respectively.  In either case, the first FORMULA on
  1771.           the left is dissected and deleted, its OP determining which
  1772.           branch will be taken.  Each branch entails one or two recursive
  1773.           calls of WANG.
  1774.  
  1775.             The recursion terminates when neither the ANTECEDENT nor the
  1776.           CONSEQUENT contains a FORMULA.  In this case, the ANTECEDENT and
  1777.  
  1778.  
  1779.  
  1780.        AI Programming in SNOBOL4       - 27 -                    August, 1982
  1781.  
  1782.  
  1783.  
  1784.  
  1785.  
  1786.  
  1787.  
  1788.  
  1789.           CONSEQUENT both consist entirely of ATOMs. Each ATOM in
  1790.           ANTECEDENT is picked off (statement 39) and searched for in
  1791.           CONSEQUENT (40).  A match produces success.  A series of non-
  1792.           matches finally terminates in failure if ANTECEDENT is reduced to
  1793.           the null string.  If any recursive path eventually leads to
  1794.           failure, the original function call fails: The formula is not a
  1795.           theorem.
  1796.  
  1797.             Besides pattern matching and recursion, this example
  1798.           illustrates SNOBOL4's version of the computed goto (statements 10
  1799.           and 25).  The odd-looking expression in the goto field
  1800.  
  1801.                                                 S( $('WANG.A.'  OP) )
  1802.  
  1803.           means, "On success, take the value of OP, stick 'WANG.A.' on the
  1804.           front of it, and jump to the statement with that label."
  1805.  
  1806.  
  1807.           English Morphology
  1808.           ------------------
  1809.  
  1810.             Winograd (1972) gives a flowchart for the analysis of English
  1811.           word-endings.  The purpose of this analysis is to reduce the
  1812.           number of lexical entries in a natural language understanding
  1813.           program.  Many inflected forms do not have to be stored in the
  1814.           dictionary.
  1815.  
  1816.             The program in Appendix C is based on Winograd's flowchart.
  1817.           The program contains one large subroutine, WORDEND (statements 7-
  1818.           49), which uses four short subroutines, MATCH, CUT, ADDON, and
  1819.           TRY.  Each of these subroutines operates on WRD, the provisional
  1820.           root lexical entry.
  1821.  
  1822.             MATCH (50-51) tests the end of WRD against a pattern PAT.
  1823.  
  1824.             CUT (52-53) deletes the last N letters of WRD.
  1825.  
  1826.             ADDON (54-55) appends a string X to WRD.
  1827.  
  1828.             TRY (56-57) looks up WRD in the DICTIONARY to see whether or
  1829.           not it is a root lexical entry.
  1830.  
  1831.             In WORDEND, the following statements deserve special attention:
  1832.           In statement 9, the pattern DOUBLE is defined so as to match any
  1833.           string which ends with two identical letters.  This is
  1834.           accomplished by a combination of immediate value assignment ($)
  1835.           and deferred evaluation (*).  When LEN(1) matches a letter, that
  1836.           letter immediately becomes the value of the variable L.  *L means
  1837.           the value of L at the time it is referenced during pattern
  1838.           matching (as opposed to the value L may have had when the pattern
  1839.           DOUBLE was initially defined).  Recall that deferred evaluation
  1840.           means evaluation at use-time instead of at definition-time.  The
  1841.           RPOS(0) element forces the pattern to match at Right POSition
  1842.           zero, that is, at the right-hand end of the subject string.
  1843.  
  1844.  
  1845.  
  1846.        AI Programming in SNOBOL4       - 28 -                    August, 1982
  1847.  
  1848.  
  1849.  
  1850.  
  1851.  
  1852.  
  1853.  
  1854.  
  1855.             Statement 13 determines, in one fell swoop, whether or not
  1856.           WORDEND is going to be helpful at all.  If none of the word-
  1857.           endings match the end of WRD, then a direct dictionary look-up is
  1858.           tried immediately.  If the disjunctive pattern does succeed, then
  1859.           WEND records just which ending did match.
  1860.  
  1861.             The rest of WORDEND is like a production system.  Each line
  1862.           consists of one or more tests which must be conjunctively
  1863.           satisfied, followed by one or more operations on WRD, and finally
  1864.           a branch to the next appropriate rule. SNOBOL4 is well-suited to
  1865.           this type of program structure, since, within each statement,
  1866.           function calls are executed from left to right until one fails or
  1867.           the statement is completed.
  1868.  
  1869.             The unary negation operator (~) used in statements 22, 26, 31,
  1870.           33, etc. converts success to failure and vice versa.
  1871.  
  1872.             The second argument to MATCH in statement 19 will match 'SS',
  1873.           'SZ', 'ZS', or 'ZZ'.
  1874.  
  1875.             The main program (59-70) illustrates a typical use of pattern
  1876.           matching in setting up the dictionary (63-66): Words are picked
  1877.           off one at a time and assigned to W.  They are recorded by
  1878.           setting DICTIONARY<W> = 1 (statement 65). DICTIONARY is a hash
  1879.           table, defined in statement 61.
  1880.  
  1881.  
  1882.           Kalah
  1883.           -----
  1884.  
  1885.             Kalah is a two-person game of strategy which involves
  1886.           distributing "stones" among "pots."  The first player to
  1887.           "capture" a majority of the stones wins.  Shapiro (1979, pp. 31-
  1888.           55) describes the rules of the game and provides a LISP program
  1889.           to play it.  The program uses the alpha-beta algorithm, a
  1890.           recursive game-tree searching algorithm.
  1891.  
  1892.             The program in Appendix D is a SNOBOL4 program based on
  1893.           Shapiro's LISP program.  To facilitate the comparison of LISP and
  1894.           SNOBOL4 code, a LISP version of the Kalah program has been
  1895.           interleaved with the SNOBOL4 version.
  1896.  
  1897.             For most subroutines, the SNOBOL4 code closely parallels the
  1898.           LISP code.  The SNOBOL4 code could have been made to parallel the
  1899.           LISP code even more closely (see Appendix H), but here no special
  1900.           package of list processing functions was used, and no special
  1901.           attempt was made to emulate LISP.  The following paragraphs will
  1902.           focus on some of the differences between the LISP and SNOBOL4
  1903.           programs.
  1904.  
  1905.             After the usual preliminaries (statements 1-11), there are two
  1906.           datatype definitions (12-13) which replace the LISP functions
  1907.           OWNER, NUM, OPP, PLAYER, and MOVEOF.  The PATHs around the
  1908.  
  1909.  
  1910.  
  1911.  
  1912.        AI Programming in SNOBOL4       - 29 -                    August, 1982
  1913.  
  1914.  
  1915.  
  1916.  
  1917.  
  1918.  
  1919.  
  1920.  
  1921.           playing board are also incorporated into the PPATH and OPATH
  1922.           fields of the POT datatype.
  1923.  
  1924.             The STACK datatype (14) is used for pushdown stacks. Statements
  1925.           15-25 define the global constants null, nil, t, and nilpot.
  1926.           Statements 26-29 define some simple utility patterns.
  1927.  
  1928.             The function-definition function DEF (30-39) requires some
  1929.           explanation.  It takes a function name (NAME), a string of dummy
  1930.           arguments separated by blanks (ARGS), a string of local variables
  1931.           separated by blanks (LOCALS), a function body (BODY), and a
  1932.           return type (RTN).  The return type is null, 'S', 'F', or 'N'.
  1933.  
  1934.             DEF does three things.  First, it constructs a prototype for a
  1935.           function named NAME with arguments ARGS and locals LOCALS.  The
  1936.           blanks in ARGS and LOCALS are changed to commas (statements 31-
  1937.           32) and a calI to DEFINE is executed (33).  Second, the
  1938.           appropriate type of return is added to BODY (34-37).  Third, the
  1939.           function is compiled (38).  There are many examples of the use of
  1940.           DEF throughout the program (e.g., statements 47-50).
  1941.  
  1942.             APPEND3 (statements 40-46) makes one stack out of three stacks
  1943.           (S1, S2, and S3).  It is used in EXPAND1 to effect a simple move-
  1944.           ranking strategy: S3, S2, and S1 contain ordinary, better, and
  1945.           best moves, respectively.  APPEND3 illustrates both recursion and
  1946.           the use of a data structure building function (43-45).
  1947.  
  1948.             CNTR (51-55) returns a string V centered in a field of N spaces
  1949.           by padding on the left and right with blanks.  PRT (57) uses
  1950.           PRT_PAT (56) to write a string to the user's terminal and to a
  1951.           "log" file, by means of chained immediate value assignment:
  1952.  
  1953.                        $ OUTPUT $ SHADOW
  1954.  
  1955.             The SNOBOL4 version of OTHER (86) uses indirect reference via
  1956.           the unary value ($) operator.  If PL = 'P', then $( "OTHER" PL )
  1957.           evaluates to $"OTHERP", that is, the value of "OTHERP", which is
  1958.           'O'.  If PL = 'O', then $( "OTHER" PL ) = $"OTHERO" = 'P'.  In
  1959.           general, $<expr> is the value of the string or name which is
  1960.           obtained by evaluating the expression <expr>.  Thus, $'X' means
  1961.           the value of X, and $X means the value of the value of X.  The
  1962.           same device is used in POTR (87), KALAHR (88), and SIDER (97).
  1963.  
  1964.             Simple stack operations are defined in VALUE (89), PUSHVAL
  1965.           (90), POPVAL (91), and CHANGEVAL (92).  TOP, KVALUE, STACK, and
  1966.           REST are all field-access functions which were created by the
  1967.           calls to DATA in statements 12 and 13.
  1968.  
  1969.             SETPATH (98-103) and SETSYM (104-109) are more obscure than
  1970.           they need to be, but they do serve to illustrate some of the
  1971.           programming techniques which are possible with SNOBOL4 pattern
  1972.           matching.
  1973.  
  1974.  
  1975.  
  1976.  
  1977.  
  1978.        AI Programming in SNOBOL4       - 30 -                    August, 1982
  1979.  
  1980.  
  1981.  
  1982.  
  1983.  
  1984.  
  1985.  
  1986.  
  1987.             First consider the arguments P and LAT that are passed to
  1988.           SETPATH (see statements 216 and 217).  P is the name of a field
  1989.           (PPATH or OPATH), and LAT is a list (loosely speaking) of the
  1990.           names of all the pots, in order along the path P.  The idea of
  1991.           SETPATH is to set the value of field P for each pot A to the
  1992.           successor of A along the path P.
  1993.  
  1994.             This is accomplished in a two-statement loop (101-102).
  1995.           Statement 101 uses SETPATH_PAT to pick off the first two names (A
  1996.           and B) in LAT, and to delete the first of these (A) from LAT.
  1997.           When LAT is used up, the :F(RETURN) exit is taken.
  1998.  
  1999.             Statement 102 is quite exotic: It constructs, compiles, and
  2000.           executes a SNOBOL4 statement.  If P = 'OPATH', A = 'O3', and B =
  2001.           'O4', then the constructed statement would be
  2002.  
  2003.                        OPATH(O3) = O4 ;
  2004.  
  2005.             The CODE function compiles the constructed statement, and the
  2006.           direct goto --  :< ... > --  causes the code to be executed
  2007.           immediately.  Since the created code has no label and is not the
  2008.           value of any variable, it goes out with the next garbage
  2009.           collection.
  2010.  
  2011.             (Editors note: A simpler rendition of this operation, avoiding
  2012.           the CODE function entirely, would be:
  2013.  
  2014.                        APPLY(P,$A) = $B
  2015.  
  2016.           However, the use of the CODE function is far more exotic.)
  2017.  
  2018.             SETSYM is similar to SETPATH.  It consists of just one pattern
  2019.           matching statement.  The pattern SETSYM_PAT, however, calls the
  2020.           function SETSYM1 and calls itself recursively.
  2021.  
  2022.             SETSYM is called (statement 215) to link pairs of POTs by the
  2023.           OPP (OPPosite) relation, that is, to make it so that OPP(P1) = O6
  2024.           and OPP(O6) = P1, OPP(P2) = O5 and OPP(O5) = P2, etc.
  2025.  
  2026.             When SETSYM_PAT is applied to L, it matches the POT names two
  2027.           at a time (A and B).  It calls SETSYM1, which does the construct-
  2028.           compile-execute trick (with two statements instead of one), e.g.,
  2029.  
  2030.                        OPP(Pl) = O6 ; OPP(O6) = P1 ;
  2031.  
  2032.             SETSYMl returns the null string and the pattern matching
  2033.           process continues with *SETSYM_PAT.  This invokes SETSYM_PAT
  2034.           recursively to match the next pair of POT names. The process
  2035.           continues until the string L is exhausted.
  2036.  
  2037.             The asterisks in *SETSYM1() and *SETSYM_PAT cause deferred
  2038.           evaluation, that is, use-time evaluation instead of define-time
  2039.           evaluation.  Deferred expressions are evaluated from scratch each
  2040.           time they are encountered during the pattern matching process.
  2041.  
  2042.  
  2043.  
  2044.        AI Programming in SNOBOL4       - 31 -                    August, 1982
  2045.  
  2046.  
  2047.  
  2048.  
  2049.  
  2050.  
  2051.  
  2052.  
  2053.             Other cases in which pattern matching is used like a LISP
  2054.           mapping function can be seen in MTSIDEP (134-135), MTSIDE (136-
  2055.           137), START (141-147), NEND (148-149), EXPAND1 (174-189), and
  2056.           INITBRD (203-225).  The FENCE built-in pattern element is used in
  2057.           many of these cases to guard against futile backtracking.  The
  2058.           pattern matching process will never backtrack through a FENCE.
  2059.  
  2060.             SETPATH and SETSYM may seem to be orthogonal to the plane of
  2061.           common sense, though I doubt they will confuse or impress
  2062.           hardened LISP programmers.  Readers who are upset by such
  2063.           nonstructured, freewheeling stuff should be aware that I have not
  2064.           written SETPATH and SETSYM in typical, natural, or even good
  2065.           SNOBOL4 style.  SNOBOL4 allows these kinds of shenanigans; it
  2066.           does not require them.
  2067.  
  2068.  
  2069.           Augmented Transition Network Compiler
  2070.           -------------------------------------
  2071.  
  2072.             Winston and Horn (1981, pp. 251-277) describe a LISP program
  2073.           which compiles a simple source language into LISP.  The source
  2074.           language is designed to facilitate the writing of programs which
  2075.           realize augmented transition networks (ATNs) for use in natural
  2076.           language parsing (cf. Winograd, 1972). Such a program consists of
  2077.           rules like those of a production system, augmented by arbitrary
  2078.           side effects.
  2079.  
  2080.             The program in Appendix E is a compiler for a language like
  2081.           Winston and Horn's ATN language.  The compiler translates an ATN
  2082.           source program into SNOBOL4.  The built-in CODE function then
  2083.           completes the translation into executable machine code.
  2084.  
  2085.             The source language accepted by this compiler consists of
  2086.           "blocks" of various types.  Each block is headed by one of the
  2087.           keywords NETWORK, FUNCTION, LEXICON, or SENTENCES, followed by a
  2088.           unique name.  A block is terminated by the word END followed by
  2089.           its name.
  2090.  
  2091.             A NETWORK block consists of one or more Iabeled "rules."  Rule-
  2092.           labels are local to the block in which they occur.  A rule
  2093.           consists of one or more IF-statements.  An IF-statement has the
  2094.           following form:
  2095.  
  2096.                IF <condition> GOTO <label> AFTER <side effects> ENDIF
  2097.  
  2098.             The condition consists of one or more function calls, including
  2099.           possible calls to other NETWORKs, all of which must succeed in
  2100.           order to activate the side effects and the state transition.  The
  2101.           side effects consist of one or more function calls, which are
  2102.           executed if the condition is satisfied.  The entire "AFTER <side
  2103.           effects>" clause may be omitted.
  2104.  
  2105.             A FUNCTION block consists of a SNOBOL4 function written in a
  2106.           slightly modified format.  Statements must be terminated by
  2107.  
  2108.  
  2109.  
  2110.        AI Programming in SNOBOL4       - 32 -                    August, 1982
  2111.  
  2112.  
  2113.  
  2114.  
  2115.  
  2116.  
  2117.  
  2118.  
  2119.           semicolons and may be continued over several lines without
  2120.           placing continuation characters in column 1.  A function begins
  2121.           with
  2122.  
  2123.                FUNCTION <name> (<arg. list>) (<locals>)
  2124.  
  2125.           and ends with
  2126.  
  2127.                        END <name>
  2128.  
  2129.             The <arg. list> and/or <locals> may be null, but the two sets
  2130.           of parentheses are required.
  2131.  
  2132.             A LEXICON block consists of a stream of features and words,
  2133.           separated by blanks.  A feature is distinguished from a word by
  2134.           its being immediately preceded by a pusher (>) or a popper (<).
  2135.           If a substring does not begin with one of these two characters,
  2136.           it is taken to be a word.
  2137.  
  2138.             The LEXICON block works by operating a feature stack.  A
  2139.           feature preceded by a pusher (>) gets pushed onto the stack.
  2140.           When a feature is preceded by a popper (<), features are popped
  2141.           off the stack until that feature is on top, or until the stack is
  2142.           empty.
  2143.  
  2144.             When a word is encountered, it is entered into the lexicon and
  2145.           is "marked" with whatever features are on the stack.  The stack
  2146.           itself is not altered.  Words with multiple entries receive the
  2147.           union of all the features from their different entries.
  2148.  
  2149.             A SENTENCES block contains one or more sentences to be parsed.
  2150.           Each sentence ends with a semicolon.  Sentences are added to a
  2151.           pushdown stack from which they are later popped and parsed.
  2152.  
  2153.             In addition to the four types of blocks, which can be
  2154.           intermixed in any order, the compiler recognizes (by ignoring)
  2155.           null lines and comments.  A comment is any record which begins
  2156.           with an asterisk (*).  Null lines and comments appear in the
  2157.           source listing but have no effect on compilation.
  2158.  
  2159.             Finally, compilation is terminated, and execution is initiated,
  2160.           by the EXEC statement.  The EXEC keyword is followed by a call to
  2161.           the top-level NETWORK.
  2162.  
  2163.             Appendix E contains a sample source program, based on Winston
  2164.           and Horn's clause parser, along with the output from that
  2165.           program.  The remainder of the present section refers to the
  2166.           compiler itself.
  2167.  
  2168.             Statements 1 to 10 are keyword settings.  Note that CODE_PRINT
  2169.           and SCREEN_ECHO can be used to control two compiler options.  The
  2170.           source input is assumed to be in the temporary file -ATNSOURCE
  2171.           (12), and the compilation listing will go into the file -SLIST
  2172.           (14).  The STACK datatype (15) is defined as in the Kalah program
  2173.  
  2174.  
  2175.  
  2176.        AI Programming in SNOBOL4       - 33 -                    August, 1982
  2177.  
  2178.  
  2179.  
  2180.  
  2181.  
  2182.  
  2183.  
  2184.  
  2185.           (Appendix D).  SENTENCES will hold the stack of sentences to be
  2186.           parsed.  LEX_STACK (21) is the feature stack used by LEXICON
  2187.           blocks.  LEXICAL_FEATURES (22) is a hash table which holds each
  2188.           word's lexical features.
  2189.  
  2190.             Statements 23-48 define various simple patterns which are used
  2191.           by the compiler.
  2192.  
  2193.             The PRT function (49-52) uses a tricky pattern matching device
  2194.           (51): "$ SLIST" causes the string X, which is always matched by
  2195.           REM, to be written immediately and unconditionally to the source-
  2196.           listing file (-SLIST) associated with SLIST.  The ". OUTPUT"
  2197.           assignment, however, is conditional upon the success of the
  2198.           entire match.  The binary operator (.) is the conditional
  2199.           assignment operator. The entire match includes the evaluation of
  2200.           the deferred expression EQ(SCREEN_ECH0,1).  Thus, the string X
  2201.           goes to the user's terminal only if SCREEN ECHO = 1.
  2202.  
  2203.             If CODE_PRINT = 1, then DISPLAY (57-72) will be used to PRT the
  2204.           SNOBOL4 code which the compiler generates for each block.  The
  2205.           code is printed following the block in the compilation listing.
  2206.           DISPLAY does some formatting of the code -- enough to make it
  2207.           more readable than one long string would be.
  2208.  
  2209.             PUT (80-84) and GET (85-89) are used to maintain registers for
  2210.           each node of the parse tree.  PROP is a global hash table which
  2211.           is reinitialized (statement 250) for each sentence.  PROP is
  2212.           indexed by node names, and PROP<NODE_NAME> itself is a hash table
  2213.           indexed by register names like 'SUBJECT', 'VERB', 'PARENT', and
  2214.           so on.  In SNOBOL4, associative lists can be implemented using
  2215.           hash tables.
  2216.  
  2217.             Statements 90-95 define string constants which are used to
  2218.           generate the code for a NETWORK.  The name of the particular
  2219.           NETWORK eventually replaces each occurrence of REPLACE_LIT.
  2220.  
  2221.             There are two sets of patterns used by the compiler.  The first
  2222.           set (96-102) is used to sort out blocks from comments, to catch
  2223.           gross syntactic errors, and to determine whether or not the
  2224.           current block is complete.
  2225.  
  2226.             The second set (105-151) performs the syntactic analysis and
  2227.           code-generation for each complete block.  This set consists of
  2228.           five patterns.  EXEC_PAT (105) recognizes the EXEC statement.
  2229.           SENTENCES_PAT (111), LEXICON_PAT (121), FUNCTION_PAT (130), and
  2230.           NETWORK_PAT (150) each recognize, parse, and generate code for
  2231.           the associated type of block.
  2232.  
  2233.             Each major block pattern is built up from component patterns.
  2234.           If these definitions are read in reverse order, each looks very
  2235.           much like a small phrase-structure grammar. In fact, the pattern
  2236.           definitions can be written directly from the phrase-structure
  2237.           grammars for each of the block types.
  2238.  
  2239.  
  2240.  
  2241.  
  2242.        AI Programming in SNOBOL4       - 34 -                    August, 1982
  2243.  
  2244.  
  2245.  
  2246.  
  2247.  
  2248.  
  2249.  
  2250.  
  2251.             These patterns, however, do more than simply match blocks.
  2252.           First, immediate value assignment ($) is used to save substrings
  2253.           needed during code-generation.  Second, the code-generation
  2254.           process is integrated with the syntactic analysis by means of the
  2255.           functions S and S_.  The method of code-generation is attributed
  2256.           to M. J. Rochkind, and is described in Gimpel (1976, Chapter 18).
  2257.  
  2258.             The function S evaluates to a pattern element.  The pattern
  2259.           element is constructed so that it always succeeds in matching the
  2260.           null string.  Hence, it is invisible as far as the syntactic
  2261.           analysis is concerned.  In addition, however, this pattern
  2262.           element will trigger a call to the function S_.  S_ has many
  2263.           small code-generating segments.  Which segment is selected
  2264.           depends upon the string argument passed to S ('LEX', 'ENW', 'F',
  2265.           etc.)  when the pattern element is generated.
  2266.  
  2267.             The code-generating call to S_ is triggered at precisely the
  2268.           point where S(...) is embedded in the pattern definition.
  2269.           Actually, Rochkind's code-generation method is more general: Code
  2270.           generation can be deferred and made conditional upon the success
  2271.           of the entire pattern matching operation.  Thus, it could be used
  2272.           effectively even with context sensitive grammars.
  2273.  
  2274.             Note that, once again, FENCE is used extensively.  It speeds up
  2275.           compilation by preventing futile backtracking.
  2276.  
  2277.             COMPILE (152-181) reads and compiles blocks until an EXEC
  2278.           statement or a fatal error is detected.  MAIN (236-258)
  2279.           constitutes the semantics of the EXEC statement.  It applies the
  2280.           indicated FIRST_PROCedure to each sentence on the SENTENCES
  2281.           stack.  It calls DUMP_PROP to dump the registers for each node
  2282.           after a successful parse.  (There is room for improvement in the
  2283.           ordering of nodes and registers).
  2284.  
  2285.             The main program for the compiler consists of statements 281-
  2286.           284.
  2287.  
  2288.  
  2289.                              LIST PROCESSING IN SNOBOL4
  2290.  
  2291.             Gimpel (1976, p. 80) writes:  "...SNOBOL3 had only one
  2292.           datatype, the string.  Even the arithmetic facilities of SNOBOL3
  2293.           were implemented as operations on strings of digits rather than
  2294.           on machine integers.  Because of this historical bias, and
  2295.           because the language is extraordinarily rich in string handling,
  2296.           SNOBOL4 is still regarded by some as exclusively a string
  2297.           language.  Yet, all the basic facilities which one expects in a
  2298.           list processing language have been incorporated into SNOBOL4;
  2299.           these include the automatic allocation and freeing of storage,
  2300.           recursive functions, the pointer, and the data structure.
  2301.           Moreover, the notation is, for the most part, conventional,
  2302.           convenient, and flexible.  Were SNOBOL4 suddenly stripped of all
  2303.           its pattern matching capabilities, it would still be a powerful
  2304.           and convenient list-processing language."
  2305.  
  2306.  
  2307.  
  2308.        AI Programming in SNOBOL4       - 35 -                    August, 1982
  2309.  
  2310.  
  2311.  
  2312.  
  2313.  
  2314.  
  2315.  
  2316.  
  2317.             There are scattered references to list processing in SNOBOL4
  2318.           which suggest, but do not spell out, the language's AI potential.
  2319.           For example, Gimpel (1976) devotes one chapter (of 18) to "basic
  2320.           list processing," but the topics covered in that chapter have no
  2321.           clear connection to AI programming.
  2322.  
  2323.             Griswold (1975) provides some examples of list processing in
  2324.           SNOBOL4, but he, like Gimpel, seems more interested in string-
  2325.           than list-processing.  His most extensive chapters cover
  2326.           cryptography, text editing, and software development.
  2327.  
  2328.             Shapiro (1979) does present an example (pp. 79-84) of AI
  2329.           programming in SNOBOL4, but he focuses on the built-in pattern
  2330.           matching functions rather than the list processing potential of
  2331.           SNOBOL4.  This gives the misleading impression that SNOBOL4 has a
  2332.           few unusual functions which are applicable to one arcane type of
  2333.           AI problem.  The fact that the rest of Shapiro's examples are
  2334.           programmed in LISP creates the illusion that LISP is
  2335.           intrinsically more suitable than SNOBOL4 for the "typical" AI
  2336.           problem.
  2337.  
  2338.             Why hasn't the list processing potential of SNOBOL4 been
  2339.           developed in the direction of artificial intelligence
  2340.           applications?  The reasons seem to be historical: The very first
  2341.           applications of SNOBOL (1963-1964) were in symbolic manipulation
  2342.           of algebraic expressions, conversion of network descriptions to
  2343.           FORTRAN programs, generation of IPL-V programs to generate
  2344.           assembly code, implementation of a FORTRAN compiler and other
  2345.           experimental compilers, text formatting, graph analysis, syntax
  2346.           analysis, simulation of automata, and "application to several
  2347.           engineering problems of interest to the Bell System" (Griswold,
  2348.           1981).
  2349.  
  2350.             The list processing capabilities of SNOBOL emerged only in the
  2351.           transition from SNOBOL3 to SNOBOL4, particularly with the
  2352.           introduction of the DATA function.  This occurred about 1966
  2353.           (Griswold, 1981).  Since AI people considered LISP to be an
  2354.           adequate language for their purposes, they had little cause to
  2355.           investigate SNOBOL's suitability for AI programming.  SNOBOL4
  2356.           experts, on the other hand, seemed uninterested in AI, being more
  2357.           concerned with text processing, compiler construction, and other
  2358.           kinds of traditional software development.
  2359.  
  2360.             The remainder of this paper describes a set of list processing
  2361.           functions written in SNOBOL4.  I have named this package
  2362.           SNOLISPIST to emphasize its SNOBOL-LISP parentage, as well as to
  2363.           reflect the solipsistic feeling I get whenever I talk about AI
  2364.           programming in SNOBOL4.
  2365.  
  2366.             I undertook the SNOLISPIST project when I still believed in
  2367.           some kind of intrinsic connection between list processing and AI
  2368.           programming.  I no longer believe in such a connection, so I no
  2369.           longer think that SNOLISPIST is central to AI programming in
  2370.           SNOBOL4.  SNOBOL4 requires no such supplementary subroutines in
  2371.  
  2372.  
  2373.  
  2374.        AI Programming in SNOBOL4       - 36 -                    August, 1982
  2375.  
  2376.  
  2377.  
  2378.  
  2379.  
  2380.  
  2381.  
  2382.  
  2383.           order to hold its own as an AI language.  Nevertheless, I include
  2384.           a description of SNOLISPIST here for several reasons.
  2385.  
  2386.             First of all, I have invested a considerable amount of time,
  2387.           energy, and computer money in this project.  I also believe (off
  2388.           and on) that these list processing functions could help improve
  2389.           communication between LISP and SNOBOL4 programmers, and could
  2390.           encourage others to undertake comparative programming projects.
  2391.           Since the SNOLISPIST functions are extremely easy to use (just
  2392.           plug them in and turn them on), and since unused functions do not
  2393.           waste space (they aren't in core, thanks to DEXTERN), they may
  2394.           have uses even outside AI.
  2395.  
  2396.  
  2397.           Overview of SNOLISPIST
  2398.           ----------------------
  2399.  
  2400.             Appendix F gives an alphabetical listing of all the SNOLISPIST
  2401.           functions, along with detailed descriptions, examples, and
  2402.           references.  The present section is a general description of the
  2403.           various kinds of functions available.
  2404.  
  2405.             The SNOLISPIST functions are patterned after typical LISP
  2406.           functions.  Their names are the same as the corresponding LISP
  2407.           functions, their arguments are the same, their effects (side
  2408.           effects and returned values) are largely the same, and they
  2409.           "really work" pretty much the same way as their LISP
  2410.           counterparts.
  2411.  
  2412.  
  2413.           Conversion Between Strings and Lists
  2414.           ------------------------------------
  2415.  
  2416.             The longest subroutines in SNOLISPIST are concerned with
  2417.           string-to-list and list-to-string data conversion.  The string-
  2418.           to-list conversion routine is called READ.  It can be invoked by
  2419.           means of the # unary operator.  READ (#) interprets a string as a
  2420.           list expression and returns the appropriate list structure.  It
  2421.           has separate routines for recognizing and interpreting an atom, a
  2422.           dotted pair, a list of one element, a list of two or more
  2423.           elements, T, or NIL. It correctly interprets literal atoms
  2424.           enclosed in single quotes or double quotes.  It also does some
  2425.           syntax checking and provides a fatal-error message if the string
  2426.           syntax is illegal.  The \ unary operator forces evaluation of its
  2427.           argument.  (This operator is effective only in an argument to
  2428.           READ.)
  2429.  
  2430.             LIST-TO-STRING CONVERSION.  The list-to-string conversion
  2431.           routine, which is used mainly in output sequences, is called
  2432.           UNREAD.  It can be invoked by the ! unary operator.  It performs
  2433.           the inverse of the READ transformation.  It has separate routines
  2434.           for converting NIL, T, a dotted pair, a list of one element, a
  2435.           list of two or more elements, or an atom.  Literal atoms that
  2436.  
  2437.  
  2438.  
  2439.  
  2440.        AI Programming in SNOBOL4       - 37 -                    August, 1982
  2441.  
  2442.  
  2443.  
  2444.  
  2445.  
  2446.  
  2447.  
  2448.  
  2449.           contain internal blanks are enclosed in quotes (") unless they
  2450.           are already enclosed in quotes (' or ").
  2451.  
  2452.             T AND NIL.  T and NIL are used in SNOLISPIST as they are used
  2453.           in LISP.  They are defined, however, as dotted pairs of strings:
  2454.  
  2455.             T = 'T' ~ 'T', and
  2456.  
  2457.             NIL = '' ~ '', where '' is the null string.
  2458.  
  2459.             CLASSES OF FUNCTIONS.  Besides the SNOBOL4 built-in functions,
  2460.           the following classes of functions are included in SNOLISPIST:
  2461.  
  2462.             The I/O and tracing functions include PRT.VIA.OUTPUT (invoked
  2463.           by the | unary operator), PRINT, IN, LTRACE, and TDUMP.  The
  2464.           default I/O associations for normal I/O and trace messages are to
  2465.           the user's terminal.
  2466.  
  2467.             Most "predicates" come in pairs.  NULL/NULLP, NOT/NOTP,
  2468.           ATOM/ATOMP, NUMBER/NUMBERP, EQU/EQP, EQUAL/EQUALP, NEG/NEGP,
  2469.           ZERO/ZEROP, LESS/LESSP, and GREATER/GREATERP have one form (the
  2470.           one whose name ends in 'P') which returns T or NIL like a LISP
  2471.           predicate.  The other form succeeds or fails like a SNOBOL4
  2472.           predicate.  The mapping predicate functions SOME and EVERY return
  2473.           T or NIL.  The special functions FAIL.IF.NIL (unary operator /)
  2474.           and FAIL.IF.NIL.ELSE.SUCCEED (unary operator %) help convert
  2475.           LISP-type predicates to SNOBOL4- type predicates.
  2476.  
  2477.             The function definition functions include DEXP and DEXTERN.
  2478.           These are due to Gimpel (1976).  DEXP is used to define short
  2479.           functions.  DEXTERN is used to define external functions.
  2480.           External functions are loaded from SNOLISP/LIB and compiled
  2481.           automatically the first time they are called.
  2482.  
  2483.             In SNOLISPIST, lists are built from objects having the defined
  2484.           datatype 'CONS' with fields 'CAR' and 'CDR'.  CAR and CDR have
  2485.           the same meanings as they do in LISP.  CAAR, CADR, etc., up to
  2486.           CDDDDR, are predefined, as they are in many LISPs.
  2487.  
  2488.             The following arithmetic functions can be used in SNOLISPIST:
  2489.           ABS, ADD1, SUB1, SIGN, FLOAT, DFLOAT, FIX, MINUS, ROUND, ADD,
  2490.           SUB, MULT, DIV, MAX, MIN, REMAINDER, PLUS, DIFFERENCE, TIMES, and
  2491.           QUOTIENT.  The last four of these take list arguments.
  2492.  
  2493.             SPITBOL supports the datatypes 'INTEGER', 'REAL', 'DREAL', and
  2494.           'NUMERIC'.  In some contexts, 'NUMERIC' can be used to mean
  2495.           'INTEGER' or 'REAL' or 'DREAL'.  The numeric functions handle all
  2496.           data conversion automatically.  In addition to the binary
  2497.           functions ADD, SUB, MULT, and DIV, SNOBOL4 also supports the
  2498.           infix operators (+ - * /), with ! or ** being used for integer
  2499.           exponentiation.  Unary + and - are also standard.
  2500.  
  2501.             The following extended numeric functions have been adapted from
  2502.           Gimpel (1976, pp. 327-340): FLOOR, CEIL, SQRT, SIN, COS, TAN,
  2503.  
  2504.  
  2505.  
  2506.        AI Programming in SNOBOL4       - 38 -                    August, 1982
  2507.  
  2508.  
  2509.  
  2510.  
  2511.  
  2512.  
  2513.  
  2514.  
  2515.           ASIN, ACOS, ATAN, LOG, CLOG, EXP, and RAISE. CLOG(X) returns
  2516.           log(X) (base 10).  LOG(X,B) returns log(X) (base B).  LOG(X)
  2517.           returns log(X) (base e).  EXP(X) returns e**X.  RAISE(X,Y)
  2518.           returns X**Y. (Note that the SNOBOL4 ! and ** operators do not
  2519.           permit real exponents, but RAISE does.)
  2520.  
  2521.             (Editor's note: SNOBOL4+ provides exponentiation of real
  2522.           numbers.)
  2523.  
  2524.             The following constants are also available in double precision:
  2525.           P...I. (pi), NAT...BASE. (e), and LN...10. [log(10)].  The
  2526.           functions RAD and DEG convert degrees to radians and radians to
  2527.           degrees, respectively.
  2528.  
  2529.             The following list-building functions have been defined: CONS,
  2530.           LIST, APPEND, EXCLUDE, INSERT, INTERSECT, UNION, LCOPY, NCONC,
  2531.           LREVERSE, RPLACA, RPLACD, RPLACN, SNOC, SUBST, and EXPLODE.
  2532.           These work like their LISP counterparts, except that LIST is
  2533.           strictly binary.  Long lists can be defined either by means of
  2534.           the ~ binary operator or the READ (#) function.  LCOPY and
  2535.           LREVERSE are renamed to avoid conflicts with the built-in SNOBOL4
  2536.           functions REVERSE and COPY.
  2537.  
  2538.             The list searching and decomposition functions include LAST,
  2539.           NTH, PRELIST, RAC, RDC, REMOVE, SUFLIST, UNCONS (POP), ASSOC,
  2540.           ASSOCL, FIND, MEMBER, and MEMQ.  The MEMBER function returns NIL
  2541.           or a non-NIL list; MEMQ succeeds or fails.
  2542.  
  2543.             Property-lists are implemented in SNOLISPIST in a way that
  2544.           simulates their implementation in LISP.  The following property
  2545.           list functions are defined: GET, PUT, GETL, PUTL, GETPROP,
  2546.           PUTPROP, ADDPROP, DEFPROP, and REMPROP.  None of these are
  2547.           synonyms for each other, though PUT and DEFPROP are near-
  2548.           synonyms.  In each of these functions, the name of the identifier
  2549.           whose property list is referenced must be passed to the function
  2550.           (using quotes or the name [.] operator).
  2551.  
  2552.             SNOLISPIST has a full complement of mapping functions,
  2553.           including MAP, MAPC, MAPCAR, MAPCARV, MAPLIST, MAPCAN, MAPCON,
  2554.           EVERY, EVLIS, SOME, and SUBSET.  The function MAPCARV
  2555.           simultaneously performs MAPCAR and LREVERSE.
  2556.  
  2557.             Finally, the following miscellaneous functions have been
  2558.           defined: GENSYM (synonym: NEWSYM), which creates and returns a
  2559.           unique name; LENGTH, which returns the number of top-level
  2560.           elements in a list or the number of characters in a string; SET
  2561.           and SETL, which work somewhat like the binary and list forms of
  2562.           the LISP SET/SETQ commands; EVALCODE, which evaluates an
  2563.           expression and returns its value; READLIST, a generalized version
  2564.           of the LISP function of the same name; CONCAT, which takes a list
  2565.           of strings and concatenates them; and TDUMP, which prints an
  2566.           informative message after certain fatal errors.
  2567.  
  2568.  
  2569.  
  2570.  
  2571.  
  2572.        AI Programming in SNOBOL4       - 39 -                    August, 1982
  2573.  
  2574.  
  2575.  
  2576.  
  2577.  
  2578.  
  2579.  
  2580.  
  2581.             The function CAL converts a one-dimensional array to a list;
  2582.           CLA inverts CAL; and SORT sorts a one-dimensional array.
  2583.  
  2584.             UNARY AND BINARY OPERATORS.  The following unary operators are
  2585.           given special definitions in SNOLISPIST:
  2586.  
  2587.                        | # \ / %
  2588.  
  2589.             The | operator is used before a literal string, a string-valued
  2590.           variable, or a string-valued expression.  It causes one line (one
  2591.           string) to be written to the output file associated with OUTPUT.
  2592.           (default: the user's terminal).  The function invoked by | is
  2593.           called PRT.VIA.OUTPUT.
  2594.  
  2595.             The # and ! operators are inverses of each other.  They perform
  2596.           string-to-list and list-to-string conversion, respectively.  If S
  2597.           is a string which is a legal list expression, then #S is the
  2598.           corresponding list; if L is a list, then !L is the corresponding
  2599.           string expression; and #!L is similar in effect to LCOPY(L).  The
  2600.           functions invoked by these operators are READ (#) and UNREAD (!).
  2601.  
  2602.             The \ operator is effective only within an argument to READ.
  2603.           This operator causes its argument to be evaluated before it is
  2604.           incorporated into the list structure which READ is building.
  2605.           Thus \\ATM returns the value of the value of ATM.  In the absence
  2606.           of \, READ treats all atoms as quoted, i.e., unevaluated.
  2607.  
  2608.             The operators / and % are used in predicate expressions to
  2609.           interface between LISP-type predicates, which return T or NIL,
  2610.           and SNOBOL4-type predicates, which succeed or fail.  The /
  2611.           operator changes NIL to fail, but it passes along any non-NIL
  2612.           argument.  The % operator changes NIL to fail and anything else
  2613.           to succeed, returning the null string.  For example, /GET could
  2614.           be used to retrieve the value associated with a property
  2615.           indicator, failing if the property was not defined.  %GET could
  2616.           be used to test whether or not a property was defined, but not to
  2617.           retrieve its value.
  2618.  
  2619.             Two binary operators are given special definitions in
  2620.           SNOLISPIST:
  2621.  
  2622.                        ~  %
  2623.  
  2624.             The ~ operator is the elementary list-building operator in
  2625.           SNOLISPIST.  It invokes the LIST function, which works like the
  2626.           CONS function in LISP.  The following examples show some list
  2627.           construction statements in LISP and their translations into
  2628.           SNOLISPIST:
  2629.  
  2630.                (SETQ X (LIST A B C))            LISP
  2631.                X = A ~ B ~ C ~ NIL              SNOLISPIST
  2632.  
  2633.                (SETQ Y '(A B C))                LISP
  2634.                Y = 'A' ~ 'B' ~ 'C' ~ NIL        SNOLISPIST
  2635.  
  2636.  
  2637.  
  2638.        AI Programming in SNOBOL4       - 40 -                    August, 1982
  2639.  
  2640.  
  2641.  
  2642.  
  2643.  
  2644.  
  2645.  
  2646.  
  2647.                (SET W (CONS A B))               LISP
  2648.                $W = A ~ B                       SNOLISPIST
  2649.  
  2650.             The % binary operator invokes the function PRINT.IN.FIELD.
  2651.           This function has no counterpart in LISP.  It is intended to help
  2652.           in formatting output.  It allows strings to be left-justified,
  2653.           right-justified, or centered in fixed-width fields.
  2654.  
  2655.  
  2656.           Programming in SNOBOL4 with SNOLISPIST
  2657.           --------------------------------------
  2658.  
  2659.             CORE FUNCTIONS AND EXTERNAL FUNCTIONS.  There are two files of
  2660.           SNOLISPIST functions.  One file -- SNOLISP/CORE -- contains the
  2661.           core functions, which include READ, UNREAD, DEXP, DEXTERN, CAR,
  2662.           CDR, LIST, and a few others. All the other functions are stored
  2663.           in SNOLISP/LIB.
  2664.  
  2665.             During program development, the programmer can rely on
  2666.           automatic retrieval of any functions which are called.   When the
  2667.           final version of a program is to be compiled, considerable time
  2668.           and space can be saved by selecting only the functions that are
  2669.           actually used (from SNOLISP/CORE as well as SNOLISP/LIB).  At the
  2670.           present time, this can be done only by copying SNOLISP/CORE and
  2671.           SNOLISP/LIB and then deleting the unneeded functions.  Though
  2672.           this is rather tedious, and should probably be automated somehow,
  2673.           it does save up to 25K in a program like SIR (Appendix H).  And
  2674.           in SNOBOL4, as in LISP, anything that saves space saves time, by
  2675.           reducing the need for garbage-collection.
  2676.  
  2677.             If a function from SNOLISP/LIB is included in the final version
  2678.           of a program, its DEXTERN statement from SNOLISP/ CORE should be
  2679.           deleted, and its DEFINE statement from SNOLISP/LIB should be
  2680.           included.
  2681.  
  2682.             CONVENTIONS.  The programming conventions recommended by Gimpel
  2683.           (1976) have been incorporated into SNOLISPIST.  The name of a
  2684.           defined function matches the label on the first statement in the
  2685.           function body.  That same name, followed by '.END', is the label
  2686.           on the last statement of the function body, which is usually a
  2687.           null statement.  Statement labels internal to the function body
  2688.           consist of the function name followed by '1', '.2', '.A', or the
  2689.           like.
  2690.  
  2691.             Names of identifiers used in contexts where name conflicts
  2692.           might occur have been chosen so as to reduce the chances of such
  2693.           conflicts.  These names contain three embedded periods and a
  2694.           terminal period, e.g., READ...SPB., LTRACE1...A., P...I..  This
  2695.           makes some parts of the SNOLISPIST source code all but
  2696.           unreadable, but it helps prevent name conflicts.  Nevertheless,
  2697.           there are still potential problems.  The programmer must be
  2698.           careful, for example, not to redefine T or NIL.
  2699.  
  2700.  
  2701.  
  2702.  
  2703.  
  2704.        AI Programming in SNOBOL4       - 41 -                    August, 1982
  2705.  
  2706.  
  2707.  
  2708.  
  2709.  
  2710.  
  2711.  
  2712.  
  2713.           Generic Bugs
  2714.           ------------
  2715.  
  2716.             In using SNOLISPIST, the programmer should be aware of the
  2717.           following potential problems.  The possible mistakes discussed
  2718.           below are easy to make and easy to fix.  They should be checked
  2719.           for first when a program behaves very strangely.
  2720.  
  2721.             As mentioned in the preceding section, name conflicts can arise
  2722.           in many subtle ways.  For example, names which conflict with
  2723.           dummy variable names and local variable names can be smuggled
  2724.           into a function by means of the name [.] operator.  One way this
  2725.           can happen is for a list of names to be passed as an argument to
  2726.           a mapping function, along with a function argument that
  2727.           implicitly or explicitly evaluates those names.
  2728.  
  2729.             Another problem may occur with the predicates NULL, NULLP, NOT,
  2730.           NOTP, FAIL.IF.NIL [unary /], and FAIL.IF.NIL.ELSE.SUCCEED [unary
  2731.           %].  All these predicates require a list argument (datatype
  2732.           CONS).  Any other datatype will cause a fatal error.  The correct
  2733.           way to test whether X is NIL, where X can have any datatype, is
  2734.           to use DIFFER(NIL,X) or IDENT(NIL,X).
  2735.  
  2736.             Sometimes the mixture of string and list datatypes in the same
  2737.           statement causes problems, especially if the programmer overlooks
  2738.           a concatenation operation.  For example,
  2739.  
  2740.                        LINE = 'VALUES = ' SETL(#'(X 5 Y 6 Z 7)')
  2741.  
  2742.           will not work, because SETL returns a list, which cannot be
  2743.           concatenated with the string 'VALUES = '.  This can be corrected
  2744.           by means of the UNREAD [unary !] operator: Use !SETL(...) instead
  2745.           of SETL(...).
  2746.  
  2747.             If several function calls are combined in the same statement,
  2748.           it is safest to enclose them in ?(...), as illustrated in the
  2749.           following two examples:
  2750.  
  2751.                ?( |'Line 1'  |''  |'Line 2'  |'' )
  2752.                ?( ~ATOM(P)  NULL(P)  %RPLACD(L,PNEW) )
  2753.  
  2754.             The SNOLISPIST routines do a fair amount of argument checking,
  2755.           and TDUMP provides extensive information about the local
  2756.           environment of a run-time error.  The programmer should also keep
  2757.           in mind the LTRACE function, the &DUMP, &FTRACE, and &TRACE
  2758.           keywords, and the other SNOBOL4 debugging facilities described in
  2759.           an earlier section of this paper.
  2760.  
  2761.  
  2762.                                       EXAMPLES
  2763.  
  2764.             The use of the SNOLISPIST routines is illustrated by two
  2765.           programs.  The first of these, TEST, simply exercises all the
  2766.           functions, sometimes in fairly improbable ways.  The second, SIR,
  2767.  
  2768.  
  2769.  
  2770.        AI Programming in SNOBOL4       - 42 -                    August, 1982
  2771.  
  2772.  
  2773.  
  2774.  
  2775.  
  2776.  
  2777.  
  2778.  
  2779.           is a more or less literal translation into SNOBOL4 of Shapiro's
  2780.           (1979) version of Bertram Raphael's Semantic Information
  2781.           Retrieval program.
  2782.  
  2783.  
  2784.           TEST
  2785.           ----
  2786.  
  2787.             The program in Appendix G provides some rudimentary tests and
  2788.           demonstrations of the SNOLISPIST function.  Here are a few
  2789.           general points about the test program.
  2790.  
  2791.             The program is largely self-documenting.  It is intended to
  2792.           interact with a user at a terminal.  The PAUSE() statements (8,
  2793.           13, etc.)  cause PAWS (statement 1) to be evaluated.  This
  2794.           reports the amount of free storage available and prompts the user
  2795.           to "Press ENTER to continue."
  2796.  
  2797.             Mapping functions are used extensively.  A typical example can
  2798.           be seen in statement 7.  DEXP(...) not only defines a function,
  2799.           but also returns the name of that function, which becomes the
  2800.           first argument to MAPC.  The second argument to MAPC is simply a
  2801.           list of expressions.  As MAPC is evaluated, each of these
  2802.           expressions is printed and then evaluated.  This same technique
  2803.           is used in statement 12, and similar mapping functions are used
  2804.           throughout the test program.
  2805.  
  2806.             In statement 23, the second argument to MAPC is a list of
  2807.           function names; each named function is applied in turn to the
  2808.           ARGUMENT.LIST defined in statement 19.
  2809.  
  2810.             Mapping is also used to test the extended numerical functions
  2811.           (statements 32, 38, 45, 54, 57, 60, 74, 78, and 82).  In
  2812.           statement 91, the second argument to MAPC is not a list of
  2813.           function names, but a list of middle segments of function names.
  2814.           These middle segments are successively assigned to S (statement
  2815.           91, second line) to make all the CAR/CDR compounds from CAAR to
  2816.           CDDDDR.
  2817.  
  2818.             Statements 96-105 test the formatted output function
  2819.           PRINT.IN.FIELD (binary $).  Statements 111-153 test some of the
  2820.           more complicated list processing functions (LCOPY, SUBST, REMOVE,
  2821.           and FIND).
  2822.  
  2823.             Statements 165-180 test the SETL and SET functions, using two
  2824.           defined functions, PLACE and INTERLEAVE.  In statement 177, AA is
  2825.           set to the list of lower-case letters; in 178, VV is set to the
  2826.           list of EBCDIC codes for these letters; in 179, the argument to
  2827.           SETL is constructed by INTERLEAVE, so that the value of each
  2828.           lower-case letter will be set to the EBCDIC code for that letter.
  2829.           The lower-case letters and their EBCDIC codes are printed in
  2830.           statement 180.
  2831.  
  2832.  
  2833.  
  2834.  
  2835.  
  2836.        AI Programming in SNOBOL4       - 43 -                    August, 1982
  2837.  
  2838.  
  2839.  
  2840.  
  2841.  
  2842.  
  2843.  
  2844.  
  2845.             Statements 182-196 test the set-manipulation functions UNION,
  2846.           EXCLUDE, and INTERSECT.  The MAPC statement (196) says, "For each
  2847.           name on the list, print a blank Iine, print the name, evaluate
  2848.           it, and print the value."
  2849.  
  2850.             All the property list functions are tested in one call to MAPC
  2851.           (statement 400), the first argument of which defines a function
  2852.           that prints a blank line, prints an expression, evaluates the
  2853.           expression, converts the value from list to string, and prints it
  2854.           preceded by five blanks.
  2855.  
  2856.             Statements 433-446 test the functions CLA (convert list to
  2857.           array), CAL (convert array to list), and SORT (sort a segment of
  2858.           an array).  Statement 446 makes anagrams by exploding a word into
  2859.           a list of single characters, converting the list to an array,
  2860.           sorting the array, converting the array back to a list,
  2861.           converting the list back to a string, and printing the string.
  2862.  
  2863.  
  2864.           SIR
  2865.           ---
  2866.  
  2867.             Shapiro (1979, pp. 123-140) presents a LISP program for storing
  2868.           and retrieving propositional information in a relational network
  2869.           (semantic network).  Shapiro's program is based on Bertram
  2870.           Raphael's Semantic Information Retrieval (SIR) program (Raphael,
  2871.           1968).
  2872.  
  2873.             The program in Appendix H is a translation of Shapiro's program
  2874.           from LISP into SNOLISPIST.  An attempt was made to follow the
  2875.           LISP code as closely as possible.  The reader can consult Shapiro
  2876.           (1979) for the original LISP program and documentation.  The
  2877.           following discussion will focus on the differences between the
  2878.           SNOLISPIST program and the LISP program.
  2879.  
  2880.             In statement 8, pattern-matching is used to determine whether
  2881.           the initial sentence fragment ends with a terminal punctuation
  2882.           mark [! or ?].  The two-statement loop (7-8) keeps S in string
  2883.           form until it is complete.  Then it is converted into a list
  2884.           (statement 9).
  2885.  
  2886.             In PROCESS.1 (statements 12-20), each rule on the list RULES is
  2887.           applied to SENTENCE until one matches, or until the list of rules
  2888.           is exhausted.  In statements 14-16, RESP is something like "I
  2889.           UNDERSTAND.", "INSUFFICIENT INFORMATION", etc.  (See statements
  2890.           103-107.)  If RESP is NIL, then the rule (CA) could not be
  2891.           applied to SENTENCE.
  2892.  
  2893.             Statement 21 defines the datatype RULE.  A RULE consists of a
  2894.           PATTERN which may match a phrase, causing some constituents of
  2895.           the phrase to be bound to certain VARIABLES. If the values of
  2896.           these VARIABLES then pass certain TESTS, the associated semantic
  2897.           ACTION will be executed.  If the input sentence was an assertion
  2898.           (ending with !), the semantic action will be to add information
  2899.  
  2900.  
  2901.  
  2902.        AI Programming in SNOBOL4       - 44 -                    August, 1982
  2903.  
  2904.  
  2905.  
  2906.  
  2907.  
  2908.  
  2909.  
  2910.  
  2911.           to the database.  If the input was a question (ending with ?)
  2912.           then the semantic action will be to retrieve previously stored
  2913.           information.  The operation of the program thus depends, in part,
  2914.           on the list of RULEs (statements 185-193).
  2915.  
  2916.             APPLY.RULE (22-25) says, "If the pattern PATTERN(RULE) matches
  2917.           the input INP, then apply the associated tests, TESTS(RULE), to
  2918.           the values of the variables, EVLIS(...), to determine whether
  2919.           ACTION(RULE) should be executed."  (The pattern-matching here
  2920.           could be handled more efficiently and more transparently by using
  2921.           the standard SNOBOL4 pattern- matching facilities, rather than
  2922.           using list processing.)
  2923.  
  2924.             APPLY.RULE.1 (51-55) builds and evaluates an expression, thus
  2925.           executing the semantic action ACT.  CAR(ACT) is the name of a
  2926.           function (see statements 108-184), and CONCAT(RMAPCAR(...))
  2927.           returns the argument list for the function call.  RMAPCAR (56-61)
  2928.           is a kind of reverse MAPCAR, in that it applies each function
  2929.           named on the list LF to the list S, returning a list of the
  2930.           results.
  2931.  
  2932.             The utility functions ADDXRY through COMPLEMENT (statements 62-
  2933.           88) are used by the semantic routines.  The semantic routines
  2934.           (108-184) are activated by the ACTION fields of the various RULEs
  2935.           (statement 193) to modify or interrogate the relational network.
  2936.  
  2937.             The program in Appendix H has been tested with Shapiro's test
  2938.           dialogue (Shapiro, 1979, 138-140).
  2939.  
  2940.  
  2941.                                       FOOTNOTE
  2942.  
  2943.             This work was made possible by leave support from Gustavus
  2944.           Adolphus College.  It was also supported in part by a grant in
  2945.           cognitive science from the Alfred P. Sloan Foundation to the
  2946.           University of Michigan.  Computer funds were made available
  2947.           through this Sloan grant and through the University of Michigan
  2948.           Computing Center.
  2949.  
  2950.             The following people have provided help and/or inspiration:
  2951.  
  2952.             Robert R. Knight helped me get started programming in SNOBOL4
  2953.           at Princeton University around 1972.  He once said, "It's a
  2954.           wonder they've made any progress in AI, since they insist on
  2955.           using LISP."
  2956.  
  2957.             George Georgacarakos, a philosopher, once told me, "It's easy
  2958.           to program in LISP because people naturally think in LISP."
  2959.  
  2960.             Paul D. Scott, Sandy Nado, and Les Johnson took the time to
  2961.           read an earlier draft of this paper.  Their comments have helped
  2962.           me to clarify my own thinking about AI programming, and their
  2963.           expertise has enabled me to correct some of my errors.  They are
  2964.  
  2965.  
  2966.  
  2967.  
  2968.        AI Programming in SNOBOL4       - 45 -                    August, 1982
  2969.  
  2970.  
  2971.  
  2972.  
  2973.  
  2974.  
  2975.  
  2976.  
  2977.           not to blame for any new errors I may have introduced without
  2978.           their knowledge or consent.
  2979.  
  2980.             Ralph E. Griswold called my attention to the work of Leonard
  2981.           Uhr and provided information about the Icon programming language.
  2982.           Fred Swartz and Paul Pickelmann also provided information about
  2983.           Icon.
  2984.  
  2985.             Gary M. Olson provided advice and encouragement.
  2986.  
  2987.             Sylvia A. S. Shafto provided criticism, perspective, and food.
  2988.  
  2989.  
  2990.                                      REFERENCES
  2991.  
  2992.             Berztiss, A. T.  "Data structures: Theory and practice" (second
  2993.                edition).  New York: Academic Press, 1975.
  2994.  
  2995.             Bundy, A., Burstall, R. M., Weir, S., & Young, R. M.
  2996.                "Artificial intelligence: An introductory course."  New
  2997.                York: North-Holland, 1978.
  2998.  
  2999.             Charniak, E., Riesbeck, C. K., & McDermott, D. V. "Artificial
  3000.                intelligence programming."  Hillsdale, New Jersey: Lawrence
  3001.                Erlbaum Associates, 1980.
  3002.  
  3003.             Darlington, J.  A partial mechanization of second-order logic.
  3004.                In B. Meltzer & D. Michie (Eds.), "Machine Intelligence 6."
  3005.                New York: Wiley, 1971.
  3006.  
  3007.             Darlington, J.  Deductive plan formation in higher-order logic.
  3008.                In B. Meltzer & D. Michie (Eds.), "Machine Intelligence 7."
  3009.                New York: Wiley, 1972.
  3010.  
  3011.             Davenport, J. H., & Jenks, R. D.  MODLISP. "SIGSAM Bulletin,"
  3012.                1981, 15, 11-20.
  3013.  
  3014.             Day,  A. C.  "FORTRAN techniques".  Cambridge, England:
  3015.                Cambridge University Press, 1972.
  3016.  
  3017.             Denning, P. J., Dennis, J. B., & Qualitz, J. E.  "Machines,
  3018.                languages, and computation."  Englewood Cliffs, NJ:
  3019.                Prentice-Hall, 1978.
  3020.  
  3021.             Dijkstra, E. w.  "A discipline of programming."  Englewood
  3022.                Cliffs, NJ: Prentice-Hall, 1976.
  3023.  
  3024.             Friedman, D. P.  "The little LISPer."  Palo Alto: Science
  3025.                Research Associates, 1974.
  3026.  
  3027.             Gimpel J. F.  "Algorithms in SNOBOL4."  New York: John Wiley &
  3028.                Sons, 1976. (Editors note: republished by Catspaw, Inc.,
  3029.                1986)
  3030.  
  3031.  
  3032.  
  3033.  
  3034.        AI Programming in SNOBOL4       - 46 -                    August, 1982
  3035.  
  3036.  
  3037.  
  3038.  
  3039.  
  3040.  
  3041.  
  3042.  
  3043.             Gries, D.  "The science of programming."  New York: Springer-
  3044.                Verlag, 1981.
  3045.  
  3046.             Griffin, R., & Redish, K. A.  Remark on Algorithm 347 [M1]: An
  3047.                efficient algorithm for sorting with minimal storage.
  3048.                "Collected algorithms from CACM," 1969, Vol. 2.
  3049.  
  3050.             Griswold, R. E.  "String and list processing in SNOBOL4:
  3051.                Techniques and applications."  Englewood Cliffs, New Jersey:
  3052.                Prentice-Hall, 1975.
  3053.  
  3054.             Griswold, R. E.  "An alternative to the concept of "pattern" in
  3055.                string processing."  Technical Report TR 78-4, Department of
  3056.                Computer Science, University of Arizona, Tucson, AZ (April
  3057.                10, 1978)a.
  3058.  
  3059.             Griswold, R. E.  "User's manual for the Icon programming
  3060.                language."  Technical Report TR 78-14, Department of
  3061.                Computer Science, University of Arizona, Tucson, AZ (Sept.
  3062.                29, 1978)b.
  3063.  
  3064.             Griswold, R. E.  A history of the SNOBOL programming languages.
  3065.                In Wexelblat, R. L. (Ed.), "History of programming
  3066.                languages."  New York: Academic Press, 1981.
  3067.  
  3068.             Griswold, R. E., & Griswold, M. T.  "A SNOBOL4 primer."
  3069.                Englewood Cliffs, New Jersey: Prentice-Hall, 1973.
  3070.  
  3071.             Griswold, R. E., & Hanson, D. R.  Language facilities for
  3072.                automatic backtracking.  "SIGPLAN Notices/SIGART Newsletter
  3073.                Special Issue.: Proceedings of the Symposium on AI and
  3074.                Programming," 1977, 12/No. 64, 94-99. (a)
  3075.  
  3076.             Griswold, R. E., & Hanson, D. R.  An overview of SL5. "SIGPLAN
  3077.                Notices," 1977, 12, 40-50. (b)
  3078.  
  3079.             Griswold, R. E., & Hanson, D. R.  "Reference manual for the
  3080.                Icon programming language."  Technical Report TR 79-la,
  3081.                Department of Computer Science, University of Arizona,
  3082.                Tucson, AZ (January, 1980).
  3083.  
  3084.             Griswold, R. E., Hanson, D. R., & Korb, J. T.  "The Icon
  3085.                programming language: An overview."  Tucson, AZ: Department
  3086.                of Computer Science, University of Arizona, 1979.
  3087.  
  3088.             Griswold, R. E., Hanson, D. R., & Korb, J. T.  Generators in
  3089.                Icon.  "ACM Transactions on Programming Languages and
  3090.                Systems," 1981, 3, 144-161.
  3091.  
  3092.             Griswold, R. E., Hanson, D. R., & Wampler, S. B. "Transporting
  3093.                the Icon programming language( Version 2.0.  Technical
  3094.                Report TR 79-2b, Department of Computer Science, University
  3095.                of Arizona, Tucson, AZ (February, 1980).
  3096.  
  3097.  
  3098.  
  3099.  
  3100.        AI Programming in SNOBOL4       - 47 -                    August, 1982
  3101.  
  3102.  
  3103.  
  3104.  
  3105.  
  3106.  
  3107.  
  3108.  
  3109.             Griswold, R. E., Poage, J. F., & Polonsky, I. P.  "The SNOBOL4
  3110.                programming Language" (second edition). Englewood Cliffs,
  3111.                New Jersey: Prentice-Hall, 1971.
  3112.  
  3113.             Hanson, D. R.  "Transporting Ratfor."  Technical Report TR 79-
  3114.                4a, Department of Computer Science, University of Arizona,
  3115.                Tucson, AZ (May, 1979).
  3116.  
  3117.             Hewitt, C.  PLANNER: A language for proving theorems in robots.
  3118.                "Proceedings of the International Joint Conference on
  3119.                Artificial Intelligence."  Bedford, Mass.: MITRE, 1969.
  3120.  
  3121.             Hopcroft, J. E., & Ullman, J. D.  "Introduction to automata
  3122.                theory, languages, and computation."  Reading, Mass.:
  3123.                Addison-Wesley, 1979.
  3124.  
  3125.             Lewis, T. G., & Smith, M. Z.  "Applying data structures."
  3126.                Boston: Houghton Mifflin, 1976.
  3127.  
  3128.             Marti, J., Hearn, A. C., Griss, M. L., Griss, C.  Standard LISP
  3129.                report.  "SIGSAM Bulletin," 1980, 14, 23-43.
  3130.  
  3131.             McCarthy, J.  History of LISP.  In R. L. Wexelblat (Ed.),
  3132.                "History of programming languages."  New York: Academic
  3133.                Press, 1981.
  3134.  
  3135.             McCarthy, J., Abrahams, P. W., Edwards, D. J., Hart, T. P., &
  3136.                Levin, M. I.  "LISP 1.5 programmer's manual" (second
  3137.                edition).  Cambridge, Massachusetts: The M.I.T. Press, 1969.
  3138.  
  3139.             McDermott, D. V., & Sussman, G. J.  "The Conniver reference
  3140.                manual."  Cambridge, Mass.: MIT AI Laboratory Memo 259a,
  3141.                1973.
  3142.  
  3143.             Maurer, W. D.  "A programmer's introduction to LISP."  New
  3144.                York: American Elsevier, 1973.
  3145.  
  3146.             Maurer, W. D.  "The programmer's introduction to SNOBOL4."  New
  3147.                York: American Elsevier, 1976.
  3148.  
  3149.             Newsted, P. R.  "SNOBOL: An introduction to programming."
  3150.                Hayden Book Co., 1975.
  3151.  
  3152.             Peto, R.  Remark on Algorithm 347 [M1]: An efficient algorithm
  3153.                for sorting with minimal storage.  "Collected algorithms
  3154.                from CACM," 1970, Vol. 2.
  3155.  
  3156.             Raphael, B.  SIR:  Semantic information retrieval.  In Minsky,
  3157.                M. (Ed.), "Semantic information processing." Cambridge,
  3158.                Mass.:  M.I.T. Press, 1968.
  3159.  
  3160.             Schank, R., & Riesbeck, C. (Eds.). "Inside computer
  3161.                understanding."  Hillsdale, New Jersey:  Lawrence Erlbaum
  3162.                Associates, 1981.
  3163.  
  3164.  
  3165.  
  3166.        AI Programming in SNOBOL4       - 48 -                    August, 1982
  3167.  
  3168.  
  3169.  
  3170.  
  3171.  
  3172.  
  3173.  
  3174.  
  3175.             Shapiro, S. C.  Techniques of artificial intelligence.  New
  3176.                York: Van Nostrand, 1979.
  3177.  
  3178.             Siklossy, L. S.  "Let's talk LISP."  Englewood Cliffs, NJ:
  3179.                Prentice-Hall, 1976.
  3180.  
  3181.             Singleton, R. C.  Algorithm 347: An efficient algorithm for
  3182.                sorting with minimal storage.  "Collected Algorithms from
  3183.                CACM," 1968, Vol. 2.
  3184.  
  3185.             Sommerville, I.  S-SNOBOL: Structured SNOBOL4.  SIGPLAN
  3186.                Notices, 1979, 14, 91-99.
  3187.  
  3188.             Uhr, L.  "Pattern recognition, learning, and thought: Computer
  3189.                programmed models of higher mental processes." Englewood
  3190.                Cliffs, NJ: Prentice-Hall, 1973.
  3191.  
  3192.             University of Michigan Computing Center.  "MTS Volume 8: LISP
  3193.                and SLIP in MTS."  Author, 1976.
  3194.  
  3195.             University of Michigan Computing Center.  "MTS Volume 9:
  3196.                SNOBOL4 in MTS."  Author, 1975.
  3197.  
  3198.             University of Michigan Computing Center.  "MTS Volume 1: The
  3199.                Michigan Terminal System."  Author, 1979.
  3200.  
  3201.             "University of Michigan Computing Center Newsletter."  Icon
  3202.                arrives at the Computing Center.  Nov. 11, 1981, p. 3.
  3203.  
  3204.             Winograd, T.  "Understanding natural language."  New York:
  3205.                Academic Press, 1972.
  3206.  
  3207.             Winston, P. H., & Horn, B. P. K.  "LISP." New York: Addison-
  3208.                Wesley, 1981.
  3209.  
  3210.             Wirth, N.  "Algorithms + Data Structures = Programs." Englewood
  3211.                Cliffs, NJ: Prentice-Hall, 1976.
  3212.  
  3213.  
  3214.  
  3215.  
  3216.  
  3217.  
  3218.  
  3219.  
  3220.  
  3221.  
  3222.  
  3223.  
  3224.  
  3225.  
  3226.  
  3227.  
  3228.  
  3229.  
  3230.  
  3231.  
  3232.        AI Programming in SNOBOL4       - 49 -                    August, 1982
  3233.  
  3234.  
  3235.  
  3236.  
  3237.  
  3238.  
  3239.  
  3240.  
  3241.                                      APPENDIX A
  3242.  
  3243.             This is a compilation listing of a SNOBOL4 version of C.A.R.
  3244.           Hoare's Quicksort algorithm.  This program is based on Wirth's
  3245.           (1976, p. 79) and Gimpel's (1976, pp. 280-282) programs.
  3246.  
  3247.              *
  3248.              * Sort array A according to predicate P.
  3249.              *
  3250.              * P(A<I>,A<J>) true for all I < J.
  3251.              *
  3252.              * Possible values of P:
  3253.              *     .LE, .GE, .LLE, .LGE
  3254.              *
  3255.              * DO NOT USE:
  3256.              *     .LT, .GT, .LLT, .LGT
  3257.              *
  3258.              *
  3259.           1   DEFINE('HSORT(A,I,N,P)J,K,C')
  3260.           2   DEFINE('HSORT.KO(V1,V2)')
  3261.           3   DEFINE('HSORT.OK(V1,V2)')
  3262.           4   DEFINE('HSORT.SWAP(I1,I2)T')              :(HSORT.END)
  3263.              *
  3264.           5  HSORT
  3265.           6        GT(N - I,  1)    :S(HSORT1)
  3266.           7        GE(I, N)         :S(RETURN)
  3267.           8        (HSORT.KO(A<I>,A<N>)   HSORT.SWAP(I,N))
  3268.           9           :(RETURN)
  3269.              *
  3270.           10 HSORT1
  3271.           11       C = A<(I + N) / 2>
  3272.           12       J = I - 1
  3273.           13       K = N + 1
  3274.              *
  3275.           14 HSORT2   J = J + 1
  3276.           15       HSORT.OK(C,A<J>)                       :F(HSORT2)
  3277.           16 HSORT3   K = K - 1
  3278.           17       HSORT.OK(A<K>,C)                       :F(HSORT3)
  3279.              *
  3280.           18       (LT(J,K)    HSORT.SWAP(J,K))           :S(HSORT2)
  3281.              *
  3282.           19       HSORT(A, I, K, P)
  3283.           20       HSORT(A, K + 1, N, P)                  :(RETURN)
  3284.              *
  3285.           21 HSORT.SWAP  T = A<I1> ; A<I1> = A<I2> ; A<I2> = T :(RETURN)
  3286.              *
  3287.           24 HSORT.OK      APPLY(P,V1,V2)           :S(RETURN)F(FRETURN)
  3288.              *
  3289.           25 HSORT.KO      APPLY(P,V1,V2)           :S(FRETURN)F(RETURN)
  3290.              *
  3291.           26 HSORT.END
  3292.              *
  3293.              *
  3294.              * Begin main program *****
  3295.  
  3296.  
  3297.  
  3298.        AI Programming in SNOBOL4       - 50 -                    August, 1982
  3299.  
  3300.  
  3301.  
  3302.  
  3303.  
  3304.  
  3305.  
  3306.  
  3307.              *
  3308.           27  OUTPUT(.OUTPUT,,80)
  3309.           28  INPUT(.INPUT,,80)
  3310.              *
  3311.           29       SETEXIT( .CONTINUE )
  3312.           30       REWIND( 'SFILE' )
  3313.           31       N = 0
  3314.           32 COUNT
  3315.           33       N = ?INPUT   N + 1      :S(COUNT)
  3316.              *
  3317.           34       A = ARRAY(N)
  3318.           35       REWIND( 'SFILE' )
  3319.           36       I = 0
  3320.           37 INPUT.LOOP
  3321.           38       I = LT(I,N) I + 1       :F(INPUT.LOOP.END)
  3322.           39       A<I> = INPUT            :(INPUT.LOOP)
  3323.           40 INPUT.LOOP.END
  3324.              *
  3325.           41    HSORT(A,1,N, .LLE)
  3326.              *
  3327.           42       I = 0
  3328.           43 OUTPUT.LOOP
  3329.           44       I = LT(I,N) I + 1       :F(OUTPUT.LOOP.END)
  3330.           45       OUTPUT = LPAD(I,5) ".   " A<I>    :(OUTPUT.LOOP)
  3331.           46 OUTPUT.LOOP.END
  3332.           47 END
  3333.  
  3334.  
  3335.  
  3336.  
  3337.  
  3338.  
  3339.  
  3340.  
  3341.  
  3342.  
  3343.  
  3344.  
  3345.  
  3346.  
  3347.  
  3348.  
  3349.  
  3350.  
  3351.  
  3352.  
  3353.  
  3354.  
  3355.  
  3356.  
  3357.  
  3358.  
  3359.  
  3360.  
  3361.  
  3362.  
  3363.  
  3364.        AI Programming in SNOBOL4       - 51 -                    August, 1982
  3365.  
  3366.  
  3367.  
  3368.  
  3369.  
  3370.  
  3371.  
  3372.  
  3373.                                      APPENDIX B
  3374.  
  3375.             This is a compilation listing of a SNOBOL4 version of Wang's
  3376.           Algorithm for proving theorems in the propositional calculus.
  3377.           The program prints a proof or refutation for any formula.
  3378.  
  3379.             This program was adapted from Griswold, Poage, and Polonsky
  3380.           (1971, pp. 183-187).  For a LISP version of Wang's Algorithm, see
  3381.           Shapiro (1979, pp. 57-62).
  3382.  
  3383.              *
  3384.              * Wang's algorlthm
  3385.              *
  3386.              * Adapted from
  3387.              *      Griswold, R. E., Poage, J. F., & Polonsky, I. P.
  3388.              *          The SNOBOL4 Programming Language.
  3389.              *          Pp. 183-185.
  3390.              *
  3391.           1   DEFINE('WANG(ANTECEDENT,CONSEQUENT)PHI,PSI')
  3392.           2   UNOP = 'NOT'
  3393.           3   BINOP  = ('AND' | 'IMP' | 'OR' | 'EQU')
  3394.           4   UNOP.FORMULA = ' ' (UNOP . OP) '(' (BAL . PHI) ')'
  3395.           5   BINOP.FORMULA = ' ' (BINOP . OP) '(' (BAL . PHI) '.'
  3396.              +                    (BAL . PSI) ')'
  3397.           6   FORMULA = UNOP.FORMULA | BINOP.FORMULA
  3398.           7   ATOM = (NOTANY(' ') (BREAK(' ') | REM)) . A
  3399.              +                                               :(WANG.END)
  3400.              *
  3401.           8  WANG
  3402.           9         OUTPUT = ANTECEDENT ' >>> ' CONSEQUENT
  3403.           10        ANTECEDENT FORMULA = NULL
  3404.              +             :F(WANG1)S( $('WANG.A.' OP) )
  3405.           11 WANG.A.NOT
  3406.           12        WANG(ANTECEDENT, CONSEQUENT ' ' PHI)
  3407.              +          :S(RETURN)F(FRETURN)
  3408.           13 WANG.A.AND
  3409.           14        WANG(ANTECEDENT ' ' PHI ' ' PSI, CONSEQUENT)
  3410.              +          :S(RETURN)F(FRETURN)
  3411.           15 WANG.A.OR
  3412.           16        WANG(ANTECEDENT ' ' PHI, CONSEQUENT)     :F(FRETURN)
  3413.           17        WANG(ANTECEDENT ' ' PSI, CONSEQUENT)
  3414.              +          :S(RETURN)F(FRETURN)
  3415.           18 WANG.A.IMP
  3416.           19        WANG(ANTECEDENT ' ' PSI, CONSEQUENT)     :F(FRETURN)
  3417.           20        WANG(ANTECEDENT, CONSEQUENT ' ' PHI)
  3418.              +          :S(RETURN)F(FRETURN)
  3419.           21 WANG.A.EQU
  3420.           22        WANG(ANTECEDENT ' ' PHI ' ' PSI, CONSEQUENT)
  3421.              +        :F(FRETURN)
  3422.           23        WANG(ANTECEDENT, CONSEQUENT ' ' PHI ' ' PSI)
  3423.              +          :S(RETURN)F(FRETURN)
  3424.           24 WANG1
  3425.           25        CONSEQUENT FORMULA =
  3426.              +          :F(WANG2)S( $('WANG.C.' OP) )
  3427.  
  3428.  
  3429.  
  3430.        AI Programming in SNOBOL4       - 52 -                    August, 1982
  3431.  
  3432.  
  3433.  
  3434.  
  3435.  
  3436.  
  3437.  
  3438.  
  3439.           26 WANG.C.NOT
  3440.           27        WANG(ANTECEDENT ' ' PHI, CONSEQUENT)
  3441.              +          :S(RETURN)F(FRETURN)
  3442.           28 WANG.C.AND
  3443.           29        WANG(ANTECEDENT, CONSEQUENT ' ' PHI)       :F(FRETURN)
  3444.           30        WANG(ANTECEDENT, CONSEQUENT ' ' PSI)
  3445.              +          :S(RETURN)F(FRETURN)
  3446.           31 WANG.C.OR
  3447.           32        WANG(ANTECEDENT, CONSEQUENT ' ' PHI ' ' PSI)
  3448.              +          :S(RETURN)F(FRETURN)
  3449.           33 WANG.C.IMP
  3450.           34        WANG(ANTECEDENT ' ' PHI, CONSEQUENT ' ' PSI)
  3451.              +         :S(RETURN)F(FRETURN)
  3452.           35 WANG.C.EQU
  3453.           36        WANG(ANTECEDENT ' ' PHI, CONSEQUENT ' ' PSI)
  3454.              +        :F(FRETURN)
  3455.           37        WANG(ANTECEDENT ' ' PSI, CONSEQUENT ' ' PHI)
  3456.              +        :S(RETURN)F(FRETURN)
  3457.           38 WANG2
  3458.           39        ANTECEDENT ATOM =                          :F(FRETURN)
  3459.           40        CONSEQUENT A                        :S(RETURN)F(WANG2)
  3460.           41 WANG.END
  3461.              *
  3462.              *
  3463.              *
  3464.           42  &ANCHOR = 0
  3465.           43  &FULLSCAN = 1
  3466.           44  &TRIM  = 1
  3467.              *
  3468.           45  INPUT(.INPUT,,80)
  3469.           46  OUTPUT(.OUTPUT,,80)
  3470.              *
  3471.           47        SETEXIT( .CONTINUE)
  3472.           48 READ
  3473.           49        EXPRESSION = INPUT                         :F(END)
  3474.           50        OUTPUT =
  3475.           51        OUTPUT = 'FORMULA:  ' EXPRESSION
  3476.           52        OUTPUT =
  3477.           53        OUTPUT = WANG( NULL, ' ' EXPRESSION ) 'VALID'
  3478.              +        :S(READ)
  3479.           54        OUTPUT = 'INVALID'
  3480.              +        :(READ)
  3481.           55 END
  3482.  
  3483.  
  3484.  
  3485.  
  3486.  
  3487.  
  3488.  
  3489.  
  3490.  
  3491.  
  3492.  
  3493.  
  3494.  
  3495.  
  3496.        AI Programming in SNOBOL4       - 53 -                    August, 1982
  3497.  
  3498.  
  3499.  
  3500.  
  3501.  
  3502.  
  3503.  
  3504.  
  3505.                                      APPENDIX C
  3506.  
  3507.             This is a compilation listing of a program to analyze English
  3508.           word endings.  It is based on the flowchart and discussion given
  3509.           in Winograd (1972, pp. 73-76).
  3510.  
  3511.              *
  3512.              *
  3513.              * Analysis of English Endings
  3514.              *
  3515.              * SNO8OL4 implementation of flowchart on p. 74 of
  3516.              *     Winograd, T.  Understanding Natural Language.
  3517.              *           New York:  Academic Press, 1972.
  3518.              *
  3519.           1   DEFINE('WORDEND(WORD)L,VOWEL,DOUBLE,LIQUID,NOEND')
  3520.           2   DEFINE('MATCH(L,PAT)')
  3521.           3   DEFINE('CUT(N)')
  3522.           4   DEFINE('ADDON(X)')
  3523.           5   DEFINE('TRY()')
  3524.           6              :(WORDEND.END)
  3525.              *
  3526.           7  WORDEND
  3527.           8          WRD = WORD
  3528.           9          DOUBLE = (LEN(1) $ L) *L RPOS(0)
  3529.           10         LIQUID = ANY("LRSVZ")
  3530.           11         NOEND = ANY("CGSVZ")
  3531.           12         VOWEL = ANY("AEIOUY")
  3532.              *
  3533.           13         WRD
  3534.              +       ("N'T" | "'S" | "S'" | "S" | "LY" |
  3535.              +        "ING" | "ED" | "EN" | "ER" | "EST")
  3536.              +        $ WEND RPOS(0) =                        :F(WTRY)
  3537.              *
  3538.           14         WEND POS(0) ("S" | "'S" | "S'" | "N'T") RPOS(0)
  3539.              +                                                :F(WORDEND.1)
  3540.              *
  3541.           15         MATCH(1,"E")                             :F(WTRY)
  3542.           16         MATCH(2,"I") CUT(2) ADDON("Y")           :S(WTRY)
  3543.           17         MATCH(2,"TH")                            :S(WTRY)
  3544.           18         MATCH(2,ANY("HX")) CUT(1)                :S(WTRY)
  3545.           19         MATCH(2,ANY("SZ") ANY("SZ")) CUT(1)      :S(WTRY)
  3546.           20         MATCH(2,ANY("SZ"))                       :S(WTRY)
  3547.           21         MATCH(2,"V")                             :F(WTRY)
  3548.           22         ~TRY() CUT(2) ADDON("FE")            :S(WTRY)F(RETURN)
  3549.              *
  3550.           23 WORDEND.1
  3551.           24         IDENT(WEND,"LY")                         :F(WORDEND.2)
  3552.           25         MATCH(1,"I") CUT(1) ADDON("Y")           :S(WTRY)
  3553.           26         ~TRY() ADDON("LE")                   :S(WTRY)F(RETURN)
  3554.              *
  3555.           27 WORDEND.2
  3556.           28         MATCH(1,VOWEL)                           :F(WORDEND.3)
  3557.           29         MATCH(1,"I") CUT(1) ADDON("Y")           :S(WTRY)
  3558.           30         MATCH(1,"Y")                             :S(WTRY)
  3559.  
  3560.  
  3561.  
  3562.        AI Programming in SNOBOL4       - 54 -                    August, 1982
  3563.  
  3564.  
  3565.  
  3566.  
  3567.  
  3568.  
  3569.  
  3570.  
  3571.           31         ~MATCH(1,"E") ADDON("E")                 :S(WTRY)
  3572.           32         MATCH(2,"E")                             :S(WTRY)
  3573.           33         ~TRY() ADDON("E")                    :S(WTRY)F(RETURN)
  3574.              *
  3575.           34 WORDEND.3
  3576.           35         MATCH(1,"H")                             :F(WORDEND.4)
  3577.           36         MATCH(2,"T")                             :F(WTRY)
  3578.           37         ~TRY() ADDON("E")                    :S(WTRY)F(RETURN)
  3579.              *
  3580.           38 WORDEND.4
  3581.           39         WRD DOUBLE                               :F(WORDEND.5)
  3582.           40         ~MATCH(1,LIQUID) CUT(1)                  :S(WTRY)
  3583.           41         ~TRY() CUT(1)                        :S(WTRY)F(RETURN)
  3584.              *
  3585.           42 WORDEND.5
  3586.           43         MATCH(2,VOWEL)                           :S(WORDEND.6)
  3587.           44         MATCH(1,"RL")                            :S(WTRY)
  3588.           45         MATCH(1,LIQUID | NOEND) ADDON("E")       :(WTRY)
  3589.              *
  3590.           46 WORDEND.6
  3591.           47         ~MATCH(3,VOWEL) ADDON("E")               :S(WTRY)
  3592.           48         MATCH(1,NOEND) ADDON("E")                :(WTRY)
  3593.              *
  3594.           49 WTRY    TRY()                             :S(RETURN)F(FRETURN)
  3595.              *
  3596.              *
  3597.              *
  3598.           50 MATCH
  3599.           51         WRD PAT RPOS(L - 1)               :S(RETURN)F(FRETURN)
  3600.              *
  3601.           52 CUT
  3602.           53         WRD RPOS(N) REM =                         :(RETURN)
  3603.              *
  3604.           54 ADDON
  3605.           55         WRD = WRD X                               :(RETURN)
  3606.              *
  3607.           56 TRY
  3608.           57         DIFFER(DICTIONARY<WRD>)           :S(RETURN)F(FRETURN)
  3609.              *
  3610.           58 WORDEND.END
  3611.              *
  3612.              *
  3613.              * Main program starts here
  3614.              *
  3615.           59  INPUT(.INPUT,,80)
  3616.           60  OUTPUT(.OUTPUT,,80)
  3617.           61  DICTIONARY = TABLE(101)
  3618.           62  WORDS  =
  3619.              +     'BASH,BATHE,LEAN,LEAVE,DENT,DANCE,DOG,KISS,CURVE,'
  3620.              +     'CURL,ROT,ROLL,PLAY,PLY,REAL,PALE,KNIFE,PRETTY,'
  3621.              +     'NOBLE,PATROL,'
  3622.           63 DICT.LOOP
  3623.           64        WORDS BREAK(',') . W LEN(1) =         :F(DICT.LOOP.END)
  3624.           65        DICTIONARY<W> = 1                          :(DICT.LOOP)
  3625.  
  3626.  
  3627.  
  3628.        AI Programming in SNOBOL4       - 55 -                    August, 1982
  3629.  
  3630.  
  3631.  
  3632.  
  3633.  
  3634.  
  3635.  
  3636.  
  3637.           66 DICT.LOOP.END
  3638.              *
  3639.           67         SETEXIT( .CONTINUE)
  3640.           68 READ   W = INPUT                                  :F(END)
  3641.           69        WORDEND(W)
  3642.           70        OUTPUT = WRD                               :(READ)
  3643.           71 END
  3644.  
  3645.  
  3646.  
  3647.  
  3648.  
  3649.  
  3650.  
  3651.  
  3652.  
  3653.  
  3654.  
  3655.  
  3656.  
  3657.  
  3658.  
  3659.  
  3660.  
  3661.  
  3662.  
  3663.  
  3664.  
  3665.  
  3666.  
  3667.  
  3668.  
  3669.  
  3670.  
  3671.  
  3672.  
  3673.  
  3674.  
  3675.  
  3676.  
  3677.  
  3678.  
  3679.  
  3680.  
  3681.  
  3682.  
  3683.  
  3684.  
  3685.  
  3686.  
  3687.  
  3688.  
  3689.  
  3690.  
  3691.  
  3692.  
  3693.  
  3694.        AI Programming in SNOBOL4       - 56 -                    August, 1982
  3695.  
  3696.  
  3697.  
  3698.  
  3699.  
  3700.  
  3701.  
  3702.  
  3703.                                      APPENDIX D
  3704.  
  3705.             This is a compilation listing of a SNOBOL4 program to play the
  3706.           game Kalah.  The game and a LISP program to play it are given in
  3707.           Shapiro (1979, pp. 31-55).  A LISP program which closely follows
  3708.           Shapiro's is included here in boxed groups of comments in order
  3709.           to facilitate comparison of LISP and SNOBOL4 code (EDITOR'S NOTE:
  3710.           LISP CODE NOT AVAILABLE AT THIS TIME).
  3711.  
  3712.               *
  3713.               *
  3714.               *       KALAH in SNOBOL4 (Spitbol)
  3715.               *
  3716.               *
  3717.               *     The program follows a LISP program which appeared in
  3718.               *      Shapiro, S.C., Techniques of Artificial Intelligence,
  3719.               *       New York: Van Nostrand, 1979, pp. 31-55.
  3720.               *
  3721.               * Try it with 4 stones per pot, and a search depth of only 1.
  3722.               *
  3723.               * This is one of the many variations of Mancala games.
  3724.               * Mancala games are popular in Africa and India.  It is a
  3725.               * very old game; boards have been found in Ancient Egyptian
  3726.               * ruins.  Some of the names of different versions are:
  3727.               * Mankala'h, Pallanguli, Wari, Awari, and Ba-Awa.
  3728.               * We do not know the precise name of this version
  3729.               *
  3730.               * The board consists of two rows of six depressions, called
  3731.               * 'pits' or 'pots'.  A larger pit at each end holds captured
  3732.               * pieces.
  3733.               *
  3734.               * The board is as follows: (integers are pot numbers, 'P' is
  3735.               * the computer, 'O' is the user.
  3736.               *
  3737.               *         P6  P5  P4  P3  P2  P1
  3738.               *  PKALAH                        OKALAH
  3739.               *         O1  O2  O3  O4  O5  O6
  3740.               *
  3741.               *       The move path is counter-clockwise.
  3742.               *        For the computer: P1-P6-PKALAH-O1-O6-P1,
  3743.               *        and for the user, O1-O6-OKALAH-P1-P6-O1.
  3744.               *
  3745.               * Initially, P1-P6 and O1-O6 are filled with the desired
  3746.               * number of stones.  A move is made by taking all the stones
  3747.               * from a numbered pot on your side, and sowing them one-
  3748.               * by-one into succeeding pots along your path.  If your last
  3749.               * stone went into the KALAH, you get another turn.
  3750.               * If the last stone went into a numbered pot ON YOUR SIDE
  3751.               * that was empty, you take that stone, and any stones in your
  3752.               * opponents opposite pot, place them in your KALAH.  The game
  3753.               * ends when one side has a majorityof the stones in their
  3754.               * KALAH.  You lose if it is your turn and all of your pots
  3755.               * are empty (you have no play).
  3756.               *
  3757.  
  3758.  
  3759.  
  3760.        AI Programming in SNOBOL4       - 57 -                    August, 1982
  3761.  
  3762.  
  3763.  
  3764.  
  3765.  
  3766.  
  3767.  
  3768.  
  3769.               *
  3770.               * Keyword settings
  3771.               *
  3772.           1           &ANCHOR = 0
  3773.           2           &DUMP = 0
  3774.           3           &FTRACE = 0
  3775.           4           &FULLSCAN = 1
  3776.           5           &MAXLNGTH = 32758
  3777.           6           &STLIMIT = -1
  3778.           7           &TRACE = 0
  3779.           8           &TRIM = 1
  3780.               *
  3781.               * I/O Associations.
  3782.               *
  3783.           9           INPUT(.INPUT)
  3784.           10          OUTPUT(.OUTPUT)
  3785.           11          OUTPUT(.SHADOW,1,,'SHADOW')
  3786.               *
  3787.               * Defined datatypes
  3788.               *
  3789.           12          DATA( 'POT(OWNER,NUM,KVALUE,OPP,PPATH,OPATH)' )
  3790.           13          DATA( 'NODE(PLAYER,MOVEOF,NEXT_PLAYER)' )
  3791.           14          DATA( 'STACK(TOP,REST)' )
  3792.               *
  3793.               * Global constants
  3794.               *
  3795.           15          null = ''
  3796.           16          nil = STACK(null,null)
  3797.           17          TOP(nil) = nil
  3798.           18          REST(nil) = nil
  3799.           19          t = COPY(nil)
  3800.           20          TOP(t) = t
  3801.           21          REST(t) = t
  3802.           22          nilpot = POT(null,0,nil,null,null,null)
  3803.           23          OPP(nilpot) = nilpot
  3804.           24          PPATH(nilpot) = nilpot
  3805.           25          OPATH(nilpot) = nilpot
  3806.               *
  3807.               * Utility patterns
  3808.               *
  3809.           26          LEFT_END = POS(0)
  3810.           27          RIGHT_END = RPOS(0)
  3811.           28          TO_NEXT_BLANK = BREAK(' ')
  3812.           29          SKIP_BLANKS = SPAN(' ')
  3813.               *
  3814.               * Utility functions
  3815.               *
  3816.               * DEF - Define other functions
  3817.           30          DEFINE( 'DEF(NAME,ARGS,LOCALS,BODY,RTN)', 'DEF1')
  3818.               +            :(DEF_END)
  3819.           31  DEF1    ARGS ' ' = ','                            :S(DEF1)
  3820.           32  DEF2    LOCALS ' ' = ','                          :S(DEF2)
  3821.               *
  3822.               * Define new function
  3823.  
  3824.  
  3825.  
  3826.        AI Programming in SNOBOL4       - 58 -                    August, 1982
  3827.  
  3828.  
  3829.  
  3830.  
  3831.  
  3832.  
  3833.  
  3834.  
  3835.           33          DEFINE( NAME '(' ARGS ')' LOCALS )
  3836.               * Build body with proper return
  3837.           34          BODY = IDENT( RTN, null) BODY ' :(RETURN)'
  3838.               +          :S(DEF3)
  3839.           35          BODY = IDENT( RTN, 'S') BODY ' :S(RETURN)F(FRETURN)'
  3840.               +          :S(DEF3)
  3841.           36          BODY = IDENT( RTN, 'F') BODY ' :F(RETURN)S(FRETURN)'
  3842.               +          :S(DEF3)
  3843.           37          BODY = IDENT( RTN, 'N') BODY ' :(NRETURN)'
  3844.           38  DEF3    CODE(NAME ' ' BODY)            :(RETURN)
  3845.           39  DEF_END
  3846.               *
  3847.               * APPEND3
  3848.           40          DEFINE( 'APPEND3(S1,S2,S3)' )  :(APPEND3_END)
  3849.           41  APPEND3
  3850.           42         APPEND3 = ( NULL(S1) NULL(S2) NULL(S3)) nil :S(RETURN)
  3851.           43         APPEND3 = ( NULL(S1) NULL(S2))
  3852.               +           STACK( TOP(S3), APPEND3( S1, S2, REST(S3)))
  3853.               +            :S(RETURN)
  3854.           44          APPEND3 = NULL(S1)
  3855.               +           STACK( TOP(S2), APPEND3( S1, REST(S2), S3))
  3856.               +            :S(RETURN)
  3857.           45          APPEND3 =
  3858.               +           STACK( TOP(S1), APPEND3( REST(S1), S2, S3))
  3859.               +           :(RETURN)
  3860.           46  APPEND3_END
  3861.               *
  3862.               * NULL - Succeed if stack empty
  3863.           47          DEF( 'NULL', 'X',, 'IDENT(X,nil)', 'S')
  3864.               *
  3865.               * TRUE - Succeed if stack not empty
  3866.           48          DEF( 'TRUE', 'X',, 'DIFFER(X,nil)', 'S')
  3867.               *
  3868.               * MAX   - Maximum of two values
  3869.           49        DEF( 'MAX', 'N1 N2',, 'MAX = N1 ; MAX = GT(N2,N1) N2')
  3870.               *
  3871.               * MIN   - Minimum of two values
  3872.           50        DEF( 'MIN', 'N1 N2',, 'MIN = N1 ; MIN = LT(N2,N1) N2')
  3873.               *
  3874.               * CNTR - Center string in field
  3875.           51          DEFINE( 'CNTR(N,V)X' )               :(CNTR_END)
  3876.           52  CNTR
  3877.           53          X = CONVERT( (N - SIZE(V)) / 2, 'INTEGER')
  3878.           54          CNTR = LPAD(RPAD(V, N - X), N)       :(RETURN)
  3879.           55  CNTR_END
  3880.               *
  3881.           56          PRT_PAT = LEFT_END REM $ OUTPUT $ SHADOW
  3882.               *
  3883.               * PRT - String to OUTPUT and SHADOW
  3884.           57          DEF( 'PRT', 'X',, 'X PRT_PAT' )
  3885.               *
  3886.               * Core functions for alpha-beta serach
  3887.               *
  3888.               * SEARCH
  3889.  
  3890.  
  3891.  
  3892.        AI Programming in SNOBOL4       - 59 -                    August, 1982
  3893.  
  3894.  
  3895.  
  3896.  
  3897.  
  3898.  
  3899.  
  3900.  
  3901.           58        DEFINE( 'SEARCH(NODE,LEVEL,ALPHA,BETA)' ) :(SEARCH_END)
  3902.           59  SEARCH
  3903.           60        NODE = START(NODE)
  3904.           61        SEARCH = DEAD(NODE,LEVEL) STATIC(NODE)    :S(SEARCH_A)
  3905.           62          SEARCH =
  3906.               +        SEARCH1( MAXER(NODE), (LEVEL - 1), ALPHA, BETA,
  3907.               +          EXPAND(NODE))
  3908.           63  SEARCH_A
  3909.           64          NEND(NODE)                               :(RETURN)
  3910.           65  SEARCH_END
  3911.               *
  3912.               * SEARCH1
  3913.           66         DEFINE( 'SEARCH1(MAXR,LVL,ALPHA,BETA,NL)' )
  3914.               +            :(SEARCH1_END)
  3915.           67  SEARCH1
  3916.           68          SEARCH1 =
  3917.               +         SEARCH2( MAXR, LVL, ALPHA, BETA, REST(NL),
  3918.               +            SEARCH( TOP(NL), LVL, ALPHA, BETA))    :(RETURN)
  3919.           69  SEARCH1_END
  3920.               *
  3921.               * SEARCH2
  3922.           70          DEFINE( 'SEARCH2(MAXR,LVL,ALPHA,BETA,NL,PBV)' )
  3923.               +            :(SEARCH2_END)
  3924.           71  SEARCH2
  3925.           72          SEARCH2 = NULL(NL) PBV                     :S(RETURN)
  3926.           73          SEARCH2 = CUTOFF( MAXR, PBV, ALPHA, BETA ) PBV
  3927.               +          :S(RETURN)
  3928.           74       SEARCH2 = TRUE(MAXR)
  3929.               +     SEARCH2( MAXR, LVL, ALPHA, BETA, REST(NL),
  3930.               +     MAX( PBV, SEARCH( TOP(NL), LVL, MAX(ALPHA,PBV), BETA)))
  3931.               +               :S(RETURN)
  3932.           75       SEARCH2 =
  3933.               +     SEARCH2( MAXR, LVL, ALPHA, BETA, REST(NL),
  3934.               +     MIN( PBV, SEARCH( TOP(NL), LVL, ALPHA, MIN(BETA,PBV))))
  3935.               +               :(RETURN)
  3936.           76  SEARCH2_END
  3937.               *
  3938.               * CUTOFF
  3939.           77          DEFINE( 'CUTOFF(MAXR,PBV,ALPHA,BETA)' ) :(CUTOFF_END)
  3940.           78  CUTOFF
  3941.           79          TRUE(MAXR)                       :F(CUTOFF1)
  3942.           80          GE( PBV, BETA)                   :S(RETURN)F(FRETURN)
  3943.           81  CUTOFF1
  3944.           82          LE( PBV, ALPHA)                  :S(RETURN)F(FRETURN)
  3945.           83  CUTOFF_END
  3946.               *
  3947.               *
  3948.               * Functions defining the representations of the board
  3949.               *
  3950.           84          OTHERP = 'O' ; OTHERO = 'P'
  3951.               *
  3952.               * OTHER - Other player (P or O)
  3953.           86     DEF( 'OTHER', 'PL',, 'OTHER = $( "OTHER" PL)' )
  3954.               *
  3955.  
  3956.  
  3957.  
  3958.        AI Programming in SNOBOL4       - 60 -                    August, 1982
  3959.  
  3960.  
  3961.  
  3962.  
  3963.  
  3964.  
  3965.  
  3966.  
  3967.               * POTR - Player's POT n
  3968.           87     DEF( 'POTR', 'PL N',, 'POTR = $( PL N)' )
  3969.               *
  3970.               * KALAHR - Player's Kalah
  3971.           88     DEF( 'KALAHR', 'PL',, 'KALAHR = $( PL "KALAH" )' )
  3972.               *
  3973.               * VALUE
  3974.           89     DEF( 'VALUE', 'POT',, 'VALUE = TOP(KVALUE(POT))' )
  3975.               *
  3976.               * PUSHVAL
  3977.           90     DEF( 'PUSHVAL', 'POT VAL',, 'KVALUE(POT) ='
  3978.               +        ' STACK(VAL,KVALUE(POT))' )
  3979.               *
  3980.               * POPVAL
  3981.           91     DEF( 'POPVAL', 'POT',, 'KVALUE(POT) = REST(KVALUE(POT))' )
  3982.               *
  3983.               * CHANGEVAL
  3984.           92     DEF( 'CHANGEVAL', 'POT VAL',, 'TOP(KVALUE(POT)) = VAL' )
  3985.               *
  3986.               * EMPTY - Succeed if pot empty
  3987.           93     DEF( 'EMPTY', 'POT',, 'EQ(VALUE(POT), 0)', 'S')
  3988.               *
  3989.               * PATHR - Player's move path
  3990.           94     DEF( 'PATHR', 'PL',, 'PATHR = PL "PATH" ' )
  3991.               *
  3992.               *
  3993.           95          PSIDE = 'P1 P2 P3 P4 P5 P6 '
  3994.           96          OSIDE = 'O1 O2 O3 O4 O5 O6 '
  3995.               *
  3996.               * SIDER - Get string of player's pots
  3997.           97          DEF( 'SIDER', 'PL',, 'SIDER = $( PL "SIDE" )' )
  3998.               *
  3999.               * SETPATH - Link pots thru PATH fields
  4000.           98          DEFINE( 'SETPATH(P,LAT)' )
  4001.           99          SETPATH_PAT = LEFT_END ( TO_NEXT_BLANK $ A )
  4002.               +                     SKIP_BLANKS
  4003.               +                     (( ( TO_NEXT_BLANK $ B) REM) $ C)
  4004.               +                        :(SETPATH_END)
  4005.           100 SETPATH
  4006.           101         LAT SETPATH_PAT = C                       :F(RETURN)
  4007.               *
  4008.               * xPATH(pot) = pot
  4009.           102         :<CODE(' ' P '(' A ') = ' B ' :(SETPATH)')>
  4010.           103 SETPATH_END
  4011.           104         SETSYM_PAT = FENCE ( TO_NEXT_BLANK $ A)
  4012.               +              SKIP_BLANKS ( TO_NEXT_BLANK $ B)
  4013.               +              SKIP_BLANKS *SETSYM1()
  4014.               +              *SETSYM_PAT
  4015.               * SETSYM - Link pots to opposites
  4016.               *
  4017.           105         DEF( 'SETSYM', 'P L',, 'L SETSYM_PAT')
  4018.               *
  4019.               * SETSYM1 - Helper for SETSYM
  4020.           106         DEFINE( 'SETSYM1()' )                 :(SETSYM1_END)
  4021.  
  4022.  
  4023.  
  4024.        AI Programming in SNOBOL4       - 61 -                    August, 1982
  4025.  
  4026.  
  4027.  
  4028.  
  4029.  
  4030.  
  4031.  
  4032.  
  4033.               *
  4034.               * OPP(pot) = pot
  4035.           107 SETSYM1
  4036.           108         :<CODE(' ' P '(' A ') = ' B ' ; '
  4037.               +          P '(' B ') = ' A ' :(RETURN)')>
  4038.           109 SETSYM1_END
  4039.               *
  4040.               * Functions to define the legal moves
  4041.               *
  4042.               * MOVE - Make player's move for pot
  4043.           110         DEFINE( 'MOVE(PL,POT)' )             :(MOVE_END)
  4044.           111 MOVE
  4045.           112         MOVE1(PL,POT,TAKE(POT),PATHR(PL),KALAHR(PL))
  4046.               +               :S(RETURN)F(FRETURN)
  4047.           113 MOVE_END
  4048.               *
  4049.               * MOVE1 - helper for MOVE
  4050.           114         DEFINE( 'MOVE1(PL,POT,STONES,PATH,KALAH)' )
  4051.               +              :(MOVE1_END)
  4052.               *
  4053.               * Distribute all stones
  4054.           115 MOVE1
  4055.           116         EQ(STONES,0)                              :S(MOVE1A)
  4056.               *
  4057.               * Next pot along path
  4058.           117         POT = APPLY(PATH,POT)
  4059.               *
  4060.               * Put a stone in it
  4061.           118         DROP(1,POT)
  4062.               *
  4063.               * One less stone
  4064.           119         STONES = STONES - 1                      :(MOVE1)
  4065.               *
  4066.               * Check capture
  4067.           120 MOVE1A
  4068.           121         CHECKCAP( POT, PL, KALAH, OPP(POT))
  4069.               *
  4070.               * Check for empty side
  4071.           122         CHECKMT()
  4072.               *
  4073.               * Ck last stone land in Kalah?
  4074.           123         IDENT( POT, KALAH)              :S(RETURN)F(FRETURN)
  4075.           124 MOVE1_END
  4076.               *
  4077.               * If there is 1 stone in the pot,
  4078.               *  and it is my pot,
  4079.               *   and it is not the Kalah,
  4080.               *    and the opponent's pot is not empty, THEN
  4081.               *     transfer stones from my pot to the Kalah and
  4082.               *     transfer stones from opponent's pot to the Kalah.
  4083.               *
  4084.               * CHECKCAP - Check for capture
  4085.           125         DEFINE( 'CHECKCAP(POT,PL,KALAH,OPPOT)' )
  4086.               +                  :(CHECKCAP_END)
  4087.  
  4088.  
  4089.  
  4090.        AI Programming in SNOBOL4       - 62 -                    August, 1982
  4091.  
  4092.  
  4093.  
  4094.  
  4095.  
  4096.  
  4097.  
  4098.  
  4099.           126 CHECKCAP
  4100.           127          ( EQ(VALUE(POT), 1)
  4101.               +           IDENT(OWNER(POT),PL)
  4102.               +            DIFFER(POT,KALAH)
  4103.               +             ~EMPTY(OPPOT)
  4104.               +              DROP(TAKE(POT), KALAH)
  4105.               +               DROP(TAKE(OPPOT), KALAH) )        :(RETURN)
  4106.           128 CHECKCAP_END
  4107.               *
  4108.               * If one side empty, transfer all stones from other side to
  4109.               * their Kalah.
  4110.               * CHECKMT - Check side empty
  4111.           129         DEFINE( 'CHECKMT()' )                 :(CHECKMT_END)
  4112.           130 CHECKMT
  4113.           131         ( MTSIDEP(PSIDE) MTSIDE(OSIDE,OKALAH) )  :S(RETURN)
  4114.           132         ( MTSIDEP(OSIDE) MTSIDE(PSIDE,PKALAH) )
  4115.               +               :S(RETURN)F(FRETURN)
  4116.           133 CHECKMT_END
  4117.               *
  4118.               * Use recursive pattern to loop scan
  4119.           134         MTSIDEP_PAT = FENCE
  4120.               +              TO_NEXT_BLANK $ P  *EMPTY($P)
  4121.               +              SKIP_BLANKS (RIGHT_END | *MTSIDEP_PAT)
  4122.               *
  4123.               * MTSIDEP - Scan side for all empty
  4124.           135         DEF( 'MTSIDEP', 'SIDE',, 'SIDE MTSIDEP_PAT', 'S')
  4125.               *
  4126.               * Use recursive pattern to loop calls
  4127.           136         MTSIDE_PAT = FENCE
  4128.               +              TO_NEXT_BLANK $ P *DROP(TAKE($P),KALAH)
  4129.               +              SKIP_BLANKS *MTSIDE_PAT
  4130.               *
  4131.               * MTSIDE - Transfer all on side to Kalah
  4132.           137      DEF( 'MTSIDE', 'SIDE KALAH',, 'SIDE MTSIDE_PAT' )
  4133.               *
  4134.               * TAKE - Remove stones from pot
  4135.           138      DEF( 'TAKE', 'POT',, 'TAKE = VALUE(POT) ;'
  4136.               +       ' CHANGEVAL(POT,0)' )
  4137.               *
  4138.               * DROP - Add stones to pot
  4139.           139      DEF( 'DROP', 'N POT',, 'CHANGEVAL(POT,'
  4140.               +       ' (N + VALUE(POT)))' )
  4141.               *
  4142.               * Functions to operate on game-tree nodes
  4143.               *
  4144.               * MULT
  4145.           140      DEF( 'MULT', 'NODE',,
  4146.               +        'IDENT(PLAYER(NODE),NEXT_PLAYER(NODE))', 'S')
  4147.               *
  4148.               * START
  4149.           141         DEFINE( 'START(NODE)' )
  4150.           142         START_PAT = FENCE
  4151.               +              TO_NEXT_BLANK $ P *PUSHVAL($P,VALUE($P))
  4152.               +              SKIP_BLANKS *START_PAT          :(START_END)
  4153.  
  4154.  
  4155.  
  4156.        AI Programming in SNOBOL4       - 63 -                    August, 1982
  4157.  
  4158.  
  4159.  
  4160.  
  4161.  
  4162.  
  4163.  
  4164.  
  4165.           143 START
  4166.           144         (PSIDE 'PKALAH ' OSIDE 'OKALAH ') START_PAT
  4167.           145         MOVE( PLAYER(NODE), MOVEOF(NODE))
  4168.           146         START =
  4169.               +        NODE( NEXT_PLAYER(NODE), MOVEOF(NODE), PLAYER(NODE))
  4170.               +          :(RETURN)
  4171.           147 START_END
  4172.               *
  4173.           148         NEND_PAT = FENCE
  4174.               +         TO_NEXT_BLANK $ P   *POPVAL($P)
  4175.               +         SKIP_BLANKS *NEND_PAT
  4176.               *
  4177.               * NEND
  4178.           149     DEF( 'NEND', 'NODE',, '(PSIDE "PKALAH " OSIDE "OKALAH ")'
  4179.               +         ' NEND_PAT' )
  4180.               *
  4181.               * DEAD
  4182.           150         DEFINE( 'DEAD(NODE,LEVEL)' )           :(DEAD_END)
  4183.           151 DEAD
  4184.           152         ( LE(LEVEL,0) ~MULT(NODE) )            :S(RETURN)
  4185.           153         GT( VALUE(PKALAH), HALFSTONES)         :S(RETURN)
  4186.           154         GT( VALUE(OKALAH), HALFSTONES)         :S(RETURN)
  4187.           155         ( EQ( VALUE(PKALAH), HALFSTONES)
  4188.               +       EQ( VALUE(OKALAH), HALFSTONES) ) :S(RETURN)F(FRETURN)
  4189.           156 DEAD_END
  4190.               *
  4191.               * STATIC
  4192.           157         DEFINE( 'STATIC(NODE)' )               :(STATIC_END)
  4193.           158 STATIC
  4194.           159         TNODES = TNODES + 1
  4195.           160         STATIC = GT( VALUE(PKALAH), HALFSTONES) INFINITY
  4196.               +              :S(RETURN)
  4197.           161         STATIC = GT( VALUE(OKALAH), HALFSTONES) -INFINITY
  4198.               +              :S(RETURN)
  4199.           162         STATIC = VALUE(PKALAH) - VALUE(OKALAH)  :(RETURN)
  4200.           163 STATIC_END
  4201.               *
  4202.               * MAXER
  4203.           164         DEFINE( 'MAXER(NODE)' )              :(MAXER_END)
  4204.           165 MAXER
  4205.           166         MAXER = nil
  4206.           167         MAXER = IDENT( PLAYER(NODE), 'P') t  :(RETURN)
  4207.           168 MAXER_END
  4208.               *
  4209.               * EXPAND
  4210.           169         DEFINE( 'EXPAND(NODE)' )             :(EXPAND_END)
  4211.           170 EXPAND
  4212.           171         BNODES = BNODES + 1
  4213.           172         EXPAND = EXPAND1( PLAYER(NODE), SIDER(PLAYER(NODE)))
  4214.               +            :(RETURN)
  4215.           173 EXPAND_END
  4216.               *
  4217.               * EXPAND1
  4218.           174         DEFINE( 'EXPAND1(PL,SIDE)LMULT,LCAP,LREG')
  4219.  
  4220.  
  4221.  
  4222.        AI Programming in SNOBOL4       - 64 -                    August, 1982
  4223.  
  4224.  
  4225.  
  4226.  
  4227.  
  4228.  
  4229.  
  4230.  
  4231.           175         EXPAND1_PAT = FENCE
  4232.               +              TO_NEXT_BLANK $ P  *EXPAND2(PL,$P)
  4233.               +              SKIP_BLANKS  *EXPAND1_PAT    :(EXPAND1_END)
  4234.           176 EXPAND1
  4235.           177         LMULT = nil ; LCAP = nil ; LREG = nil
  4236.           180         SIDE EXPAND1_PAT
  4237.           181         EXPAND1 = APPEND3( LMULT, LCAP, LREG)   :(RETURN)
  4238.           182 EXPAND1_END
  4239.               *
  4240.               * EXPAND2
  4241.           183         DEFINE( 'EXPAND2(PL,POT)' )         :(EXPAND2_END)
  4242.           184 EXPAND2
  4243.           185         EMPTY(POT)                          :S(RETURN)
  4244.           186         LMULT = MULTMOVE(POT)
  4245.               +         STACK( NODE(PL,POT,PL), LMULT)    :S(RETURN)
  4246.           187         LCAP = CAPMOVE(PL,POT)
  4247.               +         STACK( NODE(PL,POT,OTHER(PL)), LCAP) :S(RETURN)
  4248.           188         LREG =
  4249.               +         STACK( NODE(PL,POT,OTHER(PL)), LREG) :(RETURN)
  4250.           189 EXPAND2_END
  4251.               *
  4252.               * MULTMOVE
  4253.           190         DEF( 'MULTMOVE', 'POT',,
  4254.               +          'EQ(REMDR(VALUE(POT),13), 7 - NUM(POT))', 'S')
  4255.               *
  4256.               * CAPMOVE
  4257.           191         DEF( 'CAPMOVE', 'PL POT',,
  4258.               +         'CAPMOVE1(PL,POT,VALUE(POT),NUM(POT))', 'S')
  4259.               *
  4260.               * CAPMOVE1
  4261.           192         DEFINE( 'CAPMOVE1(PL,POT,V,N)' )    :(CAPMOVE1_END)
  4262.           193 CAPMOVE1
  4263.           194         EQ(V,13)                            :S(RETURN)
  4264.           195         ( LT(V, (7 - N))
  4265.               +         EMPTY( POTR( PL, (N + V)))
  4266.               +         ~EMPTY( OPP( POTR( PL, (N + V)))) )   :S(RETURN)
  4267.           196         ( GT(V, (13 - N))
  4268.               +      LT(V, 13)
  4269.               +      EMPTY( POTR( PL, (N - 13 + V))) ) :S(RETURN)F(FRETURN)
  4270.           197 CAPMOVE1_END
  4271.               *
  4272.               * Functions for controlling an interactive game
  4273.               *
  4274.               * KALAH - Play the game
  4275.           198         DEFINE( 'KALAH(N,DEPTH)' )            :(KALAH_END)
  4276.           199 KALAH
  4277.           200         ( INITBRD(N)
  4278.               +         PRINTBRD()
  4279.               +         ALTMOVE(MEFIRST()) )
  4280.           201           KALAH = 'Thanks.'                   :(RETURN)
  4281.           202 KALAH_END
  4282.               *
  4283.               * INITBRD - Iniitalize board
  4284.           203         DEFINE( 'INITBRD(VAL)' )
  4285.  
  4286.  
  4287.  
  4288.        AI Programming in SNOBOL4       - 65 -                    August, 1982
  4289.  
  4290.  
  4291.  
  4292.  
  4293.  
  4294.  
  4295.  
  4296.  
  4297.           204         INITBRD_PAT = FENCE
  4298.               +         TO_NEXT_BLANK $ P  *INITBRD1(P)
  4299.               +         SKIP_BLANKS  *INITBRD_PAT
  4300.           205         INITBRD1_PAT = LEFT_END
  4301.               +              ( (LEN(1) $ O) (LEN(1) $ N) )
  4302.           206         :(INITBRD_END)
  4303.           207 INITBRD
  4304.           208         INFINITY = 100
  4305.           209         TNODES = 0
  4306.           210         BNODES = 0
  4307.           211         HALFSTONES = 6 * VAL
  4308.           212         (PSIDE OSIDE) INITBRD_PAT
  4309.           213         PKALAH = POT('P',0,STACK(0,nil),null,null,null)
  4310.           214         OKALAH = POT('O',0,STACK(0,nil),null,null,null)
  4311.           215         SETSYM('OPP','P1 O6 P2 O5 P3 O4 P4 O3 P5 O2 P6 O1 ')
  4312.           216         SETPATH('PPATH',
  4313.               +          'P1 P2 P3 P4 P5 P6 PKALAH O1 O2 O3 O4 O5 O6 P1 ')
  4314.           217         SETPATH('OPATH',
  4315.               +          'O1 O2 O3 O4 O5 O6 OKALAH P1 P2 P3 P4 P5 P6 O1 ')
  4316.           218         REWIND(1)
  4317.           219                     :(RETURN)
  4318.           220 INITBRD_END
  4319.               *
  4320.               * INITBRD1
  4321.           221         DEFINE( 'INITBRD1(PNAME)' )          :(INITBRD1_END)
  4322.           222 INITBRD1
  4323.           223         PNAME INITBRD1_PAT
  4324.           224         $PNAME = POT(O,N,STACK(VAL,nil),null,null,null)
  4325.               +              :(RETURN)
  4326.           225 INITBRD1_END
  4327.               *
  4328.               * PRINTBRD - Print board
  4329.           226         DEFINE( 'PRINTBRD()' )            :(PRINTBRD_END)
  4330.           227 PRINTBRD
  4331.           228         PRT( DUPL(' ',7)
  4332.               +            CNTR(7,VALUE(P6))
  4333.               +            CNTR(7,VALUE(P5))
  4334.               +            CNTR(7,VALUE(P4))
  4335.               +            CNTR(7,VALUE(P3))
  4336.               +            CNTR(7,VALUE(P2))
  4337.               +            CNTR(7,VALUE(P1)) )
  4338.           229         PRT( CNTR(7,VALUE(PKALAH))
  4339.               +            DUPL(' ',42)
  4340.               +            CNTR(7,VALUE(OKALAH)) )
  4341.           230         PRT( DUPL(' ',7)
  4342.               +            CNTR(7,VALUE(O1))
  4343.               +            CNTR(7,VALUE(O2))
  4344.               +            CNTR(7,VALUE(O3))
  4345.               +            CNTR(7,VALUE(O4))
  4346.               +            CNTR(7,VALUE(O5))
  4347.               +            CNTR(7,VALUE(O6)) )
  4348.           231                    :(RETURN)
  4349.           232 PRINTBRD_END
  4350.               *
  4351.  
  4352.  
  4353.  
  4354.        AI Programming in SNOBOL4       - 66 -                    August, 1982
  4355.  
  4356.  
  4357.  
  4358.  
  4359.  
  4360.  
  4361.  
  4362.  
  4363.               * MEFIRST
  4364.           233         DEFINE( 'MEFIRST()' )               :(MEFIRST_END)
  4365.           234 MEFIRST
  4366.           235        PRT( 'Do you want to go first?')
  4367.           236        MEFIRST = REPLACE(INPUT,LOWERS,UPPERS)        :F(END)
  4368.           237        SHADOW = DUPL(' ',10) 'Answer: ' MEFIRST
  4369.           238        MEFIRST (LEFT_END ('YES' | 'NO') RIGHT_END) :S(RETURN)
  4370.           239        PRT( 'Please answer YES or NO.')        :(MEFIRST)
  4371.           240 MEFIRST_END
  4372.               *
  4373.               * ALTMOVE
  4374.           241         DEFINE( 'ALTMOVE(YN)' )                :(ALTMOVE_END)
  4375.           242 ALTMOVE
  4376.           243         IDENT(YN,'NO')                         :F(ALTMOVE2)
  4377.           244 ALTMOVE1
  4378.               *
  4379.               * Computer, then user
  4380.           245         ( PMOVE() OMOVE() )            :S(ALTMOVE1)F(RETURN)
  4381.               *
  4382.               * User, then computer
  4383.           246 ALTMOVE2
  4384.           247         ( OMOVE() PMOVE() )            :S(ALTMOVE2)F(RETURN)
  4385.           248 ALTMOVE_END
  4386.               *
  4387.               * OMOVE - Carry out user's move
  4388.           249         DEFINE( 'OMOVE()' )                     :(OMOVE_END)
  4389.               * Check for end of game
  4390.           250 OMOVE
  4391.           251         ENDGAME()                                 :S(FRETURN)
  4392.               *
  4393.               * Get and make move
  4394.           252         MOVE( 'O', GETMOVE() )                    :F(OMOVE1)
  4395.               *
  4396.               * Landed on kalah
  4397.           253         PRINTBRD()
  4398.           254         PRT( 'You go again.' )                    :(OMOVE)
  4399.               *
  4400.               * Did not land on Kalah
  4401.           255 OMOVE1
  4402.           256         PRINTBRD()                                :(RETURN)
  4403.           257 OMOVE_END
  4404.               *
  4405.               * GETMOVE - Get user's move
  4406.           258         DEFINE( 'GETMOVE()N' )             :(GETMOVE_END)
  4407.           259 GETMOVE
  4408.           260         PRT( "What's your move?" )
  4409.           261         N = INPUT                            :F(END)
  4410.           262         SHADOW = DUPL(' ',10) 'Answer: ' N
  4411.           263         GETMOVE =
  4412.               +          ( INTEGER(N) GT(N,0) LT(N,7) ~EMPTY(POTR('O',N)) )
  4413.               +         POTR('O',N)                    :S(RETURN)
  4414.           264         PRT( "That's illegal.")          :(GETMOVE)
  4415.           265 GETMOVE_END
  4416.               *
  4417.  
  4418.  
  4419.  
  4420.        AI Programming in SNOBOL4       - 67 -                    August, 1982
  4421.  
  4422.  
  4423.  
  4424.  
  4425.  
  4426.  
  4427.  
  4428.  
  4429.               * ENDGAME - Check for end of game
  4430.           266         DEFINE( 'ENDGAME()' )            :(ENDGAME_END)
  4431.           267 ENDGAME
  4432.           268         ( GT(VALUE(PKALAH),HALFSTONES) PRT( 'I win.') )
  4433.               +            :S(RETURN)
  4434.           269         ( GT(VALUE(OKALAH),HALFSTONES) PRT( 'You win.') )
  4435.               +            :S(RETURN)
  4436.           270         ( EQ(VALUE(PKALAH),HALFSTONES)
  4437.               +           EQ(VALUE(OKALAH),HALFSTONES)
  4438.               +         PRT( "It's a tie.") )              :S(RETURN)
  4439.           271                :(FRETURN)
  4440.           272 ENDGAME_END
  4441.               *
  4442.               * PMOVE - Make computer's move
  4443.           273         DEFINE( 'PMOVE()' )                  :(PMOVE_END)
  4444.           274 PMOVE
  4445.           275         PRT( 'I go.' )
  4446.           276 PMOVE1
  4447.           277         ENDGAME()                            :S(FRETURN)
  4448.           278         PRT( 'Hmmm....' )
  4449.           279         COLLECT()
  4450.           280         PLAY(-INFINITY,INFINITY,0,0,TIME())  :F(RETURN)
  4451.               *
  4452.               * If computer landed on PKALAH
  4453.           281         PRT( 'I go again.' )                 :(PMOVE1)
  4454.           282 PMOVE_END
  4455.               *
  4456.               * PLAY
  4457.           283         DEFINE( 'PLAY(ALPHA,BETA,BNODES,TNODES,SECS)' )
  4458.               +              :(PLAY_END)
  4459.           284 PLAY
  4460.           285         PLAY1(EXPAND(NODE('P',nilpot,'O')))
  4461.               +                :S(RETURN)F(FRETURN)
  4462.           286 PLAY_END
  4463.               *
  4464.               * PLAY1
  4465.           287         DEFINE( 'PLAY1(LNODES)' )          :(PLAY1_END)
  4466.           288 PLAY1
  4467.           289         NULL(REST(LNODES))                        :F(PLAY1A)
  4468.           290         CHOOSE(REST(LNODES),TOP(LNODES),"not calculated")
  4469.               +                  :S(RETURN)F(FRETURN)
  4470.           291 PLAY1A
  4471.           292         CHOOSE(REST(LNODES),TOP(LNODES),,
  4472.               +         SEARCH(TOP(LNODES),DEPTH,ALPHA,BETA))
  4473.               +                  :S(RETURN)F(FRETURN)
  4474.           293 PLAY1_END
  4475.               *
  4476.               * CHOOSE
  4477.           294         DEFINE( 'CHOOSE(LNODES,BEST,V)NV' )  :(CHOOSE_END)
  4478.           295 CHOOSE
  4479.           296         NULL(LNODES)                         :S(CHOOSE2)
  4480.           297         EQ(V,INFINITY)                       :S(CHOOSE2)
  4481.           298         NV = SEARCH(TOP(LNODES),DEPTH,V,BETA)
  4482.           299         LE(NV,V)                             :S(CHOOSE1)
  4483.  
  4484.  
  4485.  
  4486.        AI Programming in SNOBOL4       - 68 -                    August, 1982
  4487.  
  4488.  
  4489.  
  4490.  
  4491.  
  4492.  
  4493.  
  4494.  
  4495.           300         V = NV
  4496.           301         BEST = TOP(LNODES)
  4497.           302 CHOOSE1
  4498.           303         LNODES = REST(LNODES)                       :(CHOOSE)
  4499.           304 CHOOSE2
  4500.           305         MAKE(BEST,V)                   :S(RETURN)F(FRETURN)
  4501.           306 CHOOSE_END
  4502.               *
  4503.               * MAKE
  4504.           307         DEFINE( 'MAKE(CHOSEN,VAL)' )              :(MAKE_END)
  4505.           308 MAKE
  4506.           309         REPORT(NUM(MOVEOF(CHOSEN)),
  4507.               +         VAL, BNODES, TNODES, TIME() - SECS )
  4508.           310         MOVE(PLAYER(CHOSEN),MOVEOF(CHOSEN))       :F(MAKE1)
  4509.           311         PRINTBRD()                                :(RETURN)
  4510.           312 MAKE1
  4511.           313         PRINTBRD()                                :(FRETURN)
  4512.           314 MAKE_END
  4513.               *
  4514.               * REPORT - Statistics
  4515.           315         DEFINE( 'REPORT(M,V,B,T,S)' )        :(REPORT_END)
  4516.           316 REPORT
  4517.           317         PRT("I pick pot " M ".  Value " V)
  4518.           318         PRT(B " nodes expanded, " T " evaluated")
  4519.           319         PRT(S " milliseconds used")               :(RETURN)
  4520.           320 REPORT_END
  4521.               *
  4522.               * Finally, MAIN PROGRAM STARTS HERE.
  4523.               ************************************
  4524.               *
  4525.           321 GET_NUMBER_OF_STONES
  4526.           322         PRT( 'Enter number of stones per pot' )
  4527.           323         N = INPUT                                 :F(END)
  4528.           324         SHADOW = DUPL(' ',10) 'Answer: ' N
  4529.           325         ( INTEGER(N) GT(N,0) )      :F(GET_NUMBER_OF_STONES)
  4530.           326 GET_SEARCH_DEPTH
  4531.           327         PRT( 'Enter maximum search depth' )
  4532.           328         D = INPUT
  4533.           329         SHADOW = DUPL(' ',10) 'Answer: ' D
  4534.           330         ( INTEGER(D) GT(D,0) )       :F(GET_SEARCH_DEPTH)
  4535.           331         OUTPUT = KALAH(N,D)          :S(GET_NUMBER_OF_STONES)
  4536.           332         ENDFILE(1)
  4537.           333 END
  4538.  
  4539.  
  4540.  
  4541.  
  4542.  
  4543.  
  4544.  
  4545.  
  4546.  
  4547.  
  4548.  
  4549.  
  4550.  
  4551.  
  4552.        AI Programming in SNOBOL4       - 69 -                    August, 1982
  4553.  
  4554.  
  4555.  
  4556.  
  4557.  
  4558.  
  4559.  
  4560.  
  4561.                                      APPENDIX E
  4562.  
  4563.             This is a compilation listing of a compiler for an augmented
  4564.           transition network (ATN) language like the one described in
  4565.           Winston and Horn (1981, pp. 251-277).  The listing of the
  4566.           compiler is followed by a sample programming session (input in
  4567.           the ATN source language, followed by the output produced by
  4568.           executing the ATN program).
  4569.  
  4570.             Note that the ATN source language is an extension of Winston
  4571.           and Horn's, and that the compiler is not very similar to their
  4572.           LISP version.
  4573.  
  4574.               * ATN.SPT
  4575.               *          SNOBOL4 program to implement a compiler for an
  4576.               *               Augmented Transition Network Language.
  4577.               *
  4578.               * The compiler compiles a network description of English
  4579.               * sentence structure into SNOBOL4 code.  Sentences are then
  4580.               * the 'source input' to the network, which tries to parse
  4581.               * them.
  4582.               *
  4583.               * A LISP version of the compiler is described in:
  4584.               *  Winston, P.H., & Horn, B.P.K, LISP,
  4585.               *  New York: Addison-Wesley, 1981.
  4586.               *
  4587.               *
  4588.               * Keyword settings
  4589.               *
  4590.           1           &ANCHOR = 0
  4591.               *
  4592.               * Set CODE_PRINT to 1 to get listing of generated code
  4593.               *
  4594.           2           CODE_PRINT = 0
  4595.           3           &DUMP   = 0
  4596.           4           &FTRACE = 0
  4597.           5           &FULLSCAN = 1
  4598.           6           &MAXLNGTH = 32767
  4599.               *
  4600.               * Set this to 1 to get source listing echoed to terminal
  4601.               *
  4602.           7           SCREEN_ECHO = 0
  4603.           8           &STLIMIT  = -1
  4604.           9           &TRACE  = 0
  4605.           10          &TRIM   = 1
  4606.               *
  4607.               *
  4608.               * I/O Assoications
  4609.               *
  4610.           11          INPUT(.INPUT)
  4611.           12          INPUT(.ATNSOURCE,2,,'ATN.IN')
  4612.           13          OUTPUT(.OUTPUT)
  4613.           14          OUTPUT(.SLIST, 1,, 'SLIST')
  4614.               *
  4615.  
  4616.  
  4617.  
  4618.        AI Programming in SNOBOL4       - 70 -                    August, 1982
  4619.  
  4620.  
  4621.  
  4622.  
  4623.  
  4624.  
  4625.  
  4626.  
  4627.               * Defined data types
  4628.               *
  4629.           15          DATA( 'STACK(TOP,REST)' )
  4630.               *
  4631.               * Global constants
  4632.               *
  4633.           16          null = ''
  4634.           17          nil  = STACK()
  4635.           18          TOP(nil)  = nil
  4636.           19          REST(nil) = nil
  4637.               *
  4638.           20          SENTENCES = nil
  4639.           21          LEX_STACK = nil
  4640.           22          LEXICAL_FEATURES = TABLE()
  4641.               *
  4642.               * Utility patterns
  4643.               *
  4644.           23          BLANK   = ' '
  4645.           24          SC      = ';'
  4646.           25          Q1      = "'"
  4647.           26          Q2      = '"'
  4648.           27          COMMA   = ','
  4649.           28          STAR    = '*'
  4650.           29          LP      = '('
  4651.           30          RP      = ')'
  4652.           31          UL      = '_'
  4653.           32          PUSHER  = '>'
  4654.           33          POPPER  = '<'
  4655.               *
  4656.           34          LEFT_END  = POS(0)
  4657.           35          RIGHT_END = RPOS(0)
  4658.               *
  4659.           36          BLANKS  = SPAN(BLANK)
  4660.           37          OPT_BLANKS = BLANKS | null
  4661.           38          BB      = BREAK(BLANK)
  4662.           39          BXB     = BREAKX(BLANK)
  4663.               *
  4664.           40          BBSC    = BREAK(BLANK SC)
  4665.           41          SPSC    = SPAN(SC)
  4666.           42          SPBSC   = SPAN(BLANK SC)
  4667.           43          SPBSCN  = SPBSC | null
  4668.           44          BSC     = BREAK(SC)
  4669.               *
  4670.           45          LEN1    = LEN(1)
  4671.           46          L1REM   = LEN1 REM
  4672.               *
  4673.           47          BRP     = BREAK(RP)
  4674.           48          BRPN    = BRP | null
  4675.               *
  4676.               * Utility functions
  4677.               *
  4678.               * Print X to the source listing file and output file
  4679.               *
  4680.           49          DEFINE('PRT(X)')                          :(PRT_END)
  4681.  
  4682.  
  4683.  
  4684.        AI Programming in SNOBOL4       - 71 -                    August, 1982
  4685.  
  4686.  
  4687.  
  4688.  
  4689.  
  4690.  
  4691.  
  4692.  
  4693.           50  PRT
  4694.           51          OUTPUT = SLIST = X                        :(RETURN)
  4695.           52  PRT_END
  4696.               *
  4697.               * Error MSG to source listing file and output file
  4698.               *
  4699.           53          DEFINE('ERROR(MSG)')                     :(ERROR_END)
  4700.           54  ERROR
  4701.           55          ( PRT() PRT(MSG) PRT() )                  :(RETURN)
  4702.           56  ERROR_END
  4703.               *
  4704.               * Readable display of SNOBOL4 code
  4705.               *
  4706.           57          DEFINE( 'DISPLAY(SNOCODE)S,L' )      :(DISPLAY_END)
  4707.           58  DISPLAY
  4708.           59          EQ(CODE_PRINT,0)                     :S(RETURN)
  4709.           60          (PRT() PRT('------ Code ------') PRT())
  4710.           61  DISPLAY1
  4711.           62          SNOCODE LEFT_END (BSC $ S) SPSC =    :F(DISPLAY3)
  4712.           63          S LEFT_END (NOTANY(BLANK) (BB | null)) $ L =
  4713.               +             :F(DISPLAY2)
  4714.           64          PRT('     | ' L)
  4715.           65  DISPLAY2
  4716.           66          S LEFT_END BLANKS =
  4717.           67          S = TRIM(S)
  4718.           68          NULL(S)                               :S(DISPLAY1)
  4719.           69          PRT('     |  ' S)                     :(DISPLAY1)
  4720.           70  DISPLAY3
  4721.           71          (PRT() PRT('------ End of Code ------') PRT())
  4722.               +           :(RETURN)
  4723.           72  DISPLAY_END
  4724.               *
  4725.               * Succeeds if X is nil, null, or zero
  4726.               *
  4727.           73          DEFINE('NULL(X)')                         :(NULL_END)
  4728.           74  NULL
  4729.           75          IDENT(X,nil)                            :S(RETURN)
  4730.           76          IDENT(X,null)                           :S(RETURN)
  4731.           77          X = CONVERT(X,"INTEGER")                :F(FRETURN)
  4732.           78          EQ(X,0)                        :S(RETURN)F(FRETURN)
  4733.           79  NULL_END
  4734.               *
  4735.               * Put COAT on RACK using HANGER
  4736.               *
  4737.           80          DEFINE( 'PUT(RACK,COAT,HANGER)' )      :(PUT_END)
  4738.           81  PUT
  4739.           82          PROP<RACK> =
  4740.               +         DIFFER('TABLE',DATATYPE(PROP<RACK>)) TABLE()
  4741.           83          ITEM(PROP<RACK>,HANGER) = COAT         :(RETURN)
  4742.           84  PUT_END
  4743.               *
  4744.               * Get contents of HANGER off RACK
  4745.               *
  4746.           85          DEFINE( 'GET(RACK,HANGER)' )           :(GET_END)
  4747.  
  4748.  
  4749.  
  4750.        AI Programming in SNOBOL4       - 72 -                    August, 1982
  4751.  
  4752.  
  4753.  
  4754.  
  4755.  
  4756.  
  4757.  
  4758.  
  4759.           86  GET
  4760.           87          PROP<RACK> =
  4761.               +         DIFFER('TABLE',DATATYPE(PROP<RACK>)) TABLE()
  4762.               +            :S(RETURN)
  4763.           88          GET = ITEM(PROP<RACK>,HANGER)         :(RETURN)
  4764.           89  GET_END
  4765.               *
  4766.               * Program text semi-constants used in code generation
  4767.               *
  4768.           90          &ALPHABET POS(1) (LEN1 $ MAGIC_CHAR)
  4769.               *
  4770.           91          REPLACE_LIT = MAGIC_CHAR 'RePlAcE' MAGIC_CHAR
  4771.               *
  4772.           92          BEGIN_TEXT =
  4773.               +         ' HOLD = REMAINING_WORDS ;'
  4774.               +         ' REMAINING_WORDS (BREAK(" ") $ CURRENT_WORD) ;'
  4775.               +         ' THIS_NODE = GENNAME("' REPLACE_LIT '") ;'
  4776.               +         ' :(' REPLACE_LIT '_START) ;'
  4777.               *
  4778.           93          WIN_TEXT =
  4779.               +        REPLACE_LIT '_WIN'
  4780.               +        ' TESTF(THIS_NODE,FEATS) :F(' REPLACE_LIT '_LOSE) ;'
  4781.               +         ' ATTACH(THIS_NODE,PARENT) ;'
  4782.               +         ' LAST_PARSED = THIS_NODE :(RETURN) ;'
  4783.               *
  4784.           94          LOSE_TEXT =
  4785.               +         REPLACE_LIT '_LOSE'
  4786.               +         ' REMAINING_WORDS = HOLD ;'
  4787.               +         ' REMAINING_WORDS (BREAK(" ") $ CURRENT_WORD)'
  4788.               +                 ':(FRETURN) ;'
  4789.               *
  4790.           95          INITIAL_ROUTINE =
  4791.               +         REPLACE_LIT BEGIN_TEXT
  4792.               +         WIN_TEXT LOSE_TEXT REPLACE_LIT '_START ;'
  4793.               *
  4794.               * Patterns used in COMPILE routine
  4795.               *
  4796.           96          COMMENT_PAT = (LEFT_END OPT_BLANKS STAR) |
  4797.               +         (LEFT_END RIGHT_END)
  4798.               *
  4799.           97          KEYWORD_PAT = 'NETWORK' | 'FUNCTION' | 'LEXICON'
  4800.               +         | 'SENTENCES' | 'EXEC'
  4801.               *
  4802.           98          NAME_PAT    = (BB $ NAME) BLANKS FENCE
  4803.               *
  4804.           99          LEGAL_PAT   = LEFT_END KEYWORD_PAT . KEYTYPE BLANKS
  4805.               +         (BB | REM) . TNAME
  4806.               *
  4807.           100         COMPLETE_PAT = (LEFT_END 'EXEC' BLANKS)
  4808.           101         COMPLETE_PAT = COMPLETE_PAT |
  4809.               +           (LEFT_END BB BLANKS (BB $ TNAME) BLANKS FAIL)
  4810.           102         COMPLETE_PAT = COMPLETE_PAT |
  4811.               +           ('END' OPT_BLANKS *TNAME RIGHT_END)
  4812.               *
  4813.  
  4814.  
  4815.  
  4816.        AI Programming in SNOBOL4       - 73 -                    August, 1982
  4817.  
  4818.  
  4819.  
  4820.  
  4821.  
  4822.  
  4823.  
  4824.  
  4825.               * Definitions of semantic (code-generating) functions
  4826.               *
  4827.           103         DEFINE( 'S(NA)' )
  4828.           104         DEFINE( 'S_(NA)T' )
  4829.               *
  4830.               * Recognizer/compiler patterns for the five types of blocks:
  4831.               *  EXEC, SENTENCES, LEXICON, FUNCTION, and NETWORK
  4832.               *
  4833.               * Recognizer for EXEC statement
  4834.               *
  4835.           105        EXEC_PAT = LEFT_END 'EXEC' BLANKS (REM $ NAME) S('EX')
  4836.               *
  4837.               * Recognizer/Compiler for SENTENCES block
  4838.               *
  4839.           106         SENTENCES_LIT = 'SENTENCES' BLANKS FENCE
  4840.           107         SENTENCES_HEADER = LEFT_END SENTENCES_LIT NAME_PAT
  4841.           108         SENTENCE_PAT   = (BSC $ SENT) SPBSC S('SENT')
  4842.           109         SENTENCES_BODY = ARBNO(SENTENCE_PAT)
  4843.           110         SENTENCES_ENDER = 'END' OPT_BLANKS *NAME RIGHT_END
  4844.           111         SENTENCES_PAT = SENTENCES_HEADER SENTENCES_BODY
  4845.               +         SENTENCES_ENDER
  4846.               *
  4847.               * Recognizer/Compiler for LEXICON block
  4848.               *
  4849.           112         LEXICON_LIT = 'LEXICON' BLANKS FENCE
  4850.           113         LEXICON_HEADER = LEFT_END LEXICON_LIT NAME_PAT
  4851.           114         LEX_PUSH_PAT = PUSHER (BB $ F) BLANKS S('PSH')
  4852.           115         LEX_POP_PAT = POPPER (BB $ F) BLANKS S('POP')
  4853.           116         WORD_PAT = NOTANY(PUSHER POPPER) (BB | null)
  4854.           117         LEX_W_PAT = (WORD_PAT $ W) BLANKS S('LEX')
  4855.           118         ENTRY_PAT = LEX_W_PAT | LEX_PUSH_PAT | LEX_POP_PAT
  4856.           119         ENTRIES_PAT = ARBNO(ENTRY_PAT)
  4857.           120         LEXICON_ENDER = SENTENCES_ENDER
  4858.           121        LEXICON_PAT = LEXICON_HEADER ENTRIES_PAT LEXICON_ENDER
  4859.               *
  4860.               * Recognizer/Compiler for FUNCTION block
  4861.               *
  4862.           122         FUNCTION_LIT = 'FUNCTION' BLANKS FENCE
  4863.           123         FUNCTION_HEADER = LEFT_END FUNCTION_LIT NAME_PAT
  4864.           124         ARG_PAT = (( LP BRPN RP ) $ ARG ) BLANKS S('ARG')
  4865.           125         LOC_PAT = LP (BRPN $ LOC) RP BLANKS S('LOC')
  4866.           126         FUNCTION_HEADER = FUNCTION_HEADER ARG_PAT LOC_PAT
  4867.           127         STATEMENT_PAT = BSC SPSC
  4868.           128         STATEMENTS_PAT = ARBNO(STATEMENT_PAT) $ BODY BLANKS
  4869.           129         FUNCTION_ENDER = SENTENCES_ENDER
  4870.           130         FUNCTION_PAT = FUNCTION_HEADER S('FN') STATEMENTS_PAT
  4871.               +         FUNCTION_ENDER S('F')
  4872.               *
  4873.               * Recongnizer/Compiler for NETWORK block
  4874.               *
  4875.               * The IF part
  4876.               *
  4877.           131         IF_LIT = 'IF' BLANKS FENCE
  4878.               *
  4879.  
  4880.  
  4881.  
  4882.        AI Programming in SNOBOL4       - 74 -                    August, 1982
  4883.  
  4884.  
  4885.  
  4886.  
  4887.  
  4888.  
  4889.  
  4890.  
  4891.               * The conditional clause
  4892.               *
  4893.           132         CLAUSE_PAT = BXB
  4894.           133         COND_PAT = (CLAUSE_PAT $ COND) BLANKS
  4895.               *
  4896.               * The GOTO clause
  4897.               *
  4898.           134         GOTO_LIT = 'GO' OPT_BLANKS 'TO' BLANKS FENCE
  4899.           135         GOTO_LABEL_PAT = (BB $ GOTO_LABEL) BLANKS FENCE
  4900.           136         GOTO_PAT = GOTO_LIT GOTO_LABEL_PAT
  4901.               *
  4902.               * The AFTER clause (which may be null)
  4903.               *
  4904.           137         AFTER_LIT = 'AFTER' BLANKS FENCE
  4905.           138         SIDE_PAT = (CLAUSE_PAT $ SIDE) BLANKS
  4906.           139         ENDIF_PAT = 'END' OPT_BLANKS 'IF' BLANKS FENCE
  4907.           140         AFTER_PAT =
  4908.               +         ((null $ SIDE) ENDIF_PAT)
  4909.               +         | (AFTER_LIT SIDE_PAT ENDIF_PAT)
  4910.           141         IF_PAT = IF_LIT COND_PAT GOTO_PAT AFTER_PAT S('IF')
  4911.               *
  4912.               * The labelled set of IF statments (the RULE)
  4913.               *
  4914.           142         LABEL_PAT = (BB $ LABEL) BLANKS FENCE
  4915.           143         IFS_PAT = ARBNO(IF_PAT)
  4916.           144         END_LABEL_PAT = 'END' OPT_BLANKS *LABEL BLANKS FENCE
  4917.           145         RULE_PAT = LABEL_PAT S('LAB') IFS_PAT END_LABEL_PAT
  4918.               +            S('ELB')
  4919.               *
  4920.               * The set of RULEs (the NETWORK)
  4921.               *
  4922.           146         NETWORK_LIT = 'NETWORK' BLANKS FENCE
  4923.           147         NETWORK_HEADER = LEFT_END NETWORK_LIT NAME_PAT
  4924.           148         RULES_PAT = ARBNO(RULE_PAT)
  4925.           149         NETWORK_ENDER = SENTENCES_ENDER
  4926.           150         NETWORK_PAT = NETWORK_HEADER S('NTW')
  4927.               +              RULES_PAT
  4928.               +              NETWORK_ENDER S('ENW')
  4929.               *
  4930.               * Grand pattern for compiling any legal block
  4931.               *
  4932.           151         COMPILE_PAT = NETWORK_PAT
  4933.               +             | FUNCTION_PAT
  4934.               +             | LEXICON_PAT
  4935.               +             | SENTENCES_PAT
  4936.               +             | EXEC_PAT
  4937.               *
  4938.               * Read and compile all text from ATNSOURCE
  4939.               *   (source listing with comments goes to SLIST)
  4940.               *
  4941.           152         DEFINE( 'COMPILE()NEXT,TEXT' )      :(COMPILE_END)
  4942.               *
  4943.               * Comment or first line of block
  4944.               *
  4945.  
  4946.  
  4947.  
  4948.        AI Programming in SNOBOL4       - 75 -                    August, 1982
  4949.  
  4950.  
  4951.  
  4952.  
  4953.  
  4954.  
  4955.  
  4956.  
  4957.           153 COMPILE
  4958.           154         COLLECT()
  4959.           155         SETEXIT(.CONTINUE)
  4960.           156         TEXT = ATNSOURCE                     :F(RETURN)
  4961.               *
  4962.               * List record, trim leading blanks, check for legal syntax
  4963.               *
  4964.           157 COMPILE1
  4965.           158         PRT( TEXT )
  4966.           159         TEXT COMMENT_PAT                          :S(COMPILE)
  4967.           160         TEXT LEFT_END BLANKS = null
  4968.           161         TEXT LEGAL_PAT                           :S(COMPILE2)
  4969.           162         ERROR('Illegal record')                   :(FRETURN)
  4970.               *
  4971.               * Check for complete block. If incomplete, keep reading
  4972.               *
  4973.           163 COMPILE2
  4974.           164         TEXT COMPLETE_PAT                    :S(COMPILE4)
  4975.           165         SETEXIT(.CONTINUE)
  4976.           166         NEXT = ATNSOURCE                     :S(COMPILE3)
  4977.           167         ERROR('Unexpected end of file on ATNSOURCE')
  4978.               +            :(FRETURN)
  4979.               *
  4980.               * List the record, convert leading blanks to a single blank,
  4981.               *  and concatenate with TEXT
  4982.               *
  4983.           168 COMPILE3
  4984.           169         PRT( NEXT )
  4985.           170         NEXT COMMENT_PAT                     :S(COMPILE2)
  4986.           171         NEXT LEFT_END BLANKS = BLANK
  4987.           172         TEXT = TEXT NEXT                     :(COMPILE2)
  4988.               *
  4989.               * Use COMPILE_PAT to compile TEXT
  4990.               *
  4991.           173 COMPILE4
  4992.           174         TIME_ZERO = TIME()
  4993.           175         TEXT COMPILE_PAT                     :F(COMPILE5)
  4994.           176         PRT()
  4995.           177         PRT(TIME() - TIME_ZERO ' milliseconds compile time')
  4996.           178         PRT()                                     :(COMPILE)
  4997.               *
  4998.           179 COMPILE5
  4999.           180         ERROR('Compilation failed')               :(FRETURN)
  5000.           181 COMPILE_END
  5001.               *
  5002.               * Semantic (code-generation) functions
  5003.               *
  5004.           182         :(S_END)
  5005.               *
  5006.               * For immediate code generation
  5007.               *       The code is generated after a part of a syntactic
  5008.               *       pattern has successfully matched
  5009.               *
  5010.           183 S
  5011.  
  5012.  
  5013.  
  5014.        AI Programming in SNOBOL4       - 76 -                    August, 1982
  5015.  
  5016.  
  5017.  
  5018.  
  5019.  
  5020.  
  5021.  
  5022.  
  5023.           184         S = EVAL( "(NULL $ *S_('"  NA  "')) FENCE " )
  5024.               +             :(RETURN)
  5025.               *
  5026.               * This is a big computed GOTO with a branch for every
  5027.               *       semantic contigency.
  5028.               *
  5029.           185 S_
  5030.           186         S_ = .DUMMY                          :($( 'S_' NA ))
  5031.               *
  5032.               * Initialize the code for a network
  5033.               *
  5034.           187 S_NTW
  5035.           188         DEFINE( NAME '(PARENT,FEATS)THIS_NODE,HOLD' )
  5036.           189         DISPLAY(' DEFINE(' Q1 NAME
  5037.               +            '(PARENT,FEATS)THIS_NODE,HOLD' Q1 ') ;')
  5038.           190         ROUTINE = INITIAL_ROUTINE                 :(NRETURN)
  5039.               *
  5040.               * The label for a rule
  5041.               *
  5042.           191 S_LAB
  5043.           192         ROUTINE = ROUTINE REPLACE_LIT UL LABEL BLANK
  5044.               +              :(NRETURN)
  5045.               *
  5046.               * One IF statement is a network
  5047.               *
  5048.           193 S_IF
  5049.           194         ROUTINE = ROUTINE
  5050.               +         ' ?( ' COND BLANK SIDE ' ) '
  5051.               +         ':S(' REPLACE_LIT UL GOTO_LABEL ') ;'  :(NRETURN)
  5052.               *
  5053.               * The end of a labelled rule:  If none of the IF statements
  5054.               *       has been satisfied, then the LOSE branch is take
  5055.               *
  5056.           195 S_ELB
  5057.           196         ROUTINE = ROUTINE ' :(' REPLACE_LIT '_LOSE) ;'
  5058.               +           :(NRETURN)
  5059.               *
  5060.               * Wrap-up network:  (1) insert NAME in all the right places;
  5061.               *       (2) translate into machine language via CODE.
  5062.               *
  5063.           197 S_ENW
  5064.           198         ROUTINE REPLACE_LIT = NAME                :S(S_ENW)
  5065.           199         DISPLAY( ROUTINE )
  5066.           200         CODE( ROUTINE )                           :S(NRETURN)
  5067.           201         ERROR('Compilation error')                :(FRETURN)
  5068.               *
  5069.               * Push a sentence onto the stack of sentences
  5070.               *
  5071.           202 S_SENT
  5072.           203         SENTENCES = STACK(SENT,SENTENCES)         :(NRETURN)
  5073.               *
  5074.               * Push F onto the stack of lexical features
  5075.               *
  5076.           204 S_PSH
  5077.  
  5078.  
  5079.  
  5080.        AI Programming in SNOBOL4       - 77 -                    August, 1982
  5081.  
  5082.  
  5083.  
  5084.  
  5085.  
  5086.  
  5087.  
  5088.  
  5089.           205        LEX_STACK = STACK(F,LEX_STACK)            :(NRETURN)
  5090.               *
  5091.               * Pop lexical features up to, NOT INCLUDING, F
  5092.               *
  5093.           206 S_POP
  5094.           207        NULL(LEX_STACK)                           :S(NRETURN)
  5095.           208         IDENT(F,TOP(LEX_STACK))                   :S(NRETURN)
  5096.           209         LEX_STACK = REST(LEX_STACK)               :(S_POP)
  5097.               *
  5098.               * Attach all stacked features to W
  5099.               *
  5100.           210 S_LEX
  5101.           211         LEX_STACK_SAVE = LEX_STACK
  5102.           212 S_LEX1
  5103.           213         NULL(LEX_STACK)                           :S(S_LEX2)
  5104.           214         LEXICAL_FEATURES<W> = TOP(LEX_STACK) BLANK
  5105.               +              LEXICAL_FEATURES<W>
  5106.           215         LEX_STACK = REST(LEX_STACK)               :(S_LEX1)
  5107.           216 S_LEX2
  5108.           217         PRT('     | ' W ':  ' LEXICAL_FEATURES<W>)
  5109.           218         LEX_STACK = LEX_STACK_SAVE                :(NRETURN)
  5110.               *
  5111.               * Remove blanks from the formal argument list for a FUNCTION
  5112.               *
  5113.           219 S_ARG
  5114.           220         ARG BLANKS =                :S(S_ARG)F(NRETURN)
  5115.               *
  5116.               * Remove blanks from the local variable list for a FUNCTION
  5117.               *
  5118.           221 S_LOC
  5119.           222         LOC BLANKS =                :S(S_LOC)F(NRETURN)
  5120.               *
  5121.               * Initialize FUNCTION
  5122.               *
  5123.           223 S_FN
  5124.           224         DISPLAY(' DEFINE(' Q1 NAME ARG LOC Q1 ') ;')
  5125.           225         DEFINE( NAME ARG LOC )                    :(NRETURN)
  5126.               *
  5127.               * Compile a FUNCTION
  5128.               *
  5129.           226 S_F
  5130.           227         BODY = BODY " ERROR('No return from ' "
  5131.               +         Q1 NAME Q1 ") :(END) ;"
  5132.           228         DISPLAY( NAME BLANK BODY )
  5133.           229         CODE( NAME BLANK BODY )                   :S(NRETURN)
  5134.           230         ERROR('Compilation error')                :(FRETURN)
  5135.               *
  5136.               * For EXEC, call MAIN with NAME = name of first network
  5137.               *
  5138.           231 S_EX
  5139.           232         ( PRT() PRT() )
  5140.           233    PRT( '****** EXECUTION BEGINS WITH ' NAME ' ******') PRT()
  5141.           234         MAIN(NAME)                           :(NRETURN)
  5142.           235 S_END
  5143.  
  5144.  
  5145.  
  5146.        AI Programming in SNOBOL4       - 78 -                    August, 1982
  5147.  
  5148.  
  5149.  
  5150.  
  5151.  
  5152.  
  5153.  
  5154.  
  5155.               *
  5156.               *
  5157.               * This routine is triggered by the EXEC statement
  5158.               *
  5159.           236         DEFINE( 'MAIN(FIRST_PROC)LAST_PARSED,'
  5160.               +       'CURRENT_WORD,REMAINING_WORDS,S,PROP' )   :(MAIN_END)
  5161.           237 MAIN
  5162.           238         COLLECT()
  5163.           239         NULL(SENTENCES)                           :S(RETURN)
  5164.           240         S = TOP(SENTENCES)
  5165.           241         SENTENCES = REST(SENTENCES)
  5166.           242         ( PRT() PRT() )
  5167.           243         PRT(DUPL('-',SIZE(S)))
  5168.           244         ( PRT() PRT(S) PRT() )
  5169.           245         PRT(DUPL('-',SIZE(S)))
  5170.           246         PRT()
  5171.           247         LAST_PARSED = null
  5172.           248         CURRENT_WORD = null
  5173.           249         REMAINING_WORDS = S BLANK
  5174.           250         PROP = TABLE()
  5175.           251         TIME_ZERO = TIME()
  5176.           252         EVAL(FIRST_PROC)                          :S(MAIN1)
  5177.           253         ( PRT() PRT('Parsing failed') PRT() )     :(MAIN)
  5178.               *
  5179.           254 MAIN1
  5180.           255         ( PRT() PRT('Parsing Succeeded') PRT() )
  5181.           256        ( PRT(TIME() - TIME_ZERO ' milliseconds used') PRT() )
  5182.           257         DUMP_PROP()                          :(MAIN)
  5183.           258 MAIN_END
  5184.               *
  5185.               * Dump registers after parse is completed
  5186.               *
  5187.           259         DEFINE( 'DUMP_PROP()T,N,R,M,TN1,TN2,RM1,RM2' )
  5188.               +             :(DUMP_PROP_END)
  5189.           260 DUMP_PROP
  5190.           261         T = CONVERT(PROP, 'ARRAY')                :F(RETURN)
  5191.           262         PROP = null
  5192.           263         N = 1
  5193.               *
  5194.           264 DUMP1
  5195.           265         TN1 = T<N,1>                              :F(RETURN)
  5196.           266         TN2 = T<N,2>
  5197.           267         T<N,1> = null
  5198.           268         T<N,2> = null
  5199.           269         R = CONVERT(TN2, 'ARRAY')                 :F(DUMP3)
  5200.           270         PRT()
  5201.           271         PRT( 'NODE: ' TN1 )
  5202.           272         M = 1
  5203.               *
  5204.           273 DUMP2
  5205.           274           RM1 = R<M,1>                            :F(DUMP3)
  5206.           275           RM2 = R<M,2>
  5207.           276           PRT( DUPL(' ',10) RM1 ' = ' RM2 )
  5208.           277           M = M + 1                               :(DUMP2)
  5209.  
  5210.  
  5211.  
  5212.        AI Programming in SNOBOL4       - 79 -                    August, 1982
  5213.  
  5214.  
  5215.  
  5216.  
  5217.  
  5218.  
  5219.  
  5220.  
  5221.               *
  5222.           278 DUMP3
  5223.           279         N = N + 1                                 :(DUMP1)
  5224.           280 DUMP_PROP_END
  5225.               *
  5226.               *
  5227.               * Compile main program starts here
  5228.               *
  5229.           281         REWIND(1)
  5230.           282         REWIND(2)
  5231.           283         COMPILE()                                 :S(END)
  5232.           284         ERROR('****** FATAL ERROR ******')
  5233.           285 END
  5234.  
  5235.                              SAMPLE PROGRAMMING SESSION
  5236.  
  5237.           **********************************
  5238.            NETWORK PARSE_CLAUSE
  5239.           **********************************
  5240.                   S1
  5241.                       IF PARSE_NOUN_GROUP(THIS_NODE) GOTO S2
  5242.                     AFTER SETR('SUBJECT',LAST_PARSED)
  5243.                       ENDIF
  5244.                   END S1
  5245.           **********************************
  5246.                   S2
  5247.                       IF PARSE_WORD(THIS_NODE,'VERB TENSED ') GOTO S3
  5248.                     AFTER SETR('VERB',LAST_PARSED)
  5249.                       ENDIF
  5250.                   END S2
  5251.           **********************************
  5252.                   S3
  5253.                       IF TESTF(LAST_PARSED,'BE ')
  5254.                     PARSE_WORD(THIS_NODE,'PASTPARTICIPLE ') GOTO S4
  5255.                     AFTER     SETR('OBJECT',GETR('SUBJECT'))
  5256.                          SETR('SUBJECT')
  5257.                          SETR('VERB',LAST_PARSED)
  5258.                       ENDIF
  5259.                       IF TESTF(GETR('VERB'),'TRANSITIVE ')
  5260.                     PARSE_NOUN_GROUP(THIS_NODE) GOTO S4
  5261.                     AFTER SETR('OBJECT',LAST_PARSED)
  5262.                       ENDIF
  5263.                       IF TESTF(GETR('VERB'),'INTRANSITIVE ') GOTO S4 ENDIF
  5264.                       IF ~NULL(GETR('OBJECT')) GOTO S4 ENDIF
  5265.                   END S3
  5266.           **********************************
  5267.                   S4
  5268.                       IF ~NULL(GETR('SUBJECT'))
  5269.                     NULL(REMAINING_WORDS) GOTO WIN
  5270.                       ENDIF
  5271.                       IF NULL(GETR('SUBJECT'))
  5272.                     IDENT(CURRENT_WORD,'BY')
  5273.                     PARSE_WORD(THIS_NODE) GOTO S5
  5274.                       ENDIF
  5275.  
  5276.  
  5277.  
  5278.        AI Programming in SNOBOL4       - 80 -                    August, 1982
  5279.  
  5280.  
  5281.  
  5282.  
  5283.  
  5284.  
  5285.  
  5286.  
  5287.                       IF NULL(GETR('SUBJECT')) GOTO S4
  5288.                     AFTER SETR('SUBJECT','SOMEONE')
  5289.                       ENDIF
  5290.                   END S4
  5291.           **********************************
  5292.                   S5
  5293.                       IF PARSE_NOUN_GROUP(THIS_NODE) GOTO S4
  5294.                     AFTER SETR('SUBJECT',LAST_PARSED)
  5295.                       ENDIF
  5296.                   END S5
  5297.            END PARSE_CLAUSE
  5298.           
  5299.           
  5300.           1550 milliseconds compile time
  5301.           
  5302.           
  5303.           **********************************
  5304.            NETWORK PARSE_NOUN_GROUP
  5305.           **********************************
  5306.                   S1
  5307.                       IF PARSE_WORD(THIS_NODE,'DETERMINER ') GOTO S2
  5308.                     AFTER SETR('NUMBER',
  5309.                             SELECT('SINGULAR PLURAL ',
  5310.                                 GETF(LAST_PARSED)))
  5311.                           SETR('DETERMINER',
  5312.                             SELECT('DEFINITE INDEFINITE ',
  5313.                                 GETF(LAST_PARSED)))
  5314.                       ENDIF
  5315.                   END S1
  5316.           **********************************
  5317.                   S2
  5318.                       IF PARSE_WORD(THIS_NODE,'ADJECTIVE ') GOTO S2
  5319.                      AFTER ADDR('ADJECTIVES',LAST_PARSED)
  5320.                       ENDIF
  5321.                       IF PARSE_WORD(THIS_NODE,'NOUN ') GOTO WIN
  5322.                      AFTER SETR('NUMBER',
  5323.                              SELECT('SINGULAR PLURAL ',
  5324.                                  GETF(LAST_PARSED)))
  5325.                            SETR('NOUN',LAST_PARSED)
  5326.                       ENDIF
  5327.                   END S2
  5328.            END PARSE_NOUN_GROUP
  5329.           
  5330.           433 milliseconds compile time
  5331.           
  5332.           
  5333.           **********************************
  5334.            NETWORK PARSE_WORD
  5335.                   S1
  5336.                       IF NULL(null) GOTO WIN
  5337.                     AFTER PARSE_WORD_1()
  5338.                       ENDIF
  5339.                   END S1
  5340.            END PARSE_WORD
  5341.  
  5342.  
  5343.  
  5344.        AI Programming in SNOBOL4       - 81 -                    August, 1982
  5345.  
  5346.  
  5347.  
  5348.  
  5349.  
  5350.  
  5351.  
  5352.  
  5353.           
  5354.           183 milliseconds compile time
  5355.           
  5356.           
  5357.           **********************************
  5358.            FUNCTION PARSE_WORD_1 () ()
  5359.                   THIS_NODE = CURRENT_WORD ;
  5360.                   REMAINING_WORDS BREAK(" ") SPAN(" ") = ;
  5361.                   REMAINING_WORDS (BREAK(" ") | null) $ CURRENT_WORD  :(RET
  5362.           URN) ;
  5363.            END PARSE_WORD_1
  5364.           
  5365.           33 milliseconds compile time
  5366.           
  5367.           
  5368.           **********************************
  5369.            FUNCTION SETR (REGISTER,VALUE) ()
  5370.                   PUT(THIS_NODE,VALUE,REGISTER)        :(RETURN) ;
  5371.            END SETR
  5372.           
  5373.           17 milliseconds compile time
  5374.           
  5375.           
  5376.           **********************************
  5377.            FUNCTION GETR (REGISTER) ()
  5378.                   GETR = GET(THIS_NODE,REGISTER)       :(RETURN) ;
  5379.            END GETR
  5380.           
  5381.           17 milliseconds compile time
  5382.           
  5383.           
  5384.           **********************************
  5385.            FUNCTION ADDR (REGISTER,VALUE) ()
  5386.                   SETR(REGISTER,GETR(REGISTER) VALUE " ")   :(RETURN) ;
  5387.            END ADDR
  5388.           
  5389.           17 milliseconds compile time
  5390.           
  5391.           
  5392.           **********************************
  5393.            FUNCTION GENNAME (X) ()
  5394.                   GENNAME =
  5395.                       '*' X '_' STATEMENTS(0) '*'
  5396.                     :(RETURN) ;
  5397.            END GENNAME
  5398.           
  5399.           17 milliseconds compile time
  5400.           
  5401.           
  5402.           **********************************
  5403.            FUNCTION ATTACH (C,P) ()
  5404.                   PUT(C,P,'PARENT') ;
  5405.                   PUT(P,GET(P,'CHILDREN') C " ",'CHILDREN')
  5406.                     :(RETURN) ;
  5407.  
  5408.  
  5409.  
  5410.        AI Programming in SNOBOL4       - 82 -                    August, 1982
  5411.  
  5412.  
  5413.  
  5414.  
  5415.  
  5416.  
  5417.  
  5418.  
  5419.            END ATTACH
  5420.           
  5421.           17 milliseconds compile time
  5422.           
  5423.           
  5424.           **********************************
  5425.            FUNCTION SELECT (S,T) ()
  5426.                   S (BREAK(" ") $ SELECT) SPAN(" ") =  :F(FRETURN) ;
  5427.                   T (POS(0) | " ") SELECT " "
  5428.                     :S(RETURN)F(SELECT) ;
  5429.            END SELECT
  5430.           
  5431.           33 milliseconds compile time
  5432.           
  5433.           
  5434.           **********************************
  5435.            FUNCTION TESTF (X,F) (W,G)
  5436.                   NULL(F)          :S(RETURN) ;
  5437.                   G = GETF(X) ;
  5438.           TESTF1
  5439.                   F (BREAK(" ") $ W) SPAN(" ") =  :F(RETURN) ;
  5440.                   G (POS(0) | " ") W " "     :S(TESTF)F(FRETURN) ;
  5441.            END TESTF
  5442.           
  5443.           34 milliseconds compile time
  5444.           
  5445.           
  5446.           **********************************
  5447.            FUNCTION GETF (X) ()
  5448.                   GETF = LEXICAL_FEATURES<X> :(RETURN) ;
  5449.            END GETF
  5450.           
  5451.           17 milliseconds compile time
  5452.           
  5453.           
  5454.           **********************************
  5455.            LEXICON L1
  5456.                   <* >NOUN >SINGULAR BLOCK BOY
  5457.                   <* >DETERMINER >SINGULAR >INDEFINITE A
  5458.                            <SINGULAR >DEFINITE THE
  5459.                   <* >VERB >TENSED >TRANSITIVE >INTRANSITIVE
  5460.           >PASTPARTICIPLE DROPPED
  5461.                      <TENSED >BE WAS
  5462.                   <* >ADJECTIVE BIG RED
  5463.                   <* >PREPOSITION BY
  5464.                   <*
  5465.            END L1
  5466.                | BLOCK:  NOUN SINGULAR
  5467.                | BOY:  NOUN SINGULAR
  5468.                | A:  DETERMINER SINGULAR INDEFINITE
  5469.                | THE:  DETERMINER SINGULAR DEFINITE
  5470.                | DROPPED:  VERB TENSED TRANSITIVE INTRANSITIVE
  5471.           PASTPARTICIPLE
  5472.                | WAS:  VERB TENSED BE
  5473.  
  5474.  
  5475.  
  5476.        AI Programming in SNOBOL4       - 83 -                    August, 1982
  5477.  
  5478.  
  5479.  
  5480.  
  5481.  
  5482.  
  5483.  
  5484.  
  5485.                | BIG:  ADJECTIVE
  5486.                | RED:  ADJECTIVE
  5487.                | BY:  PREPOSITION
  5488.           
  5489.           84 milliseconds compile time
  5490.           
  5491.           
  5492.           **********************************
  5493.            SENTENCES S1
  5494.                   A BIG RED BLOCK WAS DROPPED BY THE BOY ;
  5495.                   THE BOY DROPPED A BIG RED BLOCK ;
  5496.                   A BLOCK WAS DROPPED ;
  5497.                   THE BLOCK DROPPED ;
  5498.            END S1
  5499.           
  5500.           0 milliseconds compile time
  5501.           
  5502.           
  5503.           **********************************
  5504.            EXEC PARSE_CLAUSE("SENTENCE",null)
  5505.           
  5506.           
  5507.           ****** EXECUTION BEGINS WITH PARSE_CLAUSE("SENTENCE",null) ******
  5508.           
  5509.           
  5510.           
  5511.           ------------------
  5512.           
  5513.           THE BLOCK DROPPED
  5514.           
  5515.           ------------------
  5516.           
  5517.           
  5518.           Parsing Succeeded
  5519.           
  5520.           150 milliseconds used
  5521.           
  5522.           
  5523.           NODE: SENTENCE
  5524.                     CHILDREN =
  5525.           
  5526.           NODE:
  5527.                     NUMBER = SINGULAR
  5528.                     CHILDREN = THE BLOCK  DROPPED
  5529.                     DETERMINER = DEFINITE
  5530.                     PARENT = SENTENCE
  5531.                     VERB = DROPPED
  5532.                     NOUN = BLOCK
  5533.                     SUBJECT = SOMEONE
  5534.           
  5535.           
  5536.           --------------------
  5537.           
  5538.           A BLOCK WAS DROPPED
  5539.  
  5540.  
  5541.  
  5542.        AI Programming in SNOBOL4       - 84 -                    August, 1982
  5543.  
  5544.  
  5545.  
  5546.  
  5547.  
  5548.  
  5549.  
  5550.  
  5551.           
  5552.           --------------------
  5553.           
  5554.           
  5555.           Parsing Succeeded
  5556.           
  5557.           150 milliseconds used
  5558.           
  5559.           
  5560.           NODE: SENTENCE
  5561.                     CHILDREN =
  5562.           
  5563.           NODE:
  5564.                     NUMBER = SINGULAR
  5565.                     CHILDREN = A BLOCK  WAS DROPPED
  5566.                     DETERMINER = INDEFINITE
  5567.                     PARENT = SENTENCE
  5568.                     VERB = DROPPED
  5569.                     NOUN = BLOCK
  5570.                     SUBJECT = SOMEONE
  5571.           
  5572.           
  5573.           --------------------------------
  5574.           
  5575.           THE BOY DROPPED A BIG RED BLOCK
  5576.           
  5577.           --------------------------------
  5578.           
  5579.           
  5580.           Parsing Succeeded
  5581.           
  5582.           267 milliseconds used
  5583.           
  5584.           
  5585.           NODE: SENTENCE
  5586.                     CHILDREN =
  5587.           
  5588.           NODE:
  5589.                     NUMBER = SINGULAR
  5590.                     CHILDREN = THE BOY  DROPPED A BIG RED BLOCK
  5591.                     DETERMINER = INDEFINITE
  5592.                     PARENT = SENTENCE
  5593.                     VERB = DROPPED
  5594.                     ADJECTIVES = BIG RED
  5595.                     NOUN = BLOCK
  5596.                     SUBJECT = SOMEONE
  5597.           
  5598.           
  5599.           ---------------------------------------
  5600.           
  5601.           A BIG RED BLOCK WAS DROPPED BY THE BOY
  5602.           
  5603.           ---------------------------------------
  5604.           
  5605.  
  5606.  
  5607.  
  5608.        AI Programming in SNOBOL4       - 85 -                    August, 1982
  5609.  
  5610.  
  5611.  
  5612.  
  5613.  
  5614.  
  5615.  
  5616.  
  5617.           
  5618.           Parsing Succeeded
  5619.           
  5620.           300 milliseconds used
  5621.           
  5622.           
  5623.           NODE: SENTENCE
  5624.                     CHILDREN =
  5625.           
  5626.           NODE:
  5627.                     NUMBER = SINGULAR
  5628.                     CHILDREN = A BIG RED BLOCK  WAS DROPPED BY THE BOY
  5629.                     DETERMINER = DEFINITE
  5630.                     PARENT = SENTENCE
  5631.                     VERB = DROPPED
  5632.                     ADJECTIVES = BIG RED
  5633.                     NOUN = BOY
  5634.                     SUBJECT = SOMEONE
  5635.           
  5636.           533 milliseconds compile time
  5637.  
  5638.  
  5639.  
  5640.  
  5641.  
  5642.  
  5643.  
  5644.  
  5645.  
  5646.  
  5647.  
  5648.  
  5649.  
  5650.  
  5651.  
  5652.  
  5653.  
  5654.  
  5655.  
  5656.  
  5657.  
  5658.  
  5659.  
  5660.  
  5661.  
  5662.  
  5663.  
  5664.  
  5665.  
  5666.  
  5667.  
  5668.  
  5669.  
  5670.  
  5671.  
  5672.  
  5673.  
  5674.        AI Programming in SNOBOL4       - 86 -                    August, 1982
  5675.  
  5676.  
  5677.