Jazz Logic

Jazz Logic. 1

IF Statements and Conditions. 1

Conditions. 2

CASE statements. 2

LOOPS – the FOR statement 3

Reusing Logic:  Introduction. 4

Internal Logic: ROUTINE and PERFORM.. 4

Best Practice when using Parameters. 5

External Routines. 5

Modular Logic: Writing Subprograms. 6

 

Even though Jazz programs are much simpler than COBOL and other low level languages, there is still a need for conditions, loops, and other forms of program logic.   Jazz provides IF, CASE, and FOR statements to allow you to control your program’s logic. ROUTINE and PERFORM statements allow you write routines within your program that can be used from several places, and may improve the clarity of your code. CALL and INVOKE combine with interface descriptions to allow you to structure your code, reuse logic, and interact with pre-written and remote services. 

 

This chapter provides a quick overview of the ways in which Jazz logic is controlled. It applies to both batch and on-line programming.

IF Statements and Conditions

As in other languages IF statements provide the basic mechanism for conditional logic.  Like IF statements in most languages you write

            IF condition

                Action if true

            [ELSEIF

                Action if true]…

            [ELSE

                Action if false]

            END IF

 

A difference from both COBOL and PL/I is the ELSEIF clause.   This provides one of several ways of writing CASE logic, where you test several conditions in order, executing the first true one.  You can achieve the same results with

            IF condition

                Action if true

            ELSE

              IF condition2

                    Action if condition2 is true

                 ELSE

                    Action if both false

                 END IF

            END IF

but in situations where there is a simple “choose first true condition” with several alternatives using several ELSEIF clauses is neater (code doesn’t get further and further to the right) and clearer (it’s obvious that all of the conditions are related as alternatives).

 

You can write THEN after the conditions – this is optional.  Jazz regards IF condition [THEN] as a statement, and will insert a semicolon if you omit it, so that this syntax will seem a little odd to users with a PL/I or Algol background: -

IF W.N1 = 5 THEN;

    W.N1 = 4;

ELSEIF W.N1 = 6 THEN;

    W.N1 = 7;

END IF;

Conditions

W.N1 = 5 in the IF statement above is a condition.  In this case it looks like an assignment, but in general a simple condition has the form: -

            Field-Reference   Comparison-operator  Value

 

In this example the field reference is W.N1.   Conditions may not use generic references: it would have been invalid to write W.*. 

 

The comparison operator is one of = (equal to), <> (not equal to), > (greater than), >= (greater than or equal to), < (less than), or <= (less than or equal to).  Thus the condition

W.N1 <= 5

is true for any negative value of W.N1, and for values 0, 1, 2, 3, and 5.

 

The Value may be a single constant (as in the examples so far), another field, or an arithmetic expression.  The expression must be compatible with the field reference: if W.N1 is an INTEGER field then you could not compare it with a string value.  For example this would be invalid: -

IF W.N1 = 'Name' THEN;

This is valid: -

IF W.N1 = W.N2 + 3 THEN;

 

Simple conditions can be linked together into compound conditions with the Boolean operators & (AND) and | (OR).  For example, for the condition

W.N1 = 5 & W.N2 = 6

to be true W.N1 must equal 5 AND W.N2 must equal 6.  If either simple condition is false the compound condition is false

 

For those with a COBOL background: note that you do not use | (OR) in the same way as in COBOL.  In COBOL you could have written

            IF N1 = 5 OR 6

but in Jazz you write

IF W.N1 = 5 | W.N1 = 6

Jazz disallows the “extended OR” syntax of COBOL because it is ambiguous.

 

When a condition is a BOOLEAN field you can leave out = true.  With definition

    IsCustomer BOOLEAN,

the IF statement

IF W.IsCustomer = true THEN;

is equivalent to

IF W.IsCustomer THEN;

 

See JazzLRM_IF for more information about IF statements, and for more information about conditions see JazzLRMConditions.

CASE statements

CASE statements are ideal for multi-choice logic where you’d otherwise have to write a long list of IF ELSEIF …. ELSEIF statements.  For example,

 

CASE (EIBAID);

    WHEN (DFHENTER) THEN;

        Custf.Region = 1;

    WHEN (DFHPA1 OR DFHPA2 OR DFHPA3 OR 'x');

        Custf.region = 2;

    ELSE;

        Custf.region = 3;

END CASE;

 

Mostly it is a matter of convenience whether you write logic with IF ELSEIF or with CASE.  For more details see JazzLRM_CASE

LOOPS – the FOR statement

PROCESS creates an I/O loop, but sometime you want your program to loop without I/O, for example handling every line of a repeating section of a screen, or every element of a table.  The FOR statement, which combines elements of COBOL’s PERFORM, PL/I’s DO, and VB’s FOR, is used for this.  

 

Here we use FOR to add up the values in Area.MonthlySales.

DEFINE Area DATA(

            MonthlySales(12) MONEY(5,2),

            SalesTotal MONEY(7,2));

FOR Work.Mth = 1 TO 12;

            Area.SalesTotal += Area.MonthlySales(Work.Mth);

END FOR;

 

There are three forms of FOR: -

1.            FOR INDEX is used as above (the keyword INDEX can be omitted) to step through a loop varying an index variable.  The index variable, Work.Mth in the example above, must be a number and usually has type SMALLINT.  The default step is 1, but STEP may specify other increments. 

 

2.            FOR TABLE steps through a table.  TABLE is implied if the FOR variable has one or more dimensions. Thus the loop above could be written

FOR Area.monthlySales(work.mth);

            Area.SalesTotal += Area.MonthlySales(Work.Mth);

END FOR;

This is preferable because Jazz can infer the table extent, avoiding error possibilities when the program is modified.   Here’s another example: here we are setting all elements of a 6-dimensional table to 555.  Also, by giving unqualified index names in the FOR statement Jazz has automatically defined suitable index variables: -

DEFINE D DATA(

    Gr1 (2,4,8) GROUP,

        Z (3,5,7) SMALLINT,

        end GROUP);

*   Table Form.  Will generate nested loops

FOR d.z(IX1, IX2, IX3, IX4, IX5, IX6);

    D.Z(JZ.IX1, JZ.IX2, JZ.IX3, JZ.IX4, JZ.IX5, JZ.IX6) = 555;

END FOR;

 

3.            FOR EACH steps through all possible values of a coded field.  Thus with

DEFINE MyTypes DATA(

    Month CODES(January,February,March,April,May,June,July,August,September,October,November,December));

            a loop to print January, February etc is: -

FOR EACH mytypes.month;

    PRINT (mytypes.month);

END FOR;

 As above with INDEX and TABLE, the keyword EACH may be omitted.  It is implied if the FOR variable has the CODES property.

 

For more information see JazzLRM_FOR.

Reusing Logic:  Introduction

Most programming languages provide a way of re-using logic, so that if you have to do something from two or more places in your program you don’t have to write the logic twice, instead you write it in some kind of routine and invoke this routine from each of the places in your program where the logic is needed.  Even when the logic is used only once you may find that writing logic within a routine makes your program clearer, allowing you to focus on either the higher level logic or the detail of the calculation.

 

A routine may be part of your overall program, or it may be a separate unit, possibly written in another language and running in a different environment.  Jazz supports both kinds of routines. 

Internal Logic: ROUTINE and PERFORM

Where you want some logic to be used more than once in your program, or if you simply want to tuck it away to make your program easier to understand, you can use ROUTINE and PERFORM: -

 

1.         Some logic can be written between ROUTINE and END ROUTINE statements.  For example: -

ROUTINE CalculateDiscount;

    R1.SalesTotal = 0;

    FOR R1.MonthlySales(IX);

        R1.SalesTotal += R1.MonthlySales(JZ.IX);

    END FOR;

    IF R1.SalesTotal > 1000 THEN;

        W.Discount = true;

    ELSE;

        W.Discount = false;

    END IF;

END ROUTINE CalculateDiscount;

 

2.         Where this logic is wanted you write PERFORM: -

PERFORM CalculateDiscount;

There may be many such PERFORM CalculateDiscount; statements in your program.

 

PERFORM statements can be written anywhere, but ROUTINE/END ROUTINE must not be within another ROUTINE or any other logic (IF, FOR, etc).   They are typically written at the beginning or end of a program.

 

COBOL programmers will recognise this as the same as COBOL’s concept of performing a paragraph.  As with a COBOL paragraph, or a PL/I internal procedure, the routine uses data defined earlier in the program.   However a little like PL/I’s internal procedure and methods in VB, they can be passed parameters. To do this,

 

1.         Somewhere in the program that precedes both the ROUTINE statement and the first PERFORM referring to it there will be a DEFINE statement defining the parameters.  Here’s an example of a parameter definition: -

DEFINE CalculateDiscount PARAMETERS ROUTINE DATA(

    Parm1 LIKE types.month INPUT,

    Result BOOLEAN OUTPUT);

 

A PARAMETERS definition defines the formats of data passed to and from a routine. The data type is PARAMETERS ROUTINE, and the individual elements are defined like any other field or group except that they have an additional property, INPUT, OUTPUT, or INOUT.  Here we see that Parm1 is INPUT to Calculate Discount, i.e. it is passed from the PERFORM to the routine. Result on the other hand is set by CalculateDiscount so it will have a value put into it on return from the PERFORM.

 

2.         The PERFORM statements must name compatible fields in their argument list: -

PERFORM CalculateDiscount(W.CurrentMonth, W.Discount);

 

The arguments do not have to be identical in format to the parameters, as long as assignments between the parameter and argument would be valid.

 

3.         Within the routine logic can refer to these variables by their parameter names: -

ROUTINE CalculateDiscount;

    IF calculatediscount.parm1 = december THEN;

        CalculateDiscount.Result = true;

    END IF;

    R1.SalesTotal = 0;

    FOR R1.MonthlySales(IX);

        R1.SalesTotal += R1.MonthlySales(JZ.IX);

    END FOR;

    IF R1.SalesTotal > 1000 THEN;

        CalculateDiscount.Result = true;

    ELSE;

        CalculateDiscount.Result = false;

    END IF;

END ROUTINE CalculateDiscount;

 

In fact, the program can refer to these CalculateDiscount. names anywhere following the DEFINE: this is not a true implementation of scope such as you find in PL/I, VB, and other modern languages.  At the PERFORM the argument value is assigned to any INPUT or INOUT parameter, and OUTPUT parameters are set to their default value (usually blank or zero). After the PERFORM values are assigned from INOUT and OUTPUT parameters to the corresponding arguments.

Best Practice when using Parameters

Since the DEFINE must precede both PERFORM and ROUTINE it is recommended that you code the DEFINE and ROUTINE into a COPY book which you can write towards the top of your program.

 

See JazzLRM_Paragraph for more information about PERFORM and ROUTINE.

External Routines – CALL and PARAMETER Definitions

Like most languages, including COBOL, you can invoke a pre-compiled subprogram, passing it parameters, and receiving modified parameters back.  

CALL RTN3(Work.X, Work.Y, Work.Z);     

The routine RTN3 is not part of the current program.  It may be written as a Jazz SUBPROGRAM, but it might also be written in another language (Assembler? COBOL? PL/I?)

 

Unlike COBOL, Jazz doesn’t just accept any old CALL statement, leaving it to you to find out that you’ve made a mistake with the parameter formats by attempting to run your program. Instead, in Jazz you pre-define the parameter formats, with COPY Rtn3; expanding to something like: -

DEFINE Rtn3 PARAMETERS BOTH DATA(

    X SMALLINT INPUT,

    Y CHAR(20) INPUT,

    Z DATE OUTPUT);

 

The use of predefined parameter lists makes the CALL statement much more convenient in Jazz than in COBOL.  Firstly, it means that errors are detected errors early when they are easy and cheap to correct.  For example, if you write

            DEFINE W DATA (…);

            CALL Rtn3 (W.X, W.Y, W.Z);

you’ll get errors for any of these arguments that are incompatible with the parameters defined for it.  In this case, the first argument must be a number, the second a character string, and the third a date field.

 

Secondly, if the arguments are different, but compatible, then Jazz will create parameters of the correct format by inserting conversions on either side of the actual CALL.  Thus if W.X does not have format SMALLINT, but it DOES have another numeric format, then before the COBOL-Level CALL Jazz inserts an assignment from W.X to RTN3.X, ensuring that the routine receives exactly the format that it expects. 

 

Thirdly, with INPUT parameters you can use constants in the CALL statement: -

            CALL Rtn3(123, 'Y Value’, W.Z);

You don’t need to create working-storage fields of the appropriate type and use MOVE to set these before the CALL.

 

Of course with parameter definitions written into COPY definitions they will be the same in all programs calling the routine.

Parameter Definitions

A PARAMETERS definition is like any other except for a few extra features: -

1.            The sub-type, BOTH in the example above, defines the environments in which the subprogram can be used.  It will be one of BOTH , BATCH ,or CICS:-

a.    Use BOTH when the subprogram is available in both batch and CICS (including web services) environments.  These subprograms may not use any I/O statements (PROCESS, GET, PRINT, etc), nor any communication statements (SEND, INVOKE).

b.    Use BATCH for subprograms that are only available for use in a batch environment.  These subprograms can use I/O statements such as PROCESS, GET, PRINT, but may not use any CICS-specific statements such as SEND or INVOKE.

c.     Use CICS for subprograms that can be used only in the CICS environment.  These may use I/O statements (GET, PROCESS etc) as well as communication statements like SEND, and they have access to the CICS control blocks such as EIB and COMMAREA. With this option the first two arguments passed are the CICS control areas, so that the Jazz statement

     CALL Rtn2(Rtn2.X);

becomes COBOL

   CALL 'Rtn2' USING DFHEIBLK DFHCOMMAREA X OF Rtn2 .

2.            Parameters have property INPUT, OUTPUT or INOUT.  INPUT parameters are passed to the subprogram, but are not changed by it.  OUTPUT parameters are set by the subprogram, but no values are passed.  Jazz will initialise the parameter to its default values (usually blanks and zeros).   INOUT is passed to the subprogram, and a value (which may have been modified) is returned.

3.            Parameters may include property OPTIONAL.

4.            Parameters may have type UNSPECIFIED. This allows the parameter to have any type. UNSPECIFIED is usually used in situations like this, where Rtn has a record as it’s argument but there are several different record formats that it might be passed: -

DEFINE Rtn PARAMETERS BATCH DATA(

    R UNSPECIFIED);

DEFINE R1 DATA(

    x SMALLINT,

    Y CHAR(100),

    Z DECIMAL(3));

DEFINE R2 DATA(

    X2 SMALLINT,

    Y2 CHAR(6),

    Y2a DECIMAL(7,2),

    Z INTEGER);

CALL Rtn(R1.*);

CALL Rtn(R2.*);

Care is needed with UNSPECIFIED arguments, which tells Jazz not to do any checks for the parameter/argument compatibility.  R1 is 104 bytes, while R2 is only 16.  If Rtn executes a statement like

            R1.Y = COBOL.SPACES;

from the second CALL statement which passes R2.*, then positions 2 to 102 of R2 will be set to SPACES.  But R2 is only 16 bytes long!  Unpredictable errors will result at run time.

Note that UNSPECIFIED is not available with type PARAMETERS ROUTINE.   This is because with a ROUTINE parameters are not passed by reference, but instead Jazz generates assignments to/from a working-storage data area that is used by the ROUTINE.

CICS or Batch and CALL Statements

In a CICS environment a CALL statement always passes two CICS control blocks as its first two arguments: -

            CALL CICSR (Parm1, Parm2);

becomes the COBOL statement

      CALL ‘CICSR’ USING DFHEIBLK DHFCOMMAREA Parm1 Parm2.

Within the CICSR routine, the logic may start with

PROCEDURE DIVISION USING DFHEIBLK DHFCOMMAREA Parm1 Parm2.

or with

PROCEDURE DIVISION USING Parm1 Parm2.

It doesn’t seem to matter.

 

In a batch environment

            CALL BATCHR (Parm1, Parm2);

becomes

      CALL ‘BATCHR’ USING Parm1 Parm2.

and PROCEDURE DIVISION USING will not reference DFHEIBLK or DFHCOMMAREA.

Modular Logic: Writing Subprograms

You can write routines like Rtn2 in Jazz, either by using the New/Logic/New Subprogram dialog, or by starting with a blank screen.  To create a routine named XXX the process is: -

1.            Start with COPY XXX;  

2.            Create a statement within this COPY book starting DEFINE XXX PARAMETERS [BATCH | CICS | ANY] DATA(…

3.            Add Data definition.  Each level 1 name is an argument/parameter.  For example

DEFINE JZTM01 PARAMETERS ANY DATA(

    TimeIn TIME INPUT,

    DPIC CHAR(12) INPUT,

    TimeOut CHAR(12) OUTPUT,

    TimeOutLth SMALLINT OUTPUT);

This anticipates statements in which subroutine JZTM01 will be invoked by statements like

CALL JZTM01 (Tm, '99#99#99.99', TPrint, TLth); 

For more information, see https://www.jazzsoftware.co.nz/Docs/JazzLRM_Type.htm#_Toc89848146

4.            Write the logic of XXX as you would a normal program, except that it starts with SUBPROGRAM instead of PROGRAM.  Thus its first two lines will be
COPY XXX;
SUBPROGRAM XXX [CICS];

If the parameter definition includes CICS then the SUBPROGRAM statement must also include CICS.

 

If you write a CICS subprogram then it can only be used in a CICS environment, either classical CICS or web services. 

 

If you write a non-CICS subprogram to be used in both environments: -

a.            If you have separate batch and CICS libraries then you will need to compile and link it into both libraries. 

b.            You may not use any I/O statements, or batch-only statements (PRINT etc) or CICS-only statements (SEND, LINK, ACCEPT with INSCREEN).