mirror of
https://github.com/PDP-10/its.git
synced 2026-01-13 15:27:28 +00:00
316 lines
11 KiB
Plaintext
316 lines
11 KiB
Plaintext
.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.
|