DO (ObjectScript)
Synopsis
DO:pc doargument,...
D:pc doargument,...
where doargument is:
entryref(param,...):pc
Arguments
Argument | Description |
---|---|
pc | Optional — A postconditional expression. |
entryref | The name of the routine to be called, preceded by a caret (DO ^myroutine), optionally with a label name (DO Label2^myroutine), or label and line offset (DO Label2+3^myroutine). To invoke the current routine you can omit the routine name and just specify a label or label and line offset. You can also invoke an object method as DO oref.Method() or using another syntax form, as listed below. |
param | Optional — Parameter values to be passed to the called routine. |
Description
The DO command and the DO WHILE command are separate and unrelated commands. This page documents the DO command. In the DO WHILE command, the DO keyword and the WHILE keyword may be separated by several lines of code; however, you can immediately identify a DO WHILE command because the DO keyword is followed by an open curly brace.
The DO command calls a specified object method, subroutine, function, or procedure. InterSystems IRIS executes the called routine, then executes the next command after the DO command. You can call the routine with or without parameter passing. For example DO ^myrou1 or DO ^myrou(a,b,c).
DO cannot accept a return value from the called routine. If the called routine concludes with an argumented QUIT, the DO command completes successfully, but ignores the QUIT argument value.
DO can specify multiple arguments as a comma-separated list. For example, DO ^myrou1,^myrou2,^myrou3. The system executes the arguments in the order specified. Execution halts if it encounters an invalid argument.
DO is commonly invoked at the Terminal prompt to execute an existing compiled routine. The DO command can, of course, also be invoked from within a routine.
Each invocation of DO places a new context frame on the call stack for your process. The $STACK special variable contains the current number of context frames on the call stack. This context frame establishes a new execution level, incrementing $STACK and $ESTACK, and providing scope for NEW and SET $ZTRAP operations issued during the DO operation. Upon successful completion, DO decrements $STACK and $ESTACK and reverts NEW and SET $ZTRAP operations.
Current Routine
When invoking DO from the Terminal, DO first searches for the currently loaded routine. If the current process has a current routine, DO executes this routine, not the corresponding routine on disk.
For example, you load the routine myroutine from disk using the ZLOAD command. This makes it the current routine (as shown by the $ZNAME special variable). You then modify the current routine using ZINSERT, then invoke DO ^myroutine. The DO command executes the modified myroutine, not the unmodified myroutine on disk. The argumentless ZREMOVE command unloads the currently loaded routine. Following an argumentless ZREMOVE, DO ^myroutine executes the unmodified version of myroutine from disk.
You can invoke the current routine in either of two ways:
-
With explicit routine name: DO ^myroutine, DO Label2^myroutine, or DO Label2+3^myroutine.
-
With implied routine name, specifying just label and (optionally) offset: DO Main, DO Label2, or DO Label2+3.
Arguments
pc
An optional postconditional expression. If the postconditional expression is appended to the DO command keyword, InterSystems IRIS executes the DO command if the postconditional expression is TRUE (evaluates to a nonzero numeric value). InterSystems IRIS does not execute the DO command if the postconditional expression is FALSE (evaluates to zero).
If the postconditional expression is appended to an argument, InterSystems IRIS executes the argument if the postconditional expression is TRUE (evaluates to a nonzero numeric value). If the postconditional expression is FALSE (evaluates to zero), InterSystems IRIS skips that argument and proceeds to evaluate the next argument (if there is one) or the next command. For example:
DO:0 $INCREMENT(myvar($INCREMENT(subvar))):1 /* myvar and subvar not incremented */
DO:1 $INCREMENT(myvar($INCREMENT(subvar))):0 /* myvar not incremented, subvar incremented */
Note that because InterSystems IRIS processes expressions from left to right, any part of the argument containing expressions (such as a parameter value or an object reference) is evaluated and can cause an error before the postconditional expression is evaluated. If DO invokes an object method with an appended postconditional, the maximum number of object method parameters is 253.
For further details, refer to Command Postconditional Expressions.
entryref
The name of the routine (Object Method, Subroutine, Procedure, or User-supplied Function) to be called. You can specify multiple routines as a comma-separated list.
entryref can take any of the following forms.
entryref Form | Description |
---|---|
label+offset | Specifies a line label within the current routine. The optional +offset can only be used when calling a subroutine to which no parameters are passed; it cannot be used when calling a procedure or when passing parameters to a subroutine. offset is a nonnegative integer that specifies the number of lines after the label at which execution of the subroutine is to start. |
label+offset^routine | Specifies a line label within the named routine that resides on disk. InterSystems IRIS loads the routine from disk and begins execution at the indicated label. The +offset is optional. |
^routine | The name of a routine that resides on disk. The system loads the routine from disk and begins execution at the first executable line of the routine. Must be a literal value; a variable cannot be used to specify routine. (Note that the ^ character is a separator character, not part of the routine name.) If the routine has been modified, InterSystems IRIS loads the updated version of the routine when DO invokes the routine. If the routine is not in the current namespace, you can specify the namespace that contains the routine using an extended routine reference, as follows: ^|"namespace"|routine. |
oref.Method() |
Specifies an object method. The system accesses the object and executes the specified method, passing the arguments (if any) specified in param, the method’s argument list. Object calls use dot syntax: oref (the object reference) and Method() are separated by a dot; blank spaces are not permitted. A method must specify its open and close parentheses, even if there are no param arguments. The following syntactic forms are supported: DO oref.Method(), DO oref.Method(), DO ..Method(), DO ##class(cname).Method(), DO i%prop(subs).Method(). |
You cannot specify an offset when calling a IRISSYS % routine. If you attempt to do so, InterSystems IRIS issues a <NOLINE> error.
If you specify a nonexistent label, InterSystems IRIS issues a <NOLINE> error. If you specify a nonexistent routine, InterSystems IRIS issues a <NOROUTINE> error. If you specify a nonexistent method, InterSystems IRIS issues a <METHOD DOES NOT EXIST> error. If you specify an existing property as a method (with parentheses), InterSystems IRIS issues an <OBJECT DISPATCH> error. If you use extended reference (for example, DO ^|"%SYS"|MyProg) and specify a nonexistent namespace, InterSystems IRIS issues a <NAMESPACE> error. If you use extended reference and specify a namespace for which you do not have privileges, InterSystems IRIS issues a <PROTECT> error, followed by the database path, such as the following: <PROTECT> *^|^^c:\intersystems\iris\mgr\|MyRoutine. For further details on these errors, refer to the $ZERROR special variable.
If you specify an offset that points to the middle of a multi-line statement, the system starts execution at the beginning of the next statement.
param
Parameter values to be passed to the subroutine, procedure, user-supplied function or object method. You can specify a single param value, or a comma-separated list of param values. A param list is enclosed in parentheses. When no param is specified, the enclosing parentheses are required when calling a procedure or user-supplied function, optional when calling a subroutine. Parameters can be passed by value or passed by reference. The same call can mix parameters passed by value and parameters passed by reference. When passing by value, you can specify a parameter as a value constant, expression, or unsubscripted local variable name. (See Passing By Value.) When passing by reference, the parameters must reference the name of a local variable or unsubscripted array in the form .name (See Passing By Reference.)
The maximum total param values for a DO entrypoint is 382; the maximum total param values for a DO method or DO with indirection is 380. This total can include up to 254 actual parameters and 128 postconditional parameters.
you can specify a variable number of parameters using ... syntax.
DO Command entryref Arguments
The DO command with entryref arguments invokes the execution of one or more blocks of code that are defined elsewhere. Each block of code to execute is specified by its entryref. The DO command can specify multiple blocks of code to execute as a comma-separated list. The execution of the DO command, and the execution of each entryref in a comma-separated list can be governed by optional postconditional expressions.
DO can invoke the execution of a subroutine (with or without parameter passing), a procedure, or a user-supplied function. Upon completion of the execution of the block of code, execution resumes at the next command after the DO command. A block of code invoked by the DO command cannot return a value to the DO command; any value returned is ignored. Thus DO can execute a user-supplied function, but cannot receive the return value of that function.
DO cannot invoke most ObjectScript system functions. Attempting to do so results in a <SYNTAX> error. A few system functions can be invoked as a DO command argument: $CASE, $CLASSMETHOD, $METHOD, $INCREMENT, and $ZF(-100). DO cannot receive the return value of a function. Like all DO command arguments, these functions can take a postconditional parameter. For example, DO $CASE(exp,0:NoMul(),2:Square(num),3:Cube(num),:Exponent(num,exp)):0. For an example program, refer to the $CASE function.
The DO Command without Parameter Passing
The DO command without parameter passing is only used with subroutines. Use of DO entryref without parameter passing (that is, without specifying the param option) takes advantage of the fact that a calling routine and its called subroutine share the same variable environment. Any variable updates made by the subroutine are automatically available to the code following the DO command.
When using DO without parameter passing, you must make sure that both the calling routine and the called subroutine reference the same variables.
In the following example, Start (the calling routine) and Exponent (the called subroutine) share access to three variables: num, powr, and result. Start sets num and powr to the user-supplied values. These values are automatically available to Exponent when it is called by the DO command. Exponent references num and powr, and places the calculated value in result. When Exponent executes the RETURN command, control returns to the WRITE command immediately after the DO. The WRITE command outputs the calculated value by referencing result:
Start ; Raise an integer to a specified power.
READ !,"Integer= ",num QUIT:num=""
READ !,"Power= ",powr QUIT:powr=""
DO Exponent()
WRITE !,"Result= ",result,!
RETURN
Exponent()
SET result=num
FOR i=1:1:powr-1 { SET result=result*num }
RETURN
In the following example, DO invokes the Admit() method on the object referred to by pat. The method does not receive parameters or return a value.
DO pat.Admit()
In the following example, DO calls, in succession, the subroutines Init and Read1 in the current routine and the subroutine Convert in routine Test.
DO Init,Read1,Convert^Test
In the following example, DO uses an extended reference to call the routine fibonacci in a different namespace (the SAMPLES namespace):
NEW $NAMESPACE
SET $NAMESPACE="USER"
DO ^|"SAMPLES"|fibonacci
DO and GOTO
The DO command can be used to invoke a subroutine (with or without parameter passing), a procedure, or a user-supplied function. At the completion of the call, InterSystems IRIS executes the next command following the DO command.
The GOTO command can only be used to invoke a subroutine without parameter passing. At the completion of the call, InterSystems IRIS issues a QUIT, ending execution.
DO with Parameter Passing
When used with parameter passing, DO entryref explicitly passes one or more values to the called subroutine, procedure, user-supplied function or object method. The passed values are specified as a comma-separated list with the param option. With parameter passing, you must make sure that the called subroutine is defined with a parameter list. The subroutine definition takes the form:
>label( param)
where label is the label name of the subroutine, procedure, user-supplied function or object method, and param is a comma separated list of one or more unsubscripted local variable names. For example,
Main
SET x=1,y=2,z=3
WRITE !,"In Main ",x,y,z
DO Sub1(x,y,z)
WRITE !,"Back in Main ",x,y,z
QUIT
Sub1(a,b,c)
WRITE !,"In Sub1 ",a,b,c
QUIT
The list of parameters passed by the DO command is known as the actual parameter list. The list of parameter variables defined as part of the label of the coded routine is known as the formal parameter list. When DO calls the routine, the parameters in the actual parameter list are mapped, by position, to the corresponding variables in the formal parameter list. In the above example, the value of the first actual parameter (x) is placed in the first variable (a) of the subroutine’s formal parameter list; the value of the second actual parameter (y) is placed in the second variable (b); and so on. The subroutine can then access the passed values by using the appropriate variables in its formal parameter list.
If there are more variables in the actual parameter list than there are parameters in the formal parameter list, InterSystems IRIS issues a <PARAMETER> error.
If there are more variables in the formal parameter list than there are parameters in the actual parameter list, the extra variables are left undefined. In the following example, the formal parameter c is left undefined:
Main
SET x=1,y=2,z=3
WRITE !,"In Main ",x,y,z
DO Sub1(x,y)
WRITE !,"Back in Main ",x,y,z
QUIT
Sub1(a,b,c)
WRITE !,"In Sub1 "
IF $DATA(a) {WRITE !,"a=",a}
ELSE {WRITE !,"a is undefined"}
IF $DATA(b) {WRITE !,"b=",b}
ELSE {WRITE !,"b is undefined"}
IF $DATA(c) {WRITE !,"c=",c}
ELSE {WRITE !,"c is undefined"}
QUIT
You can specify a default value for a formal parameter, to be used when no actual parameter value is specified.
You can leave any variable undefined by omitting the corresponding parameter from the DO command’s actual parameter list. However, you must include a comma as a place holder for each omitted actual parameter. In the following example, the formal parameter b is left undefined:
Main
SET x=1,y=2,z=3
WRITE !,"In Main ",x,y,z
DO Sub1(x,,z)
WRITE !,"Back in Main ",x,y,z
QUIT
Sub1(a,b,c)
WRITE !,"In Sub1 "
IF $DATA(a) {WRITE !,"a=",a}
ELSE {WRITE !,"a is undefined"}
IF $DATA(b) {WRITE !,"b=",b}
ELSE {WRITE !,"b is undefined"}
IF $DATA(c) {WRITE !,"c=",c}
ELSE {WRITE !,"c is undefined"}
QUIT
You can specify a variable number of parameters using ... syntax:
Main
SET x=3,x(1)=10,x(2)=20,x(3)=30
DO Sub1(x...)
QUIT
Sub1(a,b,c)
WRITE a," ",b," ",c
QUIT
The DO command can pass parameters either by value (for example, DO Sub1(x,y,z)) or by reference (for example, DO Sub1(.x,.y,.z)). You can mix passing by value and passing by reference within the same DO command. For further details, refer to Parameter Passing.
The following examples show the difference between passing by value and passing by reference:
Main /* Passing by Value */
SET x=1,y=2,z=3
WRITE !,"In Main ",x,y,z
DO Sub1(x,y,z)
WRITE !,"Back in Main ",x,y,z
QUIT
Sub1(a,b,c)
SET a=a+1,b=b+1,c=c+1
WRITE !,"In Sub1 ",a,b,c
QUIT
Main /* Passing by Reference */
SET x=1,y=2,z=3
WRITE !,"In Main ",x,y,z
DO Sub1(.x,.y,.z)
WRITE !,"Back in Main ",x,y,z
QUIT
Sub1(&a,&b,&c) /* The & prefix is an optional by-reference marker */
SET a=a+1,b=b+1,c=c+1
WRITE !,"In Sub1 ",a,b,c
QUIT
DO with Indirection
You can use indirection to supply a target subroutine location for DO. For example, you might implement a generalized menu program by storing the various menu functions at different locations in a separate routine. In your main program code, you could use name indirection to provide the DO command with the location of the function corresponding to each menu choice.
You cannot use indirection with InterSystems IRIS object dot syntax. This is because dot syntax is parsed at compile time, not at runtime.
In name indirection, the value of the expression to the right of the indirection operator (@) must be a name (that is, a line label or a routine name). In the following code segment, name indirection supplies the DO with the location of a target function in the routine Menu.
READ !,"Enter the number for your choice: ",num QUIT:num=""
DO @("Item"_num)^Menu
The DO command invokes the subroutine in Menu whose label is Item concatenated with the user-supplied num value (for example, Item1, Item2, and so on).
You can also use the argument form of indirection to substitute the value of an expression for a complete DO argument. For example, consider the following DO command:
DO @(eref_":fstr>0")
This command calls the subroutine specified by the value of eref if the value of fstr is greater than 0.
For more information, refer to the Indirection Operator reference page.
DO with Argument Postconditionals
You can use argument postconditional expressions to select a target subroutine for a DO command. If the postconditional expression evaluates to FALSE (0), InterSystems IRIS ignores the associated subroutine call. If the postconditional expression evaluates to TRUE (1), InterSystems IRIS executes the associated subroutine call, then returns to the DO command. You can use postconditionals on both the DO command and on its arguments.
For example, consider the command:
DO:F>0 A:F=1,B:F=2,C
The DO command has a postconditional expression; if F is not greater than 0, no part of the DO is executed. The DO command’s arguments also have postconditional expressions. DO uses these argument postconditionals to select which subroutine(s) (A, B, or C) to execute. All subroutines that fulfill the truth condition are executed, in the order presented. Thus, in the above example, C, with no postconditional, is always executed: if F=1 both A and C are executed; if F=2, B and C are executed; if F=3 (or any other number) C is executed. To establish C as a true default, do the following:
DO:F>0 A:F=1,B:F=2,C:((F'=1)&&(F'=2))
In this example, one and only one subroutine is executed.
In the following example, the DO command takes a postconditional, and each of its arguments also takes a postconditional. In this case, the first argument is not executed, because its postconditional is 0. The second argument is executed because its postconditional is 1.
Main
SET x=1,y=2,z=3
WRITE !,"In Main ",x,y,z
DO:1 Sub1(x,y,z):0,Sub2(x,y,z):1
WRITE !,"Back in Main ",x,y,z
QUIT
Sub1(a,b,c)
WRITE !,"In Sub1 ",a,b,c
QUIT
Sub2(d,e,f)
WRITE !,"In Sub2 ",d,e,f
QUIT
Most Object (OREF) methods invoked by DO can take an argument postconditional. However, $SYSTEM object methods cannot take an argument postconditional. Attempted to do so generates a <SYNTAX> error.
Note that because InterSystems IRIS evaluates expressions in strict left-to-right order, an argument that contains expressions is evaluated (and can generate an error) before InterSystems IRIS evaluates the argument postconditional.
When using argument postconditionals, make sure there are no unwanted side effects. For example, consider the following command:
DO @^Control(i):z=1
In this case, ^Control(i) contains the name of the subroutine to be called if the postconditional z=1 tests TRUE. Whether or not z=1, InterSystems IRIS evaluates the value of ^Control(i) and resets the current global naked indicator accordingly. If z=1 is FALSE, InterSystems IRIS does not execute the DO. However, it does reset the global naked indicator just as if it had executed the DO. For more details on the naked indicator, see Naked Global Reference.
For more information on how postconditional expressions are evaluated, see Command Postconditional Expressions.
$TEST Behavior with DO
When you use DO to call a procedure, InterSystems IRIS preserves the value of $TEST by restoring it to its state at the time of the call upon quitting the procedure. However, when you use DO to call a subroutine (either with or without parameter passing), InterSystems IRIS does not preserve the value of $TEST across the call.
To save the $TEST value across a DO call, you can explicitly assign it to a variable before the call. You can then reference the variable in code that follows the call.