statements: LIST(EXP) result: EXP x -> EXP xEach of the statements are evaluated in order, throwing away their results. Then, result is evaluated and its result is the result of the sequence.
A translator is free to rearrange the order of evaluation if there is no observable difference other than in time or space. This applies anywhere I say "something is evaluated and then ...". We find this kind of statement in definitions of local variables in section 5.3 on page 30, and in the controlling parts of the conditional constructions below.
For a more precise discussion of allowable reorderings see (S7.14)
.
Note that control does not automatically pass from one EXP to the
next; if this is required the appropriate EXP must end with an explicit
goto.
One can also pick up a label value of SHAPE POINTER {code} (usually
implemented as a program address) using make_local_lv for later use
by an "indirect jump" such as goto_local_lv . Here the same
prohibition holds - the construction which introduced the LABEL must
still be active.
The construction goto_local_lv only permits one to jump within the
current procedure; if one wished to do a jump out of a procedure into
a calling one, one uses long_jump which requires a pointer to the
destination frame (produced by current_env in the destination procedure)
as well as the label value. If a long_jump is made to a label, only
those local TAGs which have been defined with a visible ACCESS are
guaranteed to have preserved their values; the translator could allocate
the other TAGs in scope as registers whose values are not necessarily
preserved.
A slightly "shorter" long jump is given by return_to_label.
Here the destination of the jump must a label value in the calling
procedure. Usually this value would be passed as parameter of the
call to provide an alternative exit to the procedure.
Some of the constructors for NTESTs are disallowed for some _tests
(e.g. proc_test ) while others are redundant for some _tests; for
example, not_greater_than is the same as less_than_or_equal for all
except possibly floating_test. where the use of NaNs (in the IEEE
sense) as operands may give different results.
Obviously, this kind of conditional is rather different to those found
in traditional high-level languages which usually have three components,
a boolean expression, a then-part and an else-part. Here, the first
component includes both the boolean expression and the then-part;
usually we find that it is a sequence of the tests (branching to alt_label_intro
) forming the boolean expression followed by the else-part. This
formulation means that HLL constructions like "andif" and
"orelse" do not require special constructions in TDF.
A simple loop can be expressed using repeat:
Just as with conditionals, the tests for the continuing or breaking
the loop are included in body and require no special constructions.
Each of these exceptions have an ERROR_CODE ascribed to them, namely
overflow, nil_access and stack_overflow. Each ERROR_CODE can be made
into a distinct NAT by means of the constructor error_val; these NATs
could be used, for example, to discriminate the different kinds of
errors using a case construction.
When one of these exceptions is raised, the translator will arrange
that a TOKEN, ~Throw, is called with the appropriate ERROR_CODE as
its (sole) parameter. Given that every language has a different way
of both characterising and handling exceptions, the exact expansion
of ~Throw must be given by the producer for that language - usually
it will involve doing a long_jump to some label specifying a signal
handler and translating the ERROR_CODE into its language-specific
representation.
The expansion of ~Throw forms part of the language specific environment
required for the translation of TDF derived from the language, just
as the exact shape of FILE must be given for the translation of C.
Part of the TenDRA Web.6.2. Conditional flow
6.2.1. labelled, make_label
All simple changes of flow of control within a TDF procedure are done
by jumps or branches to LABELs, mirroring what actually happens in
most computers. There are three constructors which introduce LABELs;
the most general is labelled which allows arbitrary jumping between
its component EXPs:
placelabs_intro: LIST(LABEL)
starter: EXP x
places: LIST(EXP)
-> EXP w
Each of the EXPs in places is labelled by the corresponding
LABEL in placelabs_intro; these LABELs are constructed by
make_label applied to a TDFINT uniquely drawn from the LABEL name-space
introduced by the enclosing PROPS. The evaluation starts by evaluating
starter; if this runs to completion the result of the labelled
is the result of starter. If there is some jump to a LABEL
in placelabs_intro then control passes to the corresponding
EXP in places and so on. If any of these EXPS runs to
completion then its result is the result of the labelled; hence the
SHAPE of the result, w, is the LUB of the SHAPEs of the component
EXPs. 6.2.2. goto, make_local_lv, goto_local_lv, long_jump, return_to_label
The unconditional goto is the simplest method of jumping. In common
with all the methods of jumping using LABELs, its LABEL parameter
must have been introduced in an enclosing construction, like labelled,
which scopes it. 6.2.3. integer_test, NTEST
Conditional branching is provided by the various _test constructors,
one for each primitive SHAPE except BITFIELD. I shall use integer_test
as the paradigm for them all:
nt: NTEST
dest: LABEL
arg1: EXP INTEGER(v)
arg2: EXP INTEGER(v)
-> EXP TOP
The NTEST nt chooses a dyadic test (e.g. =, >=, <, etc.)
that is to be applied to the results of evaluating
arg1 and arg2. If arg1 nt arg2 then the result
is TOP; otherwise control passes to the LABEL dest. In other
words, integer_test acts like an assertion where if the assertion
is false, control passes to the LABEL instead of continuing in the
normal fashion. 6.2.4. case
There are only two other ways of changing flow of control using LABELs.
One arises in ERROR_TREATMENTs which will be dealt with in the arithmetic
operations. The other is case:
exhaustive: BOOL
control: EXP INTEGER(v)
branches: LIST(CASELIM)
-> EXP (exhaustive ? BOTTOM : TOP)
Each CASELIM is constructed using make_caselim:
branch: LABEL
lower: SIGNED_NAT
upper: SIGNED_NAT
-> CASELIM
In the case construction, the control EXP is evaluated and
tested to see whether its value lies inclusively between some lower
and upper in the list of CASELIMs. If so, control passes to
the corresponding branch. The order in which these tests are
performed is undefined, so it is probably best if the tests are exclusive.
The exhaustive flag being true asserts that one of the branches will
be taken and so the SHAPE of the result is BOTTOM. Otherwise, if none
of the branches are taken, its SHAPE is TOP and execution carries
on normally.6.2.5. conditional, repeat
Besides labelled, two other constructors, conditional and repeat,
introduce LABELs which can be used with the various jump instructions.
Both can be expressed as labelled, but have extra constraints which
make assertions about the use of the LABELs introduced and could usually
be translated more efficiently; hence producers are advised to use
them where possible. A conditional expression or statement would usually
be done using conditional:
alt_label_intro: LABEL
first: EXP x
alt: EXP y
-> EXP(LUB(x, y))
Here first is evaluated; if it terminates normally, its result
is the result of the conditional. If a jump to alt_label_intro
occurs then alt is evaluated and its result is the result of
the conditional. Clearly, this, so far, is just the same as labelled((alt_label_intro
), first, (alt)). However, conditional imposes the
constraint that alt cannot use alt_label_intro. All
jumps to alt_label_intro are "forward jumps" - a
useful property to know in a translator.
repeat_label_intro: LABEL
start: EXP TOP
body: EXP y
-> EXP y
The EXP start is evaluated, followed by body which is
labelled by repeat_label_intro. If a jump to repeat_label_intro
occurs in body, then body is re-evaluated. If body
terminates normally then its result is the result of the repeat. This
is just the same as:
labelled((repeat_label_intro), sequence((start), goto(repeat_label_intro
)), (body))
except that no jumps to repeat_label_intro are allowed in start
- a useful place to do initialisations for the loop. 6.3. Exceptional flow
A further way of changing the flow of control in a TDF program is
by means of exceptions. TDF exceptions currently arise from three
sources - overflow in arithmetic operations with trap ERROR_TREATMENT(see
section 8.1.1 on page 40) , an attempt
to access a value via a nil pointer using assign_with_mode, contents_with_mode
or move_some(see section 7.3 on page 38)
or a stack overflow on procedure entry with PROCPROPS check_stack(see
section 5.2.2 on page 29) or a local_alloc_check.
Crown
Copyright © 1998.