-.SA 1
-.TL "CUPL/CORC: the UNIX implementation"
-.AF "Thyrsus Enterprises"
-.AU "Eric S. Raymond" ESR "\&\&22 South Warren Ave, Malvern PA 19355"
-.MT 4
-.nr Pt 1
-.P
-.H 1 "Introduction"
-.P
-CUPL was an early (1966) teaching language implemented as a batch compiler on
-the IBM/360 at Cornell University. It was descended from an earlier (1962)
-experimental language called CORC (CORnell Compiler), which was in turn derived
-from PL/1. Statements made without qualification about CUPL below
-also apply to CORC.
-.P
-CUPL is documented in "CUPL: The Cornell University Programming Language", by
-R.J. Walker, a manual first printed in November 1966. This implementation, and
-the discussion in this paper, are based on the July 1967 second printing.
-.P
-CORC is documented in "An Instruction Manual For CORC", by R.W. Conway,
-W.L. Maxwell, R.J. Walker. This implementation tracks the 2nd edition
-of September 1963.
-.P
-The purpose of this implementation, and this paper, is to preserve a
-CUPL/CORC implementation for the edification of historians and
-students of programming-language design. CUPL and CORC were
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<article>
+<sect1 id='introduction'><title>Introduction</title>
+
+<para>CUPL was an early (1966) teaching language implemented as a
+batch compiler on the IBM/360 at Cornell University. It was descended
+from an earlier (1962) experimental language called CORC (CORnell
+Compiler), which was in turn derived from PL/1. Statements made
+without qualification about CUPL below also apply to CORC.</para>
+
+<para>CUPL is documented in "CUPL: The Cornell University Programming
+Language", by R.J. Walker, a manual first printed in November 1966.
+This implementation, and the discussion in this paper, are based on
+the July 1967 second printing.</para>
+
+<para>CORC is documented in "An Instruction Manual For CORC", by
+R.W. Conway, W.L. Maxwell, R.J. Walker. This implementation tracks
+the 2nd edition of September 1963.</para>
+
+<para>The purpose of this implementation, and this paper, is to
+preserve a CUPL/CORC implementation for the edification of historians
+and students of programming-language design. CUPL and CORC were
representative of a significant class of teaching languages in its
period, and study of their design casts a clear light on the
-preoccupations of their time.
-.P
-.H 1 "The CUPL and CORC Languages"
-.P
-This distribution includes, in the file cupl.doc, a transcription of all
-the relevant parts of the CUPL manual (the bulk of the text is a
-general tutorial on scientific programming). Another file,
-corc.doc, similarly excerpts the CORC manual.
-.P
-CUPL has only one scalar type, a long floating-point real corresponding to C
-double (round-off rules coerce scalars to integer in contexts like
-subscripting). It supports vector and matrix aggregates, and has operations
-specialized for linear-algebra calculations. There is no function abstraction
-and all variables are global; program chunking is achieved through BLOCK
-or BEGIN blocks which resemble parameterless subroutines.
-.P
-CUPL rather resembles early BASICs, minus BASIC's string facility. It is
+preoccupations of their time.</para>
+
+</sect1>
+
+<sect1 id='the_cupl_and_corc_languages'><title>The CUPL and CORC Languages</title>
+
+<para>This distribution includes, in the file
+<filename>cupl.doc</filename>, a transcription of all the relevant
+parts of the CUPL manual (the bulk of the text is a general tutorial
+on scientific programming). Another file,
+<filename>corc.doc</filename>, similarly excerpts the CORC
+manual.</para>
+
+<para>CUPL has only one scalar type, a long floating-point real
+corresponding to C double (round-off rules coerce scalars to integer
+in contexts like subscripting). It supports vector and matrix
+aggregates, and has operations specialized for linear-algebra
+calculations. There is no function abstraction and all variables are
+global; program chunking is achieved through BLOCK or BEGIN blocks
+which resemble parameterless subroutines.</para>
+
+<para>CUPL rather resembles early BASICs, minus BASIC's string facility. It is
oriented towards scientific calculation and linear algebra, and would be nearly
-impossible (or, at any rate, extremely painful) to use for anything else.
-.P
-The programming-support features of CUPL and CORC resembled those of the
+impossible (or, at any rate, extremely painful) to use for anything else.</para>
+
+<para>The programming-support features of CUPL and CORC resembled those of the
better-known WATFOR and WATFIV compilers, incorporating elaborate
-error-correction and trace output features using a runtime monitor.
-.P
-The only incompatibility between the CUPL and CORC languages
-documented was the interpretation of GO TO <label> when <label> is
+error-correction and trace output features using a runtime monitor.</para>
+
+<para>The only incompatibility between the CUPL and CORC languages
+documented was the interpretation of GO TO <label> when <label> is
associated with a block. In CUPL, this is a go to beginning of block;
-in CORC, it's go to end of block (which in CUPL is GO TO <block> END.
+in CORC, it's go to end of block (which in CUPL is GO TO <block> END.
The interpreter switches on CORC interpretation whenever it detects a
-CORC-specific word (such as NOTE) during lexing.
-.P
-The CORC statement TITLE and the triple iteration construct have no
-counterparts in CUPL.
-.P
-.H 1 "Differences from the original CUPL"
-.P
-The most obvious differences are also the most trivial. CUPL was
+CORC-specific word (such as NOTE) during lexing.</para>
+
+<para>The CORC statement TITLE and the triple iteration construct have no
+counterparts in CUPL.</para>
+
+</sect1>
+
+<sect1 id='differences_from_the_original_cupl'><title>Differences from the original CUPL</title>
+
+<para>The most obvious differences are also the most trivial. CUPL was
first implemented on an IBM/360 Model 30; CORC on Burroughs 1604 and
220 machines. Both used a small capital-letters-only character set
SIXBIT, and followed the archaic IBM practice of using a slashed-O for
alphabetic O and plain 0 for zero. Original CUPL/CORC listings thus
-look rather odd to the modern eye.
-.P
-The original CUPL was a batch system with a fixed-field card format;
+look rather odd to the modern eye.</para>
+
+<para>The original CUPL was a batch system with a fixed-field card format;
labels in columns 1-8, statements in 10-72, statement continuations
beginning in column 15 (CORC's format differed only in detail from
this). In CUPL, data for the program was supplied following a special
*DATA label in the same deck as the program; CORC did not require this
marker (it is not clear from the CORC documentation how end-of-program
-was recognized).
-.P
-On modern output devices, slashed-0 tends to be used, if at all, for zero. We
-have not tried to preserve IBM's reversal. Nor have we tried to enforce the
-columnation requirements, and we don't implement the continuation convention
-(new CUPL is free-format, with newlines ignored). We do preserve much of the
-visual appearance of CUPL listings by insisting on all caps and tab-indenting
-statements. We also preserve the *DATA mechanism for supplying
-initializations.
-.P
-More significant differences arise from differences between the word
+was recognized).</para>
+
+<para>On modern output devices, slashed-0 tends to be used, if at all,
+for zero. We have not tried to preserve IBM's reversal. Nor have we
+tried to enforce the columnation requirements, and we don't implement
+the continuation convention (new CUPL is free-format, with newlines
+ignored). We do preserve much of the visual appearance of CUPL
+listings by insisting on all caps and tab-indenting statements. We
+also preserve the *DATA mechanism for supplying
+initializations.</para>
+
+<para>More significant differences arise from differences between the word
size and floating-point format in CUPL's original host and those of
typical modern C implementation. The 360 had a 36-bit word; original
CUPL scalars ranged from 1e76 to 1e-78 with nine decimal digits of
precision. As for CORC: the Burroughs 1604 was documented as having a
much wider range, 1e308 to 1e-308 with 11 digits of precision; the
-Burroughs 220 supported 1e-49 to 1e50 with 8 digits of precision.
-.P
-On today's typical 32-bit microprocessor such as an Intel 486, C
+Burroughs 220 supported 1e-49 to 1e50 with 8 digits of precision.</para>
+
+<para>On today's typical 32-bit microprocessor such as an Intel 486, C
floats are 32 bits and have roughly 1e+38 to 1e-38 range and 9 digits
precision; doubles are 64 bits, with range roughly 1e308 to 1e-304 and
19 digits of precision. This implementation use doubles to emulate
-CUPL/CORC scalars.
-.P
-We know from the documentation that the original CUPL compiler ran
+CUPL/CORC scalars.</para>
+
+<para>We know from the documentation that the original CUPL compiler ran
in 64K of core. The present implementation is easily twice that size.
However, given the cycle speeds of the 1960s, it certainly runs a
-good deal faster that original CUPL, even with interpretation overhead.
-.P
-We don't implement original CUPL's error-correction facilities.
+good deal faster that original CUPL, even with interpretation overhead.</para>
+
+<para>We don't implement original CUPL's error-correction facilities.
Though clever, they would make the parser forbiddingly complex, and
-are anyway much less important in an interactive environment.
-.P
-There are many limits in original CUPL/CORC that we do not enforce. There is
-no limit on the length of variable names short of the lexer's very long
-token buffer length. There is no hard limit on the number of statements in a
-program. There is no hard limit on the size of arrays.
-.P
-While the format of number output does not exactly conform to the original
-CUPL/CORC rules, it is sufficiently ugly to please any but the
-pickiest. We implement all of 5.2 except the fixing of the decimal
-point at position 7 in each field. Instead we simply use printf(3)'s
-%f and %e at field-width precision.
-.P
-Also, by default, we wrap after three 20-char fields rather than 6, so
+are anyway much less important in an interactive environment.</para>
+
+<para>There are many limits in original CUPL/CORC that we do not
+enforce. There is no limit on the length of variable names short of
+the lexer's very long token buffer length. There is no hard limit on
+the number of statements in a program. There is no hard limit on the
+size of arrays.</para>
+
+<para>While the format of number output does not exactly conform to
+the original CUPL/CORC rules, it is sufficiently ugly to please any
+but the pickiest. We implement all of 5.2 except the fixing of the
+decimal point at position 7 in each field. Instead we simply use
+printf(3)'s %f and %e at field-width precision.</para>
+
+<para>Also, by default, we wrap after three 20-char fields rather than 6, so
as to fit on an 80-column line. Command-line options to change the
-line and field widths are available.
-.P
-.H 1 "Command-line options"
-.P
-The -w options sets the line width (default 80).
-.P
-The -f option sets the field width (default 20).
-.P
-The -v option enables debugging output. At level 1, the parse tree is
+line and field widths are available.</para>
+
+</sect1>
+
+<sect1 id='commandline_options'><title>Command-line options</title>
+
+<para>The -w options sets the line width (default 80).</para>
+
+<para>The -f option sets the field width (default 20).</para>
+
+<para>The -v option enables debugging output. At level 1, the parse tree is
prettyprinted. At level 2, definition/reference counts for each
variable and label are printed after each run. At level 3, an
execution trace is displayed as the parse tree is printed. At level
4, each token intern and cons-cell allocation during parsing is also
-dumped.
-.P
-.H 1 "Unix Implementation Notes."
-.P
-The CUPL/CORC implementation is built around YACC and LEX. The rest is
-ANSI and POSIX-conformant C.
-.P
-The YACC grammar just builds a parse tree, which is passed to
+dumped.</para>
+
+</sect1>
+
+<sect1 id='unix_implementation_notes'><title>Unix Implementation Notes.</title>
+
+<para>The CUPL/CORC implementation is built around YACC and LEX. The rest is
+ANSI and POSIX-conformant C.</para>
+
+<para>The YACC grammar just builds a parse tree, which is passed to
interpret() for interpretation. This method requires that all
programs are always small enough that the entire tree can be held
in memory, but it has the advantage that front and back end are very
-well separated. It is a winning strategy on virtual-memory systems.
-.P
-One hack that greatly simplifies the grammar productions is that the
+well separated. It is a winning strategy on virtual-memory systems.</para>
+
+<para>One hack that greatly simplifies the grammar productions is that the
lexer actually returns parse tree nodes, even for atoms like
identifiers, strings, and numbers. In fact, the lexical analyzer even
does label and variable name resolution with the same simple piece of
code; each IDENTIFIER token is looked up in the identifier list when
it's recognized, so the parse tree early becomes a DAG. (The -v1 option
-causes the compiler to dump its parse tree for inspection.)
-.P
-Most of the smarts are in interpret() and its sub-functions. Because
+causes the compiler to dump its parse tree for inspection.)</para>
+
+<para>Most of the smarts are in interpret() and its sub-functions. Because
array variables can be re-allocated, the internals have to use a
dynamic vector/array type with its own indexing machinery. The code
-to manipulate this type lives in monitor.c.
-.P
-Note that much of this machinery is quite generic and could be re-used
-for other languages with little change.
-.P
-The implementation trades away some possible efficiencies for
+to manipulate this type lives in monitor.c.</para>
+
+<para>Note that much of this machinery is quite generic and could be re-used
+for other languages with little change.</para>
+
+<para>The implementation trades away some possible efficiencies for
simplicity. Most importantly, each value has an attached malloc
object to hold its elements, even when there is only one such element
(as for scalars) which could reasonably be represented by a static
-field.
-.P
-There are some comments in the code which discuss the possibility of
+field.</para>
+
+<para>There are some comments in the code which discuss the possibility of
a back end that would emit C. This would be easy to do if there were
any serious corpus of CUPL/CORC code demanding to be translated. The
compiler back end would emit code shaped like the parse tree, which
-would then link monitor.c as runtime support.
-.P
-The only nontrivial difference between CUPL and CORC is the interpretation
-of GO TO <label> when <label> is associated with a block. In CUPL, this
-is a go to beginning of block; in CORC, it's go to end of block (which in
-CUPL is GO TO <block> END. The interpreter sets a flag when it sees any
-of the appropriate CORC-specific keywords (NOTE, BEGIN, DEC, DECREASE, EQL,
-GEQ, GTR, INC, INCREASE, INT, LEQ, LSS, NEQ, REPEAT, TITLE, UNTIL, $) during
-lexing, and execute() modifies its behavior appropriately.
-
-
+would then link monitor.c as runtime support.</para>
+<para>The only nontrivial difference between CUPL and CORC is the
+interpretation of GO TO <label> when <label> is associated
+with a block. In CUPL, this is a go to beginning of block; in CORC,
+it's go to end of block (which in CUPL is GO TO <block> END.
+The interpreter sets a flag when it sees any of the appropriate
+CORC-specific keywords (NOTE, BEGIN, DEC, DECREASE, EQL, GEQ, GTR,
+INC, INCREASE, INT, LEQ, LSS, NEQ, REPEAT, TITLE, UNTIL, $) during
+lexing, and execute() modifies its behavior appropriately.</para>
+</sect1>
+</article>