1
0
mirror of https://github.com/PDP-10/its.git synced 2026-01-13 15:27:28 +00:00
PDP-10.its/doc/clu/e}d.refman
Lars Brinkhoff 76e5b7cb8b CLU reference manual.
Written in R.
2021-08-27 05:40:30 +02:00

316 lines
11 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

.sr sect_type_set Section`13.5
.sr sect_library Section`4
.sr sect_semantics Section`3
.sr sect_expr Section`11
.sr sect_assign Section`9.2
.sr sect_except Section`12
.sr sect_module Section`13
.sr sect_mult_assn Section`9.2.2
.
.chapter "Scopes, Declarations, and Equates"
.para
We now describe how to introduce and use constants and variables,
and the scope of constant and variable names.
Scoping units are described first,
followed by a discussion of variables, and finally constants.
.section "Scoping Units"
.para
Scoping units follow the nesting structure of statements.
Generally,
a scoping unit is a body and an associated "header".
The scoping units are:
.nlist
From the start of a module to its end.
.nnext
From a cluster, proc, or iter to the matching end.
.nnext
From a then or else to the end of the corresponding body.
.nnext
From a for, do, or begin to the matching end.
.nnext
From a tag or others in a tagcase statement
to the end of the corresponding body.
.nnext
From a when or others in an except statement
to the end of the corresponding body.
.nnext
From the start of a type_set to its end.
.end_list
The last case above, the scope in a type_set, is a special case that
will be discussed in sect_type_set.
Whatever we say about scopes in the remainder of this section refers only to
cases 1 through 6.
.para
The structure of scoping units is such that if one scoping unit
overlaps another scoping unit (textually), then one is fully
contained in the other.
The contained scope is called a 2nested* scope, and the containing
scope is called a 2surrounding* scope.
.para
New constant and variable names may be introduced in a scoping unit.
Names for constants are introduced by equates, which are syntactically
restricted to appear grouped together at or near the beginning of scoping units.
For example, equates may appear at the beginning of a body,
but not after any statements in the body.
.para
In contrast, declarations, which introduce new variables,
are allowed wherever statements are allowed, and hence
may appear throughout a scoping unit.
Equates and declarations are discussed in more detail in the following two sections.
.para
In the syntax there are two distinct nonterminals
for identifiers: 2idn* and 2name*.
Any identifier introduced by an equate or declaration is an 2idn*,
as is the name of the module being defined, and any operations it has.
An 2idn* names a specific type or object.
The other kind of identifier is a 2name*.
A 2name* is used to refer to a subpiece of something,
and is always used in context; for example, 2names* are used
as record selectors.
The scope rules apply only to 2idns*.
.para
The scope rules are very simple:
.nlist
An idn may not be redefined in its scope.
.nnext
Any idn that is used as an external reference in a _module
may not be used for any other purpose in that module.
.end_list
Unlike other "block-structured" languages,
CLU prohibits the redefinition of an identifier in a nested scope.
An identifier used as an external reference names a module or constant;
the reference is resolved using the compilation environment (see sect_library).
.section "Variables"
.para
Objects are the fundamental "things" in the CLU universe;
variables are a mechanism for denoting (i.e., naming) objects.
This underlying model is discussed in detail in sect_semantics.
A variable has two properties: its type,
and the object that it currently denotes (if any).
A variable is said to be 2uninitialized* if it does not denote any object.
.para
There are only three things that can be done with variables:
.nlist
New variables can be introduced.
Declarations perform this function, and are described below.
.nnext
An object may be assigned to a variable.
After an assignment the variable denotes the object assigned.
Assignment is discussed in sect_assign.
.nnext
A variable may be used as an expression.
The value of such an expression (i.e., the result of evaluating it)
is the object that the variable denotes at the time the expression is evaluated.
Expressions and their evaluation are described in sect_expr.
.end_list
.subsection "Declarations"
.para
Declarations introduce new variables.
The scope of a variable is from its declaration to the
end of the smallest scoping unit containing its declaration;
hence, variables must be declared before use.
.para
There are two sorts of declarations: those with initialization,
and those without.
Simple declarations (those without initialization) take this form:
.show
.def decl "idn, etc : type_spec"
.eshow
A simple declaration introduces a list of variables,
all having the type given by the type_spec.
This type determines the types of objects that can be assigned to the variable.
Here are some examples of simple declarations:
.keep
.show 4
i: int s(1)% declare i to be an integer variable
i, j, k: chart(1)% declare i, j, and k to be character variables
x, y: complext(1)% declare x and y to be complex number variables
z: anyt(1)% declare z to be of type any; thus, z may denote any object
.eshow
.end_keep
The variables introduced in a simple declaration initially denote no objects,
i.e., they are uninitialized.
Attempts to use uninitialized variables (if not detected at compiler-time)
cause the run-time exception
.show
failure("uninitialized variable")
.eshow
(Exceptions are discussed in sect_except.)
.subsection "Declarations with Initialization"
.para
A declaration with initialization combines
declarations and assignments into a single statement.
A declaration with initialization is entirely equivalent to
one or more simple declarations followed by an assignment statement.
The two forms of declaration with initialization are:
.show
idn : type_spec := expression
.eshow
and
.show
decl1, etc, decln := invocation
.eshow
These are equivalent to (respectively):
.show 2
idn : type_spec
idn := expression
.eshow
and
.show 2
decl1 etc decln % declaring idn1 etc idn m
idn1, etc, idnm := invocation
.eshow
In the second form, the order of the idns in the
assignment statement is the same as in the original declaration
with initialization.
(The invocation must return 2n* objects;
see sect_mult_assn).
.para
Here are some examples of declarations with initialization:
.show 6
astr: array[string] := array[string]$create (1)
s(1)% declare astr to be an array variable and initialize it to an empty array
first, last: string, balance: int := acct$query (acct_no)
t(1)% declare first and last to be string variables, balance an integer variable,
t(1)% and initialize them to the results of a bank account query
.eshow
The above two statements are equivalent to the following sequences of statements:
.show 6
astr: array[string]
astr := array[string]$create (1)
first, last: string
balance: int
first, last, balance := acct$query (acct_no)
.eshow
.section "Equates and Constants"
.para
An equate allows a single identifier to be used as
an abbreviation for a constant that
may have a lengthy textual representation.
We use the term constant in a very narrow sense here:
constants,
in addition to being immutable,
must be computable at compile-time.
Constants are either
types (built-in or user defined),
or objects that are the results of evaluating constant expressions.
(Constant expressions are defined below.)
.end_list
.para
The syntax of equates is:
.show 5
.long_def constant
.def1 equate "idn = constant"
.or "idn = type_set"
.def1 constant type_spec
.or "expression % the expression must be a constant expression"
.eshow
This section describes only the first form of equate;
discussion of type_sets is deferred to sect_type_set.
.para
An equated identifier may be used as an expression.
The value of such an expression is the constant to which
the identifier is equated.
An equated identifier may not be used as the target of an assignment.
.para
The scope of an equated identifier is the smallest scoping unit
surrounding the equate defining it;
here we mean the entire scoping unit, not just the portion after the equate.
All the equates in a scoping unit must appear near the beginning of the scoping unit.
The exact placement of equates depends on the containing syntactic construct;
usually equates appear at the beginnings of bodies.
.para
Equates may be in any order within the group.
Thus, forward references among equates in the same scoping unit are
allowed, but cyclic dependencies are illegal.
For example,
.show 3
x = y
y = z
z = 3
.eshow
is a legal sequence of equates, but
.show 3
x = y
y = z
z = x
.eshow
is not.
Since equates introduce idns, the scoping restrictions on idns
apply (i.e., the idns may not be defined more than once).
.subsection "Abbreviations for Types"
.para
Identifiers may be equated to type
specifications, thus giving abbreviations for type names.
For example:
.show 7
at = array [int]
ot = oneof [there: rt, null: null]
rt = record [a: foo, b: bar]
pt = proctype (int, int) returns (int) signals (overflow)
it = itertype (int, int, int) yields (int) signals (bounds)
seq = sequence
mt = mark_table
.eshow
.para
Notice that since equates may not have cyclic dependencies,
directly recursive type specifications cannot be written.
However, this does not prevent the definition of recursive types:
clusters allow them to be written (see sect_module).
.subsection "Constant Expressions"
.para
Here we define the subset of objects that equated identifiers may denote,
by stating which expressions are constant expressions.
(Expressions are discussed in detail in sect_expr.)
A 2constant expression* is an expression that can be evaluated
at compile-time to produce an immutable object of a built-in type.
Specifically this includes:
.nlist
Literals.
.nnext
Identifiers equated to constants.
.nnext
Procedure and iterator names, including force [t] for any type t.
.nnext
Invocations of procedure operations of the built-in constant types
(except string$s2ac),
provided that all operands are constant expressions.
.nnext
Formal parameters.
.end_list
For completeness, here is a list of the built-in constant types:
null, int, real, bool, char, string,
oneof types, procedure types, and iterator types.
We explicitly forbid applying any operations to formal parameters,
since the values of formal parameters are not known at compile-time.
.para
Here are some examples of equates involving expressions:
.show 13
hash_modulus = 29
pi = 3.14159265
win = true
control_c = '\003'
prompt_string = "Input: "
nl = string$c2s ('\n')
prompt = nl || prompt_string
prompt_len = string$size (prompt)
quarter = pi / 2.0
ftb = int$from_to_by
ot = oneof [pair: cell, null: null]
cell = record [first, second: int]
nilptr = ot$make_null (nil)
.eshow
Note that the following equate is illegal because it uses a record constructor,
which is not a constant expression:
.show
cell_1_2 = ot$make_cell (cell${first: 1, second: 2})
.eshow
.para
Any invocation in a constant expression must terminate normally;
a program is illegal if evaluation of any constant expression would signal an exception.
(Exceptions are discussed in sect_except.)
Illegal programs will not be executed.