|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.