Annotation of researchv10dc/vol2/snocone/snocone.ms, revision 1.1

1.1     ! root        1: .so ../ADM/mac
        !             2: .XX snocone 297 "The Snocone Programming Language"
        !             3: .nr dP 2
        !             4: .nr dV 3p
        !             5: .EQ
        !             6: delim $$
        !             7: .EN
        !             8: .ds S4 \s-2SNOBOL4\s0
        !             9: .de Sx
        !            10: .P1 0
        !            11: ..
        !            12: .de Sy
        !            13: .P2
        !            14: ..
        !            15: .de Tx
        !            16: .IP "" 5
        !            17: ..
        !            18: .de Ty
        !            19: .PP
        !            20: ..
        !            21: .TL
        !            22: The Snocone Programming Language
        !            23: .AU
        !            24: Andrew Koenig
        !            25: .AI
        !            26: .MH
        !            27: .AB
        !            28: .PP
        !            29: \*(S4 is a well-known programming language with convenient
        !            30: semantics and clumsy syntax.
        !            31: Following the pattern of Ratfor and \s-2EFL\s0,
        !            32: we have added ``syntactic sugar'' to
        !            33: \*(S4, with an eye toward making it easier to use.
        !            34: We call the result
        !            35: .I Snocone.
        !            36: .PP
        !            37: This paper describes the Snocone language in enough
        !            38: detail that people not familiar with \*(S4 can
        !            39: learn to use it, although previous familiarity
        !            40: with \*(S4 will help.
        !            41: .AE
        !            42: .2C
        !            43: .NH 1
        !            44: Introduction
        !            45: .PP
        !            46: The semantics of the \*(S4 programming language |reference(poage griswold)
        !            47: include such unusual and useful
        !            48: features as
        !            49: dynamic typing,
        !            50: a general-purpose garbage collector,
        !            51: excellent character string facilities,
        !            52: associative arrays,
        !            53: and strong run-time diagnostics.
        !            54: Griswold|reference(snobol4 applications),
        !            55: Gimpel|reference(gimpel snobol4),
        !            56: and others have documented many ways of
        !            57: doing things in \*(S4 that can only be accomplished
        !            58: with much more difficulty in other languages.
        !            59: .PP
        !            60: Unfortunately, the control structures of
        !            61: \*(S4 are archaic, and the fact that
        !            62: blank is an operator tends to encourage certain types
        !            63: of errors that are hard to detect.
        !            64: Other aspects of the language make it a nuisance
        !            65: to construct large programs out of small parts.
        !            66: .PP
        !            67: To ameliorate some of these problems,
        !            68: we have designed and implemented a new language
        !            69: that provides some syntactic sugar for \*(S4.
        !            70: The obvious name for such a language is
        !            71: .I Snocone .
        !            72: .PP
        !            73: The design of the Snocone language was inspired by
        !            74: Ratfor|reference(kernighan ratfor)
        !            75: and \s-2EFL\s0|reference(efl cstr).
        !            76: Like \s-2EFL\s0, and unlike Ratfor, Snocone is a self-contained
        !            77: programming language, rather than a proper superset of
        !            78: \*(S4.
        !            79: Like Ratfor, and unlike \s-2EFL\s0, the Snocone translator
        !            80: makes no attempt to produce \*(S4 output that is
        !            81: easy for humans to understand.
        !            82: .PP
        !            83: Hanson|reference(ratsno)
        !            84: has written a similar, but simpler preprocessor
        !            85: for \*(S4.
        !            86: His preprocessor is like Ratfor
        !            87: in that it does not change the statements in
        !            88: the basic language but rather adds new syntax.
        !            89: .PP
        !            90: Griswold|reference(rebus)
        !            91: has written a preprocessor for a language
        !            92: similar to Snocone,
        !            93: the syntax of which is based on Icon|reference(icon language)
        !            94: rather than on C or \s-2EFL\s0.
        !            95: His preprocessor is written in C,
        !            96: and uses Yacc for its parsing.
        !            97: .PP
        !            98: In contrast, Snocone has a syntax roughly
        !            99: based on C and is written in Snocone.
        !           100: .SH
        !           101: What's nice about \*(S4
        !           102: .PP
        !           103: Variables in \*(S4 are dynamically typed:
        !           104: the type of any variable is the type of the
        !           105: value most recently assigned to it.
        !           106: Thus, declarations are unnecessary, and, in fact,
        !           107: \*(S4 has no declarations as such
        !           108: (except that procedures can have local variables).
        !           109: .PP
        !           110: All operators and built-in functions check
        !           111: their argument types;
        !           112: each argument is converted automatically
        !           113: to an appropriate type.
        !           114: A run-time diagnostic message results
        !           115: if a conversion is impossible.
        !           116: For instance, if an addend
        !           117: is a string, \*(S4 will try
        !           118: to convert it to an integer or real number,
        !           119: depending on the form of its value.
        !           120: If the value is inappropriate to convert to
        !           121: a numeric type, the program will halt.
        !           122: .PP
        !           123: Like Lisp, and unlike most other languages,
        !           124: \*(S4 does not have any explicit mechanism for
        !           125: returning memory to the system.
        !           126: Rather, the implementation must detect
        !           127: when memory is no
        !           128: longer needed and make that memory available
        !           129: for re-use.
        !           130: This makes the language harder to implement,
        !           131: but easier to use.
        !           132: One nice by-product of this sort of memory
        !           133: allocator is that \*(S4 programs tend
        !           134: to be free of arbitrary size limitations.
        !           135: .PP
        !           136: One \*(S4 data type is the
        !           137: .I pattern ,
        !           138: which describes an (arbitrarily complex)
        !           139: set of character strings.
        !           140: Briefly, the language incorporates a general
        !           141: top-down backtracking parser.
        !           142: This parser is so easy to use that it
        !           143: is the usual way of dealing with strings
        !           144: in \*(S4.
        !           145: .PP
        !           146: Another useful data type is the
        !           147: .I table .
        !           148: A table is like a one-dimensional array, except that
        !           149: its subscripts are not limited to being integers.
        !           150: For instance, a compiler symbol table might be maintained
        !           151: as a \*(S4 table, with the subscript being the identifier
        !           152: and the value being some structure which holds the desired
        !           153: information about that identifier.
        !           154: .PP
        !           155: An interesting and unusual semantic idea in \*(S4 is
        !           156: that of
        !           157: .I "statement failure" .
        !           158: Many operations can easily encounter conditions that
        !           159: preclude their execution.
        !           160: For example, an attempt may be made to access a non-existent
        !           161: array element, read beyond the last record of a file,
        !           162: search a string for a pattern that it does not contain,
        !           163: or even make a comparison that gives an unexpected result.
        !           164: When this happens, the operation
        !           165: .I fails .
        !           166: Usually, the failure of an operation implies the failure
        !           167: of the statement of which it is a part.
        !           168: While statement failure is not an error condition,
        !           169: it can be tested.
        !           170: In fact, conditional transfer on the success or
        !           171: failure of a statement is virtually the only
        !           172: kind of control structure in \*(S4.
        !           173: .PP
        !           174: A good implementation of \*(S4 is
        !           175: Macrospitbol, by Dewar and McCann|reference(spitbol).
        !           176: This is a portable threaded-code interpreter, which
        !           177: is fast and robust enough to be used for ``production''
        !           178: purposes.
        !           179: For example, on a VAX-11/780 it translates about 150
        !           180: statements per second and executes about 5,000.
        !           181: .PP
        !           182: \*(S4 implementations in general have excellent
        !           183: run-time diagnostic facilities.
        !           184: The language definition includes ways of tracing
        !           185: programs for debugging, and of trapping almost any
        !           186: kind of run-time error.
        !           187: .SH
        !           188: What's not nice about \*(S4
        !           189: .PP
        !           190: Here is a simple \*(S4 program to add the
        !           191: integers between 1 and 1000:
        !           192: .P1 0
        !           193:        sum = 0
        !           194:        term = 1
        !           195: loop   sum = sum + term
        !           196:        term = term + 1
        !           197:        LE(term,1000)           :s(loop)
        !           198:        OUTPUT = "The sum is " sum
        !           199: END
        !           200: .P2
        !           201: .PP
        !           202: The first two lines of this program assign values to
        !           203: variables.
        !           204: The name
        !           205: .CW loop
        !           206: in the third line is a label; it is recognized as such
        !           207: because it begins the line without any white space
        !           208: ahead of it.
        !           209: .PP
        !           210: The fifth line calls a built-in function to compare the
        !           211: value of the variable
        !           212: .CW term
        !           213: to 1000.
        !           214: \*(S4 has no comparison operators: all comparisons are
        !           215: done by
        !           216: .I predicate
        !           217: functions that either return the null string if the condition
        !           218: being tested is true or fail if the condition is false.
        !           219: The \f(CW:s(loop)\fP at the end of the line
        !           220: is a conditional
        !           221: .B "go to" :
        !           222: it causes control to transfer to the label
        !           223: .CW loop
        !           224: if the statement succeeds, and to the following
        !           225: statement if it fails.
        !           226: .PP
        !           227: In contrast, the program looks like this in Snocone:
        !           228: .P1 0
        !           229: sum = 0
        !           230: term = 1
        !           231: do {
        !           232:        sum = sum + term
        !           233:        term = term + 1
        !           234: } while (term <= 1000)
        !           235: OUTPUT = "The sum is " && sum
        !           236: .P2
        !           237: .PP
        !           238: In the past fifteen years, the trend in programming
        !           239: language design has been overwhelmingly toward
        !           240: programming languages with control structures that
        !           241: make it reasonable to write programs with at most
        !           242: a very few
        !           243: .B "go to"
        !           244: statements.
        !           245: \*(S4 exhibits almost the exact opposite of this trend:
        !           246: essentially the only control structure in \*(S4 is
        !           247: a form of conditional
        !           248: .B "go to"
        !           249: statement.
        !           250: There is no block structure, and all labels are global.
        !           251: Thus, writing a \*(S4 program requires a constant effort
        !           252: to invent new label names, and to choose names that have
        !           253: at least some chance of telling the reader whether their
        !           254: use is local or global.
        !           255: .PP
        !           256: The programmer's job is not made any easier by the peculiar way
        !           257: \*(S4 handles subroutines.
        !           258: \*(S4 subroutines are defined at run time by a built-in
        !           259: function called \f(CWDEFINE\fP.
        !           260: Its argument is a
        !           261: .I prototype
        !           262: of the subroutine to be defined \-
        !           263: a character string that describes how the
        !           264: subroutine is to be used.
        !           265: The subroutine's entry point is the label with the same name.
        !           266: Because the statement bearing this label is not special
        !           267: in any other way, it must be placed where it will not
        !           268: be executed in the ordinary course of events.
        !           269: The call to \f(CWDEFINE\fP, on the other hand, must
        !           270: be executed before the subroutine it defines can be used.
        !           271: .PP
        !           272: This leads \*(S4 programmers into contortions.
        !           273: To keep the \f(CWDEFINE\fP call near the subroutine body,
        !           274: one must invent yet another label:
        !           275: .P1 0
        !           276:         DEFINE("square(x)")    :(square.end)
        !           277: square  square = x * x         :(RETURN)
        !           278: square.end
        !           279: .P2
        !           280: Alternatively, one can put all the \f(CWDEFINE\fP calls
        !           281: in one place, at the cost of moving the body of each
        !           282: subroutine arbitrarily far from where its prototype appears.
        !           283: .PP
        !           284: The Snocone programmer has an easier job:
        !           285: .P1 0
        !           286: procedure square (x) {
        !           287:        return x * x
        !           288: }
        !           289: .P2
        !           290: .SH
        !           291: Motivation
        !           292: .PP
        !           293: Snocone's purpose, then, is to make it easy for a
        !           294: programmer used to a block-structured language like C
        !           295: to write programs that have the freedom and semantic flexibility
        !           296: of \*(S4.
        !           297: To this end, we have changed the \*(S4 language in several ways.
        !           298: .PP
        !           299: First, we introduced an explicit concatenation operator.
        !           300: \*(S4 uses blank for concatenation, so
        !           301: .P1 0
        !           302: y = f(x)
        !           303: .P2
        !           304: is a function call, but
        !           305: .P1 0
        !           306: y = f (x)
        !           307: .P2
        !           308: assigns to \f(CWy\fP the concatenation of the 
        !           309: values of the variables
        !           310: \f(CWf\fP and \f(CWx\fP.
        !           311: We chose \f(CW&&\fP to represent concatenation because
        !           312: \*(S4 programs often use concatenation for the same purpose
        !           313: that C programs use \f(CW&&\fP.
        !           314: .PP
        !           315: Second, we allow much the same freedom with spaces that
        !           316: C does, with one exception:  newline ends a statement.
        !           317: Statements may be continued onto multiple lines in much the
        !           318: same way as in \s-2EFL\s0 programs: a line is continued
        !           319: if it ends with an
        !           320: operator or some kind of open bracket.
        !           321: .PP
        !           322: Third, we have added procedure and structure declarations.
        !           323: These things are accomplished in \*(S4 by calling built-in
        !           324: subroutines, and the context of the calls is often obscure.
        !           325: .PP
        !           326: Finally, we have introduced control structures similar to
        !           327: those in C and other block-structured languages: the
        !           328: \f(CWif\fP, \f(CWwhile\fP, \f(CWfor\fP, and \f(CWdo\fP statements.
        !           329: .NH 1
        !           330: Language Description
        !           331: .NH 2
        !           332: Lexical Conventions
        !           333: .PP
        !           334: Blanks (spaces and tabs, but not newlines) may not appear within a
        !           335: token, but may freely separate tokens.
        !           336: Comments begin with a \f(CW#\fP
        !           337: character and end at the end of the line.
        !           338: .PP
        !           339: Constants may be integers, reals, or strings.
        !           340: There are no signed
        !           341: numeric constants.
        !           342: Integers range from 0 to $2 sup 31 - 1$.
        !           343: Real constants
        !           344: are short-precision only, and must contain either a decimal point or an
        !           345: \f(CWE\fP (or \f(CWe\fP).
        !           346: String constants may be delimited by single or double quotes
        !           347: with the same meaning.
        !           348: Characters in quotes receive no special
        !           349: interpretation.
        !           350: A single quote may appear in strings delimited by
        !           351: double quotes, and vice versa.
        !           352: .EQ
        !           353: delim off
        !           354: .EN
        !           355: .PP
        !           356: Identifiers may be of any length.
        !           357: All characters in an identifier are
        !           358: significant, but their case is presently not significant.
        !           359: Case may
        !           360: become significant in the future.
        !           361: An identifier is a non-empty
        !           362: sequence of letters and digits, beginning with a letter.
        !           363: Underscore
        !           364: (_) is a letter.
        !           365: The same identifier may be used independently to
        !           366: represent a procedure, label, or variable.
        !           367: .NH 2
        !           368: Statement Separation
        !           369: .PP
        !           370: Snocone delimits statements much like the Shell|reference(bstj shell)
        !           371: (the command interpreter in the
        !           372: .UX
        !           373: operating system)|reference(unix cacm ritchie thompson)
        !           374: or \s-2EFL\s0.
        !           375: A statement is
        !           376: ended either by a semicolon or newline, except that if the last token
        !           377: on a line is an operator or open parenthesis or bracket, the next line
        !           378: is automatically considered as part of the current statement.
        !           379: Newlines
        !           380: may also separate clauses of compound statements, so
        !           381: .P1 0
        !           382: if (a < 0)
        !           383:        a = 0
        !           384: .P2
        !           385: is acceptable and means the same as
        !           386: .P1 0
        !           387: if (a < 0) a = 0
        !           388: .P2
        !           389: or, spreading things as much as possible:
        !           390: .P1 0
        !           391: if (
        !           392: a <
        !           393: 0)
        !           394: a =
        !           395: 0
        !           396: .P2
        !           397: .NH 2
        !           398: File Inclusion
        !           399: .PP
        !           400: The contents of an arbitrary file can be incorporated into
        !           401: the program by writing an
        !           402: .I "include line"
        !           403: in one of the following four forms:
        !           404: .P1 0
        !           405: #include "\fIfile\fP"
        !           406: #include '\fIfile\fP'
        !           407: #include <\fIfile\fP>
        !           408: #include {\fIfile\fP}
        !           409: .P2
        !           410: Spaces may appear between \f(CW#\fP and \f(CWinclude\fP.
        !           411: The contents of the named file are substituted for
        !           412: the \f(CWinclude\fP line.
        !           413: .PP
        !           414: The exact behavior of an \f(CWinclude\fP line depends
        !           415: on the delimiters surrounding the file name.
        !           416: If quotes are used (single or double), the file is
        !           417: sought in the current directory.
        !           418: If brackets are used (angle brackets or curly braces),
        !           419: the file is sought in an installation-dependent
        !           420: system library
        !           421: (\f(CW/usr/lib/snocone\fP on our systems).
        !           422: If double quotes or angle brackets are used, the
        !           423: file is included unconditionally, even if the same
        !           424: file is included several times.
        !           425: If single quotes or curly braces are used,
        !           426: the file will only be included once, even if it
        !           427: is named in several \f(CWinclude\fP lines.
        !           428: .PP
        !           429: This latter behavior is useful in the following sort of situation.
        !           430: Suppose that procedure
        !           431: .I proc1
        !           432: is defined in
        !           433: file
        !           434: .I proc1.h ,
        !           435: and procedure
        !           436: .I proc2
        !           437: is defined
        !           438: in file
        !           439: .I proc2.h .
        !           440: A program that calls both
        !           441: .I proc1
        !           442: and
        !           443: .I proc2
        !           444: might then contain:
        !           445: .P1 0
        !           446: #include "proc1.h"
        !           447: #include "proc2.h"
        !           448: .P2
        !           449: Now, suppose that
        !           450: .I proc1
        !           451: is changed to call
        !           452: .I proc2 .
        !           453: If
        !           454: .I proc1.h
        !           455: is made to contain
        !           456: .P1 0
        !           457: #include "proc2.h"
        !           458: .P2
        !           459: then our hypothetical sample program will wind
        !           460: up with two copies of
        !           461: .I proc2.h
        !           462: and will therefore run into trouble with duplicate
        !           463: procedure definitions.
        !           464: If, however,
        !           465: .I proc1.h
        !           466: contains:
        !           467: .P1 0
        !           468: #include 'proc2.h'
        !           469: .P2
        !           470: and the main program contains:
        !           471: .P1 0
        !           472: #include 'proc1.h'
        !           473: #include 'proc2.h'
        !           474: .P2
        !           475: then
        !           476: .I proc2
        !           477: will be defined only once.
        !           478: .NH 2
        !           479: Expression Evaluation, Success, and Failure
        !           480: .PP
        !           481: Evaluating an expression has one of three
        !           482: outcomes: a value, failure, or error.
        !           483: .PP
        !           484: Errors normally result in a diagnostic message
        !           485: and termination of the program, and arise
        !           486: from the usual sorts of things:
        !           487: overflow, underflow,
        !           488: division by zero, impossible conversions,
        !           489: running out of memory, and so on.
        !           490: .PP
        !           491: Failure, on the other hand, is not an error
        !           492: condition.
        !           493: An expression that fails is merely one that
        !           494: does not yield a value.
        !           495: Examples of expressions that fail include
        !           496: attempts to refer to non-existent array
        !           497: elements,
        !           498: attempts to read beyond end of file,
        !           499: and even comparisons that yield unexpected results.
        !           500: For instance, the expression
        !           501: .P1 0
        !           502: a < b
        !           503: .P2
        !           504: yields a null string if \f(CWa\fP is indeed less than \f(CWb\fP,
        !           505: and fails otherwise.
        !           506: .PP
        !           507: Most operators fail if any of their operands fails.
        !           508: The few exceptions will be noted below.
        !           509: .PP
        !           510: An expression that always either yields a null string or fails
        !           511: is sometimes called a
        !           512: .I predicate .
        !           513: Testing such expressions for success or failure in \f(CWif\fP,
        !           514: \f(CWwhile\fP, and \f(CWdo\fP statements is the primary way
        !           515: of affecting the flow of control
        !           516: in Snocone.
        !           517: .NH 2
        !           518: Data Types, Declarations, and Scope
        !           519: .PP
        !           520: Variables in Snocone are dynamically typed: a variable has the type of
        !           521: the value most recently assigned to it.
        !           522: Except for a few predefined variables
        !           523: (described under
        !           524: .I "pattern matching" ),
        !           525: all variables have the null string as their initial values.
        !           526: All variables are global, but procedures
        !           527: can nominate variables whose
        !           528: values will automatically be saved at
        !           529: entry and restored at exit.
        !           530: The only declarations define
        !           531: structures:
        !           532: .P1 0
        !           533: struct cons {car, cdr}
        !           534: .P2
        !           535: In effect, this declaration defines three procedures named
        !           536: \f(CWcons\fP, 
        !           537: \f(CWcar\fP,
        !           538: and \f(CWcdr\fP.
        !           539: The value of \f(CWcons(a,b)\fP is a newly-created
        !           540: \f(CWcons\fP object with \f(CWa\fP and \f(CWb\fP
        !           541: as the values of its \f(CWcar\fP and \f(CWcdr\fP fields,
        !           542: respectively.
        !           543: The fields, in turn, are accessed by similarly-named
        !           544: functions.
        !           545: For instance, the \f(CWcdr\fP field of \f(CWcons\fP structure \f(CWx\fP
        !           546: is accessed as \f(CWcdr(x)\fP.
        !           547: Thus this program:
        !           548: .P1 0
        !           549: struct cons {car, cdr}
        !           550: a = cons (3, cons (4, 5))
        !           551: OUTPUT = car (cdr (a))
        !           552: .P2
        !           553: prints \f(CW4\fP.
        !           554: The procedures corresponding to field names may
        !           555: be used as the target of an assignment:
        !           556: .P1 0
        !           557: car (a) = "Hello"
        !           558: .P2
        !           559: Snocone offers other data types than those described above.
        !           560: While
        !           561: numeric constants cannot be negative, variables and expressions
        !           562: suffer from no such restriction.
        !           563: Strings may be of any length up
        !           564: to an implementation-defined upper bound, usually many thousands
        !           565: of characters.
        !           566: All variables have the null string as their
        !           567: initial value.
        !           568: .PP
        !           569: Aggregate values come in two types: arrays and tables.
        !           570: Each type
        !           571: is created by a built-in procedure with the same name.
        !           572: Thus:
        !           573: .P1 0
        !           574: a = ARRAY (20)
        !           575: .P2
        !           576: creates a 20-element array, initializes each element to the null
        !           577: string, and assigns it to a.
        !           578: The second argument to the array
        !           579: procedure is an initializing value:
        !           580: .P1 0
        !           581: a = ARRAY (20, -1)
        !           582: .P2
        !           583: makes a an array of 20 elements, each with value \-1.
        !           584: To get
        !           585: multi-dimensional arrays, express the dimensions as a string:
        !           586: .P1 0
        !           587: a = ARRAY ('4,5', -1)
        !           588: .P2
        !           589: One can also give explicit lower bounds:
        !           590: .P1 0
        !           591: a = ARRAY ('-10:10')
        !           592: .P2
        !           593: The default lower bound is 1.
        !           594: .PP
        !           595: Array elements are referenced by Algol-like subscripts:
        !           596: .P1 0
        !           597: a[i,j] = a[i,j] + b[i,k] * c[k,j]
        !           598: .P2
        !           599: Each array element behaves as a variable, and can therefore have
        !           600: a value of any data type, independently of any other element.
        !           601: .PP
        !           602: A table is like a one-dimensional array whose subscripts are not
        !           603: restricted to integers:
        !           604: .P1 0
        !           605: t = TABLE (20)
        !           606: .P2
        !           607: The argument to the \f(CWTABLE\fP procedure is an estimate of the maximum
        !           608: number of elements that will actually be stored in the table.
        !           609: If substantially more elements than
        !           610: this are stored, access will
        !           611: begin to slow down.
        !           612: On the other hand, giving too large an
        !           613: initial value wastes space.
        !           614: Once a table has been created, any
        !           615: value can be used for a subscript.
        !           616: \f(CWt[a]\fP and \f(CWt[b]\fP will refer to
        !           617: the same element if and only if a and b are identical.
        !           618: The
        !           619: precise meaning of ``identical'' is given with the description of
        !           620: the \f(CW::\fP and \f(CW:!:\fP operators;
        !           621: suffice it to say that \f(CW3\fP and \f(CW"3"\fP are
        !           622: not identical, and that after executing
        !           623: .P1 0
        !           624: a = b
        !           625: .P2
        !           626: \f(CWa\fP and \f(CWb\fP are
        !           627: identical regardless of what values they had before.
        !           628: .NH 2
        !           629: Binary Operators
        !           630: .PP
        !           631: The following operators are grouped in order of decreasing priority.
        !           632: Unless otherwise stated, they are left-associative.
        !           633: .IP "\f(CW. $\fP"
        !           634: Pattern value assignment (see Patterns)
        !           635: .IP "\f(CW^\fP"
        !           636: Exponentiation (right-associative).
        !           637: The right argument
        !           638: must be an integer.
        !           639: If both arguments are integers,
        !           640: the right argument must be non-negative
        !           641: .IP "\f(CW* / %\fP"
        !           642: .br
        !           643: Multiplication, division, and remainder.
        !           644: As in C,
        !           645: and as not in \*(S4, multiplication and
        !           646: division have the same precedence.
        !           647: .IP "\f(CW+ -\fP"
        !           648: Addition and subtraction.
        !           649: .IP "\f(CW== != < > <= >= :==: :!=:\fP"
        !           650: .IP "\f(CW:<: :>: :<=: :>=: :: :!:\fP"
        !           651: .br
        !           652: Comparison predicates.
        !           653: Each of these operators returns
        !           654: a null string if the indicated relation holds,
        !           655: and fails if not.
        !           656: The first six do
        !           657: numeric comparisons: an error results if either
        !           658: operand cannot be converted to a number.
        !           659: The next six do string comparisons: an error results
        !           660: if either operand cannot be converted to a string.
        !           661: The last two test if the two operands are identical.
        !           662: Values of different data types are never identical.
        !           663: Strings and numbers are identical if their data types and values match.
        !           664: Other values are identical if they refer to the same object.
        !           665: .IP "\f(CW&&\fP"
        !           666: Concatenation.
        !           667: The left operand is evaluated
        !           668: first; if its evaluation fails, the \f(CW&&\fP operator fails.
        !           669: Otherwise, the right operand is
        !           670: evaluated, and \f(CW&&\fP fails if the right operand fails.
        !           671: If either operand is the null string, the
        !           672: result is the other operand, even if that operand is not a string.
        !           673: Otherwise, both operands are
        !           674: converted to strings and the result is their concatenation.
        !           675: An error results if either
        !           676: operand cannot be converted to a string.
        !           677: Note that if the operands of \f(CW&&\fP are predicates,
        !           678: \f(CW&&\fP can be used as a kind of logical conjunction.
        !           679: .IP "\f(CW||\fP"
        !           680: Logical disjunction.
        !           681: The left operand is evaluated first; if it
        !           682: succeeds, its value is the value of \f(CW||\fP.
        !           683: Otherwise, the value is that of the right operand.
        !           684: If both operands fail, \f(CW||\fP fails.
        !           685: .IP "\f(CW|\fP"
        !           686: Pattern alternation.
        !           687: See
        !           688: .I "pattern matching"
        !           689: for details.
        !           690: .IP "\f(CW=\fP"
        !           691: Assignment.
        !           692: This operator is right-associative.
        !           693: .IP "\f(CW?\fP"
        !           694: Pattern match operator.
        !           695: The left operand is
        !           696: converted to a string and searched for the first
        !           697: substring that matches the pattern given by the right operand.
        !           698: If no such substring is found, the operator fails.
        !           699: If the left operand is a variable, \f(CW?\fP may be used on the left
        !           700: side of an assignment.
        !           701: .NH 2
        !           702: Unary Operators
        !           703: .PP
        !           704: Unary operators bind more tightly than all binary
        !           705: operators.
        !           706: .IP "\f(CW+ -\fP"
        !           707: Unary plus and minus.
        !           708: The result is always
        !           709: numeric, so unary plus is sometimes used for type conversion.
        !           710: Because unary operators bind so tightly, expressions that look like negative
        !           711: constants behave that way for all practical purposes.
        !           712: .IP "\f(CW.\fP"
        !           713: Name operator.
        !           714: The operand must be a variable;
        !           715: the result is essentially a pointer to the variable, similarly
        !           716: to the unary & operator in C.
        !           717: The result of applying the \f(CWDATATYPE\fP function
        !           718: to the result of the \f(CW.\fP operator is the string \f(CW"NAME"\fP.
        !           719: .IP "\f(CW$\fP"
        !           720: Indirection.
        !           721: The operand is converted to a name;
        !           722: the result is the object thus named.
        !           723: \f(CW$\fP always yields an lvalue.
        !           724: .IP "\f(CW?\fP"
        !           725: Query.
        !           726: If its operand fails, \f(CW?\fP fails.
        !           727: If its operand succeeds, \f(CW?\fP yields a null string.
        !           728: Useful for evaluating an expression solely for
        !           729: its side effects.
        !           730: .IP "\f(CW~\fP"
        !           731: Logical negation.
        !           732: If its operand fails, \f(CW~\fP yields a null string.
        !           733: If the operand succeeds, \f(CW~\fP fails.
        !           734: .IP "\f(CW&\fP"
        !           735: Keyword value.
        !           736: The operand must be the name of one of a restricted set of variables.
        !           737: The lvalue result is a system variable, whose value affects the
        !           738: execution of the program in some way.
        !           739: System variables are discussed separately.
        !           740: .IP "\f(CW@\fP"
        !           741: Pattern cursor assignment.
        !           742: See
        !           743: .I "pattern matching"
        !           744: for details.
        !           745: .IP "\f(CW*\fP"
        !           746: Deferred evaluation.
        !           747: Returns a value of type \f(CWEXPRESSION\fP that
        !           748: contains all the information necessary to evaluate the operand.
        !           749: The operand is not actually evaluated at this time, but can be
        !           750: evaluated later,
        !           751: either by the \f(CWEVAL\fP procedure or implicitly during pattern matching.
        !           752: .NH 2
        !           753: Statements
        !           754: .PP
        !           755: Elements in brackets are optional.
        !           756: If the description of a statement
        !           757: is split over more than one line, the statement itself may be split
        !           758: analogously.
        !           759: Snocone does not have a null statement.
        !           760: .Sx
        !           761: \fIexpression\fP
        !           762: .Sy
        !           763: .Tx
        !           764: The expression is evaluated for its side effects.
        !           765: The result, if any, is discarded.
        !           766: .Tm if S
        !           767: .Ty
        !           768: .Sx
        !           769: if (\fIexpression\fP)
        !           770:        \fIstatement1\fP
        !           771: [ else
        !           772:        \fIstatement2\fP ]
        !           773: .Sy
        !           774: .Tx
        !           775: The parenthesized
        !           776: .I expression
        !           777: is evaluated.
        !           778: If it
        !           779: succeeds,
        !           780: .I statement1
        !           781: is executed, otherwise
        !           782: .I statement2
        !           783: is executed.
        !           784: .Ty
        !           785: .Sx
        !           786: while (\fIexpression\fP)
        !           787:        \fIstatement\fP
        !           788: .Sy
        !           789: .Tx
        !           790: Behaves similarly to C: the
        !           791: .I expression
        !           792: is evaluated,
        !           793: and if it succeeds, the
        !           794: .I statement
        !           795: is executed and
        !           796: control passes back to the beginning of the \f(CWwhile\fP
        !           797: statement.
        !           798: Unlike C, Snocone has no
        !           799: .I break
        !           800: or
        !           801: .I continue
        !           802: statements.
        !           803: .Ty
        !           804: .Sx
        !           805: do
        !           806:        \fIstatement\fP
        !           807: while (\fIexpression\fP)
        !           808: .Sy
        !           809: .Tx
        !           810: Behaves similarly to C: the
        !           811: .I "statement"
        !           812: is executed, then the
        !           813: .I expression
        !           814: is evaluated, and if the
        !           815: .I expression
        !           816: succeeds, control passes back to the beginning of the \f(CWdo\fP
        !           817: statement.
        !           818: .Tm for        S
        !           819: .Ty
        !           820: .Sx
        !           821: for (\fIexpression1\fP, \fIexpression2\fP, \fIexpression3\fP)
        !           822:        \fIstatement\fP
        !           823: .Sy
        !           824: .Tx
        !           825: Equivalent to:
        !           826: .P1 0
        !           827: \fIexpression1\fP
        !           828: while (\fIexpression3\fP) {
        !           829:        \fIstatement\fP
        !           830:        \fIexpression2\fP
        !           831: }
        !           832: .P2
        !           833: .Ty
        !           834: .Sx
        !           835: {
        !           836:        \fIstatement list\fP
        !           837: }
        !           838: .Sy
        !           839: .Tx
        !           840: The statements in the list, which may contain zero or
        !           841: more statements, are executed in sequence.
        !           842: .Ty
        !           843: .Sx
        !           844: \fIlabel\fP: \fIstatement\fP
        !           845: .Sy
        !           846: .Tx
        !           847: All labels are global (because all \*(S4
        !           848: labels are global), even across procedure boundaries,
        !           849: so they must be chosen with care.
        !           850: A useful convention is
        !           851: to begin a label inside a procedure body with the name
        !           852: of the procedure and an underscore.
        !           853: .Ty
        !           854: .Sx
        !           855: go to \fIlabel\fP
        !           856: .Sy
        !           857: .Tx
        !           858: The space between \f(CWgo\fP and \f(CWto\fP is optional.
        !           859: It is wise
        !           860: not to jump from a point inside one procedure into another.
        !           861: Program
        !           862: execution may be terminated by jumping to the reserved
        !           863: label \f(CWEND\fP.
        !           864: Labels \f(CWRETURN\fP, \f(CWFRETURN\fP, \f(CWNRETURN\fP, \f(CWABORT\fP, and
        !           865: \f(CWCONTINUE\fP are also reserved.
        !           866: .Ty
        !           867: .Sx
        !           868: return [ \fIexpression\fP ]
        !           869: .Sy
        !           870: .Tx
        !           871: The current procedure returns to its caller.
        !           872: If the
        !           873: .I expression
        !           874: is given, the procedure yields that value.
        !           875: If no
        !           876: .I expression
        !           877: is given, the value returned is that of
        !           878: the variable with the same name as the procedure; if
        !           879: that variable was not assigned in the procedure, the
        !           880: null string is returned.
        !           881: .Ty
        !           882: .Sx
        !           883: freturn
        !           884: .Sy
        !           885: .Tx
        !           886: The current procedure returns and fails.
        !           887: .Ty
        !           888: .Sx
        !           889: nreturn [ \fIexpression\fP ]
        !           890: .Sy
        !           891: .Tx
        !           892: The current procedure returns \f(CW$(\fP\fIexpression\fP\f(CW)\fP as an
        !           893: lvalue.
        !           894: If the
        !           895: .I expression
        !           896: is not given, the indirection
        !           897: is applied to the variable with the same name as the
        !           898: procedure.
        !           899: An error results if this variable was not
        !           900: given a value inside the procedure.
        !           901: .NH 2
        !           902: Procedures
        !           903: .PP
        !           904: Here is an example of a procedure declaration:
        !           905: .P1 0
        !           906: procedure gcd (m, n) {
        !           907:        while (m != n) {
        !           908:                if (m > n)
        !           909:                        m = m % n
        !           910:                else
        !           911:                        n = n % m
        !           912:        }
        !           913:        return m
        !           914: }
        !           915: .P2
        !           916: .PP
        !           917: Arguments are passed by value, but note that the value passed for
        !           918: an aggregate argument (array, table, or structure) is really a
        !           919: pointer to the aggregate itself.
        !           920: .PP
        !           921: If a procedure is called with too few arguments, extra null strings
        !           922: are supplied as necessary.
        !           923: If called with too many arguments, the extras
        !           924: are quietly ignored.
        !           925: .PP
        !           926: Local variables can be nominated for a procedure:
        !           927: .P1 0
        !           928: procedure f(x) y, z {...
        !           929: .P2
        !           930: All variables are global, but name scoping is dynamic.
        !           931: One way to look
        !           932: at it is to imagine that when a procedure
        !           933: is entered, all the variables defined in that procedure are saved and
        !           934: set to null.
        !           935: Those variables are then restored when the procedure returns.
        !           936: .PP
        !           937: Thus, the following example prints 5 and then 1:
        !           938: .P1 0
        !           939: a = 1
        !           940: f()
        !           941: g()
        !           942: 
        !           943: procedure f() a {
        !           944:        a = 5
        !           945:        g()
        !           946: }
        !           947: 
        !           948: procedure g() {
        !           949:        OUTPUT = a
        !           950: }
        !           951: .P2
        !           952: The following example also prints 5 and then 1:
        !           953: .P1 0
        !           954: a = 1
        !           955: b = .a
        !           956: f()
        !           957: g()
        !           958: .P3
        !           959: procedure f() a {
        !           960:        a = 5
        !           961:        g()
        !           962: }
        !           963: .P3
        !           964: procedure g() {
        !           965:        OUTPUT = $b
        !           966: }
        !           967: .P2
        !           968: Local variables can only be associated with procedures.
        !           969: Procedures are
        !           970: recursive.
        !           971: .NH 2
        !           972: Input-Output
        !           973: .PP
        !           974: All I/O is done through ``associated variables''.
        !           975: A variable may be
        !           976: input-associated or output-associated (or both).
        !           977: Whenever a value is
        !           978: assigned to an output-associated variable, that value is automatically
        !           979: written in the file associated with that variable.
        !           980: Whenever a value
        !           981: is requested for an input-associated variable, a line is read from the
        !           982: file associated with that variable, and the contents of the line are
        !           983: used for the value.
        !           984: When the end of an input file is reached,
        !           985: any attempts to access variables associated
        !           986: with that file will fail.
        !           987: .PP
        !           988: Initially, the variable \f(CWOUTPUT\fP is output-associated with the standard
        !           989: output file, the variable \f(CWINPUT\fP is input-associated with the standard
        !           990: input file, and the variable \f(CWTERMINAL\fP is both input-
        !           991: and output-associated
        !           992: with the user's terminal.
        !           993: .PP
        !           994: Thus, the following program copies its standard
        !           995: input to its standard output, a line at a time:
        !           996: .P1 0
        !           997: while (OUTPUT = INPUT) {}
        !           998: .P2
        !           999: .PP
        !          1000: New associations are formed by the \f(CWINPUT\fP
        !          1001: and \f(CWOUTPUT\fP procedures:
        !          1002: .P1 0
        !          1003: INPUT  (\fIname\fP, \fIchannel\fP, \fIfile\fP)
        !          1004: OUTPUT (\fIname\fP, \fIchannel\fP, \fIfile\fP)
        !          1005: .P2
        !          1006: In both cases, 
        !          1007: .I name
        !          1008: is the name of the variable to be associated (the
        !          1009: name of the variable
        !          1010: .I x
        !          1011: is \f(CW.x\fP), and
        !          1012: .I file
        !          1013: is the file to be used.
        !          1014: .I Channel
        !          1015: is a string that you will use to identify subsequent operations
        !          1016: on that file.
        !          1017: Internally, there is a one-to-one correspondence between
        !          1018: channel names and file descriptors.
        !          1019: .PP
        !          1020: The file argument must not be given for other than the first call to
        !          1021: \f(CWINPUT\fP or \f(CWOUTPUT\fP on a given channel, as a channel can only be
        !          1022: connected to a single file.
        !          1023: .PP
        !          1024: Other procedures dealing with input-output are:
        !          1025: .Tm SET        S
        !          1026: .Sx
        !          1027: SET (\fIchannel\fP, \fIoffset\fP, \fIwhence\fP)
        !          1028: .Sy
        !          1029: .Tx
        !          1030: This function repositions the file specified by its
        !          1031: first argument in a system-dependent manner.
        !          1032: In implementations running under the
        !          1033: .UX
        !          1034: system, the behavior is similar to the
        !          1035: .I lseek
        !          1036: system call.
        !          1037: .Ty
        !          1038: .Sx
        !          1039: REWIND (\fIchannel\fP)
        !          1040: .Sy
        !          1041: .Tx
        !          1042: Repositions the named channel to the beginning of the file.
        !          1043: .Ty
        !          1044: .Sx
        !          1045: ENDFILE (\fIchannel\fP)
        !          1046: .Sy
        !          1047: .Tx
        !          1048: Indicates that you are done using the given channel.
        !          1049: All variables associated through that channel are
        !          1050: disassociated, output buffers are flushed (if any),
        !          1051: and the file is closed.
        !          1052: .Ty
        !          1053: .Sx
        !          1054: DETACH (\fIname\fP)
        !          1055: .Sy
        !          1056: .Tx
        !          1057: Disassociates the named variable.
        !          1058: Does not close
        !          1059: the file: a later call to \f(CWINPUT\fP or \f(CWOUTPUT\fP can reassociate it.
        !          1060: .NH 2
        !          1061: Pattern Matching
        !          1062: .PP
        !          1063: A
        !          1064: .I pattern
        !          1065: is a data structure that describes a class of strings.
        !          1066: Patterns
        !          1067: are used by the \f(CW?\fP operator,
        !          1068: which determines if the string given as its
        !          1069: left operand contains a substring described by the pattern given as its
        !          1070: right operand.
        !          1071: The part of the Snocone system that
        !          1072: does this is called the
        !          1073: .I scanner .
        !          1074: The input to the scanner is the string to be searched,
        !          1075: called the
        !          1076: .I subject ,
        !          1077: and the pattern sought.
        !          1078: .PP
        !          1079: The scanner tries to find a substring of the
        !          1080: subject that is matched by the pattern.
        !          1081: It first looks at substrings starting at
        !          1082: the first character of the subject.
        !          1083: If it doesn't find one, it tries the ones
        !          1084: starting at the second subject character,
        !          1085: and so on.
        !          1086: If the scanner cannot find an appropriate
        !          1087: substring, the pattern match fails.
        !          1088: .PP
        !          1089: If the \f(CW&ANCHOR\fP system variable is
        !          1090: nonzero, the scanner only looks at substrings
        !          1091: that start at the first character of the
        !          1092: subject.
        !          1093: This is said to be an
        !          1094: .I anchored
        !          1095: pattern match.
        !          1096: The \f(CW&ANCHOR\fP variable is zero at the
        !          1097: start of program execution.
        !          1098: .PP
        !          1099: The important part of pattern matching is therefore
        !          1100: determining whether the subject contains a
        !          1101: substring that starts at a given character
        !          1102: and matches a given pattern.
        !          1103: To understand how this is done, we must
        !          1104: take a closer look at patterns.
        !          1105: .PP
        !          1106: Every pattern is a concatenation of one or more
        !          1107: .I elements ,
        !          1108: each of which has zero or more
        !          1109: .I alternatives .
        !          1110: Each alternative may itself be an arbitrarily complicated pattern.
        !          1111: Some patterns have alternatives that are determined
        !          1112: dynamically as they are needed during pattern matching.
        !          1113: .PP
        !          1114: If a pattern has only one element, the scanner determines
        !          1115: if it matches at a given point by trying to match each
        !          1116: of the pattern's alternatives at that point.
        !          1117: If no alternative matches at that point, the element
        !          1118: cannot be matched.
        !          1119: .PP
        !          1120: If the pattern has more than one element,
        !          1121: matching that pattern at a given point means:
        !          1122: (a) finding an alternative for the first element
        !          1123: of the pattern that matches a subject substring
        !          1124: starting at the given point, and that also (b) allows
        !          1125: the remaining elements of the pattern to match
        !          1126: a substring that starts immediately after the
        !          1127: substring matched in (a).
        !          1128: .PP
        !          1129: During pattern matching, the scanner keeps its place
        !          1130: by means of an internal value called the
        !          1131: .I cursor ,
        !          1132: which represents the number of characters in the
        !          1133: subject that precede the current location.
        !          1134: These characters are counted from the beginning
        !          1135: of the subject even when the scanner is trying
        !          1136: to match a substring that starts at some other place
        !          1137: in the subject.
        !          1138: .PP
        !          1139: The concatenation of two patterns \f(CWP1\fP and \f(CWP2\fP
        !          1140: is a pattern whose elements
        !          1141: are \f(CWP1\fP and \f(CWP2\fP.
        !          1142: The alternation operator \f(CW|\fP similarly constructs alternatives.
        !          1143: When a string is used as a pattern, it matches only itself.
        !          1144: Thus,
        !          1145: .P1 0
        !          1146: "a" | "e" | "i" | "o" | "u"
        !          1147: .P2
        !          1148: is a pattern that matches any lower-case vowel.
        !          1149: .PP
        !          1150: Consider the following
        !          1151: statement:
        !          1152: .P1 0
        !          1153: P1 = ("ab" | "a") && ("b" | "c")
        !          1154: .P2
        !          1155: This assigns to \f(CWP\fP a pattern with two elements.
        !          1156: The first one matches
        !          1157: either \f(CWab\fP or \f(CWa\fP, and the second matches either
        !          1158: \f(CWb\fP or \f(CWc\fP.
        !          1159: Look at:
        !          1160: .P1 0
        !          1161: "ab" ? P1
        !          1162: .P2
        !          1163: The scanner first tries the first alternative of the first element of
        !          1164: P1 by matching \f(CWab\fP in the subject with \f(CWab\fP in the pattern.
        !          1165: This alternative matches, so the element (\f(CW"ab"|"a"\fP) matches, and
        !          1166: the scanner goes on to the second element (\f(CW"b"|"c"\fP).
        !          1167: Here, it will be unsuccessful
        !          1168: in matching both \f(CWb\fP and \f(CWc\fP,
        !          1169: because it has already exhausted all the
        !          1170: characters of the subject.
        !          1171: Thus the scanner fails to match the second element of
        !          1172: \f(CWP1\fP, and must back up and rematch the first element.
        !          1173: Fortunately, the
        !          1174: first element has an alternative in \f(CWa\fP;
        !          1175: this matches, and now the scanner
        !          1176: can try to match the second element (\f(CW"b"|"c"\fP) again.
        !          1177: The first alternative (\f(CWb\fP) succeeds, so
        !          1178: the \f(CW?\fP operator therefore succeeds.
        !          1179: If we wanted to examine just how the pattern elements matched,
        !          1180: we could have written:
        !          1181: .P1 0
        !          1182: P1a = ("ab"|"a").OUTPUT && ("b"|"c").OUTPUT
        !          1183: "ab" ? P1a
        !          1184: .P2
        !          1185: This would print \f(CWa\fP and \f(CWb\fP on separate lines,
        !          1186: showing that \f(CW"ab"|"a"\fP matched \f(CWa\fP and
        !          1187: \f(CW"b"|"c"\fP matched \f(CWb\fP.
        !          1188: .PP
        !          1189: When any pattern is first encountered
        !          1190: during pattern matching, it will match some string, and when the scanner
        !          1191: backs into it, it may match some other string.
        !          1192: Because many patterns behave
        !          1193: differently on their initial match than when rematched, it is useful to
        !          1194: describe the two cases separately.
        !          1195: .PP
        !          1196: For instance, when a string is used as a pattern, it initially matches
        !          1197: itself.
        !          1198: If the scanner later backs into it, it fails.
        !          1199: .PP
        !          1200: As another example, consider \f(CWP1|P2\fP.
        !          1201: This pattern matches every
        !          1202: possibility for \f(CWP1\fP, and when it has exhausted \f(CWP1\fP,
        !          1203: then matches every
        !          1204: possibility for \f(CWP2\fP before finally failing.
        !          1205: .PP
        !          1206: With this in mind, we can describe the various pattern-matching operators,
        !          1207: procedures, and pre-defined variables:
        !          1208: .IP "\f(CWP1 && P2\fP" 10
        !          1209: Tries to match \f(CWP1\fP, fails if it can't.
        !          1210: Then tries to match \f(CWP2\fP.
        !          1211: If \f(CWP2\fP fails, tries the next alternative for
        !          1212: \f(CWP1\fP, and then tries \f(CWP2\fP again.
        !          1213: Eventually, it either
        !          1214: runs out of alternatives for \f(CWP1\fP, in which case \f(CWP1&&P2\fP
        !          1215: fails, or it finds alternatives for both \f(CWP1\fP and \f(CWP2\fP which
        !          1216: allow them to match.
        !          1217: .IP "\f(CWP1 | P2\fP"
        !          1218: Tries to match \f(CWP1\fP.
        !          1219: If successful, \f(CWP1|P2\fP matches the
        !          1220: string that was matched by \f(CWP1\fP.
        !          1221: Otherwise, matches whatever
        !          1222: \f(CWP2\fP matches, and fails if \f(CWP2\fP fails.
        !          1223: .IP "\f(CWP $ V\fP"
        !          1224: Tries to match \f(CWP\fP.
        !          1225: If \f(CWP\fP matches, a copy of the substring
        !          1226: matched by \f(CWP\fP is immediately assigned to the variable \f(CWV\fP.
        !          1227: .IP "\f(CWP . V\fP"
        !          1228: Tries to match \f(CWP\fP.
        !          1229: If the entire pattern match of which
        !          1230: this element is a part is ultimately successful, a copy
        !          1231: of the substring matched by \f(CWP\fP is assigned to \f(CWV\fP after
        !          1232: the entire match completes.
        !          1233: .IP "\f(CW@N\fP"
        !          1234: Matches a null string, and immediately assigns
        !          1235: cursor position to the variable \f(CWN\fP.
        !          1236: The cursor position is
        !          1237: the number of characters that precede this null string
        !          1238: in the subject of the entire pattern match.
        !          1239: Thus:
        !          1240: .P1
        !          1241: "abcde" ? @OUTPUT && "c"
        !          1242: .P2
        !          1243: prints \f(CW0\fP, \f(CW1\fP, and \f(CW2\fP on separate lines.
        !          1244: .IP "\f(CWABORT\fP"
        !          1245: The entire pattern match is aborted.
        !          1246: .IP "\f(CWARB\fP"
        !          1247: A pre-defined variable that matches anything at all.
        !          1248: More specifically, it initially matches the null string.
        !          1249: When backed into, it matches a string one character longer
        !          1250: than the one it matched last time.
        !          1251: .IP "\f(CWBAL\fP"
        !          1252: A pre-defined variable that matches a non-empty parenthesis-
        !          1253: balanced string.
        !          1254: This string is balanced only with respect to parentheses,
        !          1255: and not any other kinds of brackets.
        !          1256: .IP "\f(CWFAIL\fP"
        !          1257: A pre-defined variable that always fails to match.
        !          1258: It
        !          1259: is sometimes useful to force the scanner to try all
        !          1260: alternatives for a pattern.
        !          1261: For instance:
        !          1262: .P1
        !          1263: s ARB $ OUTPUT && FAIL
        !          1264: .P2
        !          1265: prints all substrings of \f(CWs\fP.
        !          1266: .IP "\f(CWFENCE\fP"
        !          1267: Initially matches the null string, but if the scanner
        !          1268: backs into it, the entire pattern match is aborted.
        !          1269: Identical to to \f(CW""|ABORT\fP.
        !          1270: .IP "\f(CWREM\fP"
        !          1271: Matches from the current position to the end of the
        !          1272: subject.
        !          1273: Identical to \f(CWRTAB(0)\fP.
        !          1274: .IP "\f(CWSUCCEED\fP"
        !          1275: Identical to \f(CWARBNO("")\fP
        !          1276: .IP "\f(CWANY(s)\fP"
        !          1277: Matches any single character in \f(CWs\fP.
        !          1278: .IP "\f(CWARBNO(p)\fP"
        !          1279: A pre-defined procedure that yields a pattern that matches
        !          1280: zero or more copies of the pattern \f(CWp\fP.
        !          1281: Initially matches
        !          1282: the null string.
        !          1283: Each time the scanner backs into it,
        !          1284: it tries to extend the substring already matched by
        !          1285: matching one more instance of \f(CWp\fP.
        !          1286: .IP "\f(CWBREAK(s)\fP"
        !          1287: Matches a string starting at the current position up to
        !          1288: but not including the next character in the subject
        !          1289: that is also found somewhere in \f(CWs\fP.
        !          1290: Failure
        !          1291: if no such character is found.
        !          1292: No alternatives.
        !          1293: .IP "\f(CWBREAKX(s)\fP"
        !          1294: Like \f(CWBREAK\fP, but if backed into, it matches up to the
        !          1295: next subject character also found in \f(CWs\fP, and so on.
        !          1296: .IP "\f(CWLEN(n)\fP"
        !          1297: Matches exactly \f(CWn\fP characters.
        !          1298: .IP "\f(CWNOTANY(s)\fP"
        !          1299: Matches any single character not in \f(CWs\fP.
        !          1300: .IP "\f(CWPOS(n)\fP"
        !          1301: If exactly \f(CWn\fP subject characters precede the current
        !          1302: position, \f(CWPOS\fP matches the null string.
        !          1303: Otherwise it
        !          1304: fails.
        !          1305: .IP "\f(CWRPOS(n)\fP"
        !          1306: If exactly \f(CWn\fP subject characters remain to be matched,
        !          1307: \f(CWRPOS\fP matches the null string.
        !          1308: Otherwise it fails.
        !          1309: .IP "\f(CWRTAB(n)\fP"
        !          1310: If at least \f(CWn\fP subject characters remain in the subject,
        !          1311: \f(CWRTAB\fP matches characters until exactly \f(CWn\fP remain.
        !          1312: Otherwise
        !          1313: it fails.
        !          1314: .IP "\f(CWSPAN(s)\fP"
        !          1315: Starting at the current position, matches as many characters
        !          1316: as possible taken from \f(CWs\fP.
        !          1317: .IP "\f(CWTAB(n)\fP"
        !          1318: \f(CWTAB\fP matches from the current position forward up to
        !          1319: and including the \f(CWn\fPth character of the subject.
        !          1320: If more than \f(CWn\fP characters have already been matched
        !          1321: in the subject string, \f(CWTAB\fP fails.
        !          1322: .PP
        !          1323: All pattern-valued procedures can take an unevaluated expression as argument.
        !          1324: The expression will be evaluated when the pattern element is matched.
        !          1325: .SH
        !          1326: System Variables
        !          1327: .PP
        !          1328: The \f(CW&\fP operator looks at the name of its operand,
        !          1329: not its value.
        !          1330: For each of a small set of names, \f(CW&\fP yields a
        !          1331: .I "system variable" ,
        !          1332: whose value affects the operation of the system in some way.
        !          1333: For instance, we have already seen \f(CW&ANCHOR\fP, which
        !          1334: controls whether or not the scanner is restricted to examining
        !          1335: initial substrings of the subject.
        !          1336: The complete list of system variables is:
        !          1337: .IP "\f(CW&ABORT\fP" 13
        !          1338: The same value as the pre-defined variable \f(CWABORT\fP.
        !          1339: .IP "\f(CW&ALPHABET\fP"
        !          1340: A string that contains all the characters of the machine's
        !          1341: collating sequence, in order.
        !          1342: .IP "\f(CW&ANCHOR\fP"
        !          1343: If zero, all pattern matches are unanchored,
        !          1344: otherwise they are anchored.
        !          1345: .IP "\f(CW&ARB\fP"
        !          1346: The same value as the pre-defined variable \f(CWARB\fP.
        !          1347: .IP "\f(CW&BAL\fP"
        !          1348: The same value as the pre-defined variable \f(CWBAL\fP.
        !          1349: .IP "\f(CW&CODE\fP"
        !          1350: This variable is initially zero.
        !          1351: Its value is returned to the operating system when the
        !          1352: program finishes executing.
        !          1353: .IP "\f(CW&DUMP\fP"
        !          1354: This variable is initially zero.
        !          1355: If it is 1 at the end of execution, the values of
        !          1356: all variables are printed.
        !          1357: If it is 2, the values of all array, table, and structure
        !          1358: elements are also printed.
        !          1359: .IP "\f(CW&FAIL\fP"
        !          1360: The same value as the pre-defined variable \f(CWFAIL\fP.
        !          1361: .IP "\f(CW&FENCE\fP"
        !          1362: The same value as the pre-defined variable \f(CWFENCE\fP.
        !          1363: .IP "\f(CW&FNCLEVEL\fP"
        !          1364: The current level of procedure nesting.
        !          1365: .IP "\f(CW&INPUT\fP"
        !          1366: If this variable is set to 0, all
        !          1367: input association is suspended.
        !          1368: .IP "\f(CW&MAXLNGTH\fP"
        !          1369: The maximum length of a string.
        !          1370: This value cannot be increased beyond its
        !          1371: initial value.
        !          1372: .IP "\f(CW&OUTPUT\fP"
        !          1373: If this variable is set to 0, all output is suppressed
        !          1374: until it is again set nonzero.
        !          1375: .IP "\f(CW&REM\fP"
        !          1376: The same value as the pre-defined variable \f(CWREM\fP.
        !          1377: .IP "\f(CW&STCOUNT\fP"
        !          1378: A count of how many statements have been executed so far.
        !          1379: Because this counts \*(S4 statements, not
        !          1380: Snocone statements, it should be considered only approximate.
        !          1381: .IP "\f(CW&STLIMIT\fP"
        !          1382: When \f(CW&STCOUNT\fP reaches this value,
        !          1383: execution terminates with an error message.
        !          1384: It is initially 50000, and may be set freely.
        !          1385: If it is set to a negative value, statement
        !          1386: counting is disabled.
        !          1387: .IP "\f(CW&SUCCEED\fP"
        !          1388: The same value as the pre-defined variable \f(CWSUCCEED\fP.
        !          1389: .NH 1
        !          1390: Examples
        !          1391: .NH 2
        !          1392: Hello World
        !          1393: .PP
        !          1394: This is the canonical sample program:
        !          1395: .P1 0
        !          1396: OUTPUT = "Hello world!"
        !          1397: .P2
        !          1398: The present implementation of Snocone is case-insensitive
        !          1399: in identifiers, but future versions may well become
        !          1400: case-sensitive.
        !          1401: If so, it is likely that pre-defined names, such as
        !          1402: \f(CWINPUT\fP and \f(CWOUTPUT\fP, will have to be written
        !          1403: in upper case.
        !          1404: Keywords, such as \f(CWif\fP and \f(CWwhile\fP, will be
        !          1405: written in lower case.
        !          1406: .NH 2
        !          1407: Topological Sorting
        !          1408: .PP
        !          1409: We now develop
        !          1410: a topological sorting program based on the algorithm
        !          1411: described on page 262 of
        !          1412: .I "Fundamental Algorithms" |reference(knuth volume1).
        !          1413: The reader may wish to compare this program with the
        !          1414: \*(S4 program based on the same algorithm
        !          1415: that appears on pages 221-222 of
        !          1416: .I "The \*(S4 Programming Language" .
        !          1417: .PP
        !          1418: The input is
        !          1419: a set of pairs of objects,
        !          1420: where the first object in each pair
        !          1421: is considered to precede the second.
        !          1422: The output is a list of objects
        !          1423: in a sequence that meets all the constraints
        !          1424: implied by the input.
        !          1425: In other words, the program generates
        !          1426: a total ordering that includes a given
        !          1427: partial ordering.
        !          1428: .PP
        !          1429: If the input contains a loop, the program
        !          1430: will detect this fact and complain.
        !          1431: .PP
        !          1432: For example, given the following input:
        !          1433: .P1
        !          1434: letters alphanum
        !          1435: numbers alphanum
        !          1436: blanks optblanks
        !          1437: numbers real
        !          1438: numbers integer
        !          1439: letters variable
        !          1440: alphanum variable
        !          1441: binary binaryop
        !          1442: blanks binaryop
        !          1443: unqalphabet dliteral
        !          1444: unqalphabet sliteral
        !          1445: sliteral literal
        !          1446: dliteral literal
        !          1447: integer literal
        !          1448: real literal
        !          1449: .P2
        !          1450: the program will produce the following output:
        !          1451: .P1
        !          1452: letters
        !          1453: numbers
        !          1454: blanks
        !          1455: binary
        !          1456: unqalphabet
        !          1457: alphanum
        !          1458: real
        !          1459: integer
        !          1460: optblanks
        !          1461: binaryop
        !          1462: dliteral
        !          1463: sliteral
        !          1464: variable
        !          1465: literal
        !          1466: .P2
        !          1467: .PP
        !          1468: The basic strategy of the program is simple:
        !          1469: for each object, we remember how many immediate predecessors
        !          1470: it has, and store a list of all its immediate successors.
        !          1471: When we have finished reading the input,
        !          1472: we can immediately output the objects that have
        !          1473: no predecessors.
        !          1474: Each time we output an object, we remove it from
        !          1475: the data structure and decrement the predecessor
        !          1476: count of each of its immediate successors.
        !          1477: .PP
        !          1478: We will eventually reach a state in which we run
        !          1479: out of objects without predecessors.
        !          1480: When that happens, we are done.
        !          1481: If any objects remain, they form a loop.
        !          1482: .PP
        !          1483: To reduce the time spent searching for objects
        !          1484: without predecessors, we keep a queue of such
        !          1485: objects.
        !          1486: We must also keep a queue of the successors
        !          1487: of each object.
        !          1488: In both cases, we could use a stack instead of
        !          1489: a queue, but using a queue tends to favor the
        !          1490: order in which objects appeared in the input,
        !          1491: which makes the output more intuitively useful.
        !          1492: .PP
        !          1493: The following structure declarations and subroutines
        !          1494: manipulate queues.
        !          1495: A queue consists of a header (of type \f(CWqueue\fP)
        !          1496: which points to the first and last elements of
        !          1497: a singly-linked list of queue elements (of type \f(CWqel\fP).
        !          1498: .P1 0
        !          1499: struct queue {head, tail}
        !          1500: struct qel {obj, link}
        !          1501: 
        !          1502: .P3
        !          1503: procedure enqueue (q, x) y {
        !          1504:        if (head(q) :: "")
        !          1505:                head(q) = tail(q) = qel(x)
        !          1506:        else {
        !          1507:                y = qel(x)
        !          1508:                link(tail(q)) = y
        !          1509:                tail(q) = y
        !          1510:        }
        !          1511: }
        !          1512: .P3
        !          1513: 
        !          1514: procedure dequeue (q) x {
        !          1515:        if (head(q) :: "")
        !          1516:                freturn
        !          1517:        x = head(q)
        !          1518:        if ((head(q) = link(x)) :: "")
        !          1519:                tail(q) = ""
        !          1520:        return obj(x)
        !          1521: }
        !          1522: .P2
        !          1523: .PP
        !          1524: By convention, we use the null string to indicate
        !          1525: the end of a list.
        !          1526: This is convenient because uninitialized variables
        !          1527: and structure fields and missing arguments are automatically set to
        !          1528: the null string.
        !          1529: Thus, the test
        !          1530: .P1
        !          1531: head(q) :: ""
        !          1532: .P2
        !          1533: is a convenient way of testing whether \f(CWhead(q)\fP
        !          1534: has been set or not.
        !          1535: .PP
        !          1536: We represent each object as a structure containing
        !          1537: the object's name (so we can print it), the count of
        !          1538: immediate predecessors, and the queue of successors:
        !          1539: .P1
        !          1540: struct object {name, count, suc}
        !          1541: .P2
        !          1542: .PP
        !          1543: Since we will be reading the names of objects
        !          1544: rather than the objects directly, we will need
        !          1545: to map names to objects.
        !          1546: This can easily be done with a table and a mapping subroutine
        !          1547: that creates elements in the table as needed:
        !          1548: .P1 0
        !          1549: namemap = TABLE()
        !          1550: objects = queue()
        !          1551: 
        !          1552: procedure getobj (name) {
        !          1553:     if ((getobj = namemap[name]) :: "") {
        !          1554:         getobj = namemap[name] =
        !          1555:             object (name, 0, queue())
        !          1556:         enqueue (objects, getobj)
        !          1557:         nobj = nobj + 1
        !          1558:     }
        !          1559: }
        !          1560: .P2
        !          1561: .PP
        !          1562: This procedure uses the feature that if no return value
        !          1563: is explicitly given, the value of the variable with the
        !          1564: same name as the procedure is used.
        !          1565: If an appropriate table element already
        !          1566: exists, the \f(CW::\fP operator will fail
        !          1567: and the value of \f(CWgetobj\fP will be the
        !          1568: value retrieved from the table.
        !          1569: As a side effect, we maintain a global queue of
        !          1570: all known objects in \f(CWobjects\fP and
        !          1571: count them in \f(CWnobj\fP.
        !          1572: .PP
        !          1573: Now that we can map from names to objects, it is
        !          1574: an easy matter to enter a new relation into
        !          1575: our data structure.
        !          1576: Procedure \f(CWenter\fP takes the
        !          1577: names of two objects:
        !          1578: .P1 0
        !          1579: procedure enter (p, q) {
        !          1580:        p = getobj (p)
        !          1581:        q = getobj (q)
        !          1582:        count(q) = count(q) + 1
        !          1583:        enqueue (suc(p), q)
        !          1584: }
        !          1585: .P2
        !          1586: .PP
        !          1587: We first locate the objects to which \f(CWp\fP
        !          1588: and \f(CWq\fP refer, creating them if necessary.
        !          1589: Since \f(CWp\fP precedes \f(CWq\fP, we increment
        !          1590: the predecessor count of \f(CWq\fP and append
        !          1591: \f(CWq\fP to the successor list of \f(CWp\fP.
        !          1592: .PP
        !          1593: Building the data structure is now just a matter of
        !          1594: scanning the input file:
        !          1595: .P1 0
        !          1596: while (line = INPUT) {
        !          1597:    if (line ? FENCE && BREAK(' ').p &&
        !          1598:                SPAN(' ') && rem.q)
        !          1599:         enter (p, q)
        !          1600:     else
        !          1601:         TERMINAL = "bad input line: " && line
        !          1602: }
        !          1603: .P2
        !          1604: .PP
        !          1605: The pattern match assumes that everything up to the first
        !          1606: blank in the input line is the first object in a
        !          1607: relation, and everything after the first blank is
        !          1608: the second object.
        !          1609: If the match fails, the program complains.
        !          1610: .PP
        !          1611: Once all the input has been read, we must initialize
        !          1612: the queue of minimal objects (objects without predecessors).
        !          1613: This was the reason for keeping a queue of all objects,
        !          1614: which is now destroyed to build the queue of minimal objects.
        !          1615: It is not necessary to destroy the queue, but it is more
        !          1616: convenient because it is then possible to use \f(CWdequeue\fP:
        !          1617: .P1 0
        !          1618: zeroes = queue()
        !          1619: 
        !          1620: while (x = dequeue (objects))
        !          1621:        if (count (x) == 0)
        !          1622:                enqueue (zeroes, x)
        !          1623: .P2
        !          1624: .PP
        !          1625: As long as there is a minimal object,
        !          1626: we can print its name, delete it, and
        !          1627: decrement the predecessor count of each
        !          1628: of its successors.
        !          1629: If we decrement a predecessor count to
        !          1630: zero, that object is now minimal.
        !          1631: .P1 0
        !          1632: while (x = dequeue (zeroes)) {
        !          1633:     nobj = nobj - 1
        !          1634:     OUTPUT = name(x)
        !          1635:     while (y = dequeue (suc (x))) {
        !          1636:         if ((count(y) = count(y) - 1) == 0)
        !          1637:             enqueue (zeroes, y)
        !          1638:     }
        !          1639: }
        !          1640: .P2
        !          1641: .PP
        !          1642: This loop runs until there are no more minimal objects.
        !          1643: If there are still elements remaining (\f(CWnobj\fP is nonzero),
        !          1644: then those elements form a loop.
        !          1645: .P1 0
        !          1646: if (nobj != 0)
        !          1647:     TERMINAL = "The ordering contains a loop."
        !          1648: .P2
        !          1649: .NH 1
        !          1650: References
        !          1651: .LP
        !          1652: |reference_placement

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.