perm filename MANUAL.ECL[NET,GUE] blob
sn#016393 filedate 1972-12-20 generic text, type T, neo UTF8
↑↑
.spacing 1
####
.BREAK
.skip 5
.center
ECL PROGRAMMER'S MANUAL[*]
.skip 2
.center
by
.skip 2
.center
Ben Wegbreit
.center
Ben Brosgol
.center
Glenn Holloway
.center
Charles Prenner
.center
Jay Spitzen
.skip 5
.center
21-72
.skip 5
.center
CENTER FOR RESEARCH IN COMPUTING TECHNOLOGY
.center
Harvard University
.center
Cambridge, Massachusetts
.center
September 1972
.skip 10
.FIGURE 4
[*] This work was supported in part by the U. S. Air Force
Electronic Systems Division under Contract F19628-71-C-0173
and by the Advanced Research Projects Agency under Contract
F19628-71-C-0174.
.break
.nonumber
.page
↑&PREFACE\&
.paragraph
The ECL programming system is based on the programming
language EL1 which is the work of Ben Wegbreit and is
described in his doctoral dissertation ∀Studies in
Extensible Languages∩, Harvard University, June 1970.
.paragraph
This manual was written by Glenn Holloway, Ben Brosgol,
Pat Griffiths, and Ben Wegbreit.
.paragraph
The parsing algorithm is a modification of F.#DeRemer's
method. The parser production system was written by
Pat Griffiths, Charles Prenner and Judy Townley.
.paragraph
The interpreter was written by Ben Wegbreit.
.paragraph
The storage management routines and garbage collector
were written by Glenn Holloway and Charles Prenner.
.paragraph
The compiler for data type definitions was written
by Ben Brosgol.
.paragraph
The facility for control of data type
behavior was implemented by William Conrad, who has rewritten
and improved substantial portions of the system.
.paragraph
The utility routines described in sections 5 and 6 were written
by Mark Davis, Glenn Holloway, and Ben Wegbreit.
.paragraph
The multi-task control facility is the work of Charles
Prenner and is described in his doctoral dissertation
∀Multi-path Control Structures for Programming
Languages∩, Harvard University, June 1972.
.paragraph
The compiler for procedures is the work of Glenn Holloway
and is described in his doctoral dissertation (forthcoming).
.paragraph
The non-deterministic control facility is the work
of Jay Spitzen and is described as part of his doctoral
dissertation (forthcoming) and in ↑&Proceedings ACM 72\&.
.paragraph
Numerous individuals have made suggestions about the
design and implementation of this system. These
include Daniel Bobrow, Thomas Cheatham, Jr.,
Robert Kierr, and Richard Stallman. Their assistance is gratefully
acknowledged.
.break
.skip 2
.indent 35
September 1972
.break
.number 0
.page
1.##↑&INTRODUCTION\&
.skip 2
.title 1.1##AN OVERVIEW OF THE ECL PROGRAMMING SYSTEM
1.1##AN OVERVIEW OF THE ECL PROGRAMMING SYSTEM
.paragraph
The ECL programming system has been designed
as a tool for tackling 'difficult' programming projects--projects
on which existing languages could
be used only with considerable waste in machine
or programmer time.
.paragraph
Such projects include much of the frontier of computer
technology; they are found whenever several application areas are conjoined
and whenever solution of a problem requires linguistic
development--in algorithmic notation or information structures.
Examples range from the management of large scale distributed
data bases to applied artificial intelligence.
.paragraph
Specifically, projects of this nature are systems characterized
by two requirements:
.skip 1
.left margin 10
.indent -5
(1)##Considerable experimentation is required to develop the
system; that is, the design and development of the system must go hand
in hand. Typically, this occurs when problems are so
complex that significant computer assistance and experimentation
are needed in system design.
.skip 1
.indent -5
(2)##When a complete system is ultimately designed and programmed,
it must be possible to take the working programs and produce
a highly efficient product--both in machine time and
space--without changing the basic algorithms or their representation.
.left margin 0
.paragraph
The ECL system has been designed as a vehicle for such undertakings.
At the present time an experimental version of the system is
operational--a version which only partially meets the above
requirements. Additional system development is under
way and will continue for some time. This section outlines
the ↑&goals\& of this work and the ECL system as planned.
Thus this section should be read as a prospectus. The remainder of this
manual, however, describes the existing system--as of the last
manual revision. Periodic revisions will be issued as new features are
added.
.paragraph
The ECL programming system consists of a programming
language, called EL1, and a system built around that language
to provide a complete environment for the human-oriented
use of the language. The system allows on-line conversational
construction, testing, and running of programs.
It includes an interpreter, a fully compatible compiler,
and an editor--all callable at run-time, on programs
constructible at run-time either by the programmer or as the
result of computation.
.paragraph
EL1 is an extensible language. Thus it
provides a number of facilities for defining extensions
so that the programmer can readily shape
the language to the problem at hand, and progressively
reshape the language as his understanding of the problem
and its solution improves. Like the familiar notions of subroutine
and macro definition, these extension facilties allow one
to abstract significant aspects of a complex algorithm.
Such functional abstraction serves both as a representational aid and
as a handle on the production of an efficient product.
.left margin 0
.paragraph
Specifically, the language provides facilities for
extension on four axes: syntax, data types, operations,
and control.
.skip 1
.left margin 10
.indent -5
(1)##Syntactic extension allows the specification of new
linguistic forms and their meaning in terms of existing
forms.
.skip 1
.indent -5
(2)##Data type extension allows the programmer to define
new data types and new information structures whenever
needed to model the problem at hand.
EL1 is significant in that considerable
attention has been given to the efficient representation of
programmer-defined data types. There is a special compiler
for data-type definitions which computes space-efficient
packing of structures into machine words, and generates
machine code for rapid handling of objects and their components.
.skip 1
.indent -5
(3)##Operator extension allows the programmer to define new
operations on new data types and to extend existing operations
to cover new types. There are two key points here:
.skip 1
.left margin 15
.indent -5
(a)##Operators and procedures are not restricted
to act on built-in types in the language but can, and in
general will, take arguments whose mode is programmer-defined.
.page
.skip 1
.indent -5
(b)##Declarations can be made to allow the compiler to
perform type-checking and type-conversion
code generation. Hence, it is possible to write programs
operating on extended data types whose execution is
as efficient as that of programs using only built-in modes.
.skip 1
.left margin 10
.indent -5
(4)##Control extension allows the creation, deletion, and
coordination of independent asynchronous processes,
called ∀paths∩ in ECL. The extension mechanisms
are sufficiently flexible that co-routines, the P and V
operations of Dijkstra, multiple parallel returns, and path
scheduling are all definable in the system as proper
extensions. Hence, it is straightforward to program
almost all known control structures as well as an unknown
variety of others.
.left margin 0
.paragraph
In addition to these definition mechanisms provided by the
language, the ECL programming system provides a number of
other handles which the programmer can use to extend and
tailor the environment in which he operates. Many of the
system's facilities are written in the language and hence
are open to modification by the
programmer. These include the compiler, one of the editors,
and most of the input/output and file system.
.paragraph
Extensibility alone is, however, not
sufficient. Its counterpart--contractibility--is also required.
Once running prototype programs have been produced, it must be
possible to subject them to a sequence of ↑&contractions\&--commitments
to subsequent nonvariation--to
obtain a final system optimal for the project requirements.
.paragraph
The ECL programming system and the EL1 programming
language have been designed to allow this.
Programs can be run
either by an interpreter or by a fully compatible compiler.
Compiled and interpreted functions can call each other
in either direction.
Compilation can itself be progressively refined.
The programmer is free to supply as much declarative
information as he wishes (or knows) at a given time and the
compiler will do the best it can with the information given.
Successive recompilation with additional information will
produce progressively better code.
.test page 12
.paragraph
In summary, the intended application of the ECL
programming system is the programming project which would
otherwise be prohibitively expensive. To this end, the
ECL system has been designed to allow flexible
programmer-oriented program construction and testing
as well as facilities for subsequent optimizing
contractions to produce an efficient final
product.
.skip 1
.title
1.2##PURPOSE OF THIS MANUAL
.paragraph
This manual is an introductory reference. It provides
little or no motivation for the material it presents. It
is not organized so as to lead the reader gently into
ECL as a true primer would.
Rather, it assumes that the reader, though perhaps ignorant of
ECL, has some familiarity with programming languages and
programming systems.
.paragraph
Section 2 describes the EL1 language in some detail, independent of the
ECL system. It is an introduction in the
sense that it proceeds from simple to more complicated notions, and usually
defines terms before they are used. However, it is primarily organized to permit quick reference and easy comparison of EL1 with
other languages.
.paragraph
Section 3 describes the interactive use of ECL. It
assumes an understanding of section 2 as well as the
ability to cope with TECO, the PDP-10 text editor.
.paragraph
Section 4 briefly describes each built-in routine
of the system.
.paragraph
Section 5 describes a number of EL1 procedures, mostly utility
packages of general applicability.
.paragraph
Section 6 describes a package of routines for making
measurements on programs and functions.
.paragraph
This copy is a draft of the manual. It is incomplete.
Sections which are planned but not yet written are
indicated in the text by a section number and a title in
square brackets. Your corrections of and suggestions
about this draft will be appreciated.
.skip 3
.break
↑↑
.title
.page
.spacing 1
2.##↑&INTRODUCTION TO EL1\&
.skip 2
.title 2.1##FORMS
2.1##FORMS
.paragraph
EL1 programs are composed of basic units called
.index form
∀forms∩.
Examples of EL1 forms which have counterparts in
most other programming languages are:
.left margin 10
.skip 1
.indent -5
(1)##constants such as 13 and TRUE,
.indent -5
.skip 1
(2)##variables such as##x##and##pressure,
.indent -5
.skip 1
(3)##expressions composed of infix and prefix operations
such as ##x+y,##i-j*k, and ##-q1/q2,
.indent -5
.skip 1
(4)##selections of components of compound objects such as
b[i]## and ##position[3*x],
.indent -5
.skip 1
(5)##procedure calls like f(x) and foo(i,j+k,a[n]).
.skip 1
.left margin 0
A form in EL1 is a syntactically complete unit, and each form represents
a value.
Forms may be combined according to the composition
rules of the language to obtain larger forms.
The remainder of this section describes
EL1 by describing the individual forms which make up the language: how they are
written and how they will be evaluated.
.test page 10
.skip 3
.index constant
.title 2.2##CONSTANTS AND THE BASIC DATA TYPES
2.2##CONSTANTS AND THE BASIC DATA TYPES
.paragraph
Although the number of data types in EL1 is virtually
unlimited, all are based on a relatively small number
of primitive types, including:
.left margin 10
.skip 1
.indent -5
(1)##Booleans: logical truth values;
.indent -5
.skip 1
(2)##numbers:##integers and reals;
.indent -5
.skip 1
(3)##characters;
.indent -5
.skip 1
(4)##labels: values used for controlling program flow;
.indent -5
.skip 1
.index reference
(5)##references: pointers to data objects;
.indent-5
.skip 1
(6)##the empty value.
.left margin 0
.paragraph
A few other data types, although not
technically primitive, are basic to the implementation
and are also built-in. These include:
.skip 2
.left margin 10
.indent -5
.index mode
.index data type
(7)##modes: values which describe data types
(in this manual, "mode" and "data type" are synonymous);
.indent -5
.skip 1
.index SYMBOL
(8)##symbols: which permit efficient representation of symbolic expressions
(e.g. programs);
.indent -5
.skip 1
.index STRING
(9)##strings: arrays of characters;
.skip 1
.indent -6
.index procedure
(10)##procedures: values which specify a transformation
from a set of input values to a result, possibly with some
side-effects.
.left margin 0
.paragraph
Constant values for most of these built-in data
types have explicit representations in the EL1 language:
.skip 1
.left margin 10
.indent -5
.index TRUE
.index FALSE
(1)##Boolean constants are written TRUE and FALSE.
.indent -5
.skip 1
(2)##Integer constants are sequences of digits,
such as
.skip 1
.indent 5
6#####1596####6600
.skip 1
Real constants
are digit sequences including exactly one radix point:
.skip 1
#####2.71828#######.01745
.skip 1
Reals may also be written in 'scientific notation':
.skip 1
#####6.627E23######137E-2
.skip 1
where the suffix E followed
by a signed integer N represents multiplication by
a factor of ten to the power N:##137E-2 is the same as 1.37##.
.indent -5
.skip 1
(3)##Character constants are denoted by a percent
sign (%) followed by any character:
.skip 1
.indent 5
%Z####%C####%=####%%
.indent -5
.skip 1
(4)##There are no label constants. Label values
are meaningful only in the context of compound forms
(see section 2.5).
.indent -5
.skip 1
.index NIL
(5)##The only reference constant is NIL. It represents
a pointer to the empty value.
.indent -5
.skip 1
.index NOTHING
(6)##The empty value is called NOTHING.
.indent -5
.skip 1
.index BOOL
.index INT
.index REAL
.index CHAR
.index LABEL
.index REF
.index NONE
.index mode
.index SYMBOL
.index STRING
.index ROUTINE
(7)##The mode constants represent the built-in
data types of the language:##BOOL, INT, REAL, CHAR,
LABEL, REF, NONE, MODE, SYMBOL, STRING, ROUTINE.
One mode constant, ANY,
plays a special role in the language (see section 2.10).
There are no values with data type ANY.
.indent -5
.skip 1
.index SYMBOL
(8)##Symbol constants are strings of characters
enclosed in double quotation marks:
.skip 1
.indent 5
"ABC"######"1B16"#####"@#="#####
.skip 1
To include a double quotation mark in a symbol, one
precedes it by a percent sign:
.skip 1
.break
#####"%"Who are You?%", said the Caterpillar."
.skip 1
The same rule allows inclusion of percent sign:
.skip 1
############"50%%##overlap"
.skip 1
.indent -5
.index STRING
(9)##String constants have the same form as symbol
constants except that single quotation marks are
used instead of double ones:
.skip 1
.indent 5
'Johann Sebastian Bach'
.indent 8
'Rake%'s Progress'
.skip 1
.indent -6
(10)##Procedure constants are procedure definitions.
They are not elementary constants since they are
composed of other forms. For example:
.skip 1
.indent 5
EXPR(i:INT;INT)##(i+1)
.skip 1
.title
.test page 15
is a procedure constant which represents the transformation
of an integer into its successor.
The meaning of the syntax of procedures is explained
in section 2.10.###########
.skip 3
.left margin 0
.index variable
2.3##VARIABLES
.title 2.3##VARIABLES
.paragraph
Variables give the programmer an abstract notation
for the data his program manipulates. In EL1 every
variable has a ∀name∩, a ∀mode∩, a ∀scope∩, and a
∀value∩. Only the value may change.
.index identifier
.paragraph
A variable name is called an ∀identifier∩. EL1 has two kinds of identifiers:
.skip 1
.left margin 10
.indent -5
(1)##a sequence of consecutive letters (upper or lower case),
digits, and backslashes (←\), the first of which is a letter,
for example
.skip 1
.indent 5
Igor##U235##real←\matrix##FUM
.indent -10
.skip 1
.skip 1
.indent -5
.test page 10
(2)##a sequence of characters from the set
.skip 1
.indent 2
#.#←##$#*#+#-#/#<#=#>#?#@#→E∩#←↑#←!
.skip 1
.left margin 0
Identifiers may be of any length. Upper and lower case
letters are ↑¬\& considered identical: cat and CAT
are distinct names. The user's identifiers must not conflict
with certain reserved words of the language. Appendix D
contains a list of these reserved words.
.index scope
.paragraph
.index global variable
Scope is the most subtle attribute of variables.
Roughly speaking, the scope of a variable is the
span of time over which it is defined. Some variables
have meaning independent of the evaluation of a
.index top-level variable
particular program. These are called ∀top-level∩, or ∀global∩
variables. Many global variables are pre-defined as part of the
language. The set of globals may be expanded or contracted
by the user (see section 3.2). Other variables are created
and destroyed dynamically by programs as they run. These
are called ∀local∩ variables. Their scope
lasts only as long as an activation of the form
.index local variable
which creates them. Creation of local variables is discussed
in section 2.5 and a more precise definition of local scope
is given there. It should be emphasized here, however,
that scope is no less a distinguishing feature
of a variable than its name. Two variables may have the
same name and may even be created by the same
line of a program, and yet have different scope and different
behavior.
.paragraph
.index mode
A variable's mode describes the class of values the variable
can assume. It is defined when the variable is created and remains
fixed throughout its lifetime.
.paragraph
What can change, of course, is the variable's value, through
.index assignment
a built-in operation called ∀assignment∩. The format
.test page 10
of an assignment is[*]
.footnote 7
.skip 2
←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
.index <-
[*]#Left-arrow (→E∩) and the symbol
<- are predifined in ECL to have identical meaning.
!
.skip 1
.indent 10
form##<-##form
.skip 1
When this expression is evaluated, the value of the
right-hand form replaces that of the left-hand form.
For example, suppose temp and count are variables of mode
INT. The assignment
.skip 1
.indent 10
temp#<-#1079
.skip 1
gives temp the value 1079; temp's value can be passed on to the
variable count by
.skip 1
.indent 10
count#<-#temp
.skip 1
These assignments are legal because INT variables are
being given INT values. In general, unless the modes
of the two sides of an assignment agree, the evaluator of the
program will signal an error. This kind of error is called
.index type fault
a ∀type fault∩. In certain cases, however, a right-hand value will be
converted during assignment to match an expected mode.
An INT will be converted to the equivalent REAL value on
assignment to a REAL variable; a REAL will be ↑&rounded\& to the
nearest INT, if necessary. Other permissible conversions are listed in Appendix F.
.paragraph
The value of an assignment is the value of its left-hand
form ↑&after\& the assignment has been completed. Thus
the value of
.skip 1
.indent 10
count#<-#4.9
.skip 1
(where count remains an INT) is the INT value 5,
not the REAL value 4.9. Moreover, in the (useless
but legal) event that an assignment is itself the left-hand
form of another assignment, the value of a variable may
change twice, for example
.title
.test page 15
.skip 1
.indent 10
(count#<-#2)#<-#50
.skip 1
results in a value of 50 for count.
.skip 2
.index operator
2.4##OPERATOR EXPRESSIONS
.title 2.4##OPERATOR EXPRESSIONS
.paragraph
EL1 provides the programmer with a set of built-in operators
on built-in data types, and with facilities
for defining both new data types and new operations.
Operator expressions provide a notation for computation which
resembles the formula notation of standard algebra and logic.
.paragraph
An operator expression is either
.break
.blank 1
.indent 5
.index prefix expression
(1) a ∀prefix∩ expression, written
.skip 1
.indent 15
identifier#####form
.skip 1
.break
.indent 5
.index infix expression
(2) an ∀infix∩ expression, written
.skip 1
.indent 15
form#####identifier#####form
.blank 1
.break
.index nofix expression
.blank 1
.indent 2
or (3) a ∀nofix∩ expression, written
.break
.blank 1
.indent 15
identifier
.paragraph
In a prefix expression, the identifier names an operation
to be applied to the ∀operand∩ form, producing a value.
For example, the identifier NOT is a built-in prefix
operator which produces the logical inverse of a Boolean value; if b
has mode BOOL and value FALSE, then NOT
b produces TRUE.
.paragraph
An infix expression is evaluated by applying the operator
named by the identifier to the two operand forms;
producing a value. An example is +, the built-in
infix operator which represents addition of two numbers
to produce a number. If delta is an INT variable with value 4,
then 6.3#+#delta has the REAL value 10.3.
.paragraph
A nofix expression is evaluated by calling the procedure named
by the identifier with no arguments. For example, if f and g
have the same procedure value, but f is nofix and g is a simple identifier
(i.e. without fixity), then 1+f has the same meaning as 1+g().
.paragraph
A particular identifier can be used as both an infix and
a prefix operator. Minus (-), for example, can be used either as
arithmetic negation, or as the arithmetic difference operator. The
meaning of a particular use is determined from context.
In the expression -i-4, the first appearance of minus is
prefix, while the second is infix
with arguments -i and 4.
.paragraph
An operator may be nofix and infix, nofix and prefix, or all three.
In these cases the context determines the meaning used--an
infix meaning is used if possible, a prefix meaning if an infix
meaning isn't appropriate, and a nofix meaning only if
neither infix nor prefix meanings would result
in well-formed expressions.
.paragraph
The built-in operators fall into seven categories. In the brief
descriptions which follow, x and y represent numbers (INT or REAL),
p and q represent Boolean values, and v and w represent values
of any mode.
.skip 1
.left margin 10
.test page 5
.indent -5
.index arithmetic operators
(1)##↑&Arithmetic\& operators. These are written:
.left margin 15
.skip 1
.index +
.index -
.index *
.index /
x#+#y###sum of x and y
.break
x#-#y###x less y
.break
##-#x###negative of x
.break
x#*#y###product of x and y
.break
x#/#y###x divided by y
.left margin 10
.skip 1
These operators use INT arithmetic and produce an INT
result if and only if both operands are INT. Otherwise REAL
arithmetic is used to yield a REAL result. The quotient of two INTs is obtained
by truncating toward zero. That is, 5/3 becomes 1 and
(-3)/2 is -1.
.skip 1
.indent -5
.index Boolean operators
(2)##↑&Boolean\& operators. These are:
.skip 1
.left margin 26
.index AND
.index OR
.index NOT
.indent -10
p#AND#q###the logical product or conjunction
of p and q; TRUE if and only if
both are TRUE
.skip 1
.indent -10
p#OR#q####the logical sum or disjunction of
p and q: TRUE unless both
are FALSE
.skip 1
.indent -10
##NOT#p###the logical negation of p, TRUE
if and only if p
is FALSE.
.skip 1
.left margin 10
For AND and OR it is guaranteed that the
second operand form will not be evaluated if the value of the
first determines the outcome of the expression.
.skip 1
.indent -5
.index arithmetic relational operators
(3)##↑&Arithmetic relational\& operators. All are
infix operators. They compare two numbers (INT or REAL)
and produce a BOOL result.
.skip 1
.left margin 24
.indent -10
.index LT
.index LE
.index GT
.index GE
x#LT#y####TRUE exactly when x is strictly
less than y.
.skip 1
.indent -10
x#LE#y####TRUE exactly when x is less than
or equal to y.
.skip 1
.indent -10
x#GT#y####TRUE exactly when x is strictly
greater than y.
.skip 1
.indent -10
x#GE#y####TRUE exactly when x is greater
than or equal to y.
.left margin 10
.skip 1
If one but not both of the operands is an INT, it is converted to
REAL before the comparison.
.skip 1
.indent -5
.index general relational operators
(4)##↑&General relational\& operators. These two operators
test the equality of pairs of values which may be of
any mode.
.skip 1
.left margin 23
.indent -8
.index =
.index ←#
v#=#w###TRUE if v and w have the same
modes and equal values (see section 4.2 for a more exact definition);
FALSE otherwise.
.skip 1
.indent -8
v#←##w###The logical negation of v = w.
.skip 1
.left margin 10
.indent -5
(5)##∀Assignment∩, discussed in section 2.3, is
an infix operation which can be applied to operands of
any mode.
.skip 1
.indent -5
.index ->
(6)##∀Conditional execution∩. -> is an infix operator. The
conditional is written form#->#form. In a conditional, the
left form is called the ∀test clause∩, the
right form is called the ∀consequent∩, and the right arrow
may be read roughly as a causation or implication sign.
The consequent is evaluated exactly when the
test clause evaluates (possibly after conversion) to TRUE. In this case, the
value of the form is the value of the consequent.
Otherwise the value is NOTHING. This is further
described in section 2.5.2.
.left margin 0
.paragraph
In mathematics, notational conventions usually govern the
interpretation of potentially ambiguous formulas. For example,
##sin#x→2∩#+#y##probably means##sin#(x→2∩)#+#y##and not##(sin#x)→2∩#+#y##or##sin#(x→2∩#+#y)##.
In any case, parentheses can be used to resolve ambiguities.
.paragraph
Similarly in EL1, any form may be
replaced by the same form in parentheses, so the programmer can group sub-expressions explicitly. To retain the convenience
.index precedence
of notational conventions, however, rules of ∀precedence∩
are used when an expression has not been completely broken
into sub-expressions. The rules are:
.skip 1
.left margin 10
.indent -5
(1)##Prefix operators always take precedence over infix operators.
.skip 1
.indent -5
(2)##Every infix operator is given a numeric precedence.
Operations of higher precedence are evaluated first.
.skip 1
.indent -5
(3)##Every infix operator is designated either
.index left-association
.index right-association
∀left-associative∩ or ∀right-associative∩. When two or more
consecutive operators have the same precedence and are in conflict
(i.e. appear to share operands) the conflict is resolved by a
left-to-right scan through the sub-expression: each
left-associative operator takes the operand immediately following
as its right operand; each right-associative operator takes
the rest of the sub-expression as its right operand.
.left margin 0
.paragraph
Rule (1) means that, for example, -i-4 is interpreted as
(-i)-4. The relational operators take precedence over
AND, so by rule (2)
.skip 1
.indent 10
x#GT#y##AND#v#=#w
.skip 1
means
.skip 1
.indent 10
(x#GT#y)#AND#(v=w)
.skip 1
.test page 8
Since * takes precedence over + and both take precedence
over <-, the form
.skip 1
.indent 10
u#<-#x*y#+#z*a
.skip 1
is parenthesized
.skip 1
.indent 10
u#<-#((x*y)#+#(z*a))
.skip 1
For precedences of all built-in infix operators, consult
section 4.
.paragraph
The built-in operator * is left-associative.
Thus by rule (3), a*b*c means (a*b)*c. Multiple assignments,
however, associate to the right:
.skip 1
.indent 10
p#<-#q<-#r#<-#FALSE
.skip 1
is evaluated as if it were
.skip 1
.indent 10
p#<-#(q#<-#(r#<-#FALSE))
.paragraph
Although rules (1) through (3) completely specify
the association of operands with operators, they do not
specify which operand of an operator will be evaluated
first. Only for AND and OR have we said that one
necessarily precedes the other. For the other built-in
operators, the order of operand evaluation is specifically
undefined. Different evaluators of the expression
(a*b)#+#(c/d) are free to choose whether to multiply
a by b before or after
dividing c by d.
As will be seen in section 4.4, the
programmer can define his own operators, complete with
precedence, and right or left associativity. He can likewise
choose an order of operand evaluation or leave the order
unspecified.
.paragraph
Finally, before considering more intricate forms, it
is well to note an intrinsic difference in EL1, as in most
programming languages, between the values represented by identifiers,
like x, and those represented by constants or by expressions
like y+1. It makes sense to assign a new value to x, e.g. x<-y+1; but an
assignment like y+1<-x, although legal in EL1 and quite harmless,
is useless. The value of x is persistent and reusable; a
change made to x can have an effect on the later evaluation of
.index proper object
the program. We call such values ∀proper objects∩. We
will see shortly that forms other than simple identifiers can
represent proper objects. Forms like y+1, TRUE, or 1.414
which are not useful targets for assignment are called
∀pure values∩. All the built-in operators except assignment,
non-deterministic assignment (see section 4.9),
and conditional execution (see section 2.3) produce pure values.
↑↑
.spacing 1
.skip 2
.index compound forms
.title
.test page 12
2.5##COMPOUND FORMS
.title 2.5##COMPOUND FORMS
.paragraph
When a computation must be performed which cannot
be expressed using only built-in routines,
it is often useful to evaluate a set of forms with control
passing from one form to another according to some
sequencing rule.
.paragraph
This need is satisfied in EL1 by the ∀compound form∩,
also called a ∀block∩.
.paragraph
To begin with an example, suppose one needs to compute the
square root of the value of s, a REAL variable, to
within an accuracy specified by epsilon, another REAL variable.
The following compound form computes this root by iterative
approximation:
.left margin 5
.skip 1
.test page 8
BEGIN
.indent 3
DECL root: REAL BYVAL first;
.indent -5
loop:###abs(root*root-s)#LT#epsilon#=>#root;
.indent 3
root#<-#(root#+#s/root)/2;
.indent 3
GOTO loop
.break
END
.skip 1
.left margin 0
This block uses a ∀local variable∩ named root to
hold the successive approximations. As an initial
approximation root is set to the value of a variable
called first. Thereafter the current value of root
is repeatedly used to compute a new root, until root
squared is within epsilon of s. When (and ↑&if\&) a good
enough approximation is reached, control leaves the block
and the value of the block becomes the last value of root.
.paragraph
.index declaration
A compound form consists of a sequence of ∀declarations∩
(possibly none) and ∀statements∩ (at least one) separated
by semicolons and surrounded by the delimiters
.index BEGIN
.index END
BEGIN and END, which may be abbreviated
[) and (], respectively.
.page
.skip 1
2.5.1##↑&Declarations\&
.index SHARED
.index LIKE
.paragraph
∀Declarations∩ serve to establish any local
variables needed within a block; they must all appear
before the first statement. A declaration consists
.index DECL
of the word DECL followed by a list of identifiers
(separated by commas), a colon(:), and a form whose
value must be of data type MODE. A declaration may
optionally end with an initial value specification, which
.index bind-class
.index BYVAL
is a bind-class indicator (either BYVAL, SHARED, or LIKE) followed by any form.
.index local variable
.test page 10
.skip 1
2.5.1.1##↑&Local Variables\&
.paragraph
A declaration is
evaluated by first evaluating the mode-specification
and the initial-value form (if present), then
creating new variables whose name, mode and value are as specified.
.paragraph
We say these variables are ∀local∩ to the
compound form in which they are defined because they have
meaning only while this block is being evaluated. The
range of definition or ∀scope∩ of a local
.index scope
variable, extends from its declaration to the end of the
block in which it is defined, and includes any forms
which may be evaluated during the evaluation of that
block. When control leaves a block, any variables defined within
it vanish.
.paragraph
Within their scope, however, local variables supersede any
"less local" variables with the same names. For example,
suppose the block above is embedded in another block in
which the variable root has meaning:
.test page 18
.skip 1
.left margin 5
BEGIN
.break
.indent 3
DECL chord:#STRING BYVAL 'GBDF';
.indent 3
DECL root:#CHAR BYVAL %G;
.indent 3
#.
.indent 3
##.
.indent 3
###.
.indent 3
BEGIN
.indent 6
DECL root:#REAL BYVAL#first;
.break
loop:#abs(root*root-s)LT#epsilon#=>#root
.indent 6
#.
.indent 6
##.
.indent 6
###.
.indent 3
END
.indent 3
#.
.indent 3
##.
.indent 3
###.
.skip 1
.left margin 0
These two uses of root represent quite separate variables.
During the square root computation the outer meaning of root
is set aside; each use of root in the inner block refers
to the REAL root, not the CHAR root.
When the evaluation of the square root is finished, the
outer meaning of root is restored: the CHAR variable with
whatever value it had just before the inner block.
Variables used but not declared (or not yet declared) in a
block are called ∀free variables∩ with respect to that block.
.index free variable
In the square root block, for example, first, s, epsilon, abs
are free variables[*].
.footnote 6
.skip 2
←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
[*] The LABEL identifier loop is treated as local to the innermost
block which contains its definition. See section 2.5.2.
!
A free variable has the same meaning inside a
block as it had just before control entered the block:
either it is a local variable in some broader context
or else it is a variable of global context, a so-called
.index top-level variable
∀top-level variable∩. Top-level variables
have meaning independent of the evaluation of any compound form.
Most of the built-in routines in EL1, for example, are
top-level routine-valued variables. The creation of global variables
is described in section 3.2. The set of top-level and local
variable definitions in effect at any point during a
program's execution is called the
.index environment
∀environment∩ of that point.
.paragraph
Note that an identifier may represent a free variable and
a local within a single block. The declaration
.skip 1
.indent 12
DECL#x:INT#BYVAL#x+1
.skip 1
uses the free variable x to compute an initial value for
the local x being defined.
.skip 1
.test page 10
2.5.1.2##↑&Initialization\&
.paragraph
As mentioned above, a declaration may
specify an initial value for local variables
using any of the indicators BYVAL, LIKE, or
SHARED, or it may contain no initial value specification
at all. These four situations are called
.index initialization by value
initialization ∀by value∩, ∀by coercion∩, ∀by sharing∩, and
∀by default∩, respectively.
.paragraph
Initialization by value guarantees that each new variable
has a value independent of any proper object which exists
just prior to the declaration. A copy of the value will be
used to initialize the variable if the initial value form
evaluates to a proper object.
.
.test page 10
.paragraph
.index initialization by sharing
Initialization by sharing, on the other hand, results in
a sharing of values. For example, if k has mode BOOL and
if i and j are created by
.skip 1
.indent 10
DECL#i,j:BOOL#SHARED#k
.skip 1
then i, j, and k will share the same Boolean value
throughout the scope of i and j. Any change to one
(for example, j#<-#NOT#j) changes both of the others
also. Initialization by sharing with a proper object makes an
identifier a synonym for that object.
.test page 10
.index initialization by coercion
.paragraph
Initialization by coercion results in binding
either by value or by sharing depending upon the
result of the evaluation of the form following LIKE.
This means
.skip 1
.indent 10
DECL#x:REAL#LIKE#f(z)
.skip 1
will cause x to be initialized by value to
f(z) whenever f(z) is a pure value,
and to be shared when f(z) evaluates to a proper object of mode
REAL. If f(z) is not REAL,
then conversion to REAL takes place.
Binding is by value or by sharing depending on the
conversion function used (see section 2.13.4.1).
.paragraph
.index initialization by default
If no initial specification at all appears in the
declaration, the declared variables are given default values
corresponding to their modes. These values
for the principal built-in data types are given in Table 2.5-1.
.tab stops 15, 30
.footnote 21
.break
.nofill
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
Mode Default Value
.break
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
BOOL FALSE
.break
INT 0
.break
REAL 0.0
.break
CHAR (ASCII#NULL)
.break
REF NIL
.break
NONE NOTHING
.break
MODE NIL
.break
SYMBOL NIL
.break
STRING ''
.break
ROUTINE NIL
.break
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
.center
Table 2.5-1##Default Values of Built-in Modes
.fill
!
.skip 2
.paragraph
.index default value
Default values for compound data types (see section 2.7) are
constructed from default values for their component modes.
.paragraph
Generally speaking, when an initial value is supplied for a local
variable, this value's mode is expected to match the mode
specified in the declaration. As with assignment, however,
the evaluator will in certain cases automatically convert an initial value
to match an expected mode (see Appendix F).
Note that built-in conversion ↑&always\& produces a
pure value, so that declaration by coercion acts exactly
like declaration by value when the initial value must be
converted. For example
.skip 1
.indent 10
DECL#x:#REAL;
.indent 10
DECL#y,z:#INT#LIKE#x
.skip 1
introduces three variables with three independent
values, all zero.
.skip 1
.test page 10
2.5.2##↑&Statements\&
.paragraph
A statement is either a form
.index conditional
.index exit-conditional
or an ∀exit conditional∩, written
.skip 1
.indent 10
.index =>
form#=>#form
.skip 1
.index test clause
.index consequent
As with ordinary conditionals (section 2.4) the left form is called the ∀test clause∩ and
the right form is called the ∀consequent∩.
.paragraph
Any statement in a block may be given
one or more labels for use in controlling flow of control within
the block. The label is an identifier,
followed by a colon, and must appear before the body of
the statement. If several labels mark
a single statement, each must be terminated by a colon.
Label names have the scope properties of local variables.
Their scope begins just before the first statement of the
block is evaluated (i.e. after the last declaration,
if any) and extends until evaluation of the block terminates.
Between those times, any similarly named value
in the environment of the block's evaluation is superseded
by the label value. Also, like variables, labels may
be passed as operands to routines, and may be returned
as the value of a routine. However assignment of label values is
not permitted.
.paragraph
Ordinarily, a block is evaluated by evaluating its declarations and
statements in sequence. When an exit-conditional statement is reached,
its test clause is evaluated and converted, if necessary, to mode BOOL. If
it cannot be converted, the evaluator signals a ∀type fault∩.
If it is FALSE, the consequent is ignored and control
proceeds to the next statement. If, however, the test
result is TRUE, then the consequent is evaluated,
evaluation of the block terminates,
and the value of the consequent becomes
the block's value.
.paragraph
The standard flow of control can be altered by the
built-in prefix operator GOTO, which takes a label
value as its operand. When the form GOTO loop is
executed, control switches to the statement labeled by loop.
If, as in the square root example given above, the environment
of the GOTO is the same as the environment of its label
operand, the effect of the GOTO is to perform a
simple jump between statements of the same block.
.paragraph
If, however, GOTO loop is executed inside one or more
blocks entered ↑&within\& the scope of the label loop , these
block activations are terminated and the environment is reset to
that which was in effect when loop's scope began before evaluation
proceeds. For example, in
.test page 21
.skip 1
.indent 5
BEGIN
.left margin 5
.indent 2
DECL#c:#CHAR;
.indent 2
#.
.indent 2
##.
.indent 2
###.
.indent 5
BEGIN
.indent 7
DECL#c:#INT;
.indent 7
#.
.indent 7
##.
.indent 7
###.
.indent 10
(c=10)#->#GOTO#out;
.indent 10
#.
.indent 10
##.
.indent 10
###.
.indent 5
END;
.break
out:#PRINT(c);
.indent 7
#.
.indent 7
##.
.indent 7
###.
.break
END
.left margin 0
.skip 1
if the conditional form in the inner block is reached when the
integer c has value 10, the inner block terminates, and the
outer block continues at out. The mode of c at that point
will be CHAR.
.paragraph
When a block evaluation runs to completion, its value becomes
either the consequent of the terminating exit-conditional or the value of
the last statement, if that is a simple form. If the last
statement is a conditional whose test proves FALSE, the
value of the block is NOTHING.
.paragraph
Note that it is reasonable for a block, whose value
is a proper object with scope global to the block,
to be the left-hand side of an assignment.
For example,
.skip 1
.indent 10
[)p#=>#a;#b(]#<-#c+1
.skip 1
selects either a or b to receive the new value on the basis
of the value of p.
.skip 3
.test page 5
.index iteration
2.6##ITERATION
.title 2.6##ITERATION
.paragraph
Often in an algorithmic language it is necessary
to evaluate a form repeatedly, possibly with an index variable
changing for each repetition. This repetition
can be indefinite or can be terminated by the
occurrence of some condition such as an index exceeding
a limit value or a Boolean condition evaluating to TRUE.
.index iteration form
The ∀iteration form∩ is provided for this purpose.
.paragraph
The general format of the statement is the following:
.skip 1
.center
FOR identifier FROM form BY form TO form test form DO form
.skip 1
where test is either WHILE or TILL.
Any of the following pairs may be omitted:
.skip 1
.left margin 10
.index FOR
.index FROM
.index BY
.index TO
FOR identifier
.break
FROM form
.break
BY form
.break
TO form
.break
test form
.skip 1
.left margin 0
Only "DO form" must always be present. For example,
the following fragment computes the sum of the positive
integers less than or equal to n.
.skip 1
.indent 10
s<-0;
.indent 10
FOR#i#FROM#1#BY#1#TO#n#DO#s<-s+i;
.skip 1
.paragraph
In an iteration, the identifier given after the FOR
is the index variable, and may be used explicitly in the
forms following test and DO. The index variable is a new
variable, of mode INT, which is local to the iteration.
It has no relation to any
other variable of the same name which might exist in the
program of which the iteration is a part. If
the FOR identifier part of the iteration is omitted, there is no
named index variable, although the indexing is still done.
.paragraph
.test page 10
The forms which follow FROM, BY, and TO may
be any forms which evaluate to integer values
(not necessarily positive). They are the
initial value of the index, the step size, and
the limit, respectively.
.skip 1
.left margin 5
If the FROM form is omitted, a default initial value of 1 is used.
.skip 1
If the BY form is omitted, a default step size of 1 is used.
.skip 1
If the TO form is omitted, a default limit of infinity is used.
(In essence this means that the check (index#<#limit) always succeeds.
The programmer is warned that in this case he should have another means
of terminating the iteration since otherwise he may encounter overflow
errors.)
.left margin 0
.paragraph
If one or more of FOR identifier, FROM form, BY form, or TO form are
explicitly included in the iteration form, the following actions will be
performed: the index will be initialized, it will be incremented by the step size
after each iteration,
and limit checking will be performed, using the default values
given above for those values not explicitly provided. If none of these
are explicitly included these actions will not be performed. The limit
check is as follows: the iteration is terminated when
.skip 1
.center
(index#-#limit)*step#size##GT##0
.skip 1
If this occurs initially,
then the iteration is not executed.
.paragraph
In FROM 10 TO -9 DO form, the form following the DO will not be
evaluated.
.paragraph
.test page 10
The following 7 iteration forms accomplish the same results:
.left margin 10
.skip 1
FOR#i#FROM#1#BY#1#TO#5#DO#x<-x+1;
.break
FROM#1#BY#1#TO#5#DO#x<-x+1;
.break
BY#1#TO#5#DO#x<-x+1;
.break
FROM#1#TO#5#DO#x<-x+1;
.break
FOR#i#TO#5#DO#x<-x+1;
.break
TO#5#DO#x<-x+1;
.break
FOR#i#BY#1#TO#5#DO#x<-x+1;
.left margin 0
.skip 1
Each of these iterations will be performed 5 times.
However the iteration
.skip 1
.indent 10
FOR##i#FROM#10#BY#-1#TO#-9#DO#x<-i;
.skip 1
will be performed 20 times and x will have the value -9 after the
iteration terminates.
.paragraph
It may also be desirable to terminate an iteration upon the occurrence
of some condition before the index has reached its limit. That is, an
iteration proceeds until its limit is reached or
.index TILL
TILL some test condition which evaluates to a Boolean
value becomes TRUE. For example, FOR#i#TO#n#TILL#f(x)#DO#x<-g(i,x)
repeatedly assigns to x the value g(i,x) for i
in the sequence 1,#2,#3,#...,#n except that if f(x) is ever
TRUE, the iteration halts.
.paragraph
A variation is to carry on the iteration
.index WHILE
WHILE some condition is TRUE.
FOR#i#TO#n#WHILE#h(x)#DO#x<-g(i,x) repeatedly assigns g(i,x) to
x as long as h(x) remains TRUE.
.paragraph
With both the TILL and WHILE tests the form following them can be
any form evaluating to a Boolean value. The test condition is evaluated
after the limit check (if any) and immediately before each evaluation of
the form being iterated. If the value of the test calls for stopping
the iteration, the evaluation of the iteration form is terminated.
.paragraph
In the case where only the "test form DO form" are
present in the iteration form, neither incrementing the index nor limit
checking is performed. If the test form is also omitted, (i.e. only
.index DO
"DO form" is present), no incrementing, no limit checking, and no condition
testing are done and the form following the DO is evaluated indefinitely.
The iteration FOR#i#WHILE#i#LT#5#DO#x<-i is performed 4 times and
terminates with x having the value 4. WHILE#h(x)#DO#x<-g(x) is evaluated
as long as h(x) evaluates to TRUE. The form DO#x<-g(x) is evaluated
indefinitely until g(x) (hopefully) does something (like a GOTO) to exit
the iteration form.
.paragraph
The value of an iteration form is always NOTHING.
.skip 3
.index mode-valued form
.title
.test page 10
2.7##MODE-VALUED#FORMS
.title 2.7##MODE-VALUED#FORMS
.paragraph
Those aspects of EL1 discussed thus far do not differ significantly
from Algol 60 or, indeed, any other algorithmic language. Notation
and syntax have been somewhat idiosyncratic but the underlying semantics
and facilities provided have been quite conventional. However,
having explained our notation for familiar concepts, we now have
sufficient foundation to present the innovative aspects of EL1.
The most important of these is the ∀mode-valued form∩.
.test page 8
.skip 1
2.7.1##↑&Mode-Valued Constants and Identifiers\&
.paragraph
As mentioned in section 2.2, the term ∀mode∩ is used in EL1
to designate a certain class of objects: objects corresponding to
the intuitive notion of data type.
Seven modes are primitive: those denoted by the mode-valued constants
INT, REAL, LABEL, BOOL, CHAR, NONE, and REF.
.paragraph
Just as variables can be declared of type INT and thereby
restricted to INT values, variables can be declared of type MODE
and restricted to MODE values. For example, consider
.skip 1
.indent 5
DECL m1, m2, complex, rational:#MODE
.skip 1
The variables m1, m2, complex, and rational are mode-valued variables.
Hence, it is legal to assign
.skip 1
.indent 5
m1#<-#BOOL;
.break
.indent 5
m2#<-#m1
.break
.skip 1
The variables m1 and m2 now have the same value as the constant
BOOL. Hence, m2=BOOL is TRUE while m1=CHAR is
FALSE.
.paragraph
Of themselves, mode constants and mode-valued variables are
uninteresting. However, we next consider operators which take
modes as operands and produce new modes. Five such
mode-producing operators are pre-defined: SEQ, STRUCT,
PTR, VECTOR, and ONEOF. From these, other mode-valued forms
can be synthesized using conditional expressions, functional
composition, and recursion.
.test page 8
.skip 1
.index SEQ
.index VECTOR
2.7.2##↑&VECTOR and SEQ\&
.paragraph
The operators VECTOR and SEQ are best introduced by means of an example.
Consider the fragment
.skip 1
.indent 10
DECL triple:#MODE
.skip 1
The variable triple has been declared to be an object whose value
is a mode.
.skip 1
.indent 10
triple#<-#VECTOR(3,INT)
.skip 1
The variable triple now has a value--the mode
integer-arrays-of-length-three. Hence, it is possible
to later write
.skip 1
.indent 10
DECL x,y:#triple
.skip 1
The variables x and y are of mode triple.
.paragraph
As a consequence of the above EL1 declaration, x and y are
objects having several of the properties of an Algol 60 array.
They can be subscripted, e.g. x[2], y[i], y[f(x[i])] are well-formed.
The result of the subscripting is a proper object of mode INT
which may be changed by assignment. For example,
.skip 1
.indent10
x[1]#<-#10;##x[2]#<-#20;##x[3]#<-#30;
.skip 1
.indent 10
y[1]#<-#x[3]#-#x[2]
.skip 1
The y can serve as operand for various operators--in
particular, for the assignment operator. For example,
.skip 1
.indent 10
y#<-#x
.skip 1
is legal and assigns to y[1], y[2], and y[3] the values of
x[1], x[2], and x[3], respectively. This is ↑©ing\& of
values, not sharing. For example, if we next assign
.skip 1
.indent 10
x[2]#<-#79
.skip 1
this does not change y[2] which remains 20.
.test page 13
.paragraph
We say that VECTOR(3,INT) is a form having a mode value and
that the class of this mode is
∀row∩. An object (such as y) whose mode is of class ∀row∩[*]
.footnote 7
.left margin 0
.skip 2
←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
[*] It will be useful to abbreviate this notation and say,
for example, "y is a ∀row∩," meaning that y is an object
whose mode is of class ∀row∩.
.left margin 10
!
has a number of properties.
.skip 1
.left margin 10
.indent -5
(1)##It is composed of a sequence of components of like
mode (e.g. x consists of 3 components each having mode INT).
.skip 1
.indent -5
(2)##Any one of these components may be ∀selected∩ by
subscripting (e.g. x[1] is the first of the 3 INTs).
.skip 1
.indent -5
.index LENGTH
(3)##The number of components may be determined by applying the
function LENGTH to the object (e.g. LENGTH(x)
is 3).
.left margin 0
.paragraph
It is frequently useful to define a mode of class ∀row∩ in which
the number of components is not fixed at the time the mode is
created. For example, the built-in mode STRING
might have been defined by:
.skip 1
.indent 10
DECL STRING:#MODE;
.skip 1
.indent 10
.index STRING
STRING#<-#SEQ(CHAR)
.skip 1
This first declares STRING to be a variable of data type mode and then
assigns to STRING the mode intuitively described by
array-of-any-number-of-characters. Since the number of components in
a STRING is not predetermined, STRING is said to be ∀length unresolved∩.
This is not to say that objects of mode STRING have variable length,
but rather that the mode STRING leaves the length open. A specific
object of mode STRING will have some fixed length, but different
STRINGs may have different lengths.
.paragraph
When a variable is declared to be a STRING, it is necessary to
∀resolve∩ the mode by specifying an initial value, and hence a
length, for the variable. For example,
.skip 1
.indent 5
DECL#s:#STRING#BYVAL#CONST(STRING SIZE n)
.skip 1
declares s to be a STRING whose length is the value of n.
(Use of CONST(...) will be explained in section 2.9).
Now s behaves like any other object whose mode is of class
∀row∩: it has a fixed number of
components which may be obtained by LENGTH(s); it
may be subscripted; it may be changed by assignment.
.paragraph
To summarize, the operator VECTOR takes two arguments, an
integer i and a mode m, and then produces the mode: n-tuple of i components
each of mode m. The operator SEQ takes one argument, a mode m, and then
produces the mode: ∀length unresolved∩ sequence of components
each of mode m. Both VECTOR and SEQ produce modes of class ∀row∩.
.paragraph
Note that VECTOR(i,m) and SEQ(m) are always different modes. If r1 has
mode SEQ(INT), r1 may not be directly assigned a value of
mode VECTOR(10,INT) even if LENGTH(r1) is 10.
.paragraph
In general, the arguments of SEQ and VECTOR
may be arbitrary forms, provided that they evaluate to objects of the appropriate type.
For example, consider
.skip 1
.indent 10
some←\row#<-
.indent 13
VECTOR(#[)i>j=>i;#10*j(],
.indent 21
[)p(x)=>INT;
.indent 23
q(x)=>triple;
.indent 23
CHAR(]#)
.paragraph
It should be noted that SEQ and
VECTOR, like the other three mode-valued operators,
are treated as procedures: the arguments are
evaluated, the body is executed, and
a result whose data type is MODE is delivered. One
consequence of this is that the output of SEQ or VECTOR
can be used as the argument to a second evaluation of
SEQ or VECTOR, e.g.
.skip 1
.indent 10
bool←\matrix#<-#SEQ(SEQ(BOOL))
.skip 1
The mode bool←\matrix corresponds to the notion of a two-
dimensional array of Booleans. Since length has been specified for
neither evaluation of SEQ, bool←\matrix is length unresolved
with two unresolved dimensions.
To construct a particular value of mode bool←\matrix
(see section 2.9), one must specify both dimensions, e.g.,
.skip 1
.indent 5
DECL b:#bool←\matrix CONST(bool←\matrix SIZE 5, 10)
.skip 1
By virtue of the above declaration, b has the following properties:
.left margin 10
.skip 1
.indent -5
(1)##b is a ∀row∩ of 5 objects, each object being a ∀row∩ of
10 BOOLs.
.skip 1
.indent -5
(2)##LENGTH(b) is 5.
.skip 1
.indent -5
(3)##b can be subscripted, e.g., b[3], the result being a ∀row∩ of
10 BOOLs.
.skip 1
.indent -5
(4)##LENGTH(b[i]) is 10 for i between 1 and 5.
.skip 1
.indent -5
(5)##b[i] may be subscripted, e.g., b[i,j], the result being a BOOL
.left margin 0
.paragraph
Repeated subscripting is written as x[j1,#j2,#...,#jn].
Each application of a subscript to an object yields a new object whose order
is one less than before.
↑↑
.spacing 1
.skip 1
.test page 8
.index STRUCT
2.7.3##↑&STRUCT\&
.paragraph
A ∀row∩ is subject to the restriction that all
its components have the same mode and identical sizes. A
second class of modes, ∀struct∩, allows composite
objects whose components do not necessarily have the same mode. The
built-in operator STRUCT takes as arguments a list of pairs
of the form id:m where id is
the name of the component and m is its mode.
STRUCT delivers the mode which describes objects so constructed.
.paragraph
For example, the following definition might be used to
represent a household fuse
.skip 1
.indent 5
fuse #<-#STRUCT(amps:#INT,
.indent 21
manufacturer:#VECTOR(10, CHAR),
.break
.indent 21
blown←\flag:#BOOL)
.skip 1
The mode fuse is the data type defined as follows. An object
of mode fuse consists of three components:
.skip 1
.left margin 10
(1)##an INT
.break
(2)##a ∀row∩ of 10 CHARs
.break
(3)##a BOOL.
.skip 1
.left margin 0
Since rows are homogenous objects, it is appropriate
to select their components by numerical subscripts
(e.g., x[4]). However, ∀structs∩ are typically
inhomogenous and it is useful to refer to their
components by symbolic names. The above definition specifies
both the modes of the components of a fuse
and the names of these components. That is,
.skip 1
.left margin 10
(1)##the INT is named "amps",
.break
(2)##the ∀row∩ of 10 CHARs is named "manufacturer",
.break
(3)##the BOOL is named "blown←\flag".
.skip 1
.left margin 0
Fuse having been defined, it can later be used in declaring variables, e.g.
.skip 1
.indent 10
DECL kitchen←\fuse, basement←\fuse:#fuse
.skip 1
The variable kitchen←\fuse is a fuse; hence, it has
three components one of which is an INT named "amps".
A component may be ∀selected∩ by ∀name qualification∩
which is denoted by the object name,
followed by a period, followed by the name of the component.
For example,
.skip 1
.indent 10
kitchen←\fuse.amps
.skip 1
Since this is an INT, it may be given an INT value
.skip 1
.indent 10
kitchen←\fuse.amps#<-#15
.skip 1
or used as an operand of an arithmetic expression
.skip 1
.indent 10
basement←\fuse.amps#<-#kitchen←\fuse.amps#+#5
.paragraph
We have discussed two methods of ∀selecting∩ a component
of a compound object: subscripting (e.g., x[i]) and
name qualification (e.g., kitchen←\fuse.amps). These
may be intermixed. For example, kitchen←\fuse.manufacturer
is of mode VECTOR(10,CHAR); hence, it may be
subscripted, e.g., kitchen←\fuse.manufacturer[i], yielding
a CHAR. If we define
.skip 1
.indent 10
.test page 6
fuse←\box#<-#VECTOR(4,fuse)
.indent 10
#.
.indent 10
##.
.indent 10
###.
.break
.indent 10
DECL central←\control:#fuse←\box
.skip1
then we may write
.skip 1
.indent 10
central←\control[2].blown←\flag
.skip 1
obtaining a BOOL, or
.skip 1
.indent 10
central←\control[i].manufacturer[1]
.skip 1
obtaining a CHAR. In general, selection
proceeds from left to right, obtaining successively
lower-level components.
.paragraph
It is occasionally useful to compute which component
of a ∀struct∩ is to be selected, as in the case
of ∀rows∩. This is done by subscripting. For
example, consider
.skip 1
.test page 6
.indent 10
complex#<-#STRUCT(re:#INT,#im:#INT)
.indent 10
#.
.indent 10
##.
.indent 10
###.
.indent 10
DECL z: complex
.skip 1
The form z[1] has precisely the same meaning as z.re and z[2]
the same as z.im; z[i] is one of these two depending on
the value of i.
.paragraph
In the discussion of rows, we introduced the notion of
length unresolved modes resulting from forms such as
SEQ(INT) which defer binding of the number of components.
Because ∀structs∩ may have components whose types are such
modes, it is possible to have length unresolved modes of class
∀struct∩. For example,
.skip 1
.indent 10
shipment#<-#STRUCT(item:#STRING, quantity:#INT)
.skip 1
Since STRING is length unresolved, so is shipment.
If a declared variable of a mode is initialized
completely by default, as in
.skip 1
.indent 10
DECL j6:#shipment
.skip 1
then LENGTH(j6.item) is 0 and will remain
so for the lifetime of the variable. To generate
less vacuous shipment variables with default initialization
one uses the ∀data generator∩ CONST (see section 2.9),
e.g.
.skip 1
.indent 10
DECL j6:#shipment CONST(shipment SIZE 16)
.skip 1
which specifies that j6.item has length 16.
.test page 10
.skip 1
.index REF
2.7.4##↑&REF\&
.paragraph
In section 2.2, we mentioned the mode constant REF
but deferred explanation; we now remedy this omission.
REF denotes a primitive mode which can
be intuitively described as the set of objects which can point
to arbitrary objects. Consider, for example,
.skip 1
.indent 10
DECL p1, p2:#REF
.skip 1
The variables p1 and p2 can point to (reference) objects of
any mode. Further, if p1 points to an object,
.skip 1
.indent 10
p2#<-#p1
.skip 1
copies the value of p1 (essentially an address) into p2 so
that p1 and p2 both point to the same object.
.paragraph
The question arises: how does one get p1 to point to an object
in the first place?##The form
.skip 1
.indent 10
p1#<-#x
.skip 1
.test page 14
where x is some object, say an INT, does ↑¬\& work, for
it is interpreted as: copy the value of x (an INT)
into p1 (a REF) which does not achieve the desired result[*].
.footnote 7
.skip 2
←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
[*] In fact, mode checking performed on assignment will
detect this as an illegal operation, for a REF cannot
contain an INT value.
!
In general, there is no way to take a declared object x and
obtain a pointer to it.
.paragraph
It is, however, possible to create a new object which
is pointed to by a REF. For example, consider
.skip 1
.indent 10
p1#<-#ALLOC(bool←\matrix SIZE 5, 10)
.skip 1
The right-hand side of the assignment creates a new object
of mode bool←\matrix (dimension 5 by 10) and returns a
pointer to the object; the assignment operation copies this
pointer into p1. Hence, p1 points to the bool←\matrix.
This bool←\matrix differs from all objects discussed thus
far: it was created by an ALLOCation\&,
not a DECLaration, and therefore has no name. It is
designated only by means of a pointer.
If p1 is assigned to p2, as in
.skip 1
.indent 10
p2#<-#p1
.skip 1
then both p1 and p2 point to the same bool←\matrix.
.paragraph
The link between a pointer P and an object Q to which
.index VAL
it points is provided by a primitive function VAL, i.e.,
VAL(P)→X∩Q. For example,
.skip 1
.indent 10
VAL(p1)
.skip 1
is the bool←\matrix allocated earlier. In particular,
.skip 1
.indent 10
VAL(p1)[5]
.skip 1
is the 5th component of the bool←\matrix, a ∀row∩ of
10 BOOLS, and
.skip 1
.indent 10
VAL(p1)[5,3]
.skip 1
is a BOOL. Hence,
.skip 1
.indent 10
VAL(p1)[5,3]#<-#TRUE
.skip 1
is a legal form which sets the (5,3) element of the
matrix to TRUE.
.paragraph
Since p1 is a REF, it is unrestricted in the mode of objects
it can address, and it is legal to assign
.skip 1
.indent 10
p1#<-#ALLOC(bool←\matrix SIZE 5, 10);
.skip 1
.indent 10
p2#<-#p1;
.skip 1
.indent 10
p1#<-#ALLOC(INT)
.skip 1
This leaves p2 pointing to the bool←\matrix and p1 pointing
to a single INT of value 0.
.test page 8
.skip 1
.index PTR
2.7.5##↑&PTR\&
.paragraph
We have emphasized that objects of mode
REF are unrestricted in the
mode of objects they can reference precisely because
there are other pointers which are so restricted. We
next consider this type of pointer.
.paragraph
The built-in operator PTR takes a mode m as argument
and produces the mode: the set of pointers restricted to
address objects of type m. For example, consider
.test page 7
.skip 1
.indent 10
int←\row#<-#SEQ(INT);
.break
.indent 10
int←\row←\ptr#<-#PTR(int←\row);
.indent 10
#.
.indent 10
##.
.indent 10
###.
.indent 10
DECL ip1,#ip2:#int←\row←\ptr
.skip 1
The variable ip1 is of class ∀pointer∩ (abbreviated "∀ptr∩")
but its specific mode is int←\row←\ptr. It can point only to an
int←\row. Hence,
.skip 1
.indent 10
ip1#<-#ALLOC(int←\row SIZE n)
.skip 1
is legal, but
.skip 1
.indent 10
ip1 #<-#ALLOC(fuse←\box)
.skip 1
is not. The right hand side of the latter form returns a
pointer to a fuse←\box; assignment of this to an
int←\row←\ptr is a ∀type fault∩. As with REF, it is
possible for two objects of mode int←\row←\ptr to refer to
the same object, e.g. the assignment
##ip2#<-#ip1##
leaves ip1 and ip2 equal and VAL(ip1) identical
to VAL(ip2).
.paragraph
We have thus far omitted discussion of pragmatics;
however, a pragmatic note is unavoidable at this
point lest it appear that pointer modes of
restricted referent are an arbitrary whimsey. From
considerations based only on semantic grounds, the charge
is well-founded. The variable ip1 can be used for no purpose
for which p1 (having mode REF) could not be used;
p1 can point to any object to which ip1 can point, the
assignment p1#<-#ip1 being legal. Modes such as
int←\row←\ptr are introduced for two reasons,
.skip 1
.left margin 5
.indent -5
(1)##In our
implementation it is possible to use less storage for
an int←\row←\ptr than for a REF since the REF will
carry a type code as well as an address.
.skip 1
.indent -5
(2)##Possibly more important is that tightly bound modes
such as int←\row←\ptr allow more efficient (i.e., complete)
compilation. For example, the compiler when confronted with the form
.skip 1
.indent 10
VAL(ip1)[k]
.skip 1
can determine that it is well-formed, that it involves
subscripting a ∀row∩ of INTs, and that it yields an INT.
Code generation is straightforward. The analogous
form involving a REF
.skip 1
.indent 10
VAL(p1)[k]
.skip 1
could have any number of possible selection operations and
result types, depending on what sort of object p1 references
at the time the form is evaluated. Code compiled
for this must reflect the uncertainty.
.left margin 0
.paragraph
It is frequently useful to deal with pointers which can
reference objects of more than one mode but which are
not totally unrestricted. The mode of such a pointer is said
to be of sub-class ∀united pointer∩. For example,
.skip 1
.indent 10
int←\or←\bool←\ptr#<-#PTR(INT,#BOOL)
.skip 1
The operator PTR can be given more than one argument
and when so invoked it produces a mode defining
pointers which may reference more than one type of
object. If we declare
.skip 1
.indent 10
DECL q#:#int←\or←\bool←\ptr;
.skip 1
then q can point to INTs or BOOLs but to
nothing else.
.paragraph
.skip 1
.test page 40
2.7.6##↑&Summary\&
.paragraph
Section 2.7 has discussed four
groups of mode-valued
forms: constants, rows, structs, and pointers. (A fifth group, ∀generic∩, can best be presented after
discussion of procedures. It will be treated in section 2.10.)##Since
a large number of notions have been presented in a loose,
discursive exposition, Table 2.7-1 summarizes this material.
.skip 1
.tab stops 5, 17, 35, 42
.nofill←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
Class Sub-Class Example
.break
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
constants non-pointer INT
.skip 1
pointer REF
.break
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
row resolved VECTOR(5,#CHAR)
.skip 1
unresolved SEQ(INT)
.break
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
struct resolved STRUCT(a:INT,
b:VECTOR(5, CHAR))
.skip 1
unresolved STRUCT(a:INT,
b:SEQ(CHAR))
.break
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
pointer simple PTR(SEQ(INT))
.skip 1
united PTR(SEQ(INT), BOOL)
.break
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
.center
Table 2.7-1
.fill
.title
.page
.index selection
2.8##SELECTION
.title 2.8##SELECTION
.paragraph
The operation of selection has been discussed
as a peripheral issue in sections 2.7.2-5. In this
section, we summarize this discussion and treat one
additional point.
.paragraph
The operators SEQ (or VECTOR) and STRUCT produce modes
of class ∀row∩ and ∀struct∩, respectively--objects whose modes are compound and consist of
∀components∩ which may be
designated by ∀selection∩ operations. There are two such operations:
qualified naming and subscripting. The former is applicable
only to ∀structs∩. It is denoted by writing the name of the object,
followed by a period, followed by the name of the
component (e.g., z.re), the name of the component having been
specified in the mode definition. Subscripting is applicable
to either ∀rows∩ or ∀structs∩.
It is denoted by writing the name of the object,
followed by one or more forms separated by commas and
enclosed in square brackets (e.g.##x[i+j,#k]). The
operation is carried out by evaluating each form inside
the brackets, which is expected to yield an INT result, say
[1,2], and taking as result the second component of the first
component of the object.
.paragraph
The object on which selection is performed is not restricted
to be an identifier: any form whose value is a ∀row∩
or ∀struct∩ may be employed. For example, it can be a
prior selection (e.g., a[i].re), in which case the selection
is carried out from left to right. It can be a function
which delivers a ∀row∩ or ∀struct∩ (e.g., f(x)[i]).
In particular, it can be the application of VAL
to a pointer; e.g., if p points to a bool←\matrix, then
.skip 1
.indent 10
VAL(p)[i][j]###(or equivalently#VAL(p)[i,j])
.skip 1
is a legal form having a BOOL value.
.paragraph
In connection with the last example, one additional point
bears mentioning. Since such forms occur frequently, it
is desirable to abbreviate their representation. An
abbreviation is immediate if it is observed that the
appearance of "VAL" is redundant. Given that p is a
pointer, it is clear that p cannot itself be the object
of a selection, for it has no components. Hence,
.skip 1
.indent 10
VAL(p)[i][j]
.skip 1
can be unambiguously abbreviated
.skip 1
.indent 10
p[i][j]
.skip 1
The latter is defined to be identical to the former. This
scheme of abbreviation is, of course, completely general.
For example, if f(x) returns a pointer to a ∀row∩ of ∀structs∩
each of which has a component named "tx" which is a pointer
to a bool←\matrix, then
.skip 1
.indent 10
f(x)[n].tx[i][j]
.skip 1
.title
.test page 15
may be written with the same meaning as the explicit form
.skip 1
.indent 10
VAL(VAL(f(x))[n]#.#tx)[i][j]
.paragraph
In the case of multiple or repeated selection, two
formats are equivalent and equally permissible: x[i,j] has
the same meaning as x[i][j]. Of course these formats may
be combined as in x[i,j][k][l,m,n].
.break
↑↑
.test page 15
.skip 3
.spacing 1
.index generation
2.9##DATA GENERATION
.title 2.9##DATA GENERATION
.paragraph
Two forms are provided in EL1 for the purpose of
creating and initializing objects of any mode. This
process is called ∀data generation∩. The
two forms, called CONST and ALLOC, differ
only in that the first creates a new instance of
some data class, while the second does the same thing
but returns a ∀pointer∩ to the new instance.
ALLOC's role as pointer-generator was mentioned
in section 2.7.5. Now we will see in more
detail how data may be created and copied.
.skip 1
.test page 12
.INDEX DEFAULT GENERATION
2.9.1##↑&Default Generation\&
.paragraph
Given a mode m, one can construct an object of mode
m with the default initialization corresponding to that
mode by writing
.skip 1
.indent 10
CONST(m)
.break
or
.indent 10
ALLOC(m)
.skip 1
The first form evaluates to the object itself; the second
returns a pointer to the new object. One point concerning the
modes of these two expressions must be
stressed: CONST(m) has mode m, while ALLOC(m) or any
ALLOC form evaluates to the mode REF. This
particular REF will be compatible with (i.e. convertible
to) the mode PTR(m). The programmer should, however, be
aware that a conversion step is required.
.paragraph
Examples of default generation are CONST(complex)
which would create a complex number with real and
imaginary parts equal to zero and ALLOC(INT) which would allocate
space for an integer, initialize it to zero, then
return a pointer to it. But suppose one wrote
.skip 1
.indent 10
CONST(SEQ(SEQ(BOOL)))
.skip 1
Since the default initial value for
sequences is an empty sequence, this expression produces a rather
vacuous object: the empty sequence of empty sequences of BOOLs.
.skip 1
.test page 10
.index SIZE
2.9.2##↑&Generation by SIZE\&
.paragraph
To generate data structures of length-unresolved mode
without having each length-uncertain component assumed empty,
one uses a SIZE designator
in either type of generator. For example
.skip 1
.indent 5
CONST(SEQ(SEQ(BOOL)) SIZE 2, 6)
.skip 1
creates a 2 by 6 Boolean matrix, each of whose
elements has the default initial value FALSE. If
s is a ∀struct∩ mode defined by
.skip 1
.indent 5
s<-STRUCT(a:STRING,
.left margin 15
b:INT,
.break
c:SEQ(SEQ(INT)))
.left margin 0
.skip 1
then ALLOC(s SIZE 4, 14, 2) constructs an object of mode s whose first component is a four
character string and whose third component is a 14 by 2
INT matrix, all default initialized.
.paragraph
When the SIZE designator is used, the list
of specific dimensions must be exactly as long as the
number of length uncertainties inherent in the given
mode. The sizes are given in the order in which the
length uncertainties occur in the definition of the mode,
read left to right, top to bottom. In the last example above, there
must be three dimension specifications, one for the first
component and two for the third. The first size applies
to the STRING, the second to the outer SEQ, the
last to the inner SEQ.
.paragraph
To take a still more detailed example:
.skip 1
.left margin 6
introw#<-#SEQ(INT);
.skip 1
matrix#<-#SEQ(SEQ(BOOL));
.skip 1
matrixrow#<-#SEQ(matrix);
.skip 1
comp#<-#STRUCT(a:introw,#b:matrixrow,#c:STRING)
.break
#.
.break
##.
.break
###.
.break
DECL x:#comp BYVAL CONST(comp SIZE 10, 3, 20, 25, 6)
.skip 1
.indent -10
This results in the following:
.skip 1
.left margin 5
(1)##LENGTH(x.a) is 10
.skip 1
(2)##LENGTH(x.b) is 3
.skip 1
(3)##x.b[i] is a matrix, for i=1, 2, 3
.skip 1
(4)##LENGTH(x.b[i]) is 20,# for i=1,#2,#3
.skip 1
(5)##LENGTH(x.b[i,j]) is 25, for appropriate i and j
.skip 1
(6)##LENGTH(x.c) is 6
.skip 1
.left margin 0
.skip 1
.test page 10
2.9.3##↑&Generation from Components\&
.paragraph
.index OF
Objects whose modes are of class ∀row∩ or ∀struct∩
may be generated from a list of the intended components.
In this case, unspecified dimensions and all initial
values will be inferred from the list given. The
OF designator is used for this type of generation, as in
.skip 1
.indent 5
CONST(SEQ(INT)#OF#4,#i*j,#-7)
.skip 1
This form produces a result equivalent to that of the
following block:
.skip 1
.left margin 10
.indent -3
BEGIN
.break
DECL ir:SEQ(INT)#BYVAL
.indent 5
CONST(SEQ(INT)#SIZE 3);
.break
ir[1]#<-#4;
.break
ir[2]#<-#i*j;
.break
ir[3]#<-#-7;
.break
ir
.indent -3
END
.skip 1
.left margin 0
A pointer to an object of mode s (defined in section 2.9.2) could
be generated by
.skip 1
.indent 5
ALLOC(s#OF#'AB',
.break
.indent 16
4,
.break
.indent 16
CONST(SEQ(SEQ(INT))#OF
.indent 22
CONST(SEQ(INT)#OF#1,2),
.indent 22
CONST(SEQ(INT)#OF#3,4)))
.skip 1
Notice that any form may appear in the list of components.
.skip 1
.test page 10
2.9.4##↑&Generation by Copy\&
.paragraph
.index BYVAL
Finally, an object may be generated as a copy of an existing
object. The designator BYVAL in either generator
specifies that the value of the form which follows is to
be copied, with automatic conversion to the target mode
if necessary and permissible (see Appendix F for compatible mode
pairs). For example,
.skip 1
.indent 5
pv#<-#ALLOC(SEQ(REAL)#BYVAL#vector2)
.skip 1
creates (and points to) a copy of vector2. That is,
VAL(pv)#=#vector2 is TRUE but assignments like
pv[i]#<-#5.0 have no effect on vector2. The form
.skip 1
.indent 5
CONST(PTR(BOOL)#BYVAL#ALLOC(BOOL#BYVAL#TRUE))
.skip 1
.title
.test page 15
produces a PTR(BOOL) since this mode is compatible with a
REF whose VAL is a BOOL.
.skip 2
↑↑
.spacing 1
.skip 2
.index procedure
2.10##PROCEDURES
.title 2.10##PROCEDURES
.paragraph
A ∀procedure∩ is an EL1 form whose value is the
text of a program. This program, called the procedure body,
is executed whenever the procedure is ∀applied∩
to a set of input values. The application of a
procedure is often called a ∀procedure call ∩, and
the values supplied as inputs to the procedure
are called ∀arguments∩.
.paragraph
Here is a typical procedure call:
.skip 1
.indent 10
maximum(k, limit)
.skip 1
A procedure call consists of a form which designates a
procedure value, followed by a list of forms which
designate the arguments. The arguments are separated
by commas and surrounded by parentheses.
A procedure call is a form and it has a value, namely
the value produced when the procedure
body is evaluated.
.paragraph
Since each element of a procedure call may be an arbitrary form,
elaborate procedure calls may be
written:
.skip 1
.indent 10
[)i>j#=>#transform;#revert(]##(p(i))
.skip 1
.index ROUTINE
More commonly, however, the procedure is specified
by a simple ∀procedure name∩, an identifier whose
value is a procedure. Such identifiers have mode
ROUTINE. Several are built-in
and some of these have been mentioned in
earlier sections (LENGTH, VAL).
.paragraph
.index explicit procedure
The programmer may extend the set of usable
procedures with an EL1 form called an ∀explicit
procedure∩. For example,
.skip 1
.indent 5
rem#<-#EXPR(k:#INT#BYVAL,#j:#INT#BYVAL;#INT)(k-(k←/j)*j)
.skip 1
defines a new procedure, called rem, which produces,
when called with two integer arguments, the remainder of
the first after division by the second.
.paragraph
The explicit procedure is technically a constant of
mode ROUTINE. EL1 programs are collections
of these constants. The components of the
explicit procedure are, from left to right:
.left margin 10
.skip 1
.indent -5
.index EXPR
(1)##The special delimiter EXPR.
.skip 1
.indent -5
.index formal parameter
(2)##A list of ∀formal parameter∩ descriptors,
each consisting of an identifier, a form which
must reduce to a mode, and a bind-class indicator.
.skip 1
.indent -5
.index result type
(3)##The ∀result type∩, a form which must evaluate
to a mode. The procedure result must be
compatible with this mode.
.skip 1
.indent -5
.test page 10
.index procedure body
(4)##The ∀procedure body∩, the form[*] whose value becomes the
value of a call of the procedure.
.footnote 6
.left margin 0
.skip 2
←←←←←←←←←←←←←←←←←←←←←←←←←←
.skip 1
[*] The procedure body form must be either parenthesized or a block.
!
.skip 1
.left margin 0
.paragraph
The formal parameter names listed in the procedure
heading normally also appear in the body.
Their role is to represent the actual arguments
supplied as inputs to the procedure. When a procedure
such as rem is called, e.g.,
.skip 1
.indent 10
rem(13, 4)
.skip 1
the arguments are put in correspondence with the formal
parameters in left to right order before the
procedure body is executed. The effect is as if the
programmer had written the following compound form
instead of the above procedure call:
.skip 1
.left margin 10
BEGIN
.indent 3
DECL k:#INT#BYVAL 13;
.indent 3
DECL j:#INT#BYVAL 4;
.indent 3
DECL result:#INT LIKE (k-(k/j)*j);
.indent 3
result
.break
END
.left margin 0
.paragraph
When rem is called, the arguments are evaluated and
checked for compatibility with the modes of the
corresponding formal parameters. Where necessary,
an argument will be converted to agree with the
formal parameter mode. REAL arguments to rem,
for example, are rounded to the nearest integers.
Then the formal identifiers are bound to the actual arguments,
just as the local variables j and k would be in the
block model. The procedure body is evaluated, producing
a result. Finally, this result is checked for compatibility
with the result type specified for the procedure. If
it matches (or can be converted to match) it becomes the
value of the procedure call.
.paragraph
Next, consider a slightly more complicated explicit
procedure, a program to compute the greatest common
divisor of two positive integers by Euclid's
algorithm, using the remainder routine defined above:
.test page 15
.skip 1
.indent 5
gcd#<-
.indent 10
EXPR(m:#INT BYVAL,#n:#INT BYVAL;#INT)
.break
.left margin 10
BEGIN
.indent 3
DECL#r:#INT;
.indent -2
l:###r#<-#rem(m, n);
.indent 3
r#=#0#=>#n;
.indent 3
m#<-#n;
.indent 3
n#<-#r;
.indent 3
GOTO#l
.break
END
.left margin 0
.paragraph
This procedure uses its formal parameters as variables which
may change in the course of computing the greatest common
divisor. This technique is often convenient,
but one may well ask what becomes of the actual arguments
when such a procedure is applied. For example, suppose
the statements
.skip 1
.indent 10
a#<-#30;
.indent 10
b#<-#21;
.indent 10
gcd(a,b);
.skip 1
are executed in sequence. Will the variables a and b
be affected by assignments to their representatives
m and n within the procedure body?
.paragraph
A compound form model of the above procedure call
shows that the answer is no.
.skip 1
.left margin 10
.test page 13
a#<-#30;
.break
b#<-#21;
.break
BEGIN
.indent 3
DECL#m:#INT#BYVAL a;
.indent 3
DECL#n:#INT#BYVAL b;
.indent 3
DECL result:#INT LIKE
.left margin 18
BEGIN
.break
#.
.break
##.
.break
###.
.break
END;
.left margin 10
.indent 3
result
.break
END
.left margin 0
.skip 1
Since the formal parameters of gcd have bind-class
BYVAL and not SHARED or LIKE,
they act as distinct variables, merely initialized
by (copies of) the values of the arguments
at the point the procedure is called. Binding
by value allows the programmer to alter formal
variables without endangering argument variables in
the calling program.
.paragraph
On the other hand, one often writes a procedure expressly
intended to have an effect on one or more
of its arguments. Shared binding should be used in
this situation, and changes made to the formal parameters
inside the procedure body are reflected in the
actual arguments which are bound shared.
.paragraph
An example of a procedure intended to leave its argument modified
is the following routine to transpose the elements of a
square#matrix of REALs#(defined as VECTOR(n,#VECTOR(n,#REAL))#)
about the main matrix diagonal:
.skip 1
.indent 5
transpose#<-#EXPR(a:square←\matrix#SHARED;NONE)
.indent 15
(FOR#i#TO#LENGTH(a)#DO
.indent 17
FOR#j#TO#i-1#DO
.indent 20
BEGIN
.indent 23
DECL temp:REAL BYVAL a[i,j];
.indent 23
a[i,j]#<-#a[j,i];
.indent 23
a[j,i]#<-#temp
.indent 20
END)
.skip 1
Since the bind-class indicator in the heading
of the EXPR is SHARED, the formal parameter will be
bound shared. Thus any proper object will be
shared between the calling program
and the activation of the routine:
.skip 1
.indent 10
transpose(mat1)
.skip 1
leaves the matrix mat1 transposed. Recall that
proper objects need not be simple variable names.
Suppose cube represents a three-dimensional array of mode
VECTOR(n,#square←\matrix). Then cube[i] is a proper object and
transpose (cube[i]) is a perfectly reasonable procedure
application, which transposes the ith "plane" of a cube.
.paragraph
If the bind-class indicator had been not SHARED but LIKE,
the matrix mat1 would have been left transposed after the call
transpose(mat1), since mat1 is a proper object of the
expected mode. Had we called transpose with a pure value argument:
.skip 1
.indent 5
transpose(CONST(square←\matrix OF
.indent 21
CONST(VECTOR(2,REAL)#OF#1.2, 3.4),
.indent 21
CONST(VECTOR(2,REAL)#OF#5.6, 7.8))
.skip 1
the result would be the same as if the bind-class
indicator had been BYVAL. A matrix
is generated, transposed, and forgotten. In this case,
the call to transpose is harmless but
pointless.
.paragraph
If no explicit bind-class indicator is included in the descriptor
of a particular EXPR formal parameter, the
parameter will be bound by coercion, i.e. as if LIKE had
been explicitly included.
.paragraph
Notice also that the value returned
by a procedure may be a proper object. In the block
models of procedure application given above, the last
declaration, which models the treatment of procedure results,
has bind-class LIKE. This is the
case for all procedures. Thus if the value of the
procedure body is a proper object in the scope of the
procedure call, so will be the value of the call itself.
.paragraph
A simple example should make this clear. Let the procedure
choose be defined by the assigment
.skip 1
.indent 5
choose#<-#EXPR(b:#BOOL;#INT)#[)b#=>#x;#y(]
.skip 1
The sequence
.skip 1
.indent 10
DECL x,y:#INT;
.indent 10
choose(TRUE)#<-#1492;
.indent 10
choose(FALSE)#<-#1066
.skip 1
leaves x and y with values 1492 and 1066, respectively.
.paragraph
As mentioned earlier, the actual arguments to each
procedure call will be checked against the modes of the
formal parameters, as evaluated on entry to the procedure,
and the result value will be checked against the formal result
mode. In case of a mode mismatch, the evaluator will try to convert the value
in hand to the expected mode. The situations in which such
conversions are possible are listed in Appendix F.
Basically, an INT may be converted to a REAL,
a REAL may be rounded to an INT, a pointer may be converted
to some compatible pointer mode (e.g. a PTR(INT)
may become a REF, though ↑¬\& a PTR(REAL)),
and any value can be converted to mode NONE,
i.e. replaced by the value NOTHING. The
programmer must take care, however, that data objects
intended to be shared between the calling program
and a procedure, either ↑&via\& its arguments or its
results, are ↑¬\& subjected to automatic
conversion. Built-in conversion routines always create a pure value
of the expected mode, even if the specified bind-class
is LIKE. Suppose, for example, that a
variable p has been declared to be a PTR(INT),
and that within its scope the procedure
.skip 1
.indent 10
set←\ptr#<-#EXPR(r:#REF LIKE; NONE)
.break
.indent 20
(r#<-#ALLOC(INT BYVAL j*j))
.skip 1
is called with p as argument: set←\ptr(p).
Nothing happens to p. The proper object of mode PTR(INT)
is converted into a pure value of mode REF, the
expected mode for r. Thus the values of p and r will be distinct,
and the assignment to r has no effect upon p. Had the
bind-class indicator been SHARED, then set←\ptr(p) would
have resulted in a ∀type fault∩.
The
difficulty can be eliminated by changing the
declared mode of the formal r to match p's, since it is
clear that set←\ptr only deals in PTR(INT)'s.
Or set←\ptr could return the new pointer as its
value and the assignment could be made on the
calling side:
.skip 1
.indent 10
p#<-#set←\ptr()
.skip 1
Perhaps, best of all in this artificial example, would
be to eliminate the procedure completely.
.paragraph
.index generic mode
For less trivial cases in which a variable is to
be bound to a value whose mode is not certain until
the program is run, or in which automatic data type
conversion is undesirable, EL1 provides a special
class of modes, called ∀generic∩ modes. Generic
modes may only appear as the declared modes of local
variables and formal parameters or as the result
mode of an explicit procedure. Each is actually a ↑&set\&
of modes representing the alternative mode possibilities
for the variable or result. Generic modes are created
by the built-in function ONEOF; for example,
.skip 1
.indent 5
scalar#<-#ONEOF(INT, REAL, complex)
.skip 1
creates a mode which matches actual values of
type INT, REAL or complex
(defined in section 2.7.3) when scalar is the
expected mode in a declaration or EXPR heading.
No automatic conversion takes place when an expected
mode is of class generic. The actual value must agree
in mode with one alternative of the generic
mode or the evaluator will signal an error.
.paragraph
Once an alternative has been chosen during a
variable declaration or on procedure entry, the
mode of the variable is fixed for its lifetime.
That is, its mode is uncertain only before the
variable is bound, not afterwards. Thus, there are,
in fact, ↑&no values\& of generic mode.
Nor may generic modes appear as component modes
in the forms STRUCT, VECTOR, SEQ, or PTR.
Within the procedure body, the mode of a variable
may be tested using the built-in routine MD, which returns
the mode of the value to which it is applied.
For example, MD(scalar) is MODE.
.test page 14
.paragraph
To illustrate the use of generic modes, here is a
routine which defines addition for scalars, using
the built-in operator#+# which adds INTs and REALs.
.skip 1
.indent 3
scalar←\add#<-
.indent 5
EXPR(x:#scalar, y:#scalar; scalar)
.indent 7
BEGIN
.indent 9
MD(x)#=#complex#=>
.indent 11
[)MD(y)=complex=>
.indent 13
CONST(complex OF x.re+y.re,#x.im+y.im);
.indent 13
CONST(complex OF x.re+y, x.im)#(];
.indent 9
MD(y)#=#complex#=>#CONST(complex OF x+y.re, y.im);
.indent 9
x#+#y;
.indent 7
END
.paragraph
.index ANY
There is a mode constant of class ∀generic∩
called ANY. When the expected mode of a variable
or procedure result is ANY, values of any mode
whatever will be passed without
conversion or ∀type fault∩. Of course, a variable
so declared is subject to the same restrictions that
apply to other generic modes: after its initial binding,
the variable's mode is fixed. Using ANY, the
procedure choose could be revised to choose between
x and y regardless of what modes they may have when the
routine is called
.skip 1
.left margin 5
choose#<-#EXPR(b:#BOOL;#ANY)[)b#=>#x;#y(]
.break
#.
.break
##.
.break
###.
.break
.left margin 8
[)DECL#x:#INT;
.indent 2
DECL y:#thorble;
.indent 2
#.
.indent 2
##.
.indent 2
###.
.indent 5
choose(MD(v)←#thorble)#<-#v;
.indent 5
#.
.indent 5
##.
.indent 5
###.
.left margin 0
.skip 2
.title
.test page 10
2.11##BINDING#CLASSES
.title 2.11#BINDING#CLASSES
.paragraph
.index bind-class
The bind-class specifies the relationship between the
actual argument and the formal parameter to a routine.
This has been touched upon in 2.10; here it is treated in detail.
.paragraph
There are five bind classes in EL1: BYVAL, SHARED, LIKE,
UNEVAL, and LISTED. To discuss the first three, it
will be useful to use a standard example:
.skip 1
.indent 4
scale#<-#EXPR(x:REAL#<class#1>,#y:INT#<class#2>;#INT)
.indent 16
BEGIN#...#END;
.skip 1
For this example let <class#1> and <class#2> each
be either BYVAL, SHARED, or LIKE.
.index BYVAL
.paragraph
BYVAL specifies that the formal parameter is to be bound
∀by value∩, i.e. to a ∀copy∩ of the value of the
corresponding actual argument. For example, consider
.skip 1
.indent 10
scale(aa,#bb)
.skip 1
If <class#1> and <class 2> are BYVAL, the value of
variable aa is obtained,
∀copied∩ and the formal parameter x is bound to this copy.
Since the value of x is a copy of the value of aa, changes to x
(e.g. by assignment) do ↑¬\& affect the value of aa and changes to aa do not
affect the value of x. Since a copy is made, the value of the
actual parameter can be converted to the type of
the formal parameter, if needed and legal
(c.f. Appendix F). For example, consider
.skip 1
.indent 10
scale(11,#12.6)
.skip 1
The first argument, the integer 11, is converted to a real;
the second argument, the real 12.6, is converted to an integer by
rounding.
.paragraph
.test page 10
.index SHARED
SHARED specifies that the formal parameter is to
be bound to the very same object as given
by the value of the actual argument. For example, consider
.skip 1
.indent 10
scale(aa,#bb)
.skip 1
If <class 1> and <class 2> are SHARED, the value of aa
is obtained and formal parameter x is bound to this
value. (Similarly, for bb and y.)##Since aa and x are
names for the very same object, changes to
aa change the value of x and changes to x change
the value of aa. Since ↑&no\& copy is made, the value
of the actual argument is ↑&never\& converted. If the
mode of the actual argument disagrees with the mode of the formal
parameter, then a ∀binding fault∩ occurs. For example,
.skip 1
.indent 10
scale(11,#12)
.skip 1
causes a binding fault for the first argument.
.paragraph
If <class 1> and <class 2> are
.index LIKE
LIKE, x is bound to the value of aa, or to a copy of the
value of aa. Which is done depends upon whether the
mode of the expected value matches the mode of aa.
If aa evaluates to a proper object of the expected
mode, then x is bound to that object as if the
bind class were SHARED. If aa evaluates to a proper
object of any other mode or to a pure value, the binding
is as if it were BYVAL. In this case, changes
to x do not affect any other variable.
LIKE should be used when it is unknown whether type conversion will
be necessary and a binding to a proper object is desired if
possible but a pure value (possibly converted) will be acceptable.
LIKE binding is the default and is assumed whenever a bind class is
omitted. If necessary and legal, conversion between the actual argument mode
and the formal parameter mode will be carried out (as is the case
with BYVAL).
.paragraph
The three classes BYVAL, SHARED, and LIKE can be used in
specifying the initialization of a local declared variable
(c.f. 2.5.1.2). The binding relation between local variable
and initialized form in a declaration is the same as the relation between
formal
parameter and actual argument in a procedure call.
.paragraph
Two additional bind classes, UNEVAL and LISTED, can appear as the
binding specification for procedures, but not for declared
variables. An example for these two bind classes will be useful:
.skip 1
.indent 10
foo<-EXPR(s:FORM#<class 1>;#FORM)
.indent 20
BEGIN#...#END
.skip 1
Let <class 1> be either UNEVAL or LISTED.
.paragraph
.test page 10
.index UNEVAL
UNEVAL specifies that the formal parameter is to be bound to the
internal representation of the actual argument with evaluation of that
argument inhibited. For example, in
.skip 1
.indent 10
foo(aa#<-#bb)
.skip 1
if <class 1> is UNEVAL then the assignment is ↑¬\& performed; s is bound
to the internal representation of the first argument--a
list of three elements: the assignment symbol, the symbol
for aa, and the symbol for bb. (In LISP notation this is
(<-#aa#bb).)##Similarly, if the argument
had been aa+bb*cc then s would be bound to a list of three elements:
the symbol for +, the symbol for aa, and a sublist of three elements
(*#bb#cc).
.paragraph
.test page 10
.index LISTED
LISTED specifies that the formal parameter is bound to a
list whose first element is the corresponding actual
parameter and whose successive elements are the actual arguments
remaining to the right. Evaluation is inhibited for ↑&all\&
arguments included in the list. For example,
.skip 1
.indent 10
foo(aa<-bb,#aa*(bb+cc))
.skip 1
If <class 1> is LISTED, then s is bound to the list
((<-#aa#bb)#(*#aa(+#bb#cc))). Effectively,
s has taken possession of its own actual argument and
all arguments remaining to the right. Hence, it is
meaningful to use the binding LISTED ↑&only\& for the
rightmost formal parameter.
.skip 2
.title
.test page 15
.index GENERIC
.title 2.12##GENERIC FORMS
.paragraph
↑&Note to the reader\&:##The next two sections (2.12 and 2.13) are very
technical and may easily be skipped upon the first reading of this
manual without loss of continuity.
.skip 2
2.12##GENERIC FORMS
.paragraph
Routines need an efficient means of switching on mode
types in general mode n-tuples. This is especially
useful for compiling code.
For this purpose, the ∀generic form∩ is introduced with the
following syntax.
.skip 1
GENERIC(<x1>,<x2>,...,<xn>)
.indent 2
[<modeform#1>,<modeform#2>,...]<predicate#1>#=>#<form>;
.indent 8
←.
.indent 8
←.
.indent 8
←.
.indent 2
[<modeform#N>,<modeform#N+1>,...]<predicate#I>#=>#<form>;
.break
END
.skip 1
.paragraph
Each statement inside the generic form is called a ∀generic∩
∀statement∩. The xi are the arguments to the generic form.
The semicolon following the last generic statement is optional.
.paragraph
The arguments to the generic form are the forms which
will be evaluated.
The GENERIC form is evaluated by
considering each generic statement in turn. The mode of the
current value of each form is checked from left to right with
the value of the modeform in the corresponding position and, if each form's
mode is covered (i.e. is equal to the value of the modeform or one
of its alternatives), the predicate is evaluated. If TRUE, then the
generic form is exited with the value produced by evaluating the
form on the right hand side of the matching generic statement.
If no match is found or the predicate evaluates to
FALSE, the next generic statement is considered.
.paragraph
Either the mode list or the predicate but not both may be missing on
the left hand side of a generic statement, and a match or TRUE
respectively is assumed. If the number of modeforms in the mode list is less
than the number of arguments to the generic form, a match of those
present is acceptable. For example,
.skip 1
.indent 10
GENERIC#(a,b)
.indent 14
[INT,REAL]#=>#a;
.indent 14
[INT]#a#LT#b#=>#b;
.indent 14
TRUE#=>#NOTHING;
.indent 10
END
.skip 1
returns the value of a if a has mode INT and b has mode
REAL. If not, it returns the value of b if a has mode INT and the value of a
is less than the value of b. If neither of these cases apply,
the value NOTHING is returned.
##
.SPACING 1
.SKIP 2
.TEST PAGE 8
.TITLE 2.13##↑↑USER DEFINED MODE BEHAVIOR\\
2.13##↑↑USER DEFINED MODE BEHAVIOR\\
.SKIP 2
2.13.1##↑&↑TERMINOLOGY\&
.PARAGRAPH
↑A ∀MODE FUNCTION∩ IS A PROCEDURE WHICH CONTROLS THE BEHAVIOR OF
AN OBJECT OF A GIVEN MODE.
↑THERE ARE CONVERSION, ASSIGNMENT, SELECTION, PRINTING,
AND GENERATING FUNCTIONS FOR A MODE. ↑A ∀BASE MODE∩ IS A
MODE WITH ONLY SYSTEM DEFINED MODE FUNCTIONS.
↑AN ∀EXTENDED MODE∩ IS A MODE WITH A USER DEFINED ∀SHORTNAME∩
AND, POSSIBLY, ONE OR MORE USER DEFINED MODE FUNCTIONS. ↑IT IS
ALWAYS CONSTRUCTED FROM AN ∀UNDERLYING MODE∩, WHICH MAY BE EITHER
A BASE MODE OR SOME OTHER EXTENDED MODE. ↑MODES WHICH DIFFER
EITHER IN STRUCTURE OR BEHAVIOR ARE CONSIDERED TO BE SEPARATE
.INDEX ↑↑DDB\\
DATA TYPES, EACH WITH ITS OWN ∀DATA
DEFINITION BLOCK∩ (↑↑DDB\\). ↑EACH ↑↑DDB\\ HAS A ↑↑UR\\ FIELD
WHICH POINTS TO THE UNDERLYING REPRESENTATION
(MODE) FROM WHICH THE ↑↑DDB\\
WAS CONSTRUCTED. ↑THE ↑↑UR\\ FIELD OF A BASE MODE IS ↑↑NIL\\ SINCE IT
HAS NO UNDERLYING REPRESENTATION.
↑A MODE IS SAID TO ∀COVER∩ A GIVEN MODE IF THE TWO MODES
ARE EQUAL OR IF THE FIRST MODE IS GENERIC, AND ONE OF ITS ALTERNATIVES
IS EQUAL TO THE SECOND GIVEN MODE. ↑↑ANY\\ IS THE
MODE WHOSE SET OF ALTERNATIVES COMPRISES ALL POSSIBLE MODES.
↑HENCE IT WILL COVER ANY GIVEN MODE.
.SKIP 2
.TEST PAGE 8
.INDEX DOUBLE COLON OPERATOR
.INDEX ::
2.13.2##↑&↑DOUBLE#↑COLON#↑OPERATOR\&
.PARAGRAPH
↑THE DOUBLE COLON OPERATOR IS USED TO CREATE UNIQUE
NEW MODES AND, AS AN INFIX OPERATOR, HAS THE
FOLLOWING SYNTAX:
.SKIP 1
.CENTER
<FORM2>::<FORM>
.SKIP 1
↑THE RIGHT OPERAND IS BOUND EVALUATED AND MUST DELIVER A
MODE. ↑THIS MODE WILL BE THE UNDERLYING REPRESENTATION OF
THE MODE WHICH THE DOUBLE COLON OPERATOR WILL CREATE.
↑THE LEFT OPERAND IS BOUND UNEVALUATED, AND TWO CASES
MAY OCCUR. ↑IF THE LEFT OPERAND IS A SYMBOL, THEN
IT IS TAKEN LITERALLY AS THE SHORTNAME, AND THE USER
DEFINED MODE FUNCTIONS ARE CONSIDERED TO BE ABSENT.
↑OTHERWISE, THE LEFT OPERAND IS NOT A SYMBOL AND IS EVALUATED.
↑THE RESULT OF THIS EVALUATION MUST BE EITHER A SYMBOL OR
A LIST OF FORMS. ↑IF A SYMBOL, IT IS A LITERAL SHORTNAME AS ABOVE.
↑OTHERWISE, THE ELEMENTS OF THE LIST ARE INTERPRETED AS FOLLOWS.
↑THE FIRST
ELEMENT MUST EVALUATE TO A SYMBOL FOR THE SHORTNAME. ↑THE
SUCCEEDING ELEMENTS MUST EVALUATE TO ROUTINES WHICH WILL BE TAKEN
TO BE THE USER DEFINED MODE FUNCTIONS IN THE ORDER ∀CONVERSION∩,
∀ASSIGNMENT∩, ∀SELECTION∩, ∀PRINTING∩, AND ∀GENERATION∩.
↑MISSING ROUTINES MAY BE INDICATED BY EITHER ↑↑NIL\\ OR
↑↑NOTHING\\ IN THE APPROPRIATE POSITION. ↑IT SHOULD BE NOTED
THAT THESE ROUTINES WILL BE BOUND BY VALUE TO THE MODE AT
THE TIME OF ITS DEFINITION.
.INDEX ↑↑QL\\
↑THE ↑↑QL\\ ROUTINE USED IN THE EXAMPLES BELOW RETURNS ITS
ARGUMENT LIST UNEVALUATED, AND IS A CONVENIENT MEANS OF
PREPARING A LIST FOR THE DOUBLE COLON OPERATOR.
.SPACING 1
.SKIP 1
↑EXAMPLES:
.SKIP 1
.CENTER
WW<-WALDO::↑↑VECTOR\\(2,↑↑INT\\)
.SKIP 1
.CENTER
FOO<-↑↑QL\\("FOO",CFN,↑↑NIL\\,SFN)::↑↑SEQ\\(↑↑CHAR\\)
.SKIP 1
.CENTER
ABC<-ZORT::[)P(X)=>↑↑INT;BOOL\\(]
.SKIP 1
.CENTER
MAKENAME(N).↑↑TLB<-
.CENTER
ALLOC(MODE BYVAL\\ MAKENAME(N)::↑↑VECTOR\\(N,ZORT))
.SKIP 1
↑EVEN THOUGH SHORTNAMES CURRENTLY BECOME MODE-VALUED
PARSE CONSTANTS, THEY SHOULD BE USED ONLY AS IDENTIFYING TAGS.
↑AN ASSIGNMENT SHOULD BE MADE TO SOME IDENTIFIER WHICH CAN BE THE
SHORTNAME'S SYMBOL, AND THE MODE REFERENCED BY
THAT IDENTIFIER.
.PARAGRAPH
↑THE CONSTRUCTION OF A ↑↑DDB\\ FOR THE NEW MODE BEING
DEFINED PROCEEDS AS FOLLOWS.
↑THE UNDERLYING ↑↑DDB\\ (FROM EVALUATING THE RIGHT OPERAND)
IS COPIED AND THE COPY'S SHORTNAME
FIELD FILLED WITH THE SPECIFIED SHORTNAME; ITS ↑↑UR\\ FIELD,
WITH THE VALUE OF THE UNDERLYING MODE; AND ITS MODE FUNCTION FIELDS,
WITH THE GIVEN FUNCTIONS, IF ANY.
↑THE NEW MODE WILL ALWAYS HAVE SYSTEM DEFINED MODE FUNCTIONS
ASSOCIATED WITH IT WHICH WILL BE USED WHEN THERE IS NO
USER DEFINED ROUTINE FOR A PARTICULAR TASK.
↑REPETITION OF MODE DEFINITIONS WILL BE ALLOWED AND WILL
DELIVER THE PREVIOUSLY DEFINED MODE WITH ITS ↑↑DDB\\ UNCOPIED;
HOWEVER, NEW MODE FUNCTIONS WILL BE SUBSTITUTED FOR THE PREVIOUS
ONES IN THE ↑↑DDB\\, POSSIBLY CHANGING THE BEHAVIOR OF
OBJECTS OF THAT MODE.
.SKIP 1
↑↑
2.13.3###&L&I&F&T, &L&O&W&E&R, ↑&and FLUSHMD Routines\&
\\
.PARAGRAPH
↑IN ORDER FOR USER DEFINED MODE FUNCTIONS TO BE ABLE TO
MANIPULATE OBJECTS OF A GIVEN MODE WITHOUT RECURSIVELY (AND
INCORRECTLY) CALLING THEMSELVES, IT IS NECESSARY TO BE ABLE
.INDEX ↑↑LIFT\\
.INDEX ↑↑LOWER\\
TO ATTRIBUTE DIFFERENT MODES TO THE SAME OBJECT. ↑FOR THIS PURPOSE, THE SYSTEM FUNCTIONS ↑↑LIFT\\ AND
↑↑LOWER\\ ARE PROVIDED. ↑↑LOWER\\(OBJ) RETURNS THE VALUE OF
OBJ ∀BY SHARING∩ WITH THE MODE FOUND
IN THE ↑↑UR\\ FIELD OF ITS FORMER MODE.
↑↑LOWER\\(OBJ,NEWM) WILL ATTEMPT TO LOWER AN OBJECT ONE OR
MORE LEVELS DOWN THE ↑↑UR\\ FIELD CHAIN TO THE SPECIFIED
NEW MODE.
↑↑LIFT\\(OBJ,NEWM) RETURNS THE VALUE
OF OBJ ∀BY SHARING∩ WITH MODE
NEWM IF THERE IS A CHAIN OF ↑↑UR\\ FIELDS WHICH LEAD FROM NEWM
BACK TO THE ORIGINAL MODE OF OBJ.
↑&↑IN ALL CASES THE OBJECT AND NOT A COPY IS RETURNED\&.
↑BOTH THE ↑↑LIFT\\ AND ↑↑LOWER\\ ROUTINES WILL GIVE
A CONTINUABLE ∀type fault∩ IF THEY CANNOT PERFORM THE
REQUESTED OPERATION. ↑↑CONT(\\OBJ) WILL SUBSTITUTE A NEW OBJECT TO
BE LIFTED OR LOWERED.
.PARAGRAPH
↑IT IS ALSO DESIRABLE TO BE ABLE TO REMOVE
MODE DEFINITIONS ONCE THEY ARE NO LONGER NEEDED.
.INDEX ↑↑FLUSHMD\\
↑THE SYSTEM FUNCTION ↑↑FLUSHMD\\(<MODE1>,<MODE2>, ... <MODE↑N>) WILL REMOVE
THE SHORTNAMES OF THE GIVEN MODES AS CONSTANTS, MAKING THEM
POTENTIALLY GARBAGE COLLECTABLE. ↑IN THE CASE OF SELF REFERENCING MODES, IT MAY BE
NECESSARY TO FLUSH UNDERLYING MODES ALSO.
.SKIP 2
2.13.4###↑&↑USER ↑DEFINED ↑MODE ↑FUNCTIONS\&
.PARAGRAPH
↑THE FORMATS FOR USER DEFINED MODE FUNCTIONS
AND THE CONDITIONS FOR CALLING THEM ARE GIVEN BELOW.
↑FOR EACH OF THE MODE FUNCTIONS, A SKELETON FORMAT
WILL BE GIVEN, FOLLOWED BY A CONCRETE EXAMPLE. ↑IN THE SKELETONS,
MYMODE IS SOME USER DEFINED MODE, AMODE IS SOME MODE WHICH
INCLUDES THE CASES WHICH THE USER EXPECTS TO OCCUR, AND RMODE IS
SOME SUITABLE RESULT MODE. ↑FOR THE EXAMPLES BELOW, WE WILL DEFINE THE
MODE DBL TO REPRESENT DOUBLE PRECISION INTEGERS.
.NOFILL
.SKIP 1
DBL<-↑Q↑L("DBL",DCFN, DAFN, DSFN, DPFN, DGFN)::↑↑VECTOR(2, INT)\\
.SKIP 1
.FILL
↑OF COURSE, THE USER MUST PREPARE ROUTINES FOR THE
ARITHMETIC OPERATORS TO MAKE DBL ACTUALLY BEHAVE LIKE
A DOUBLE PRECISION INTEGER.
.PARAGRAPH
↑BEFORE GOING INTO THE DETAILS OF THE ROUTINES FOR EACH OF
THE SPECIFIC FUNCTIONS, IT MIGHT BE HELPFUL TO INDICATE WHEN EACH
OF THESE FUNCTIONS IS INVOKED, EITHER IMPLICITLY OR EXPLICITLY.
↑CONVERSION IS INVOKED IN ASSIGNMENT AND THE BINDING OF ARGUMENTS
AND RESULTS IN PROCEDURE CALLS WHENEVER THE EXPECTED MODE
DOES NOT EQUAL THE MODE OF THE
OBJECT AT HAND. ↑WHEN THIS OCCURS, THE SYSTEM CHECKS TO SEE
IF THE EXPECTED MODE IS GENERIC, AND IF SO, WHETHER IT
COVERS THE ACTUAL MODE AT HAND. ↑ONLY IF THE EXPECTED MODE
DOES NOT COVER THE ACTUAL MODE CAN CONVERSION INVOLVE
CALLING A USER DEFINED ROUTINE.
↑ASSIGNMENT IS INVOKED WHEN A NEW VALUE IS GIVEN TO
AN OBJECT, EITHER EXPLICITLY WITH THE <- OPERATOR OR
IMPLICITLY BY THE ↑↑OF\\ OR ↑↑BYVAL\\ CONSTRUCTION
WITH ↑↑ALLOC\\ OR ↑↑CONST\\. ↑SELECTION IS EXPLICITLY CALLED BY THE CONSTRUCTIONS
↑↑FOO.A\\ AND ↑↑FOO[I]\\ AND IMPLICITLY CALLED BY
BY USING THE ↑↑OF\\ OPTION IN GENERATION.
↑PRINTING IS CALLED BY THE ↑↑PRINT\\ ROUTINE AND BY TYPING
AN ↑↑ALTMODE\\ AFTER AN EXPRESSION TO THE INTERPRETER. ↑GENERATION
IS EXPLICITLY CALLED BY THE ↑↑CONST\\ AND ↑↑ALLOC\\
OPERATORS AND IS IMPLICITLY INVOLVED IN THE CREATION OF
DEFAULT OBJECTS BY DECLARATION AND ARGUMENT BINDING.
.TEST PAGE 8
.SKIP 2
.INDEX CONVERSION ROUTINE
2.13.4.1###↑CONVERSION
.PARAGRAPH
↑FOR CONVERSION, THE SOURCE MODE DETERMINES WHICH
CONVERSION ROUTINE IS TO BE USED. ↑THE USER ROUTINE WILL BE CALLED IN PREFERENCE TO
THE SYSTEM ROUTINE IN ALL CASES EXCEPT WHERE THE EXPECTED
MODE (T IN THE EXAMPLE BELOW) IS GENERIC. ↑IN THAT CASE,
SYSTEM ROUTINES ARE USED TO DETERMINE IF THE GENERIC MODE
COVERS (I.E. HAS AS AN ELEMENT) THE ACTUAL MODE.
.NOFILL
.skip 1
.skip 1
.skip 1
.skip 1
↑SKELETON:
.SKIP 1
MYMODE←\CFN<-↑↑EXPR\\(S:MYMODE,T:↑↑MODE\\;RMODE)
.SKIP 1
↑EXAMPLE:
.SKIP 1
DCFN<-↑↑EXPR\\(S:DBL,T:↑↑MODE;ONEOF(INT,REAL))\\
#####[)T=↑↑INT=>LOWER\\(S)[2];
#######T=↑↑REAL=>FLOAT(LOWER\\(S)[1])*MAX+↑↑FLOAT(LOWER\\(S)[2]);
#######TYPEFAULT(T, DBL);
#####(]
.SKIP 2
.FILL
↑NOTE THAT THE ↑↑LIFT\\ AND ↑↑LOWER\\ ROUTINES HAVE
THE PROPER ARGUMENT STRUCTURE TO BE USED DIRECTLY AS USER
DEFINED CONVERSION FUNCTIONS.
.TEST PAGE 6
.INDEX ASSIGNMENT ROUTINE
.SKIP 2
2.13.4.2###↑ASSIGNMENT
.PARAGRAPH
↑FOR ASSIGNMENT, THE TARGET MODE DETERMINES THE CHOICE OF
THE ASSIGNMENT FUNCTION. ↑FOR THIS FUNCTION, THE USER DEFINED ROUTINE WILL ALWAYS
BE CALLED IN PREFERENCE TO THE SYSTEM ROUTINE.
.NOFILL
.skip 1
↑SKELETON:
.SKIP 1
MYMODE←\AFN<-↑↑EXPR\\(T:MYMODE,S:AMODE;RMODE)
.SKIP 1
↑EXAMPLE:
.SKIP 1
DAFN<-↑↑EXPR\\(T:DBL,S:↑↑ONEOF(INT,REAL\\,DBL);DBL)
#####↑↑GENERIC\\(S)
######[↑↑INT]=>[)LOWER\\(T)[1]<-0;↑↑LOWER\\(T)[2]<-S;T(];
######[↑↑REAL]=>[)LOWER\\(T)[1]<-↑↑FIX\\(S/MAX);
################↑↑LOWER\\(T)[2]<-↑↑FIX\\(S-MAX*↑↑LOWER\\(T)[1]);T(]
######[DBL]=>[)↑↑LOWER\\(T)<-↑↑LOWER\\(S);T(];
#####↑↑END\\;
.SKIP 2
.FILL
.TEST PAGE 20
.INDEX SELECTION ROUTINE
2.13.4.3###↑SELECTION
.PARAGRAPH
↑FOR SELECTION, THE INDEX NORMALLY USED IS EITHER A SYMBOL
OR AN INTEGER. ↑HOWEVER, AN INDEX OF ANY MODE MAY
BE USED PROVIDED THE USER IS WILLING TO ATTACH A
MEANING TO IT. ↑FOR THIS FUNCTION, THE USER DEFINED ROUTINE WILL ALWAYS
BE CALLED IN PREFERENCE TO THE SYSTEM ROUTINE.
.NOFILL
.skip 1
↑SKELETON:
.SKIP 1
MYMODE←\SFN<-↑↑EXPR\\(O:MYMODE,I:AMODE;RMODE)\\
.SKIP 1
↑EXAMPLE:
.SPACING 1
.SKIP 1
DSFN<-↑↑EXPR\\(O:DBL,I:↑↑ONEOF(INT,SYMBOL);INT)\\
#####↑↑GENERIC\\(I)
########[↑↑INT]=>LOWER\\(O)[I];
########[↑↑SYMBOL\\]=>[)I="↑↑HI"=>LOWER\\(O)[1];
####################I="↑↑LOW"=>LOWER\\(O)[2];
####################BADINDEX(I, DBL)(];
#####↑↑END\\;
.FILL
.SKIP 2
.TEST PAGE 20
.INDEX PRINT ROUTINE
2.13.4.4###↑PRINTING
.PARAGRAPH
↑FOR PRINTING, THE FIRST ARGUMENT IS THE OBJECT
TO BE PRINTED, AND THE SECOND ARGUMENT IS THE OUTPUT PORT.
↑FOR THIS FUNCTION, THE USER DEFINED ROUTINE WILL ALWAYS
BE CALLED IN PREFERENCE TO THE SYSTEM ROUTINE.
.NOFILL
.skip 1
↑SKELETON:
.SKIP 1
MYMODE←\PFN<-↑↑EXPR\\(O:MYMODE,P:↑↑PORT\\;RMODE);
.SKIP 1
↑EXAMPLE:
.SKIP 1
DPFN<-↑↑EXPR\\(O:DBL,P:↑↑PORT\\;DBL)
#####[)↑↑PRINT('HI\\=',P);↑↑PRINT(LOWER\\(O)[1],P);
#######↑↑PRINT(' LOW\\=',P);↑↑PRINT(LOWER\\(O)[2],P);O(]
.FILL
.SKIP 2
.TEST PAGE 5
.INDEX GENERATING FUNCTION
2.13.4.5###↑GENERATION
.PARAGRAPH
↑THE GENERATING FUNCTION
ALWAYS TAKES A FIRST ARGUMENT, A ↑↑BOOL\\,
WHICH SPECIFIES STACK GENERATION IF ↑↑FALSE\\; HEAP GENERATION IF ↑↑TRUE\\.
↑THE NEXT ARGUMENT IS AN INDICATOR
OF WHAT TYPE OF SPECIFICATION LIST IS TO FOLLOW, IF ANY.
↑IT MAY BE THE SYMBOL ↑↑ SIZE, OF, BYVAL\\ OR THE EMPTY SYMBOL ↑↑NIL\\.
↑IF THE SECOND ARGUMENT IS NOT ↑↑NIL\\, THEN THE THIRD ARGUMENT IS
A LIST WHOSE ELEMENTS ARE INTEGERS FOR ↑↑SIZE\\,
VALUES OF THE COMPONENTS OF THE OBJECT BEING GENERATED FOR ↑↑OF\\,
OR THE VALUE OF THE OBJECT FOR ↑↑BYVAL\\.
↑FOR THIS FUNCTION, THE USER DEFINED ROUTINE WILL ALWAYS
BE CALLED IN PREFERENCE TO THE SYSTEM ROUTINE.
.NOFILL
.skip 1
↑SKELETON:
.SPACING 1
.SKIP 1
MYMODE←\GFN<-↑↑EXPR\\(B:↑↑BOOL\\,S:↑↑SYMBOL\\,L:↑↑FORM\\;
############↑↑ONEOF\\(MYMODE,↑↑ PTR\\(MYMODE)))
.SKIP 1
↑EXAMPLE:
.SKIP 1
DGFN<-↑↑EXPR\\(B:↑↑BOOL\\,S:↑↑SYMBOL\\,L:↑↑FORM;ONEOF\\(DBL,↑↑PTR\\(DBL)))
#####↑↑BEGIN\\
########B=>↑↑ALLOC(ANY BYVAL\\ DGFN(FALSE,S,L));
########S="↑↑BYVAL"=>LIFT(CONST\\(DBL.↑↑UR BYVAL
##################################EVAL\\(L.↑↑CAR\\)),DBL);
########S="↑↑OF"=>LIFT(CONST\\(DBL.↑↑UR OF EVAL\\ (L.↑↑CAR),
#####################EVAL\\(L.↑↑CDR.CAR\\)),DBL);
########S="↑↑SIZE\\"=>GENERROR(DBL);
########↑↑LIFT(CONST\\(DBL.↑↑UR\\),DBL);
#####↑↑END\\;
.fill
.justify
↑↑
.title
.skip 2
.page
.title 2.14##COMMENTS
2.14##COMMENTS
.paragraph
.index IE
.index /*
.index */
Two operators are built into ECL which permit comments
to appear wherever a form is acceptable. Their definitions
are equivalent to
.skip 1
.indent 5
IE <- EXPR(a:ANY; ANY) (a);
.skip 1
.indent 5
/* <- IE;
.skip 1
.indent 5
*/ <- EXPR(f:FORM UNEVAL, a:ANY; ANY) (a);
.skip 1
*/ is an infix operator; IE and /* are each both infix and
prefix. Some examples of their use follow.
.skip 1
.indent 5
DECL sigma:REAL /* 'Standard Deviation';
.skip 2
.indent 5
switch <- NOT switch IE 'Flip switch';
.skip 2
.indent 5
/* 'Here when all else fails';
.indent 2
help: ...
.skip 2
.indent 5
'Exchange next two elements' */
.indent 8
BEGIN
.indent 11
t <- a[i];
.indent 11
a[i] <- a[i+1];
.indent 11
a[i+1] <- t;
.indent 8
END;
.break
↑↑
.title
.page
.fill
.spacing 1
3.##↑&HOW TO USE ECL\&
.skip 2
.test page 10
3.1##GETTING INTO ECL
.title 3.1##GETTING INTO ECL
.paragraph
Assuming you have logged in to the PDP-10,
you can enter ECL by typing, in response to the monitor's
prompting dot, the command
.skip 1
.indent 8
#.R#ECL
.skip 1
ECL will respond with a title line identifying the current
version of the system and will then type the prompt
symbol, "->". This is ECL's signal that it is expecting input
from the user.
.paragraph
A ∀command∩ may now be typed for evaluation by the ECL
command interpreter. A command can be either:
.skip 1
.left margin 10
.indent -5
(1)##a ∀form∩ followed by an ALTMODE
.indent -10
or
.indent -5
(2)##a ∀form∩ followed by a semicolon (;)
.left margin 0
.skip 1
In case (1), ECL will, after executing the form, print
out the computed result. In case (2), no automatic printout
is given. Note that, while an ALTMODE automatically
terminates the command line on which it is entered,
commands of type (2) must be followed by some line
terminating character, usually CARRIAGE-RETURN.
.paragraph
In either case, after the form is evaluated, the command
interpreter is ready to accept another command, which it
signals by giving the prompt arrow.
For example:
.skip 1
.left margin 10
.break
->#X#<-#4*3+2;
.skip 1
->#Y#<-#X+1$
.break
15
.skip 1
->#Y+1$
.break
16
.left margin 0
.paragraph
.index syntax errors
Syntax errors in input commands are signalled by an error message
on the teletype. Commands containing syntax errors are not evaluated--the
command interpreter simply waits for another
command.
(If a syntax error occurs in the middle of a multi-line
command, it may be necessary to type ←↑Z(CTRL-Z) to terminate
the input of the incorrect command.)
.skip 1
.left margin 10
.test page 10
->#X+1)$
.skip 1
X#+#1#???#)#;
.skip 1
->#(X+1)$
.break
15
.skip 1
->
.left margin 0
.paragraph
The question marks in the error message indicate where the parse error was detected
in the command.
.skip 2
.title
.test page 10
.index top-level environment
3.2##THE TOP-LEVEL ENVIRONMENT
.title 3.2##THE TOP-LEVEL ENVIRONMENT
.paragraph
In ECL, variables may be used at two "levels":
.skip 1
.left margin 10
.indent -5
(1)##local to blocks (i.e., DECLared variables) or as formal parameters
in routines;
.indent -10
and
.indent -5
(2)##at the "top level", outside any block or routine.
.left margin 0
.paragraph
There are two points we want to emphasize with
respect to these two uses. The first concerns the special use
of the assignment operator at the top level. Recall that when
a form such as X<-F(Y) gets evaluated, the
interpreter will normally check that the mode of X is
appropriate before performing the assignment.
However, if this form is given at command level,
the interpreter behaves differently. If X has not been
previously set (by an earlier top-level assignment),
then the mode of X, as well as the value of X, is
determined by F(Y), and this mode cannot be changed.
If X has been previously set, then it has a mode, and
normal ECL assignment occurs. In short, the
initial assignment to a variable at the top level
has the effect of DECLaration, in that it fixes
the mode of the variable.
To illustrate:
.skip 1
.left margin 5
.test page 20
->#X#<-#8*7####IE 'FIXES THE MODE OF TOP-LEVEL X';
.skip 1
->#X#<-#"ABCDE";
.break
TYPE FAULT
.break
.skip 1
1:>RESET########IE 'SEE SECTION 3.5 FOR THE MEANING
.indent 20
OF "1:>"';
.skip 1
->#Y#<-#X+2####IE 'NOW Y IS ALSO AN INT';
.skip 1
->#Y#<-#[)DECL X:STRING#BYVAL#CONST(STRING SIZE 6);
.indent 9
#X#<-#'HAMLET';
.indent 9
#17(]$
.break
17
.skip 1
->#X$
.break
56
.skip 1
->
.left margin 0
.paragraph
The top-level binding of a variable exists independently of any block or
routine; a variable defined at the top-level may be
present for the duration of the user's session in
ECL. Local or formal variables exist concurrently with
the block or routine in which they are defined.
.paragraph
In general, the top-level binding
of a variable X is used if and only if X is neither local
to any dynamically enclosing block, nor a formal parameter
of any dynamically enclosing procedure.
To illustrate:
.skip 1
.left margin 10
->#X#<-#47;
.skip 1
->#P1#<-#EXPR(X:BOOL; INT)#[)P3();#X#=>#-1;#5(]
.skip 1
->#P2#<-#EXPR(;INT)[)X#<-#X+1(];
.skip 1
->#P3#<-#EXPR(;BOOL)[)X#<-#NOT(X)(];
.skip 1
->#P1(FALSE)$
.break
-1
.skip 1
->#P2();
.skip 1
->#X$
.break
48
.skip 1
->
.left margin 0
.title
.test page 10
.skip 2
.title
.test page 10
3.3##CREATING AND EDITING PROGRAMS
.title 3.3##CREATING AND EDITING PROGRAMS
.paragraph
Commands may be issued to the ECL command interpreter
either (1) directly from the teletype, or (2) through a
prepared file of commands.
.paragraph
The editing facilities available to the user entering
commands at the teletype are as follows:
.skip 1
.left margin 10
.indent -5
(1)##RUBOUT will erase a single character; repeated
RUBOUTs can erase characters until (but not including) the
most recent "activation character". (Activation characters include
CARRIAGE-RETURN, LINE-FEED, and ALTMODE.)##The erased character
will be echoed on the teletype, enclosed in back-slashes("←\").
.skip 1
.indent -5
(2)##CTRL-U will erase everything typed in since the
most recent activation character (usually this means
the current line).
.left margin 0
.paragraph
It is recommended that the user prepare on a separate file
any commands longer than one or two lines. The reasons for
this suggestion are that (1) there is no way of editing a
previous line of a command being entered on the teletype
and (2) if a syntax error is detected in a command, the whole
command must be retyped.
.paragraph
Command files can be created and modified in ECL
by calling the PDP-10 Text Editor (TECO) as a
routine. (For a description of TECO, see the PDP-10 Timesharing Handbook, book 8, or
the PDP-10 Reference Handbook, book 4).
To create a new file, call the routine MAKE
with one argument--a SYMBOL--which will be the
name of the file. If no extension is specified with
the file name, the default is ".ECL".
.paragraph
For example,
.left margin 10
.skip 1
->#MAKE("XANADU");
.break
*IX#<-#5;#X#<-#X+1;
.break
$$
.break
*EX$$
.skip 1
->
.left margin 0
.skip 1
Closing a file by EX will return control to ECL.
Closing a file by EG will cause control to return
to ECL, after which the file will be loaded
(i.e. each command in the file will be parsed and evaluated
in turn).
.skip 1
.left margin 10
->#MAKE("HAMLET");
.break
*IZ#<-#14;
.break
$$
.break
*EG$$
.skip 1
->#Z$
.break
14
.skip 1
->
.skip 1
.left margin 0
Files may be edited by calling the routine TECO
with one argument--a SYMBOL--with the same
conventions as MAKE.
.skip 1
.left margin 10
.test page 6
->#TECO("XANADU");
.break
*LIX#<-#X+2;
.break
$EG$$
.break
->#X$
.break
8
.break
->
.skip 2
.left margin 0
.title
.test page 12
.index READ
.index PRINT
3.4##SIMPLE TELETYPE ROUTINES: READ AND PRINT
.title 3.4#SIMPLE TELETYPE ROUTINES: READ AND PRINT
.paragraph
ECL provides a wide variety of input/output facilities; most
of these will be discussed in a later section, though
the two most basic routines will be covered here.
.paragraph
In order to input and evaluate a form to be typed
on the teletype, the user may employ the built-in routine
READ which reads one form terminated by a semicolon. For example:
.test page 15
.skip 1
.left margin 10
->#W#<-#[)DECL#X,Y:INT;
.indent 7
L: X#<-#READ();
.indent 10
(X=0)#=>#Y;
.indent 10
Y#<-#Y+X;
.indent 10
GOTO#L(];
.break
34#####IE 'N0W X IS 34';
.break
X-30###IE 'N0W X IS 4';
.break
2*X####IE 'AND N0W X IS 8';
.break
0;
.skip 1
->#W$
.break
46
.skip 1
->
.left margin 0
.paragraph
In order to output on the teletype the value of a form,
the user may employ the system routine PRINT
with one argument (the value to be printed).
For example,
.skip 1
.left margin 10
.test page 10
->#PRINT(3.142-.718);
.break
2.424
.skip 1
->#[)PRINT("MADAM I"); PRINT(%');
.indent 5
PRINT('M#ADAM')#(];
.break
MADAM I'M ADAM
.skip 2
->
.left margin 0
.paragraph
PRINT returns as its value the argument given
for printing. To
illustrate a use for this, as well as the editing features described
in the preceding section:
.skip 1
.test page 13
.left margin 10
->#MAKE("FIBSEQ");
.break
*IM <- 0;
.break
N <- 1;
.break
FOR K TO 11 DO
.breaK
.indent 3
[) K=2*(K/2) => N; M (] <-
.indent 3
[) PRINT(% ); PRINT(M+N) (];
.break
$EG$$
.break
#1#2#3#5#8#13#21#34#55#89#144
.skip 1
->
.skip 1
.left margin 0
The above program prints the first eleven elements of the Fibonacci
sequence.
.title
.skip 2
.test page 10
3.5##ERRORS AND PROGRAM SUSPENSION
.title 3.5##ERRORS AND PROGRAM SUSPENSION
.skip 1
.index execution errors
3.5.1##↑&Execution Errors\&
.paragraph
Program errors may fall into two categories: syntax
errors, detected by the parser (discussed in section 3.1)
and execution errors detected during evaluation. When
the latter occur, the ECL break package is invoked;
this will print out (1) a descriptive message identifying
the error (see Appendix E for a complete list of
these messages), (2) a line identifying where the error
occurred (unless it occurred at the top-level), and (3) a special prompting signal consisting of an integer followed by ":>".
.paragraph
For example:
.skip 1
.left margin 10
.test page 12
->#X<-'ABCD';
.skip 1
->#S<-48;
.skip 1
->#T<-5;
.skip 1
->#F<-EXPR(S:STRING; CHAR)[)X[T]<-S[1](];
.skip 1
->#F('HAMLET')$
.break
INVALID INDEX
.break
F BROKEN
.break
1:>
.skip 1
.left margin 0
The error occurred because X has only 4 components.
The message "F BROKEN" indicates that F was the
most recent routine entered before the error. The integer 1 preceding ":>"
indicates the ∀break level∩
on which the program has been interrupted. The break level
is the number of breaks pending in the current environment.
When the
":>" occurs, the user may type in commands just as he
would at the top level; the difference is that the
∀environment∩ of the computation--the set of variables and
their bindings--is the one current when the error
occurred. The user in this way can examine (and change)
the values of variables to determine why the error
took place. To continue with the above example:
.skip 1
.left margin 10
.test page 4
1:>#S$
.break
HAMLET
.break
1:>#X$
.break
ABCD
.skip 1
.left margin 0
When the error is correctable, the user may
continue the interrupted computation by calling the built-in routine
CONT. CONT takes one argument: a form
whose value will replace the value which caused the error.
.skip 1
.left margin 10
.test page 6
1:>#CONT(T-1);
.break
H
.skip 1
->#X$
.break
ABCH
.skip 1
.left margin 0
An error may occur which
cannot be corrected. In this case the user may
return to the top level by executing the nofix procedure RESET.
.skip 1
.left margin 10
.test page 13
->#Y<-14;
.skip 1
->#G<-EXPR(;CHAR)(Y[1]);
.skip 1
->#G();
.break
CANT SELECT
.break
G BROKEN
.break
1:>#Y$
.break
14
.break
1:>#RESET;
.skip 1
->
.skip 1
.left margin 0
.skip 1
3.5.2##↑&Setting Breakpoints\&
.paragraph
The ECL break package can be invoked under
circumstances other than error conditions. In
particular, the routine BREAK may be called from
a user routine in order to set a ∀breakpoint∩ at that spot
(a valuable feature in debugging). BREAK takes
one argument. Unless the argument is NOTHING,
it will be printed on the user's teletype when
BREAK is called.
.skip 1
.left margin 10
.test page 18
->#P <- EXPR(X:INT; INT)
.indent 8
BEGIN#X#LT#0 => BREAK("VALUE NEGATIVE");
.indent 16
2*X##END;
.skip 1
->#P(3)+100$
.break
106
.skip 1
->#P(-3)+100$
.nofill
VALUE NEGATIVE BREAK
P BROKEN
1:>#X$
-3
1:>#CONT(X-7);
90
.skip 1
->
.break
.fill
.skip 1
.left margin 0
Note that CONT has been used to exit
from the break package and to return to the interrupted
computation. If an argument is provided to CONT,
it will be the value returned by the call on BREAK
in the program which was suspended.
.skip 2
3.5.3##[↑&User-Defined Trap-Handling Routines\&]
.skip 4
3.5.4##↑&Emergency Measures\&
.paragraph
It is sometimes necessary to terminate execution
of a program prematurely (because of an infinite loop,
for example). In such cases the user should type
CTRL-C twice: this will cause a return to the PDP-10
monitor. Following the monitor's
prompting ".", the REENTER command should be typed.
This will pass control back to the ECL break
package, using which the user can examine or change variables, RESET,
etc.
.paragraph
Manual intervention may also be useful when
unwanted output is produced by an ECL program.
In this case, typing CTRL-O will terminate
the output while allowing the ECL
program to continue its computation. Under some
circumstances, the prompt symbol "->" gets suppressed along with the unwanted output.
If the system has not given the prompt symbol within
a few seconds, type a short command (e.g. 1$) to
obtain it.
.title
.page
↑↑
.spacing 1
.skip 2
.page
.title
4.##↑&BUILT-IN ROUTINES\&
.paragraph
This section describes all of the routines which are built-in
to the ECL system by hand-coding. Those labelled "SUBR"
are permanently embedded in the basic system. Others, labelled
"CEXPR" (for compiled-EXPR), are written in the format
of compiled procedures. Some of these are not loaded with the
basic system, but may be read-in using LOAD (see section 4.5).
The non-resident routines are so indicated before their descriptions,
and file names from which they may be LOADed are given.
.paragraph
For each built-in routine, the following information is given:
.left margin 10
.indent -5
.skip 1
(1)##the top-level name to which the routine is bound.
.skip 1
.indent -5
(2)##the type of routine, i.e. SUBR or CEXPR;
.skip 1
.indent -5
(3)##argument modes and their bind-classes;
.skip 1
.indent -5
(4)##the mode of the result;
.skip 1
.indent -5
(5)##a brief description of the routine;
.skip 1
.indent -5
(6)##parsing properties of the routine name. If it can be used
as a prefix (or nofix) operator, the word PREFIX (or NOFIX) appears
below its description. If it is infix, both the word INFIX
and a priority appear.
(Priorities specify the relative binding strength of operators--highest
being tightest--and run from 1 to 255).
.left margin 0
.skip 2
.title
.test page 10
4.1##ARITHMETIC AND TRIGONOMETRIC ROUTINES
.title 4.1##ARITHMETIC AND TRIGONOMETRIC ROUTINES
.skip 1
.left margin 10
.index +
.indent -10
+#########SUBR(X:ARITH,#Y:ARITH;#ARITH)
.skip 1
The arithmetic sum of X and Y. The result is an
INT if both X and Y are INT; otherwise it is REAL.
.skip 1
INFIX##175
.skip 2
.indent -10
.index -
-#########SUBR(X:ARITH, Y:ONEOF(INT,REAL,NONE); ARITH)
.skip 1
If the argument Y is omitted or has mode NONE the result is the negative of X.
Otherwise the result is X less Y and is an INT unless either X or
Y is REAL.
.skip 1
PREFIX,#INFIX##175
.test page 12
.skip 3
.indent -10
.index *
*#########SUBR(X:ARITH,#Y:ARITH;#ARITH)
.skip 1
The arithmetic product of X and Y.
The result is an INT if both X and Y are
INT; otherwise it is REAL.
.skip 1
INFIX##200
.test page 11
.skip 3
.indent -10
.index /
/#########SUBR(X:ARITH,#Y:ARITH;#ARITH)
.skip 1
X divided by Y. If both arguments are INT,
the result is an INT and is truncated toward zero. Otherwise,
the result is REAL.
.skip 1
INFIX##200
.test page 8
.skip 3
.indent -10
.index SUM
SUM#######SUBR(X:ARITH,#Y:ARITH;#ARITH)
.skip 1
Identical in meaning to +
.test page 7
.skip 3
.indent -10
.index DIFF
DIFF######SUBR(X:ARITH, Y:ONEOF(INT,REAL,NONE); ARITH)
.skip 1
Identical in meaning to -
.test page 7
.skip 3
.indent -10
.index PRODUCT
PRODUCT###SUBR(X:ARITH,#Y:ARITH;#ARITH)
.skip 1
Identical in meaning to *
.test page 7
.skip 3
.indent -10
.index QUOTIENT
QUOTIENT##SUBR(X:ARITH,#Y:ARITH;#ARITH)
.skip 1
Identical in meaning to /
.skip 3
.left margin 0
NOTE: The routines SIN, COS, ATAN, and LN are not
resident in the basic system. To load them, type
LOAD("SYS:TRANS");
.left margin 10
.skip 2
.index SIN
.indent -10
SIN#######CEXPR(X:REAL; REAL)
.skip 1
Trigonometric sine of X. Argument is expressed in radians.
.skip 3
.index COS
.indent -10
COS#######CEXPR(X:REAL;#REAL)
.skip 1
Cosine of X.
.skip 3
.index ATAN
.indent -10
ATAN######CEXPR(X:REAL;#REAL)
.skip 1
Arctangent of X. Result is expressed in radians.
.skip 3
.index LN
.indent -10
LN########CEXPR(X:REAL;#REAL)
.skip 1
Natural logarithm of X.
.skip 3
.indent -10
.index AND
.title
.test page 10
4.2##LOGICAL AND RELATIONAL ROUTINES
.title 4.2##LOGICAL AND RELATIONAL ROUTINES
.skip 2
.indent -10
AND#######SUBR(L:FORM LISTED;#BOOL)
.skip 1
The elements of L are evaluated in turn, and each value
is converted to BOOL. If it is FALSE,
then AND immediately returns
FALSE. If it is TRUE, the next
element is considered. If the list L is exhausted before a FALSE
value is encountered, then AND returns TRUE.
If a value not convertible to BOOL is encountered, a ∀type fault∩
occurs.
.skip 1
INFIX##125
.test page 16
.skip 3
.indent -10
.index OR
OR########SUBR(L:FORM LISTED;#BOOL)
.skip 1
The elements of L are evaluated in turn, and each value
is converted to BOOL. If it is TRUE,
then OR immediately returns
TRUE. If it is FALSE, the next
element is considered. If the list L is exhausted before a TRUE
value is encountered, then OR returns FALSE.
If a value not convertible to BOOL is encountered, a ∀type fault∩
occurs.
.skip 1
INFIX##100
.test page 9
.skip 3
.indent -10
.index NOT
NOT#######SUBR(X:BOOL;#BOOL)
.skip 1
The logical negation of X.
.skip 1
PREFIX
.test page 14
.skip 3
.indent -10
.index =
=#########SUBR(X:ANY,#Y:ANY;#BOOL)
.skip 1
TRUE of primitive modes iff X and Y are
identical values (except that if X is REAL and
Y is INT then TRUE iff CONST(REAL OF Y)#=#X or
similarly for INT X and REAL Y). TRUE of pointers iff X
and Y point to the same object.
TRUE of ∀structs∩ and ∀rows∩ iff componentwise TRUE.
.skip 1
INFIX##150
.test page 7
.skip 3
.indent -10
.index EQUAL
EQUAL#####SUBR(X:ANY,#Y:ANY;#BOOL)
.skip 1
Identical in meaning to =.
.test page 7
.skip 3
.indent -10
.index ←#
←##########SUBR(X:ANY,#Y:ANY;#BOOL)
.skip 1
Defined as NOT(X=Y).
.skip 1
INFIX##150
.test page 9
.skip 3
.indent -10
.index GT
GT########SUBR(X:ARITH,#Y:ARITH;#BOOL)
.skip 1
TRUE iff X is arithmetically greater than Y.
.skip 1
INFIX##150
.test page 9
.skip 3
.indent -10
.index GE
GE########SUBR(X:ARITH,#Y:ARITH;#BOOL)
.skip 1
TRUE iff X is arithmetically greater than or equal to Y.
.skip 1
INFIX##150
.test page 9
.skip 3
.indent -10
.index LE
LE########SUBR(X:ARITH,#Y:ARITH;#BOOL)
.skip 1
TRUE iff X is arithmetically less than or
equal to Y.
.skip 1
INFIX##150
.test page 9
.skip 3
.indent -10
.index LT
LT########SUBR(X:ARITH,#Y:ARITH;#BOOL)
.skip 1
TRUE iff X is arithmetically less than Y.
.skip 1
INFIX##150
.skip 3
.indent -10
.title
.test page 12
4.3##GENERAL ROUTINES
.title 4.3##GENERAL ROUTINES
.test page 7
.indent -10
.index IE
.skip 1
IE########SUBR(A:ANY; ANY)
.skip 1
This permits the inclusion of comments in EL1 programs.
See section 2.14 for details.
.skip 1
PREFIX, INFIX#253
.test page 7
.indent -10
.skip 3
.index /*
/*########SUBR(A:ANY; ANY)
.skip 1
Identical in meaning to IE.
.skip 1
PREFIX, INFIX#253
.test page 7
.indent -10
.skip 3
.index */
*/########SUBR(A:FORM UNEVAL B:ANY; ANY)
.skip 1
Permits insertion of a comment as the
left hand operand. See section 2.14 for details.
.skip 1
INFIX#253
.skip 3
.indent -10
.index <-
<-#or#→E∩###NSUBR(X:ANY,#Y:ANY;#ANY)
.skip 1
Assigns the value of the object Y to the object X.
.skip 1
INFIX##50
.test page 7
.skip 3
.indent -10
.index EVAL
EVAL######SUBR(X:FORM;#ANY)
.skip 1
Evaluates X and returns its value.
.test page 7
.skip 3
.indent -10
.index MD
MD########SUBR(X:ANY;#MODE)
.skip 1
The mode of X#(i.e. its data type).
.test page 10
.skip 3
.indent -10
.index LENGTH
LENGTH####SUBR(X:ANY;#INT)
.skip 1
If X is a ∀struct∩ or a ∀row∩ then the number of components
in X is returned. If X is
a pointer, returns the LENGTH of VAL(X). If X is primitive, returns 1
(except that LENGTH(NOTHING) is 0).
.test page 7
.skip 3
.indent -10
.index VAL
VAL#######SUBR(X:ANY;#ANY)
.skip 1
If X is not a pointer, issues ∀type fault∩. Otherwise returns
the object pointed to by X.
.test page 7
.skip 3
.indent -10
.index QUOTE
QUOTE#####SUBR(X:FORM UNEVAL;#FORM)
.skip 1
The value of QUOTE(f) is f, for any form f in the
language.
.test page 11
.skip 3
.indent -10
.index QL
QL########SUBR(L:FORM LISTED; FORM)
.skip 1
Returns L unevaluated. QL(a,#b,#c) is the list (a#b#c).
.skip 3
.test page 16
.indent -10
.index RECLAIM
RECLAIM###SUBR(N:INT,#M:MODE,#B:BOOL;#INT)
.skip 1
Invokes a garbage collection which attempts to obtain at least
N words to hold objects of mode M. If the desired storage
cannot be obtained, a ∀storage-allocation fault∩
occurs. If successful,
RECLAIM returns the number of free words available for objects
of mode M. B becomes the value of an ECL internal flag which,
when TRUE, inhibits the release of wholly free pages to the
PDP-10 monitor. This feature is used to avoid repeated
garbage collections by preallocating large blocks of storage.
.indent -10
.skip 3
.title
.test page 10
4.4##OPERATOR-DEFINING ROUTINES
.title 4.4##OPERATOR-DEFINING ROUTINES
.skip 2
.indent -10
.index NOFIX
NOFIX#####SUBR(S:SYMBOL;#NONE)
.skip 1
Causes S to be a nofix operator. Example: after
executing NOFIX("LOGOUT"), use of the identifier
LOGOUT will be interpreted ↑&as if\& (before executing NOFIX)
the code had
read LOGOUT().
.test page 7
.skip 3
.indent -10
.index PREFIX
PREFIX####SUBR(S:SYMBOL;#NONE)
.skip 1
Causes S to be a prefix operator.
.test page 10
.skip 3
.indent -10
.index INFIX
INFIX#####SUBR(S:SYMBOL,#N:INT,#F:BOOL;#NONE)
.skip 1
Causes S to be an infix operator with priority N.
(If N is zero, it acts like N=254).
If the flag F is TRUE, then S is right associative;
otherwise, S is left associative.
Example: if ←& is a right associative operator, then x#←y#←z
is treated as x#←(y#←z).
.test page 11
.skip 3
.indent -10
.index FLUSHFIX
FLUSHFIX##SUBR(S:SYMBOL;#NONE)
.skip 1
Causes S to be no longer an operator of any sort,
i.e. flushes the effect of any previous
NOFIX, PREFIX, or INFIX calls with S.
.indent -10
.title
.test page 25
.skip 3
4.5##INPUT/OUTPUT ROUTINES
.title 4.5##INPUT/OUTPUT ROUTINES
.skip 2
.indent -10
.index OPEN
OPEN######SUBR(F:SYMBOL,#D:SYMBOL,#
T:SYMBOL;#PORT)
.skip 1
Opens the file F, in direction D, for I/O of type T.
F must be a file designator of the form:
.indent 10
.skip 1
dev:name.ext[proj,#prog]
.skip 1
Any portion of F may be omitted with the following defaults:
.nofill
.skip 1
.spacing 2
.left margin 12
dev - DSK
name - DEFALT
ext - ECL
proj - user's
prog - user's
.fill
.left margin 10
.spacing 1
.skip 1
D must be either "IN", or "OUT". (If D is
missing, the default is "IN").
.skip 1
T must be either "SYMBOLIC" or "BINARY". (If T is
missing, the default is "SYMBOLIC").
.skip 1
Having opened the file as specified by <F,#D,#T>, OPEN
returns a port which can be used to access this file.
.left margin 10
.test page 8
.skip 3
.indent -10
.index CLOSE
CLOSE#####SUBR(P:PORT;#NONE)
.skip 1
Closes the port P. Subsequent attempts to
do I/O through P will cause an ∀end-of-file fault∩.
.test page 13
.skip 3
.indent -10
.index MAKEPF
MAKEPF####SUBR(X:REF,
D:SYMBOL;#PORT)
.skip 1
Makes a pseudo-file from the STRING or SEQ(INT)
pointed to by X. If X is a PTR(STRING) then the pseudo-file is
open for symbolic I/O; if X points to a SEQ(INT)
then the pseudo-file is open for binary I/O.
D specifies the direction ("IN" or "OUT").
If the direction is OUT, MAKEPF zeroes the
buffer pointed to by X. Subsequent I/O operations using
the port returned by MAKEPF will read
from or write into the pseudo-file.
.left margin 10
.test page 10
.skip 3
NOTE: All I/O routines that take a port
argument may default that argument.
In such cases, the port used is
as follows:
.left margin 15
.skip 1
.indent -5
(a)##the ∀primary∩ input or output
port, if that is non-NIL, otherwise
.skip 1
.indent -5
.skip 1
.indent -5
(b)##the ∀teletype∩ input or output port, which will
never be NIL.
.test page 7
.skip 3
.left margin 10
NOTE: The teletype input and output ports are always open
for ∀symbolic∩ I/O. Attempts to perform binary I/O
on them will cause an ∀I/O fault∩.
.test page 7
.skip 3
.indent -10
.index OUTCHAR
OUTCHAR###SUBR(C:CHAR,#P:PORT;#NONE)
.skip 1
Writes the character C on port P. P must be open
for symbolic output.
.test page 7
.skip 3
.indent -10
.index INCHAR
INCHAR####SUBR(P:PORT;#CHAR)
.skip 1
Reads one CHAR from the port P. P must be open
for symbolic input.
.test page 8
.skip 3
.indent -10
.index OUTWD
OUTWD#####SUBR(N:INT,#P:PORT;#NONE)
.skip 1
Writes the INT N on the port P. P must be open for
binary output.
.test page 7
.skip 3
.indent -10
.index INWD
INWD######SUBR(P:PORT;#INT)
.skip 1
Reads one INT from the port P. P must be open for binary input.
.test page 11
.skip 3
.indent -10
.index PRINT
PRINT#####SUBR(X:ANY,#P:PORT;#ANY)
.skip 1
Prints X onto port P in symbolic format and returns
X as the value of PRINT.
.skip 3
.indent -10
.index PFORM
PFORM#####SUBR(X:FORM,#P:PORT;#FORM)
.skip 1
Prints X on port P in a LISP-like notation.
Returns the form X.
.test page 11
.skip 3
.indent -10
.index LEX
LEX#######SUBR(P:PORT;#ANY)
.skip 1
Reads one lexeme from the port P, and returns:
.left margin 15
.indent -5
.skip 1
(1)##an INT, REAL, STRING, or CHAR, if the lexeme is a constant
of one of these modes,
.indent -5
.skip 1
(2)##a SYMBOL, if the lexeme is an identifier or delimiter (e.g. (],
FIE, or // ),
.indent -5
.skip 1
(3)##a REF which points to a SYMBOL if the lexeme is a SYMBOL constant like "ABC",
.indent -5
.skip 1
(4)##the value NOTHING, if the PORT P is at end-of-file.
.left margin 10
.test page 4
.skip 3
.indent -10
.index READ
READ######SUBR(P:PORT;#ANY)
.skip 1
Reads, parses, and evaluates a command from port P. In
case of syntax error, an error message is typed on the
user's teletype and READ accepts a new command from P.
.test page 13
.skip 3
.indent -10
.index PARSE
PARSE#####SUBR(P:PORT, P2:PORT; DTPR)
.skip 1
Attempts to read and parse a form from port P.
If unsuccessful, returns the dotted pair (NOGO.NIL) after emitting an error message to
port P2 (or to the command output port if P2 is NIL).
If parsing is successful, and the form was terminated with
a semicolon, PARSE returns the dotted pair
(SUCCESS#.#form). If successful and the form
was terminated with an ALT MODE, then PARSE
returns (PSUCCESS.form). When port P reaches end-of-file, PARSE returns
(EOF.NIL).
.test page 11
.skip 3
.indent -10
.index LOAD
LOAD######SUBR(P:PORT;#NONE)
.skip 1
Repeatedly executes the following cycle (until end-of-file is
encountered):
.skip 1
.left margin 15
.indent -5
(1)##call PARSE to read and parse one form from port P,
.skip 1
.indent -5
(2)##call EVAL to evaluate the form if the parse
has been error-free so far.
.left margin 10
.skip 1
PREFIX
.indent -10
.skip 3
.title
.test page 10
4.6##CONVERSION ROUTINES
.title 4.6##CONVERSION ROUTINES
.skip 2
.indent -10
.index BASIC←\STR
BASIC←\STR#SUBR(X:BASIC;#STRING)
.skip 1
Converts X to a STRING.
.test page 7
.skip 3
.indent -10
.index HASH
HASH######SUBR(X:STRING;#SYMBOL)
.skip 1
Converts X to a SYMBOL.
.test page 11
.skip 3
.indent -10
.index CHAR←\INT
CHAR←\INT##SUBR(C:CHAR;#INT)
.skip 1
The INT value corresponding to the CHAR
C in ASCII representation.
.test page 11
.skip 3
.indent -10
.index INT←\CHAR
INT←\CHAR##SUBR(N:INT;#CHAR)
.skip 1
The CHAR value corresponding to the INT N
in ASCII representation.
.skip 3
.title
.indent -10
.test page 16
4.7##DEBUGGING ROUTINES
.title 4.7##DEBUGGING ROUTINES
.skip 2
.test page 15
.indent -10
.index BREAK
BREAK#####SUBR(S:ANY;#ANY)
.skip 1
Suspends execution of the program.
A message
.skip 1
.indent 5
<mess>#BREAK
.skip 1
.indent 5
<fn>#BROKEN
.skip 1
is typed out on the teletype, where <mess> is the value of S
and <fn> is the surrounding function. The state of the
computation is preserved. A prompt character is output to
the teletype and further commands will be accepted.
.test page 9
.skip 3
.indent -10
.index CONT
CONT######SUBR(X:ANY;#NONE)
.skip 1
Causes program execution to resume from the point where a
break occurred, with X as the value of the break.
.test page 8
.skip 3
.indent -10
.index RESET
RESET#####SUBR(#;#NONE)
.skip 1
Flushes all non-global context.
.skip 3
.index RETBRK
.indent -10
RETBRK####SUBR(N:INT;#NONE)
.skip 1
Returns control to the N-th break level. N=0 brings control
to the top level.
.test page 7
.indent -10
.skip 3
.index ASSERT
ASSERT####SUBR(X:FORM UNEVAL; BOOL)
.skip 1
ASSERT is used to make assertions which will be checked if
ASSERT←\FLAG is TRUE. If the Boolean variable ASSERT←\FLAG
is not TRUE, then X is ignored and ASSERT returns TRUE.
Otherwise, X is evaluated to obtain a Boolean value. If
this value is TRUE, then ASSERT returns TRUE. If this value is FALSE,
then an ∀assertion fault∩ occurs.
.test page 7
.indent -10
.skip 3
.index STEP
STEP######SUBR(N:INT, C:ANY; NONE)
.skip 1
Routine to be used at breakpoints to perform (essentially) a
CONT(C) and break again after finishing
the current statement and evaluating N more statements.
For example,
.skip 1
.nofill
#####X#<-#BREAK()+5;
#####PRINT("NEXT")
.skip 1
If the command
.skip 1
#####STEP(0,1);
.skip 1
.fill
is executed at the indicated BREAK,
then X is assigned the value 6 and another BREAK occurs before
the PRINT statement is evaluated.
.test page 11
.skip 3
.indent -10
.index DDT
DDT#######SUBR(#;#NONE)
.skip 1
Jumps into DDT. Note: this is ↑¬\& available
on most versions of the system. To get back from DDT
to ECL, give the DDT command GOBAK#$G.
.skip 3
.title
.test page 10
.test page 20
.indent -10
4.8##ENVIRONMENTAL ROUTINES
.title 4.8##ENVIRONMENTAL ROUTINES
.skip 2
.index STACKS
.indent -10
STACKS####SUBR(K:INT, V:INT, N:INT; NONE)
.skip 1
Replaces current stack triple with a new triple using
K→v∩1024 words of core. V and N are percentages to be allotted to
the value and name stacks, respectively. (The remainder is
used for the control stack.)##If the arguments V or N are omitted,
the remaining amount of space is divided evenly.
.skip 1
CAUTION: STACKS finishes with an automatic RESET, i.e. the environment
of its call is lost.
.skip 2
.indent -10
.index RTIME
RTIME#####SUBR( ;INT)
.skip 1
Returns the accumulated running time, in milliseconds, of the
current job.
.skip 1
Note: the current implementation is quantized to 16 millisecond
units.
.test page 5
.indent -10
.skip 3
.index GCTIME
GCTIME####SUBR(;INT)
.skip 1
Returns the accumulated garbage collection time in milliseconds
since the last .R ECL.
.skip 2
.test page 15
.indent -10
.index FLUSH
FLUSH#####SUBR(L:FORM LISTED; NONE)
.skip 1
L must be a list of variable names. For each variable name
on L, the top-level binding of that variable is flushed
(i.e. set to NOTHING).
.test page 10
.skip 2
.title
.test page 10
.indent -10
4.9##PRIMITIVES FOR NON-DETERMINISTIC ALGORITHMS
.title 4.9##PRIMITIVES FOR NON-DETERMINISTIC ALGORITHMS
.skip 1
.left margin 0
The NDA routines are not resident in the
basic system. They may be loaded by typing LOAD("SYS:NDA").
.left margin 10
.skip 1
.indent -10
.index TAG
TAG#######CEXPR(X:FORM UNEVAL;#SYMBOL)
.skip 1
A call on TAG labels a point in the program's dynamic execution sequence.
A subsequent "branch" to a tag-point restores the program's stack environment to
that extant at the time the tag-point was created (possibly modified by
normal assignments, after the tag-point's creation, to variables local to
blocks dynamically enclosing the tag-point). The argument
to TAG yields the symbolic "tag-value" thereafter associated
with the tag-point as follows:##if
VAL(x) has the mode ATOM then a SYMBOL s such that VAL(s)=VAL(x) is
the tag-value; otherwise if s=EVAL(x) has the mode SYMBOL then s
is the tag-value; otherwise a ∀type fault∩ occurs. The tag-value s may subsequently be used in referring to the
tag-point thus created, and is the value returned by the creating
call of TAG.
A block or procedure that contains a tag-point is said to be
"tagged" and an exit from such a block or procedure is a "tagged exit".
(Note that this containment need not be immediate--i.e.
if a block B is tagged and B' contains B, then B' is also tagged).
.skip 1
PREFIX
.skip 2
.indent -10
.index NASSIGN
NASSIGN###CEXPR(X1:ANY,#X2:ANY;#ANY)
.skip 1
NASSIGN, like #<-, changes the value of an existing object. However it also records
the location of the object X1 and its old value for use by FAIL.
When not dynamically preceded by an existing tag-point,
an NASSIGNment is equivalent to the corresponding ASSIGNment. (Note however that NASSIGN may not be used to introduce
and implicitly declare new top level objects.)
.skip 2
.test page 6
.indent -10
.index <=
<=########CEXPR(X1:ANY,#X2:ANY;#ANY)
.skip 1
Identical in meaning to NASSIGN.
.skip 1
INFIX##50
.skip 2
.indent -10
.index FAIL
FAIL######CEXPR(X:FORM UNEVAL,#I:INT,#F:FORM;#BOOL)
.skip 1
X and I specify at most one
tag-point. I must be a nonnegative integer.
If X is NIL or defaulted, then the Ith most recent tag-point is sought;
otherwise a tag-value s is obtained from X as described under TAG (see above)
and the Ith most recent tag-point with tag-value s is sought
('0th most recent' means 'most recent').
If no existing tag-point satisfies these conditions, FAIL returns TRUE and takes no
other action. Having isolated a tag-point, FAIL partially restores the stack environment of
that tag-point as follows:
.skip 1
.indent 5
(1) Portions of the current stack environment
added since the tag-point are deleted.
.skip 1
.indent 5
(2) The effect of the most recent tagged exit since the tag-point
is reversed by restoring the portion of the stack environment of
the tag-point deleted when the
exit occurred. Note that a tagged block or procedure may have been exited by
calling RETFROM or doing a non-local GOTO as well as in the normal way.
.skip 1
.indent 5
(3) The effect of the most recent NASSIGN call since the tag-point is reversed by restoring
the old value of the left hand side.
.skip 1
.indent 5
(4) Parts (2) and (3) are repeated for each tagged exit or NASSIGNment since the tag-point
(most recent ones first). The resulting stack environment is consistent with control being inside
the original call on TAG prior to the creation of the tag-point
and following the processing of TAG's argument.
.skip 1
.indent 5
(5) The form F is evaluated in this environment.
The effect of a call of GOTO or RETFROM during this
evaluation is that of such a call occurring
within TAG. If the evaluation does not cause an exit from TAG,
control passes to TAG which continues as usual. Note that this
will result in the recreation of the tag-point
and the return of the tag-value to TAG's calling environment.
.skip 2
.indent -10
.index UNTAG
UNTAG#####CEXPR(X:FORM UNEVAL,#I:INT;#BOOL)
.skip 1
UNTAG, like FAIL, isolates a tag-point (having no effect apart from a return
of FALSE if no tag-point is found). It then modifies the current environment, with respect
to subsequent FAIL's, to be as though (i) that and all succeeding
tag-points had never been created and (ii) all
calls on NASSIGN since that tag-point's creation had actually been calls on ASSIGN.
It then returns TRUE.
.skip 3
.title
.test page 10
.indent -10
4.10##MODE ROUTINES
.title 4.10##MODE ROUTINES
.skip 3
.indent -10
.index ::
::########SUBR(X:FORM UNEVAL, Y:MODE; MODE)
.skip 1
X must evaluate either to a symbol to be used
as a shortname or to a list of user-defined
mode functions. Details of the usage of this
operator are given in section 2.13.2.
.skip 1
INFIX 175
.test page 7
.indent -10
.skip 3
.index LOWER
LOWER#####SUBR(M:ANY, NEWM:MODE; ANY)
.skip 1
Returns the object M of mode NEWM if NEWM
is explicitly provided, otherwise of the mode found
in the UR field of the DDB of the mode of M. This is further
described in section 2.13.3.
.test page 7
.indent -10
.skip 3
.index LIFT
LIFT######SUBR(M:ANY, NEWM:MODE; ANY)
.skip 1
Returns object M with mode NEWM. This is further described in section
2.13.3.
.test page 7
.indent -10
.skip 3
.index FLUSHMD
FLUSHMD###SUBR(L: FORM LISTED; NONE)
.skip 1
Takes a list of forms evaluating to modes and flushes them
by removing permanent references to them so that the
storage they use will be reclaimable.
Section 2.13.3 also discusses
the use of this function.
.test page 7
.indent -10
.skip 3
.index COVERS
COVERS####SUBR(A:MODE, B:MODE; BOOL)
.skip 1
Returns TRUE if mode A covers mode B. Mode A is said to cover
mode B if and only if:
.skip 1
.left margin 15
.indent -5
(1)##A=B, or
.skip 1
.indent -5
(2)##A=ANY, or
.skip 1
.indent -5
(3)##A is of class ∀generic∩ and B is one of its alternatives, or
.skip 1
.indent -5
(4)##A is of class ∀generic∩ and B is of class ∀generic∩ and all
of B's alternatives are alternatives of A.
.left margin 0
↑↑
.title
.page
.spacing 1
5.##↑&SYSTEM ROUTINES\&
.paragraph
System routines are procedures (generally
packages of procedures) which have been defined in EL1
and which are sufficiently useful to merit inclusion
in this manual. Each subsection below describes a
package found in a single file. LOADing that file makes the procedures
available (along with a number of auxiliary procedures.)
.skip 2
.title
5.1##GENERAL ROUTINES
.title 5.1##GENERAL ROUTINES
.paragraph
Certain routine names are reserved for use by other packages
described in this section. For the most part these names
correspond to standard routines in other programming
languages (e.g. LISP 1.5, ALGOL 68).
.paragraph
The entire collection of general routines may be brought in as the LIB package.
Since these routines are also defined in various special
packages, the user who wishes to rebind their names
to other definitions must be extremely careful.
.skip 2
.left margin 5
.left margin 5
.nofill
File Name: LIB.ECL
.skip 1
ATOMP
CAAR
CADDR
CADR
CAR
CDAR
CDDR
CDR
CONS
EQUALP
LIST
MEMBER
NULL
.fill
.left margin 0
.title
.test page 10
.skip 2
5.2##DEBUGGING ROUTINES
.title 5.2##DEBUGGING ROUTINES
.skip 1
.left margin 12
File Name: TRACE.ECL
.skip 1
.indent -12
.index TRACE
TRACE#######EXPR(F:FORM UNEVAL, I:INT; NONE)
.skip 1
F must be an identifier--the name of a procedure to be traced. That procedure
is modified to print out trace information as it is entered. A call to BREAK
may optionally be included in the modified entry sequence.
.
Trace information always includes the procedure name;
additional information is controlled by the value of I:
.skip 1
.nofill
I=0 print name only.
.break
I=1 print name, argument values.
.break
I=2 print name, call BREAK.
.break
I=3 print name, argument values, and
call BREAK.
.fill
.skip 1
When a BREAK created by TRACE has been reached, the user may
choose to proceed through several successive entries of the
function before breaking there again. He may do so by executing
CONT(n), where n is the number of BREAKs to be skipped.
TRACE information will still be printed on routine entry. Other
traced routines are not affected. CONT() is equivalent to CONT(0)
at a TRACE-generated break.
.skip 2
.indent -12
.index UNTRACE
UNTRACE#####EXPR(F:FORM UNEVAL; NONE)
.skip 1
F must be an identifier--the name of a procedure which is no
longer to be traced. The original definition of F is
restored.
.skip 2
.left margin 12
NOTE: The top-level variable TRACEDFNS is
a list of the names of all TRACEd procedures. TRACE
adds names and UNTRACE removes names from this list.
.skip 2
.left margin 12
.indent -12
.index UNTRACEALL
UNTRACEALL##EXPR( ; NONE)
.skip 1
Calls UNTRACE on all elements of the list TRACEDFNS.
.skip 2
File Name: BT.ECL
.skip 1
.indent -12
.test page 10
.index BT
BT##########EXPR(F:INT BYVAL, VRBSE:BOOL; NONE)
.skip 1
Prints on COPORT a ∀back trace∩ of all procedure calls and blocks
which have been entered but not yet exited. Print order is
most recent context first. F is the depth to which statements
will be printed as follows: F=0--no text of statements;
F>0--F innermost statements will be printed out in full
(using UNPARSE if it is loaded). If VRBSE=FALSE (or is defaulted)
then in printing nested statements, '***' will be printed in
place of an inner statement when printing an outer statement.
NOTE: In the event of errors (e.g. STACK OVERFLOW), ↑&do not use
RESET or RETBRK\&. Instead call RECOVER (defined within BT) to
return to environment just prior to the call of BT.
.left margin 0
.skip 2
.title
.title
.test page 10
5.3##UNPARSING ROUTINES
.title 5.3##UNPARSING ROUTINES
.left margin 12
.skip 1
File Name: UP.ECL
.skip 1
.left margin 12
.indent -12
.index UNPARSE
UNPARSE#####EXPR(F:FORM,P:PORT;NONE)
.skip 1
Converts the internal representation of the form F into a character
string and outputs that string to the port P. The character
string includes sufficient spaces, tabs, and line-feeds to make
the rendering fairly readable. In particular, BEGIN-END
blocks are indented, unneeded parentheses in sequences of binary
operators are suppressed, and small expressions are
kept on a single line where possible.
.skip 2
.indent -12
.index UNPARSF
UNPARSF#####EXPR(FI:SYMBOL, FO:SYMBOL; NONE)
.skip 1
Parses successive commands from the input file FI and unparses the resulting
form onto the output file FO. FI and FO are expected to be either NIL or
file designators (see description of OPEN, section 5.5).
If either is NIL, "TTY:" will be assumed.
.left margin 12
.skip 1
Rebinding the top-level variable LSZ←\UP will change the right
margin used by the unparsing routines. LSZ←\UP is initially
set to 65.
.title
.page
.indent -12
5.4##INPUT/OUTPUT OF DATA STRUCTURES
.title 5.4##INPUT/OUTPUT OF DATA STRUCTURES
.skip 1
File Name: DUMP.ECL
.skip 1
.indent -12
DUMP########EXPR(S:SYMBOL,#L:FORM LISTED;#NONE)
.skip 1
Opens a file designated by the value of S for output, then
dumps a representation of each of the values of the names
supplied in the list L. The file produced, although not
prettily formatted, is symbolic, and the objects dumped
can be restored by LOADing the file.
All objects referenced by one or more of
the primary objects will also be dumped. The
representation is structure preserving: distinct pointers to the same object
will be recognized as equal so that sharing can be
restored when the file is LOADed. This rule applies throughout
the list of values being dumped. The file is closed after
the last item has been output.
.left margin 0
↑↑
↑↑
.spacing 1
.title
.page
.left margin 0
.skip 2
.title APPENDIX C: BUILT-IN MODES
APPENDIX C:##↑&BUILT-IN MODES\&
.skip 2
C.1##↑&Commonly Used Modes\&
.skip 2
.left margin 3
.index ARITH
ARITH###<-##ARITH::ONEOF(INT,#REAL);
.skip 1
.nofill
.index BASIC
BASIC###<-##BASIC::ONEOF(NONE, BOOL,#CHAR,#INT, REAL,
"MODE",#"SYMBOL",#"STRING");
.skip 1
.index DTPR
DTPR####<-##DTPR::STRUCT(CAR:"FORM",
.indent 25
CDR:"FORM");
.skip 1
.index FORM
FORM####<-##FORM::PTR(INT,#REAL,#REF,
"DDB",#"ATOM",#"DTPR");
.fill
.skip 1
.index mode
MODE####<-##MODE::PTR("DDB");
.skip 1
.index ROUTINE
ROUTINE#<-##ROUTINE::PTR("DTPR", "CEXPR", "SUBR");
.skip 1
.index STRING
STRING##<-##STRING::SEQ(CHAR);
.skip 1
.index SYMBOL
SYMBOL##<-##SYMBOL::PTR("ATOM");
.skip 2
.left margin 0
C.2##↑&Other Modes\&
.skip 1
.left margin 3
.index ATOM
ATOM#<-#ATOM::STRUCT(TLB:REF,
.indent 21
SBLK:PTR("SBLOCK"),
.indent 21
LINK:"HWD");
.skip 1
.index CEXPR
CEXPR#<-#CEXPR::STRUCT(BODY:SEQ(INT),
.indent 23
FORMALS:SEQ("FDS"),
.indent 23
RETYPE:"FORM",
.indent 23
DATAB:SEQ(REF));
.skip 1
.left margin 3
.index FDS
FDS#<-#FDS::STRUCT(TYPE:"FORM",
.indent 19
SYM:"SYMBOL");
.skip 1
.index HWD
HWD#<-#HWD::VECTOR(18,BOOL);
.skip 1
.index SBLOCK
SBLOCK#<-#SBLOCK::STRUCT(SINFO:"HWD",
.indent 25
PLIST:"FORM",
.indent 25
SPARE:"FORM",
.indent 25
CONSTF:"FORM");
.skip 1
.index SUBR
SUBR#<-#SUBR::STRUCT(BODY:SEQ(INT),
.indent 21
PRMD:"MODE",
.indent 21
RETYPE:"MODE",
.indent 21
FORMALS:SEQ("FDS"));
.NOFILL
.NOJUSTIFY
↑↑
.skip 1
.blank 1
PDESC#<-#PDESC::STRUCT(FORMALS:SEQ("FDS"),
#######################RETYPE:"FORM");
.blank 1
STRTE#<-#STRTE::STRUCT(JUNK:"HWD",
#######################SBMD:"MODE",
#######################RLPT:INT);
.blank 1
.test page 30
.index DDB
DDB#<-#DDB::STRUCT(HFLG:BOOL,
###################SFLG:BOOL,
###################PROCFLG:BOOL,
###################PROCD:PTR("PDESC"),
###################CNAME:"SYMBOL",
###################UR:MODE,
###################SFN:"HWD",
###################AFN:"HWD",
###################EPFLG:BOOL,
###################WDFLG:BOOL,
###################LRFLG:BOOL,
###################FINFLG:BOOL,
###################SAFLG:BOOL,
###################CLASS:"SYMBOL",
###################GENFN:"HWD",
###################TRFN:"HWD",
###################SNAME:"SYMBOL",
###################BYPT:"HWD",
###################SZ:"HWD",
###################D0:PTR(SEQ(INT)),
###################STRTB:PTR(SEQ("STRTE")),
###################D:REF,
###################BNDFLG:BOOL,
###################CRCFLG:BOOL,
###################CYCFLG:BOOL,
###################UCFN:REF,
###################UAFN:REF,
###################USFN:REF,
###################UPFN:REF,
###################UGFN:REF);
.BLANK 1
\\
.title
.page
.left margin 0
.skip 2
↑↑APPENDIX D:##↑&RESERVED IDENTIFIERS\&
.skip 1
.tab stops 10, 25, 40
.nofill
ALLOC FALSE ROUTINE
ARITH FOR SEQ
ATOM FORM SHARED
BASIC FROM SIZE
BEGIN GENERIC STRING
BOOL INT STRUCT
BY LIKE SYMBOL
BYVAL LISTED THUNK
CHAR LTHUNK TILL
CONST MODE TO
DDB NIL TRUE
DECL OF UNEVAL
DO PORT VECTOR
DTPR PROC WHILE
END REAL =>
EXPR REF .
.justify
.fill
.skip 2
.page
.fill
APPENDIX E:##↑&ECL ERROR MESSAGES\&
.title APPENDIX E: ECL ERROR MESSAGES
.skip 2
Parse Error Messages
.paragraph
These are described in section 3.1
.skip 2
.index execution error messages
Execution Error Messages
.paragraph
Currently these errors are non-recoverable. All of
them except ILL MEM REF leave you at break level at the point
at which the error occured.
.skip 3
.left margin 3
UNDEFINED PROC: <procedure form>
.paragraph
A form being applied as a procedure does not yield a
procedure value. The offending form is printed.
.skip 3
TYPE FAULT
.paragraph
The expected mode of a value and the mode of the actual value provided
are incompatible and no conversion is possible.
∀Type faults∩ often occur during procedure calls, either while
actual arguments are being bound to the corresponding formal
arguments, or when the result mode is checked against the
procedure's formal result type.
Another common cause of ∀type faults∩ is the attempt to assign
an object a value of incompatible mode, as in number#<-#FALSE,
when number already has mode INT.
.skip 3
INVALID INDEX
.paragraph
An attempt has been made to select a non-existent component from
an object, such as x[11] when x has only 10 components.
.skip 3
CANT SELECT
.paragraph
An attempt has been made to select from an object with no
components, such as 5[1].
.skip 3
FILE NOT AVAILABLE
.paragraph
An attempt has been made to perform an OPEN or some other
input-output operation upon a file which is non-existent or upon
a device (such as a tape drive) which is not available.
A file can not be open for output on two ports at the
same time. The port on which it is already
open must be closed before it can be reopened.
Note that RECLAIM automatically closes inaccessible ports.
.test page 7
.skip 3
ILL MEM REF
.paragraph
Technically, this is caused by an attempt to access an address in memory which
is not legal for the user's job. This error is often in the ECL system and
not in the user's program. Fill out an ECL Trouble Report and provide
a minimal protocol which causes the error to occur. (A minimal
protocol is a step by step set of instructions to follow starting
from .R ECL which involves as few ECL
commands as possible.)
.skip 3
CANT GEN OBJ
.paragraph
An attempt has been made to CONST or ALLOC an object of a mode
whose components are not all defined.
.skip 3
UNDEFINED MODE
.paragraph
An attempt has been made to create an object of a mode which has
not been defined. For example,
.skip 1
##########BEGIN
.break
#############DECL#M:MODE;
.break
#############DECL#N:M;
.break
###############.
.break
###############.
.break
###############.
.break
##########END
.skip 3
.test page 8
.nofill
<NAME> STACK OVERFLOW
<VALUE> STACK OVERFLOW
<CONTROL> STACK OVERFLOW
.fill
.paragraph
One of the evaluator's stacks has overflowed. The first
occurrence of this message is a warning only. Part of
each stack is held in reserve until the first overflow
occurs. Then the stacks are automatically extended, so
the user may resume computation by typing CONT(). When
an extended stack overflows, the message CONTEXT LOST
appears along with the regular overflow message. At this
point the computation cannot be CONTinued.
The STACKS routine(section 4.8) may be used to reapportion
or enlarge the stacks. Both STACKS and RESET
leave the stacks in 'unextended' state.
.left margin 0
.title
.page
.title APPENDIX F: MODE COMPATIBILITY FOR CONVERSION
APPENDIX F:##↑&MODE COMPATIBILITY FOR CONVERSION\&
.paragraph
The following expected mode/actual value pairs will
(in the absence of user defined conversion functions)
invoke automatic data type conversion:
.skip 1
.tab stops 10, 33
↑&Expected Mode\& ↑&Actual Value\&
.skip 1
NONE any value
.skip 1
INT a REAL
.skip 1
REAL an INT
.skip 1
PTR(..., m, ...) any pointer to an m
.skip 1
REF any pointer
.skip 1
.title
.skip 2
.page
.indent 15
↑&INDEX\&
.skip 3
.left margin 6
.right margin 90
.print index
##↑↑
.title
.nonumber
.page
.spacing 1
.nofill
.paper size 47,96
.tab stops 14, 19, 56
.figure 4
<command> ::= <form> $
↓|∩ <form> ;
↓|∩ ;
↓|∩ $
.skip 1
<form> ::= <for←\id> <from> <by> <to> <test> DO <form>
↓|∩ <form2> <infnt> <form>
↓|∩ EXPR (<exprl> ; <form>)<spec>
↓|∩ <form2>
.skip 1
<form2> ::= PREFIXOP <form2>
↓|∩ <form3>
.skip 1
<form3> ::= <form3>()
↓|∩ <form3>(<fmpls>)
↓|∩ <form9>
.skip 1
<form9> ::= PROC(<proclist> ; <form>)
↓|∩ CONSTANT
↓|∩ <spec>
↓|∩ ID
↓|∩ <form3> . ID
↓|∩ <form3>[<fmpls>]
↓|∩ <regionspec>(<form> <init>)
↓|∩ STRUCT(<structlist>)
.skip 1
<spec> ::= GENERIC(<fmstr>)<genbody> <sem> END
↓|∩ <begin> <blockbody> <sem> <end>
↓|∩ (<form>)
.skip 1
<fmstr> ::= →e∩
↓|∩ <fmpls>
.skip 1
<fmpls> ::= <form>
↓|∩ <fmpls> , <form>
.skip 1
.test page 4
<for←\id> ::= →e∩
↓|∩ FOR ID
.skip 1
<from> ::= →e∩
↓|∩ FROM <form>
.skip 1
<by> ::= →e∩
↓|∩ BY <form>
.skip 1
<to> ::= →e∩
↓|∩ TO <form>
.skip 1
<test> ::= →e∩
↓|∩ WHILE <form>
↓|∩ TILL <form>
.skip 1
<infnt> ::= INFIXOP
.skip 1
<exprl> ::= →e∩
↓|∩ <exprl2>
.skip 1
<exprl2> ::= ID : <form> <bindclass>
↓|∩ <exprl2> , ID : <form> <bindclass>
.skip 1
<proclist> ::= →e∩
↓|∩ <proclist1>
.skip 1
<proclist1> ::= <form> <bindclass>
↓|∩ <proclist1> , <form> <bindclass>
.skip 1
<bindclass> ::= →e∩
↓|∩ LISTED
↓|∩ UNEVAL
↓|∩ BYVAL
↓|∩ SHARED
↓|∩ LIKE
.skip 1
<regionspec> ::= CONST
↓|∩ ALLOC
.skip 1
.test page 6
<init> ::= →e∩
↓|∩ OF <fmpls>
↓|∩ SIZE <fmpls>
↓|∩ BYVAL <form>
.skip 1
<structlist> ::= ID : <form>
↓|∩ <structlist> , ID : <form>
.skip 1
<genbody> ::= <genstat>
↓|∩ <genbody> ; <genstat>
.skip 1
<genstat> ::= [<fmstr>] => <form>
↓|∩ [<fmstr>] <form> => <form>
↓|∩ <form> => <form>
.skip 1
<sem> ::= →e∩
↓|∩ ;
.skip 1
<begin> ::= BEGIN
↓|∩ [)
.skip 1
<end> ::= END
↓|∩ (]
.skip 1
<blockbody> ::= <declstar> <stat>
↓|∩ <blockbody> ; <stat>
.skip 1
<declstar> ::= →e∩
↓|∩ <declstar> DECL <idpls> : <form> <initd> ;
.skip 1
<idpls> ::= ID
↓|∩ <idpls> , ID
.skip 1
<initd> ::= →e∩
↓|∩ BYVAL <form>
↓|∩ SHARED <form>
↓|∩ LIKE <form>
.skip 1
<stat> ::= <form>
↓|∩ <form> => <form>
↓|∩ ID : <stat>
.paper size 58,60
.spacing 1
.tab stops
.fill
↑↑
.title
.nonumber
.page
.spacing 1
.nofill
.paper size 47,99
.tab stops 14, 19, 56
.figure 4
<command> ::= <form> $ @ <form>
↓|∩ <form> ; @ <form>
↓|∩ ; @ NIL
↓|∩ $ @ NIL
.skip 1
<form> ::= <for←\id> <from> <by> <to> <test> @ (FOR)+<for←\id>+<from>+<by>+
###DO <form> ###<to>+<test>+(DO <form>)
↓|∩ <form2> <infnt> <form> @ (<infnt> <form2> <form>)
↓|∩ EXPR (<exprl> ; <form>)<spec> @ (EXPR <exprl> <form> <spec>)
↓|∩ <form2> @ <form2>
.skip 1
<form2> ::= PREFIXOP <form2> @ (<prefixop> <form2>)
↓|∩ <form3> @ <form3>
.skip 1
<form3> ::= <form3>() @ (<form3>)
↓|∩ <form3>(<fmpls>) @ <form3> ¬←&∩ <fmpls>
↓|∩ <form9> @ <form9>
.skip 1
<form9> ::= PROC(<proclist> ; <form>) @ (PROC <proclist> <form>)
↓|∩ CONSTANT @ <constant>
↓|∩ <spec> @ <spec>
↓|∩ ID @ <id>
↓|∩ <form3> . ID @ (. <form3> <id>)
↓|∩ <form3>[<fmpls>] @ ([ ([ ... ([ <form3> <fmpls[1]>)
###... <fmpls[n-1]>) <fmpls[n]>)
↓|∩ <regionspec>(<form> <init>) @ <regionspec> ¬←&∩ <form> ¬←&∩ <init>
↓|∩ STRUCT(<structlist>) @ STRUCT ¬←&∩ <structlist>
.skip 1
<spec> ::= GENERIC(<fmstr>)<genbody> <sem> END @ (GENERIC <fmstr> <genbody>)
↓|∩ <begin> <blockbody> <sem> <end> @ BEGIN ¬←&∩ <blockbody>
↓|∩ (<form>) @ <form>
.skip 1
<fmstr> ::= →e∩ @ NIL
↓|∩ <fmpls> @ <fmpls>
.skip 1
<fmpls> ::= <form> @ (<form>)
↓|∩ <fmpls> , <form> @ <fmpls> →E∩¬←&∩ <form>
.skip 1
.test page 4
<for←\id> ::= →e∩ @ NIL
↓|∩ FOR ID @ (FOR <id>)
.skip 1
<from> ::= →e∩ @ NIL
↓|∩ FROM <form> @ (FROM <form>)
.skip 1
<by> ::= →e∩ @ NIL
↓|∩ BY <form> @ (BY <form>)
.skip 1
<to> ::= →e∩ @ NIL
↓|∩ TO <form> @ (TO <form>)
.skip 1
<test> ::= →e∩ @ NIL
↓|∩ WHILE <form> @ (WHILE <form>)
↓|∩ TILL <form> @ (TILL <form>)
.skip 1
<infnt> ::= INFIXOP @ <infixop>
.skip 1
<exprl> ::= →e∩ @ NIL
↓|∩ <exprl2> @ <exprl2>
.skip 1
<exprl2> ::= ID : <form> <bindclass> @ ((<id> <form> <bindclass>))
↓|∩ <exprl2> , ID : <form> <bindclass> @ <exprl2> →E∩¬←&∩ (<id> <form> <bindclass>)
.skip 1
<proclist> ::= →e∩ @ NIL
↓|∩ <proclist1> @ <proclist1>
.skip 1
<proclist1> ::= <form> <bindclass> @ ((<form> <bindclass>))
↓|∩ <proclist1> , <form> <bindclass> @ <proclist1> →E∩¬←&∩ (<form> <bindclass>)
.skip 1
<bindclass> ::= →e∩ @ LIKE
↓|∩ LISTED @ LISTED
↓|∩ UNEVAL @ UNEVAL
↓|∩ BYVAL @ BYVAL
↓|∩ SHARED @ SHARED
↓|∩ LIKE @ LIKE
.skip 1
<regionspec> ::= CONST @ CONST
↓|∩ ALLOC @ ALLOC
.skip 1
.test page 6
<init> ::= →e∩ @ NIL
↓|∩ OF <fmpls> @ OF ¬←&∩ <fmpls>
↓|∩ SIZE <fmpls> @ SIZE ¬←&∩ <fmpls>
↓|∩ BYVAL <form> @ (BYVAL <form>)
.skip 1
<structlist> ::= ID : <form> @ ((<id> <form>))
↓|∩ <structlist> , ID : <form> @ <structlist> →E∩¬←&∩ (<id> <form>)
.skip 1
<genbody> ::= <genstat> @ (<genstat>)
↓|∩ <genbody> ; <genstat> @ <genbody> →E∩¬←&∩ <genstat>
.skip 1
<genstat> ::= [<fmstr>] => <form> @ (<fmstr> NIL <form>)
↓|∩ [<fmstr>] <form> => <form> @ (<fmstr> <form> <form>)
↓|∩ <form> => <form> @ (NIL <form> <form>)
.skip 1
<sem> ::= →e∩ @ NIL
↓|∩ ; @ ;
.skip 1
<begin> ::= BEGIN @ NIL
↓|∩ [) @ NIL
.skip 1
<end> ::= END @ NIL
::= ↓|∩ (] @ NIL
.skip 1
<blockbody> ::= <declstar> <stat> @ <declstar> + <stat>
↓|∩ <blockbody> ; <stat> @ <blockbody> + <stat>
.skip 1
<declstar> ::= →e∩ @ NIL
↓|∩ <declstar> DECL <idpls> : <form> @ <declstar> + (DECL ¬←&∩ <idpls> ¬←&∩
###<initd> ; ###<form> ¬←&∩ <initd>)
.skip 1
<idpls> ::= ID @ (<id>)
↓|∩ <idpls> , ID @ <idpls> →E∩¬←&∩ <id>
.skip 1
<initd> ::= →e∩ @ NIL
↓|∩ BYVAL <form> @ (BYVAL <form>)
↓|∩ SHARED <form> @ (SHARED <form>)
↓|∩ LIKE <form> @ (LIKE <form>)
.skip 1
<stat> ::= <form> @ (<form>)
↓|∩ <form> => <form> @ ((=> <form> <form>))
↓|∩ ID : <stat> @ (: <id>) ¬←&∩ <stat>
.paper size 58,60
.spacing 1
.tab stops
.fill
.justify
.number
.page
Augments perform one of five actions on internal list structure:
.skip 2
.left margin 10
.indent -5
(1)##<name> signifies direct promotion.
.skip 1
<form9>#::=#<select>###@#<select>
.skip 1
means that the list structure specified by <select> is to be
associated with <form9>.
.skip 1
.indent -5
(2)##The list structure may be given explicitly.
.skip 1
<form9>#::=#<form3>#.#ID###@#(.#<form3>#<id>)
.skip 1
means that the list structure to be associated with <form9> is a list
of three elements, the symbol ".", the list structure
specified by <form3>, and the list structure specified by <id>. (Note:
In all cases, the list structure specified by a terminal of the
grammar, such as ID, is the atomic form for that particular instance; in
the case of ID, it is the actual identifier name.)
.skip 1
.indent -5
(3)##The list structure may be obtained by CONSing
list structure together, using the ¬←&∩ infix operator.
.skip 1
<init>#::=#OF#<fmpls>###@#OF ¬←&∩ <fmpls>
.skip 1
means that the list structure to be associated with <init> is
obtained by CONSing the symbol OF onto the head of the
list structure specified by <fmpls>.
.skip 1
.indent -5
(4)##The list structure may be obtained by CONSing an
element onto the tail of some list structure, using the
#→E∩¬←&∩#infix operator.
.skip 1
<fmpls>#::=#<fmpls>#,#<form>###@#<fmpls>#→E∩¬←&∩#<form>
.skip 1
means that the list structure to be associated with the
resultant <fmpls> is obtained by placing the <form> as the
last element of the list specified by <fmpls>. For example, if
the <form> is 4 and the <fmpls> is (1#2#3), the resulting
<fmpls> is the list (1#2#3#4).
.skip 1
.test page 25
.indent -5
(5)##The list structure may be obtained by concatenating list
structure together, using the + infix operator.
.skip 1
<form>#::=#<for←\id>#<from>###@#<for←\id>#+#<from>
.skip 1
means that the list structure to be associated with
<form> is obtained by appending the list specified by <from> to that specified by <for←\id>.
For example,
if <from> is (FROM#1) and <for←\id> is (FOR#X), then the
resulting <form> is (FOR#X#FROM#1). We further specify the actions of
+ when the operands are NIL.
.skip 1
.center
NIL#+#(a)#=#(a)#+#NIL#=#(a)
.center
NIL#+#NIL#=#NIL
.page
.nofill
.tab stops 32
.left margin 0
Examples of Internal Representation
.skip 2
↑&Form\& ↑&Internal Representation\&
.skip 2
a + b (+ a b)
.skip 2
BEGIN (BEGIN
###DECL X:INT BYVAL 2; ###(DECL (X) INT BYVAL 2)
###TRUE => X; ###(=> TRUE X))
END
.skip 2
X[5] ([ X 5)
.skip 2
FOO.LHS (. FOO LHS)
.skip 2
FUNCT(ARG1, ARG2, ARG3) (FUNCT ARG1 ARG2 ARG3)
.skip 2
FOR I FROM 10 BY 2 TO 20 (FOR FOR I FROM 10 BY 2
###WHILE B DO X ###TO 20 WHILE B DO X)
.skip 2
EXPR(X:INT, Y:REAL BYVAL;BOOL) (EXPR((X INT LIKE)
###(X-Y) ###(Y REAL BYVAL))
###BOOL
###(- X Y))
.skip 2
.test page 4
GENERIC (A,B) (GENERIC (A,B)
###[INT,REAL] => A; ###((INT REAL) NIL A)
###[INT] B GT A => B; ###((INT) (GT B A) B)
###TRUE => A + B; ###(NIL TRUE (+ A B)))
END
.skip 2
BEGIN (BEGIN
###DECL X:INT; ###(DECL (X) INT)
###X<-0; ###(DECL (L) LABEL
######BYLAB (<- Y X) Y)
L:#Y<-X; ###(<- X 0)
###Y; ###(:#L)
END ###(<- Y X)
####Y)
.fill
.tab stops
.justify
.PAPER SIZE 100,60
.SPACING 1
####
.SKIP 5
↑↑TO THE READER:\\
.break
.PARAGRAPH
↑PLEASE HELP US TO IMPROVE THE QUALITY AND USEFULNESS OF OUR
DOCUMENTATION. ↑TO DO SO WE NEED YOUR SUGGESTIONS ABOUT THIS
MANUAL. ↑IS SOME SECTION PARTCULARLY UNCLEAR?##↑ARE THERE ANY
FEATURES THAT REQUIRE FURTHER DESCRIPTION?##↑HAVE YOU FOUND ANY
ERRORS?##↑PLEASE GIVE SPECIFIC PAGE AND LINE REFERENCES WHERE
THIS IS APPROPRIATE.
.spacing 2
.SKIP 1
.NOFILL
.NOJUSTIFY
↑↑ERRORS NOTED IN THIS DOCUMENT\\:
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
.SKIP 1
↑↑SUGGESTIONS AND COMMENTS:\\
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
↑↑
.skip 1
Name←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←Date←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.break
Organization←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.break
Position←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.break
Street←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.break
City←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←State←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←Zip Code←←←←←←←←←←←←←←←←
.SPACING 1
####
.skip 4
↑↑
↑&
.CENTER
ECL TROUBLE REPORT
\\
\&
.SKIP 2
.PARAGRAPH
↑PLEASE ATTACH A MINIMAL PROTOCOL EXHIBITING THE DIFFICULTY
YOU'VE ENCOUNTERED. ↑PROTOCOLS SHOULD BEGIN WITH AN .↑R ↑E↑C↑L
AND BE AS SHORT AS POSSIBLE. ↑WHERE APPROPRIATE, TRY TO SAVE
ANY FILES OR OTHER DATA NECESSARY TO REPRODUCE THE PROTOCOL.
.spacing 2
.SKIP 2
.NOFILL
.NOJUSTIFY
↑E↑C↑L ↑VERSION: (←←←←←←-←←←←←←-←←←←←←##←←←←←←:←←←←←←)
.skip 1
↑DESCRIPTION OF ↑PROBLEM←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.BREAK
↑↑
.skip 1
Name←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←Date←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.break
Organization←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.break
Position←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.break
Street←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
.break
City←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←State←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←Zip Code←←←←←←←←←←←←←←←←