.chapter "Statements" . .sr self Section`chapter .sr exceptions Section`12 .sr assign Section`9 .sr scope Section`8.1 .sr fetch Section`10.5.1 .sr getops Section`10.5.2 .sr exstmt Section`12.4 .sr itermod Section`13.3 .sr oneofs Section`7.10 .sr procmod Section`13.2 . .sr s1 1 .sr s2 2 . .para In this section, we describe most of the statements of CLU. We omit discussion of the signal, exit, and except statements, which are used for signalling and handling exceptions, as described in exceptions. .para CLU is a statement-oriented language, i.e., statements are executed for their side-effects and do not return any values. Most statements are 2control* statements that permit the programmer to define how control flows through the program. The real work is done by the 2simple* statements: assignment and invocation. Assignment has already been discussed in assign; the invocation statement is discussed in self.1 below. Two special statements that look like assignments but are really invocations are discussed in self.2. .para The syntax of CLU is defined to permit a control statement to control a group of declarations, equates, and statements rather than just a single statement. Such a group is called a 2body*. Scope rules for bodies were discussed in scope. Occasionally, it is necessary to explicitly indicate that a group of statements should be treated like a single statement; this is done by the block statement, discussed in self.3. .para The conditional statement is discussed in self.4. Loop statements are discussed in self.5, as are some special statements that control termination of a single iteration or a single loop. The tagcase statement is discussed in self.6. Finally, the return statement is discussed in self.7, and the yield statement in self.8. .section "Invocation" .para An invocation statement invokes a procedure. Its form is the same as an invocation expression: .show primary ( lbkt expression, etc rbkt ) .eshow The 2primary* must evaluate to a procedure object, and the types of the 2expressions* must be included in the types of the formal arguments for that procedure. The procedure may or may not return some results; if it does return results, they are discarded. .para For example, the statement .show array[int]$remh (a) .eshow will remove the top element of 2a* (assuming 2a* is an array[int]). Remh also returns the top element, but it is discarded in this case. .section "Update Statements" .para Two special statements are provided for updating components of records and arrays. In addition they may be used with user-defined types with the appropriate properties. These statements resemble assignments syntactically, but are really invocations. .subsection "Element Update" .para The element update statement has the form .show primary [ expressions1 ] := expressions2 .eshow This form is merely syntactic sugar for an invocation of a store operation, and is completely equivalent to the invocation statement .show T$store (primary, expressions1, expressions2) .eshow where T is the type of 2primary*. For example, if 2a* is an array of integers, .show a[27] := 3 .eshow is completely equivalent to the invocation statement .show array[int]$store (a, 27, 3) .eshow .para The element update statement is not restricted to arrays. The statement is legal if the corresponding invocation statement is legal. In other words, T (the type of 2primary*) must provide a procedure operation named 2store*, which takes three arguments whose types include those of 2primary*, 2expressions2*, and 2expressions2*, respectively. In case 2primary* is an array[S] for some type S, 2expressions1* must be an integer, and 2expressions2* must be included in S. .para We recommend that the use of 2store* for user-defined types be restricted to types with array-like behavior, i.e., types whose objects contain collections of indexable elements. For example, it might make sense for an associative_memory type to provide a 2store* operation for changing the value associated with a key. Such types may also provide a 2fetch* operation; see fetch. .subsection "Component Update" .para The component update statement has the form .show primary 1.* name := expression .eshow This form is merely syntactic sugar for an invocation of a set_name operation, and is completely equivalent to the invocation statement .show T$set_2name* (primary, expression) .eshow where T is the type of 2primary*. .para For example, if 2x* has type record[first:`int,`second:`real], then .show x.first := 6 .eshow is completely equivalent to .show record[first: int, second: real] $ set_first (x, 3) .eshow .para The component update statement is not restricted to records. The statement is legal if the corresponding invocation statement is legal. In other words, T (the type of 2primary*) must provide a procedure operation called set_2name*, which takes two arguments whose types include the types of 2primary* and 2expression*, respectively. When T is a record type, then T must have a selector called 2name*, and the type of 2expression* must be included in the type of the component named by that selector. .para We recommend that 2set* operations be provided for user-defined types only if record-like behavior is desired, i.e., it is meaningful to permit some parts of the abstract object to be modified by selector name. In general, 2set* operations should not perform any substantial computation, except possibly checking that the arguments satisfy certain constraints. For example, in a bank_account type, there might be a set_min_balance operation to set what the minimum balance in the account must be. However, 2deposit* and 2withdraw* operations make more sense than a set_balance operation, even though the set_balance operation could compute the amount deposited or withdrawn and enforce semantic constraints. .para In our experience, types with 2set* operations occur much less frequently than types with 2get* operations (see getops). .section "Block Statement" .para The block statement permits a group of statements to be grouped together into a single statement. Its form is .show begin body end .eshow Since the syntax already permits bodies inside control statements, the main use of the block statement is to group statements together for use with the except statement; see exstmt. .section "Conditional Statement" .para The form of the conditional statement is .show 4 if expression then body lcurly elseif expression then body rcurly lbkt else body rbkt end .eshow The expressions must be of type bool. They are evaluated successively until one is found to be true. The body corresponding to the first true expression is executed, and the execution of the if statement then terminates. If none of the expressions is true, then the body in the else clause is executed (if the else clause exists). The elseif form provides a convenient way to write a multi-way branch. .section "Loop Statements" .para There are two forms of loop statements: the while statement and the for statement. Also provided are a continue statement, to terminate the current cycle of a loop, and a break statement, to terminate the innermost loop. These are discussed below. .subsection "While Statement" .para The while statement has the form: .show while expression do body end .eshow Its effect is to repeatedly execute the body as long as the expression remains true. The expression must be of type bool. If the value of the expression is true, the body is executed, and then the entire while statement is executed again. When the expression evaluates to false, execution of the while statement terminates. .subsection "For Statement" .para The for statement invokes an iterator (see itermod). The iterator produces a sequence of 2items* (groups of zero or more objects) one item at a time; the body of the for statement is executed for each item in the sequence. .para The for statement has the form: .show for lbkt idn, etc rbkt in invocation do body end .eshow or .show for lbkt decl, etc rbkt in invocation do body end .eshow The invocation must be an iterator invocation. The 2idn* form uses previously declared variables to serve as the loop variables, while the 2decl* form introduces new variables, local to the for statement, for this purpose. In either case, the variables must agree in number, order, and type with the number, order, and types of the objects (the item) yielded each time by the iterator. .para Execution of the for statement proceeds as follows. First the iterator is invoked, and it either yields an item or terminates. If the iterator yields an item, its execution is temporarily suspended, the objects in the item are assigned to the loop variables, the body of the for statement is executed, and then execution of the iterator is resumed (from the point of suspension). Whenever the iterator terminates, the entire for statement terminates. .para An example of a for statement is .show 6 a: array [int]; etc sum: int := 0; for s(1)x: int in array[int]$elements (a) do t(1)sum := sum + x; t(1)end; .eshow which will compute the sum of all the integers in an array of integers. This example makes use of the 2element* iterator on arrays, which yields the elements of the array one by one. .subsection "Continue Statement" .para The continue statement has the form .show continue .eshow Its effect is to terminate execution of the body of the smallest loop statement in which it appears, and to start the next cycle of that loop (if any). .subsection "Break Statement" .para The break statement has the form .show break .eshow Its effect is to terminate execution of the smallest loop statement in which it appears. Execution continues with the statement following that loop. .para For example, .show 7 sum: int := 0; for s(1)x: int in array[int]$elements (a) do t(1)sum := sum + x; t(1)if s(2)sum >= 100 t(2)then sum := 100; break; t(2)end; t(1)end; .eshow computes the minimum of 100 and the sum of the integers in 2a*. Note that execution of the break statement will terminate both the iterator and the for loop, continuing with the statement following the for loop. .section "Tagcase Statement" .para The tagcase statement is a special statement provided for decomposing oneof objects. Recall that a oneof type is a discriminated union, and each oneof object is a pair consisting of a 2tag* and some other object, called the 2value* (see oneofs). The tagcase statement permits the selection of a body to perform based on the tag of the oneof object. .para The form of the tagcase statement is .show 4 tagcase expression tag_arm lcurly tag_arm rcurly lbkt others : body rbkt end .eshow where .show .def tag_arm "tag name, etc lbkt (idn: type_spec) rbkt : body" .eshow The expression must evaluate to a oneof object. The tag of this object is then matched against the names on the tag_arms. When a match is found, if a declaration (2idn*: 2type_spec*) exists, the value part of the oneof object is assigned to the local variable 2idn*. The body of the matching tag_arm is then executed; 2idn* is defined only in that body. If no match is found, the body in the others arm is executed. .para In a syntactically correct tagcase statement, the following constraints are satisfied. The type of the expression must be some oneof type, T. The tags named in the tag_arms must be a subset of the tags of T, and no tag may occur more than once. If all tags of T are present, there is no others arm; otherwise an others arm must be present. Finally, on any tag_arm containing a declaration (2idn*:`2type_spec*), 2type_spec* must equal the type specified as corresponding in T to the tag or tags named in the tag_arm. .para An example of a tagcase statement is .show 10 x: oneof [cell: cell, null: null] cell = record [car: int, cdr: int_list] etc tagcase x tag null: return (false) tag cell (r: cell): if s(1)r.car = y t(1)then return (true) t(1)else x := r.cdr t(1)end end .eshow The tagcase statement shown might be used in a list (of integers) operation that determines whether some given integer (2y*) is on the list. .section "Return Statement" .para The form of the return statement is: .show return lbkt (expression, etc) rbkt .eshow The return statement terminates execution of the containing procedure or iterator. If the return statement is in a procedure, the number, types, and order of the 2expressions* must agree with the requirements stated in the returns clause of the procedure header. The expressions (if any) are evaluated from left to right, and the objects obtained become the results of the procedure. If the return statement occurs in an iterator no results can be returned. .para For example, inside a procedure 2p* with type .show proctype (etc) returns (int, char) .eshow the statement .show return (3, 'a') .eshow is legal and returns the two result objects 3 and 'a'. .section "Yield Statement" .para 1Yield* statements may occur only in the body of an iterator. The form of a yield statement is: .show yield lbkt (expression, etc) rbkt .eshow It has the effect of suspending operation of the iterator, and returning control to the invoking for statement. If the expression list is present, the values obtained by evaluating the expressions (in left to right) are passed to the for statement to be assigned to the corresponding list of identifiers. The number, types, and order of the expressions must agree with the requirements stated in the iterator heading.