This document describes control structures in the shape edit command language (SCL), including loops, conditionals and procedures, and related subjects such as labels for dependency graph nodes, label scope, and modification operations.
Many of the extensions require that SCL be run in its lazy evaluation mode. To do this execute the following line:
set demand_eval true;
<expr> <statement> <val_stmt> <control_stmt> <true> <false><expr_stmt> is used below to indicate:
<expr>; i.e., an expression followed by a semi-colon.Other grammar meta-notation is used below for convenience and defined where it is used.
A label identifier followed by '':'' may appear at the start of any <val_stmt> (i.e., either a <expr_stmt> or <control_stmt>). In this case we refer to the label as being on the statement and the label as being defined by the colon syntax. Note that <expr_stmt> and <control_stmt> both return values.
Here's an example:
Axis : arcEndCenterEnd( pt(0,1), Origin, pt(1,0) ); SrfA : circTubeConstantWidth( Axis, .2 ); The above statements define the labels ''Axis'' and ''SrfA''. The label ''Origin'' is predefined.Labels are also used by loop constructs and procedure definitions.
For example, the loop construct:
forCollect ( I; 1; I < 10; I + 1 ) my_proc( I, 3, 4 ); uses ''I'' as the label for its induction (index) variable, whereas the procedure definition:
procedure my_proc( A, B, C ) C * (A+B); uses ''A'', ''B'', and ''C'' as the labels of its formal arguments. Note that '':''s are not used in the declaration of the label for the induction variable of a loop or the declaration of the labels for the formal arguments of a procedure.Labels have scopes. Once a label is defined in a particular scope, it may be used in subsequent expressions in that scope to stand for the value associated with the labeled statement, index variable, or formal argument. Sequences, loops, and procedure definitions are currently the only SCL statements that define scopes. If an SCL statement is outside the scope of any sequence, loop, or procedure definition, then we say that the statement is at the top level scope.
The following statements establish inner scopes for labels: sequences, loops (the induction variable), and procedure definitions (the formal arguments).
The scope of a label on a statement in a sequence of statements starts at the statement after the labeled statement, and extends to the last statement in the sequence.
A ''for'' or ''forCollect'' statement establishes a scope for its index label. The scope extends to the test and update expressions, but not to the initialization expression. The scope of the index label extends also to the statement comprising the body of the ''for'' or ''forCollect''.
The formal arguments of a procedure definition establish labels whose scope is the statement that comprises the procedure body.
seq { <val_stmt> <val_stmt> <val_stmt> ... } seqCollect { <val_stmt> <val_stmt> <val_stmt> ... } In both cases, the <val_stmt>s within the sequence are evaluated and propagated in the order given. The value of ''seq'' is the value of the last statement in the statement list. The value of ''seqCollect'' is a group consisting of the values of each statement in order.The ''seq'' statement can be written using curly braces alone, that is:
{ <val_stmt> <val_stmt> <val_stmt> ... } Example:
Prof1 : { Pt1 : ptIntersect2Lines( LeftTanLine, LLine ); Pt2 : ptIntersect2Lines( LLine, BLine ); Pt3 : ptIntersect2Lines( BLine, RLine ); Pt4 : ptIntersect2Lines( RLine, RightTanLine ); profile( Pt1, Pt2, Pt3, Pt4 ); } Typical uses of ''{'' and ''}'' are for specifying the bodies of procedures, loops, and conditional clauses.To use sequences you must first execute the line:
set demand_eval true;
if ( <expr> ) <val_stmt> else <val_stmt> If the test expression <expr> evaluates to <true>, or is non-zero, then the first statement is executed. Otherwise the second statement is executed. The value of the ''if'' is the value of whichever statement is executed.Example:
if ( Val > 0.4 ) 1.0; else { if ( Val > -1.8 ) TaperVal; else 1.0; } Note that an ''if'' clause not followed by an ''else'' clause is currently invalid (and flagged as a syntax error). The reason for this is that the underlying functional model of the dependency graph requires a conditional node to produce a value (whether the test is true or not).To use conditionals you must first execute the line:
set demand_eval true;
for ( <label>; <init>; <test>; <update> ) <val_stmt> forCollect ( <label>; <init>; <test>; <update> ) <val_stmt> <label> constitutes a definition of a label for a model_obj containing a number (float or integer) which is local to the loop evaluation (note that no colon is necessary in this definition). This label will be used as the index variable of the loop and will not be available for reference outside of the loop. The label scope is that of the <test> and <update> expressions, as well as the <val_stmt> body. The <label> is undefined in the <init> expression.<init> is an <expr> which must evaluate to a number (float or integer). This value will be used to initialize the value of the <label> index object. Note: <init> may not reference <label>.
<test> is an <expr> that gives termination criteria for the loop. The''for'' or ''forCollect'' will continue to loop as long as <test> evaluates to <true> or is non-zero. Typically, <test> is some relational expression that involves <label>.
<update> is an <expr> which must evaluate to a number (float or integer). This value will be used to update the value of <label> after each iteration. The expression will typically reference <label>.
The value of a ''for'' loop is the value of it's <val_stmt> the last time it was executed.
The value of a''forCollect'' loop is a group consisting of the values returned by the <val_stmt> body each time it was executed (in the order of execution).
Here's an example:
FirstGroove : ...; GrooveList : forCollect( I; 1; I <= NumGrooves; I + 1 ) objtransform( FirstGroove, tx( I * OffWidth ) ); Note that loops can nest and the index <label> of the outer loop may be used by the inner loop:
forCollect ( I; 0; I < N; I+1 ) forCollect ( J; 0; J < I; J+1 ) <val_stmt> Note: A ''for'' loop is meaningful only if it: a) is within the scope of a ''startWith'' statement and, b) has modification statements within the scope of its body. Otherwise there is no way to export the value or effect of inner iterations of a ''for'' loop's statement body. (This is because side-effect functions are not well integrated into the dependency graph and modification operators in c_shape_edit currently can only be used within the scope of a ''startWith'' statement.)To use loops you must first execute the line:
set demand_eval true;
procedure <identifier>( <label>, <label>, ... ) <val_stmt> Where <identifier> is the name of the procedure and the <label>s are the names of the formal arguments to the procedure (note that no colons are used in declaring the names of the formal arguments).To call a user defined procedure, use the same syntax as any procedure (i.e. constructor) call:
<identifier>( <arg>, <arg>, ... ); The value of the procedure call is the value of <val_stmt>, the procedure body, evaluated with the actual arguments bound to the formal arguments of the procedure. The scope of the formal arguments is the statement body.Note: The current implementation does not support recursive use of user defined procedures.
To use user defined procedures you must first execute the line:
set demand_eval true;
The ''startWith'' statement is used to help alleviate this problem, while stopping short of implementing full fledged modification operations in SCL (which is difficult because of the functional nature of the language model). The format of the statement is as follows:
startWith ( <expr> ) <val_stmt> A ''startWith'' statement first copies its <expr> and then applies all the modifications in its <val_stmt> body to the copy. The value of the ''startWith'' statement is the modified copy of <expr>.A modification statement is a special form of <val_stmt> that is valid only withing the scope of a ''startWith''. Modification statements have a rather bizarre syntax:
<access_list> = <val_stmt> Note that a typical access expression has the syntax:
<object> <access_list> So modification expressions are not preceded by an object designation (see Accessors for more details about access expressions.)An example will probably make all this somewhat more clear. Here's one from $scle/propeller.scl:
RIdx : numRows( BladeSrf.ctlMesh ) - 1; # Modify some of the control points of Edge1. NewEdge1 : startWith( Edge1 ) for ( I; 0; I <= 2; I+1 ) { .ctlMesh[I][RIdx] = linProjInterp( Edge3.ctlMesh[I][0], Edge1.ctlMesh[I][RIdx-1], Edge3.ctlMesh[I][1] ); } In the example above ''.ctlmesh[I][RIdx] = ...'' is a modification of a point in the control mesh of the copied version of Edge1. Outside the scope of the ''startWith'' the copy can be referenced as NewEdge1Note: the reason why the object designation is left off in the syntax of modification statements, is to dissallow references to the object being modified while it is being modified. Allowing such references would lead to general modification operations; something we're trying to avoid in SCL.
<< <statement> <statement> <statement> ... >> The value of the block is the value of the last statement in the statement list. The semantics of sequence are not implied and no label scope is created by these blocks. Statement blocks can be used only at the top level and following the ''commentOutCode'' keyword at any other level.
commentOutCode <statement> A typical use of this mechanism is to comment out formal variable assignments at the beginning of a user defined procedure definition. This is handy for debugging the procedure, by first setting up an environment where the formal arguments are defined, and then executing the procedure definition one line at a time from within an editor.Here's an example:
########################################################## # fooProc - Takes 3 arguments and does something with them. # procedure fooProc( Test1, Test2, A, B ) { commentOutCode << # Try it out with some arguments. Test1 : 1; Test2 : 2; A : 3.14159; B : 2.71828; >> if ( Test1 < Test2 ) A * B; else A - B; }
set parse_echo false; The parser echo can be turned back on by:
set parse_echo true; Alternatively, the parser echo can be turned off for a single line by terminating that line with ''$;''.
set demand_eval true;
set propagate_changes false; and then to re-enable change propagation,
set propagate_changes true; To monitor the changes being made,
set trace_changes true;
set time_command true; Upon completion of each command, it prints the total elapsed time, the time spent in the system, and the time spent executing the command. Times are reported in seconds.