Application Development


Chapter 19. Programming in JPL

JPL is an interpreted language with a C-like syntax. Because you can write and edit JPL code within the editor, you can write and execute procedures without interrupting your development work flow. You can also write JPL procedures directly to a library and call them via event-type properties provided in the Properties window for screens and widgets. Use JPL for rapid prototyping and later rewrite the procedures in C. Or leave the code unchanged; JPL can get most jobs done quickly and efficiently.


JPL Modules and Procedures

JPL modules contain one or more procedures written in JPL. You create modules through Panther's own JPL editor (described under "Writing JPL in the Editor") or in a text editor. Screen modules are created through the FocusJPL Procedures property; widget modules through the ValidationJPL Validation property; and report modules through the InclusionsJPL Procedures property. Widget, screen and report JPL modules are saved in the screen binaries; Panther automatically reads these at the appropriate stage of program execution. You can also create library modules directly in the editor workspace. For faster access, you can install library modules in the application's memory-resident list.

Module Structure

A module contains one or more procedures. The first procedure of a module can be unnamed. All subsequent procedures are named through JPL's proc command. For example, the following module has two procedures; the first is unnamed, the second named warning:

if actual_cost > forecast_cost
call warning()

proc warning()
msg emsg "Value exceeds budget forecast."

Unnamed and named procedures are different in two ways:

Refer to Figure 19-1 to view a sample JPL module.

Parameters

The proc command can specify parameters that receive arguments passed by the procedure's caller. You specify parameters as a comma-delimited argument list within parentheses. The procedure's caller can pass in constants, global constants, variables, or colon-expanded variables as arguments. Panther passes arguments by value—that is, the called procedure gets its own private copies of the values in the calling procedure's arguments. This means that the called procedure cannot directly alter variables in its caller; it can only alter its own copies.

For example, the earlier warning procedure is modified below; it now expects its caller to supply two arguments that are copied to actual and forecast. A more informative message is produced by using the colon-expanded values of these variables:

if actual_cost > forecast_cost
call warning(actual_cost, forecast_cost)

proc warning (actual, forecast)

vars diff = actual - forecast

msg emsg "Value in :actual exceeds budget forecast by $:diff"

Passing Standard Arguments to JPL Procedures

If a procedure is called as an event function for a widget or screen—for example, as a screen's exit function—and the function name omits parentheses, Panther automatically passes standard arguments to the procedure. These arguments indicate the current status of the widget or screen. Their number and type vary; for example, two arguments are passed for screens, four for widgets, and three for grid widgets. The procedure's proc statement must contain the appropriate number of parameters in order to receive these arguments.

For example, you might define the following procedure in a screen's JPL module in order to handle grid data:

proc gridProc( basefld, occ_no, status )

You can set gridProc in any of several grid properties. When Panther calls this procedure at runtime, it sets parameters basefld, occ_no and status with the three standard arguments associated with grids. So, if a grid's Row Entry Function (row_entry_func) property contains the string gridProc, Panther calls the procedure each time the cursor enters a new row and sets its three parameters to the grid's base widget number, the number of the current occurrence, and an integer bitmask that describes why the procedure was called.

Note: Precedence is always given to arguments that are specified in the property string. For example, if a grid widget's Entry Function property contains the string gridEntry(val), Panther supplies the contents of val to procedure gridEntry. If arguments are explicitly omitted through empty parentheses (), Panther does not supply the standard arguments.

The unnamed procedure of the module for a screen or widget module is always supplied standard arguments that indicate the current status of the screen or widget. To receive these arguments, the unnamed procedure must have a parms statement.

For more information about the standard arguments available for screens and widgets, refer to "Calls from Screens and Widgets." The parms command description shows how to declare parameters in an unnamed procedure.

Return Types

An unqualified proc command returns an integer value. You can specify to return a string or double precision value by qualifying the proc command with the keywords string or double, respectively. For example, the following sequence of statements passes data from variables data1 and data2 to procedure process_input, which is defined to return a double precision value. This return value is used to determine whether the if statement evaluates to true or false:

if process_input(data1, data2) > 0.16667
...

double proc process_input(d1, d2)
vars retval
//process d1 and d2 values
return retval

Procedure Execution

Procedure execution begins with the first statement of the procedure and continues to the end of the procedure, or until a return statement executes. If an execution error occurs, Panther aborts execution of the current procedure, posts an error message, and returns to the procedure's caller. In all cases, a procedure returns to its caller when execution ends.

Panther interprets each physical line as a separate statement, unless the line ends with the backslash (\) continuation character. JPL physical lines can be up to 253 characters in length.

Control Flow Statements

By default, Panther executes JPL procedures sequentially from start to finish. You can use JPL's if, else, for, while, switch, case, default , break, and next statements to manipulate the order of statement execution. JPL has no limit to how many levels deep you can nest control flow statements.

Conditional and loop statements (if, else, for, while, switch) allow curly braces { } as blocking characters so you can conditionally execute multiple statements. Each blocking character must have its own line except to specify a null statement—{}. If you nest multiple blocks, make sure that all block characters are paired correctly.

The following example shows an if statement that contains a block of two statements:

if cost > 1000
{
exceptions = exceptions + 1
msg emsg "The cost is very great."
}

A left and right brace on the same line indicate a null statement. In the following example, the for statement keeps count while testing a condition. Because no other statements are required, the for block consists of a null statement:

for i = 1 while str(i, 1) != " "
{ }

Included Modules

Panther procedures (including the unnamed procedure) can contain include statements that specify a JPL library module. At runtime, Panther compiles and inserts this module within the calling procedure before execution begins.

Include statements have the following syntax:

include module 

where module is any JPL library module. The included module can also contain its own include statements. You can nest up to eight include statements.

Panther looks for module in this order:

  1. Memory-resident modules.
  2. Library module in all open libraries.

Comments

You can enter commented text in JPL in three ways:

Sample JPL Module

Figure 19-1 This sample screen-level JPL module makes a JPL module public and displays additional options for front desk employees.


Module Types

Panther lets you create the following types of JPL modules:

An application's ability to access the procedures in a JPL module depends on its type and how it is loaded and called. For instance, Panther executes a widget module only during widget validation. The procedures in this module can only be called by each other and are invisible to the rest of the program. Conversely, named procedures in screen modules are available to the entire application while the screen is active.

Panther's ability to access library and memory-resident modules depends on how they are loaded and called. If you load a module into memory as a public module, its named procedures are visible to the entire application and can be called directly. If a module is not public, the library in which it resides must be open and the module can only be called by its filename; this invokes the module's unnamed procedure. The named procedures in this module are accessible only through its unnamed procedure.

The following sections describe each module type and how Panther executes it.

Widget Modules

Widget modules are associated with individual widgets. You create and modify widget modules through the widget's JPL Validation property. This property is available for most widget types, including grids and groups. When you select this property, the JPL Program Text dialog box opens. You use the dialog box's editing window to enter and modify JPL code. For more information on using this editing window, refer to "Writing JPL in the Editor."

Panther executes a widget module only when it performs validation for the widget. In the case of data entry widgets such as text widgets, validation occurs when the user exits via TAB. For push buttons, radio buttons, check boxes, list boxes, and toggle buttons, validation occurs when the widget is clicked with the mouse or otherwise activated, for example, by the NL key. Because a widget module is accessible only to its widget, use it to perform tasks that are specific only to that widget.

The first procedure of a widget module must be unnamed. The unnamed procedure in a widget module is always this module's entry point. The module can also include named procedures; however, these can only be called by other procedures in the same module. When you save the module, the editor automatically compiles it. If an error prevents compilation, Panther issues a message and returns you to the JPL Program Text dialog box, where you must correct the error.

Because Panther saves the module as part of the widget, you can view and edit this module only through the editor. When you copy this widget to another screen or to the repository, Panther copies the module along with other widget data.

Executing Widget Modules

Panther calls a widget module after it executes the widget's validation function, if one exists. Panther first executes the module's unnamed procedure and passes the standard arguments associated with widget processing. For widgets such as single line text widgets, four arguments are passed that describe the widget and its current status: its field number, contents, occurrence number, and a set of context-sensitive flags. The unnamed procedure must have a parms statement in order to receive these arguments. For more information about arguments for different widget types, refer to "Calls from Screens and Widgets."

Screen Modules

Screen modules are associated with specific screens. All the named procedures in a screen module are available to the application while the screen remains active. You create and modify screen modules through the screen's JPL Procedures property. When you select this property, the JPL Program Text dialog box opens. You use this dialog box's editing window to enter and modify JPL code. For more information on using this editing window, refer to "Writing JPL in the Editor."

The first procedure of a screen module can be unnamed; an unnamed procedure is optional. All subsequent procedures must be named. When you save the module, the editor automatically compiles it. If an error prevents compilation, Panther issues a message and returns you to the JPL Program Text dialog box, where you can correct the error.

Because Panther saves the module as part of the screen, you can view and edit this module only through the editor. If you save the screen as another file or as a repository entry, Panther copies the module along with all other screen data.

Executing Screen Modules

When you open a screen at runtime, Panther loads all its named procedures into memory. It then executes the screen module's unnamed procedure, if any. Panther passes the two standard arguments associated with screen processing to this procedure: the name of the screen and a set of context-specific flags. The unnamed procedure must have a parms statement in order to receive these arguments. For more information about these arguments, refer to "Calls from Screens and Widgets."

While the screen is active—that is, displayed on top—every named procedure in its JPL module can be called. You can use these procedures to perform any task required by the screen.

Panther executes the unnamed procedure only when the screen first opens, after which it executes the global screen function and the screen's entry function, if any. Panther does not execute a screen module's unnamed procedure on subsequent exposures of an already open screen—for example, when a child or sibling screen closes.

Report Modules

Report modules are similar to screen modules; each report module is associated with a report and saved with the report binary. All named procedures in a report module are available while the report is running.

Unlike screen modules, all report procedures should be named and can be accessed by a corresponding call node in the report structure or called as a subroutine by another process.

You create and modify report modules through the report's JPL Procedures property. When you select this property, the JPL Program Text window opens. For more information on using this editing window, refer to "Writing JPL in the Editor." When you save the module, the editor automatically compiles it. If an error prevents compilation, Panther issues a message and returns you to the JPL Program Text window, where you can correct the error.

External Modules

External modules are modules saved to disk in libraries or in a file and are therefore not saved or associated with any particular Panther screen. You can extract library modules and install them in the application's memory-resident list. Unlike widget and screen modules, external modules are available to the entire application at any time. Panther finds the modules in memory, in open libraries, or on disk. For details on the search order for library modules, refer to "Precedence of Called Objects."

External modules are accessible to the application in two ways:

Library Modules

You can create library JPL modules from within the editor (choose FileNewJPL) or with any text editor (and add them to a library). You can also write the contents of widget and screen JPL modules to a library, thereby making them accessible to other screens and to the application as a whole if necessary.

File Modules

File modules can be stored in ASCII, in binary, or in an ASCII/binary format. Modules that are stored as ASCII files are easy to modify and are available to the entire application. However, because Panther must recompile the module each time it is called, an ASCII file module also incurs more processing time than screen or widget modules. To improve performance, precompile the module with jpl2bin. If an error occurs during compilation, Panther issues an error message and returns to the module's caller.

Module Compilation

Panther compiles library modules when you save them from within the editor. The module is saved in binary format and Panther uses this compiled format at runtime. If the compiler finds syntax errors, it issues a warning and lets you save the module in an uncompiled format.

The source file is also stored to the library. Library module names should conform to operating system conventions. For filtering purposes, use a .jpl extension on library modules that you create within the editor.

You can call a library module only if its library is already open (via sm_l_close or on startup via the SMFLIBS variable). Panther loads the module into memory each time you call it.

If you create the JPL module outside of the Panther environment, you can later store the module in a Panther library. To store a disk file module in an application library, you can perform either of the following procedures:

From the editor:
  1. Be sure a library is open (choose FileOpenLibrary).
  2. Choose FileNewJPL. An untitled JPL dialog box opens.
  3. Choose EditRead File. The Insert JPL File dialog box opens.
  4. Select the desired JPL file and choose OK. The text is inserted at the cursor position in the JPL editing window.
  5. Choose FileSave. The Save JPL Module dialog box opens, where you can choose the library in which to store the module.
From the library table of contents:
  1. Compile the module with the jpl2bin utility from the command line.
  2. Be sure a library is open (choose FileOpenLibrary).
  3. Open the Library Table of Contents dialog box (choose ViewLibrary TOC).
  4. Choose Add...
  5. Select the desired JPL file and choose OK.
From the command line:
  1. Compile the module with the jpl2bin utility.
  2. Add the module to the library using the formlib utility.

Memory-Resident Modules

You can add a JPL module to an application's memory-resident list. Making a JPL module memory-resident reduces I/O time. The module is held in memory during the life of the application; therefore, ample memory might be required to run your application.

You add a module to the memory-resident list in these steps:

  1. Extract the module from its library with formlib.
  2. Compile the module with jpl2bin.
  3. Convert the binary file to source with bin2c.
  4. Install the array with the function sm_formlist.

You must recompile your application after creating or editing a memory-resident list. For more information on memory-resident lists, refer to "Including Memory-Resident Components."


Writing JPL in the Editor

JPL modules are created within the editor:

The JPL modules created through these properties are saved with the screen binary in an ASCII and binary format. The ASCII version allows you to view and edit the JPL; the binary version is used at runtime.

Screen- and Report-Level JPL

Selection of the screen and report JPL property invokes the JPL Program Text window, where you can examine and edit the JPL code currently stored with that property:

Figure 19-2 Create or edit screen-and report-level JPL in the JPL Program Text dialog box.

Screen- and report-level JPL is compiled and saved with the screen binary. Therefore, if the JPL compiler detects a syntax error, you must correct the error before you can save the module. However, you can save it to disk by choosing FileSave AsASCII Text File.

When writing or editing screen-and report-level JPL, you can take full advantage of the editor's File and Edit menu options.

To access the default text editor, choose Editor, or select the Direct to External Editor option to automatically open the window using the default text editor.

Widget-Level JPL

Figure 19-3 Create or edit widget-level JPL code in the JPL Program Text dialog box.

Widget-level JPL is compiled and saved with the screen binary. Therefore, if the JPL compiler detects a syntax error, you must correct the error before you can save the module. However, you can save it to disk by choosing the Save File button.

Since the widget-level JPL dialog box is modal, you cannot access the editor workspace menus.

Library Modules

You can create and edit library JPL modules within the editor by opening a library and then choosing FileNewJPL of FileOpenJPL to access an existing module. The JPL text dialog box opens.

Figure 19-4 Library JPL modules can be created and updated from within the editor.

The name of the module and the library in which it resides are displayed in the title bar in the form: module@library_name.

Use the Edit menu options to copy, cut, and paste text, use FileSave options to save the module to an open library, or use FileSave AsASCII Text File to save the module as a disk file.

The JPL is compiled when you save the module to a library. If the compiler detects a syntax error, a warning is issued and you can choose to correct the error or save the module in an uncompiled format (Save AsASCII Text File) which you can collect later.

Using Your Own Editor

You can type your JPL directly into the dialog box, or you can invoke your local editor, for example, Notepad in Windows or vi in UNIX, by choosing EditExternal Editor (for widget-level JPL, choose the Editor button in the JPL Program Text window). If the OptionsDirect to External Editor option is selected, your preferred text editor is invoked immediately on entry into the JPL dialog box. The local editor is defined by the configuration variable SMEDITOR. When you exit the editor, you are returned to the JPL dialog box, which contains your latest edits.

You can also use your favorite text editor outside of the Panther workspace to write your JPL modules. Later you can compile the modules and import them to the appropriate library.

Note: If you exceed the maximum line length of 253 characters, Panther issues an error message when you try to return to the dialog box and returns you to your editor to make the necessary corrections.

Inserting JPL To and From Disk

You can write and read code to and from disk files as well. To read a disk file into the JPL window, choose EditRead File (for widget-level JPL, choose the Insert File button). To save a JPL module to disk, choose FileSave AsASCII Text File (for widget-level, choose the Save File button. These invoke the Insert JPL Text File and save JPL Text File dialog boxes, respectively. When you read a disk file into a module, Panther inserts its contents at the cursor's current position.

To insert JPL from another library, choose EditInsert From Library (for widget-level, choose the Insert button).

The dialog box accepts line lengths of up to 253 characters. If you try to read from a file that contains longer lines, Panther copies all text preceding the erroneous line into the editing window, then issues an error message.

Compiling and Saving

To compile and save a JPL module to its originating library, choose FileSave AsLibrary Member (for widget-level, choose the Insert button). If an error prevents compilation of a library module, a message is issued and you can correct the error or save the module in an uncompiled format which you can correct later.

To compile and save screen- and report-level JPL, choose FileCloseJPL (for widget-level, choose the OK button). If an error prevents compilation while compiling screen_ or widget-level JPL, the editor issues a message and returns you to the JPL dialog box.

Note: Library modules that are referenced (by an include statement) within a module are not checked for compilation errors.


Calls

An application can call JPL modules and their named procedures from various screen and widget hooks, and from control strings. The same calling options are available for any installed C or built-in function. Unless otherwise indicated, all references to procedures in this section apply equally to JPL procedures and installed C functions. For more information about installing C functions, refer to Chapter 20, "Writing C Functions."

Panther provides several ways of issuing calls:

You can also call JPL from C through the library function sm_jplcall.

A screen module's named procedures can be called from outside the module while the screen is active. Named procedures in library modules are accessible if the module is public; otherwise, the procedures can be called only by the module's unnamed procedure. Named procedures in a widget module can be called only by the module's unnamed procedure.

Arguments

All calls can supply comma-delimited arguments to their corresponding parameters. Enclose the arguments in parentheses. If the procedure takes no arguments, use the void argument specifier (). You can pass the following as arguments:

Panther passes arguments by value, so changes to the receiving parameter's value leave its corresponding caller's argument unchanged. If you call an installed C function, you must prepare it for installation with the correct macro (SM_INTFNC, SM_STRFNC, SM_DBLFNC, or SM_ZROFNC) in order to pass arguments by value to that function. Refer to Chapter 20, "Writing C Functions," for more information about installing functions.

Note: If the message file sets SM_DECIMAL to a comma (,), insert a space between numeric constant arguments; otherwise, JPL interprets the comma that separates these arguments as a decimal point. Both methods shown in the following examples are equally successful in avoiding this problem:

ret = (1, 2)
ret = (3 ,4)

Returns

A procedure always returns to its caller with a return value—either integer, string, or double, according to the procedure definition. If the procedure lacks an explicit return statement, or the return statement omits a return argument, the procedure returns to its caller with a value of 0 or an empty string. If an execution error causes the procedure to return prematurely, it returns with -1.

Calls from Screens and Widgets

You can specify modules and procedures in various properties of screens and widgets—for example, in a screen's Exit Function property. If you call a JPL module or procedure and supply no arguments, Panther automatically passes arguments that describe the state of the calling screen or widget. The called procedure must define the parameters needed to receive these arguments.

You can supply the same arguments to a C function if it is installed appropriately for the object that calls it. For example, if a C function is installed as a screen function, it can be called on screen entry and exit and receive arguments that describe the state of the screen. You can install C functions to be called from a screen, widget, group, or grid. For more information, refer to Chapter 44, "Installed Event Functions."

Table 19-1 describes the properties that can specify calls to a JPL procedure and the default arguments that are passed:

Table 19-1 Default arguments passed to procedure

Caller Property Arguments

Screen

Entry Function
Exit Function

screenName, flag

Widget

Entry Function
Exit Function
Validation Function

widgetNum, widgetContents, occurrenceNum, flags

Grid widget

Entry Function
Exit Function
Row Entry Function
Row Exit Function
Validation Function

baseWidgetNum, occurrenceNum, flags

Tab card widget

Card Entry Function
Card Exit Function
Hide Function
Expose Function

cardObjectId, flags

Group

Entry Function
Exit Function

groupName, flag

For example, if a widget's Exit Function property specifies the procedure fld_xt and no arguments are specified, Panther automatically passes in four arguments to this procedure; the second of these arguments is the widget's current value. fld_xt gets this value in parameter val and tests it as follows:

proc fld_xt (num, val, occ, flg)
{
if val = 'MR' sex = 'M'else sex = 'F'
return
}

The flag or flags that Panther passes are bit values, which you manipulate through JPL's bitwise operators & (AND), | (OR), and ~ (one's complement). You can test these flags for conditional processing when you use the same procedure to handle different execution stages of a Panther object—for example, entry and exit of a widget. For information on flags that are set:

Using a Memory-resident Screen

If a screen is memory-resident, Panther passes a null string to the called procedure instead of the screen's name.

Calls from Control Strings

You can use control strings to call procedures on specific input, for example, keyboard input or menu choices. You issue calls from a control string as follows:

^[ (target-string  [ ; target-string ] ) ] name [ (arg-spec ) ]

where name can be the name of a procedure or module, and arg-spec is one or more comma- or space-delimited arguments to pass to parameters in name. The control string can optionally test the return value against one or more semicolon-delimited target strings. Each target string has this syntax:

[test-value = ] control-string

Panther compares name's return value to each test-value, reading from left to right. If it finds a match, it executes the specified control string. If you omit a test value, Panther executes the control string unconditionally. The control string can itself contain a JPL call with its own target strings; you can thereby nest multiple control strings with recursive calls.

For example, given this control string for a push button:

^(-1=^(^jm_exit)cleanup; 1=&welcome_scr)process

Panther calls the JPL module or procedure process when the user chooses this push button. It then evaluates the return value from process to determine its next action: either to call cleanup, or to invoke the welcome_scr screen. On return from cleanup, Panther unconditionally calls the built-in function jm_exit.

Refer to Chapter 18, "Programming Control Strings," for more detailed information about control string syntax.

JPL Call Command

You call a JPL procedure or module through the call command from other procedures or modules. The call command uses this syntax:

call executable ( [arg-spec] )

where executable can be the name of a module or procedure, and arg-spec is one or more comma-delimited arguments optionally to pass to parameters in executable. The entire argument list is enclosed in parentheses.

Inline Calls

Because JPL evaluates a procedure call to its return value, you can embed a procedure call within any expression. The following statement embeds a call to the credit_eval procedure:

if credit_eval() == 1
msg emsg "Creditworthy applicant"
else if credit_eval() == 0
msg emsg "Reject application"

You can also specify a procedure as an argument to another procedure. In the following statement, JPL first calls foobar, then passes its return value into foo as that procedure's second argument:

ret = foo(a, foobar(b), c)

Precedence of Called Objects

When Panther processes a call, it cannot know whether the called object is a JPL module, a JPL procedure, or an installed function. Panther attempts to execute a JPL call by searching for functions and JPL modules or procedures in this order:

  1. An installed C or built-in function.
  2. If the call is issued from a JPL module, a named procedure in that module.
  3. A named procedure in the current screen's module.
  4. A named procedure in a public module. If the procedure name exists in more than one public module, Panther uses the procedure in the most recently loaded module.
  5. A memory-resident module.
  6. A library module in an open library.

Variables

JPL recognizes four kinds of variables:

This chapter shows how to declare and reference variables in JPL.

Declaring JPL Variables

Earlier sections in this chapter showed how JPL declares parameter variables through the proc and parms commands. You can also declare a JPL variable with the vars command. JPL variables are not typed; you can assign a variable any string or numeric value. All values are stored as strings.

The vars command declares one or more JPL variables:

vars var-spec [, var-spec]

var-spec specifies the variable's name and properties as follows:

var-name [[num-occurs]] [(size)] [= init-value]

The following sections describe required and optional elements in a variable declaration.

var-name
The name of the variable, where var-name is a string that contains up to 31 characters. JPL variable names can use any combination of letters, digits, or underscores, where the first character is not a digit. Panther also allows usage of two special characters, the dollar sign ($) and period (.).

[num-occurs]
Optionally declares var-name as an array of num-occurs occurrences. The default number of occurrences is 1. For example the following statement declares dependents as an array of ten occurrences:
vars dependents[10]

(size)
Optionally specifies the number of bytes allocated for this variable; Panther allocates an extra byte for the terminating null character. The default size is 255 bytes. For example, the following statement declares the variable fname with a size of 15 bytes:
vars fname (15)

If the value assigned to a variable is too large for its allocated size, Panther truncates it. For example, if fname is programmatically assigned a value of Russell-Carrington, it accepts only the first 15 characters, Russell-Carring.

= init-value
Optionally initializes the variable to init-value, where init-value can be any constant, variable, or string or numeric expression. For example:
vars workweek = 5
vars avg_sale = @sum(sale_amt) / sale_amt->num_occurrences
vars name = fname##lname

If the variable is declared as an array, you can initialize its occurrences. For example:

vars ratings[5] = {"G", "PG", "PG-13", "R", "NC-17"}

Occurrence values are comma-delimited.

If no value is assigned, Panther initializes the variable to an empty string ("").

Declaring Global Variables

You can declare global variables that are recognized throughout the application with the following syntax:

global var-spec [, var-spec]

where var-spec specifies the variable's name and properties as follows:

var-name [ [num-occurs] ] [ (size) ] [ =  init-value]

Like the vars command, global can declare multiple comma-delimited variables; each declaration can optionally declare the global as an array, specify its size (1 to 255 bytes), and assign its initial value.

To reinitialize or clear a global variable, declare it again.

Panther Variables

Panther supplies several predefined variables where it stores application status information. These global variables (beginning with the character @) are automatically defined at application startup and maintained by Panther.

After each dbms statement is executed, one group of global variables contains any error, warning, or status information returned by the database engine. Refer to "Variables for Logging Error and Status Information" for information on the variables, such as @dmengerrmsg, available through the database interface.

In the transaction manager, global variables contain information about transaction manager processing, such as the occurrence being processed. For more information, refer to Chapter 36, "Runtime Transaction Manager Processing."

When a Web browser makes a request in a Web application, the HTTP header fields are stored in global variables starting with the characters @cgi. Refer to Chapter 11, "HTTP Variables," in the Web Development Guide for more information.

The @NULL variable can be used for any parameter in a C function that accepts NULL as an argument.

Caution: The Panther @ variables can be updated frequently. If a variable's value is needed for further processing, copy its value to another location.

Variable Scope and Lifetime

JPL's ability to reference a variable depends on the variable's scope and lifetime. LDB entries, widgets, and groups can be referenced by any module. LDB entries are available as long as their LDB remains loaded in memory. Widgets and groups are available as long as their screen is in memory. Global variables are available for the duration of the application.

Variables declared in an unnamed procedure are accessible to all procedures in the module; those declared in a named procedure are known only to that procedure.

Variables declared inside a procedure remain in memory until the procedure returns, while variables declared in the unnamed procedure remain in memory until the module returns. Two exceptions apply: variables declared in a screen module's unnamed procedure remain in memory until the screen exits; variables in a public module's unnamed procedure remain in memory until the module itself is removed from memory.

Colon Preprocessing

JPL's colon preprocessor expands any colon-prefixed variable to its literal value. This lets you reference variables in any JPL statement whose syntax otherwise excludes variables; for example, you can embed variables in a string. You can also supply JPL variables as arguments for several JPL commands that take only literal values as arguments, for example dbms and public.

The preprocessor expands colon-prefixed variables to their literal values before JPL executes the statement. For example, you can reference the variable acctno in a msg command, even though the command takes only a string value. For example:

msg emsg "I cannot find account number :acctno."

The colon preprocessor expands :acctno to its assigned value before execution. Thus, if acctno has a value of 91956, Panther executes the statement by displaying this message:

I cannot find account number 91956.

Conversely, the following statement:

msg emsg "I cannot find account number acctno."

yields this message:

I cannot find account number acctno. 

Notes: The colon preprocessor always expands a variable to a string value. You can use this in order to force treatment of numeric values as strings.

Syntax

A colon variable begins with a colon and ends with any non-expandable character, such as a blank or newline, as shown in the following syntax:

:var-name

Panther has two variations of colon variable syntax for applications that use its database interface, :+ and :=. For more information on these, refer to "Colon Preprocessing."

To prevent expansion of variables that contain colons, prefix the colon with another colon (::) or backslash (\:), or follow it with a space. In the first two cases, the colon preprocessor discards the first colon or the backslash. In the third case, the colon and following space are preserved.

Expansion

After Panther compiles and loads a JPL module, the colon preprocessor scans each statement from right to left for colons. When it finds one, it starts expansion during which it:

  1. Checks for a left parenthesis immediately after the colon, then begins to accumulate characters from left to right.
  2. If a left parenthesis exists, the preprocessor accumulates characters until it encounters a right parenthesis. Otherwise, it continues until it encounters a character that cannot be expanded, such as space or a quote character.
  3. Tries to identify this string as a variable according to the precedence rules described earlier under "Precedence of Called Objects."
  4. Expands the variable to its current value, then returns control to JPL for statement execution.

Controlling Expansion with Parentheses

Parentheses explicitly delimit three scope of expansion. For example:

vars ref x4
vars alpha[3] = {"bits", "centuri", "rays"}

ref = "alpha"
x4 = :(ref)[3] // Now x4 = rays

The colon preprocessor expands :(ref) to alpha. JPL then assigns the value of alpha[3]rays—to the variable x4.

Substring Expansion

If a substring specifier immediately follows a variable name, the colon preprocessor gets the specified characters from the expanded value. If you enclose the variable name with parentheses, the colon preprocessor ignores the specifier, and JPL uses the specifier when it executes the statement.

For example, given these variables and assignments:

vars xyz = "Belgium"
vars xy = "New Zealand"
vars abc = "xyz"
vars m

the following statement assigns the value New Zealand to variable m:

m = :abc(1, 2)

The colon preprocessor expands :abc(1, 2) to the first two characters of the expanded value that is, it expands :abc to xyz, then extracts xy from that value. After the expansion, JPL assigns to m the value of xy, which is New Zealand.

By contrast, examine the following statement, where the expanded variable is enclosed by parentheses:

m = :(abc)(1, 2)

This time, the colon preprocessor expands :abc to xyz. After the expansion, JPL executes the substring specifier on the value of xyzBelgium—and assigns its first two characters Be to m.

For more information, refer to "Substring Specifiers."

Array Expansion

Colon preprocessing recognizes the subscript, or index, of an array reference as part of the variable and expands it accordingly. If an array reference omits the array's occurrence number, the colon preprocessor concatenates all the non-blank array occurrences and inserts a space between each pair of occurrence values.

The following examples show how Panther expands array references, given these variable declarations and assignments:

vars xyz[3] = {"alpha", "beta", "gamma"}
vars alpha[3] = {"bits", "centuri", "rays"}
vars v = "alpha"
vars w = "xyz"
vars x1 x2 x3 x4 x5
x1 = xyz[3] // x1 = gamma
  1. The colon preprocessor expands :xyz[1] to alpha. Thus, :xyz[1][3] becomes alpha[3]. JPL changes the value of x2 to rays:
    x2 = :xyz[1][3] // x2 = alpha[3] = rays
  2. The colon preprocessor expands v to alpha. x3 then equals the third occurrence of alpha, which is rays. The parentheses enclosing v prevent the colon preprocessor from trying to expand the third occurrence of v:
    x3 = :(v)[3] // x3 = alpha [3] = rays
  3. The colon preprocessor tries to replace :v[3] with the third occurrence of v. Because v has only one occurrence, Panther displays an error message:
    x5 = :v[3] // error occurs because v[3] does not exist
  4. The colon preprocessor concatenates all non-blank occurrences of xyz, separating the occurrences with single blank spaces. :xyz must be enclosed in quotes; otherwise, Panther displays an error message because beta and gamma are not variables:
    x4 = ":xyz" 
    // x4 = ":xyz[1] :xyz[2] :xyz[3]" = "alpha beta gamma"

Reexpansion

By default, the colon preprocessor evaluates colon-expanded text only once, even if the expanded text itself contains another colon reference. For example, the following code yields display of the message Thank Goodness, it's :day:

vars day = "Friday"
vars period = "day"
msg emsg "Thank goodness it's :period"

To display the message Thank goodness it's Friday, append an asterisk (*) to the colon:

msg emsg "Thank goodness it's :*period"

When the colon preprocessor finds a reexpansion operator, it repeats expansion from the rightmost character of the expanded text. You can nest reexpansion operators to reexpand the same text more than once.


Constants

JPL has the following constant types:

Non-Decimal Number System Formats

In addition to decimal numeric constants, Panther supports octal, hexadecimal, and binary numeric formats. Panther recognizes these formats through a number's leading characters, shown in Table 19-2:

Table 19-2 Non-decimal numeric formats supported by Panther

Numeric format Leading characters Example

Binary

0b, 0B

0b1 + 0b2 = 11

Octal

0

02 * 04 = 10

Hexadecimal

0x, 0X

0x3 * 0x5 = E

If your application requires decimal numbers with leading zeros, you can turn off support for octal numbers by setting the setup variable OCTAL_SUPPORT to OCTAL_SUPPORT_OFF. The default setting is OCTAL_SUPPORT_ON.

Quoted String Constants

String constants are widely used in JPL, especially in msg and invocation statements. At runtime, JPL strips off the quote characters. You can use single or double quote symbols; however, the same symbol must open and close the string constant:

"55 Baker St."
'(212) 555-1212'

A quoted constant with no characters—"" or '' is a null string.

To reference variable values in a string constant, use the colon preprocessor:

"The amount is :total"

To use a special character in a quoted constant—colon, quote character, or backslash—prefix the character with a backslash.


Setting Properties Using the Property API

Panther objects and their properties can be referenced through JPL. For example, this if statement conditionally unhides a widget (emp_salary) at runtime by changing its hidden property to PV_NO:

if (login == "super")
emp_salary->hidden = PV_NO

The basic JPL syntax for referencing a Panther object and, optionally, any of its properties is as follows:

object-spec[ -> property-spec]

The following sections describe syntactical elements and options.

Object Specification

You specify a Panther object either by its name or with object modifiers as follows:

object-name
@object-modifier(object-identifier)

For example, you can refer to the widget named last_name as follows:

last_name
@widget("last_name")

Object Modifiers

Object modifiers make explicit the type of object required. Panther provides an @ modifier for each type of Panther object (except JPL variables): @widget for widgets, @screen for screens, and so on. Use these modifiers to avoid name conflicts—for example, between a screen that is being used simultaneously for data input and as an LDB. They are also useful for referencing objects whose names are otherwise considered illegal—for example, a screen whose name begins with a number. Thus, you can reference a screen with the name 1001.frm as follows:

@screen("1001.frm")

Each object modifier takes either a string or integer argument. The argument can be a constant or variable, or an expression that evaluates to a string or integer. Table 19-3 lists the available modifiers and valid arguments for each.

Table 19-3 Object type modifiers in JPL

Modifier Argument Examples

@app

Always @app(). The string identifier @app() always refers to the current program and allows access to application-wide properties. Use @app() to reference the application directly.

gui = @app()->in_gui
ms_fld = @app()->mouse_field

@id

An integer handle that uniquely identifies an application object. This integer can be obtained from an object's id property or by calling sm_prop_id.

Because each object's id property is unique, you can use @id to reference objects that have the same name—for example, multiple instances of the same screen, or widgets on different screens that have the same name.

nextObj = \
sm_list_objects_next(objList)

@id(nextObj)-> mdt = PV_NO

@screen

The name of a Panther screen that is on the window stack. To specify the active window, supply @current as a string.

@screen("custlist.frm")
@screen("@current")

@screen_num

The number of a Panther screen that is on the window stack, where 0 is the active window, -1 is the window below it, and so on.

Positive numbers number from the bottom of the window stack: 1 is the base window, 2 refer to the window above it, and so on.

@screen_num(0)
@screen_num(sm_wcount())

@ldb

The name of an LDB screen.

@ldb("sales_data.ldb")

@widget

The name of a widget or group on a screen that is on the window stack or in an active LDB. To specify the current widget, supply @current as a string.

@widget("city")
@widget("@current")

@field_num

The number of a widget on a screen that is on the window stack or in an active LDB. Panther consecutively numbers all widgets that accept data from top to bottom and from left to right.

If a widget has more than one element (array_size > 1), @field_num(element_num) always evaluates to the first element. Refer to "References to Element Field Numbers" for more information.

@field_num cannot be used to reference static labels, boxes, lines, graphs, and grid widgets.

@field_num(1)

@field_num(numflds - n)

@tp_req

(JetNet/Oracle Tuxedo only) A string identifier (callid) associated with a service request. The identifier is stored in the tp_return application property immediately after a service call is initiated.To specify the current service, supply @current as a string.

@tp_req("@current")->property

@obj

An object reference to be used in the following functions to specify a method's parameter or a property's value: sm_obj_call and sm_obj_set_property

@obj(object-spec)

a=sm_com_load_picture("a.bmp") ret=sm_com_call_method(images, "Add", 1, '', @obj(a))

Array Subscripts

If a widget or JPL variable is an array, you can reference occurrences and elements within that array. Occurrences are specified with the following syntax:

object-name[n]
@object-modifier(object-identifier)[n]

Array elements are specified with the following syntax:

object-name[[n]]
@object-modifier(object-identifier)[[n]]

The subscripts [n] and [[n]] indicate the occurrence and element to reference, respectively, where n can be a signed (and for occurrences, unsigned) integer constant or expression. Occurrences and elements are both one-based. For example, @widget("customer")[3] refers to the third occurrence in customers, while @widget("customer")[[1]] refers to the array's first element.

If a widget or JPL variable is an array but no occurrence is specified, Panther uses the default occurrence. When executing a field entry, field exit, or validation function, the default occurrence is the occurrence currently being processed. Otherwise, the default occurrence is 1.

Signed and Unsigned Subscripts

Panther interprets an unsigned subscript as an absolute offset within an array. An unsigned subscript must be between 1 and the array's max_occurrences property. Because references to an array element must be absolute, subscripts for element references are always unsigned.

Panther treats a signed subscript as a relative offset from the array's current occurrence. You can reference the current occurrence as array-name[+0] or array-name[-0]. For example, the following JPL procedure, called as array steps's exit function, increments by 1 the value in steps's current occurrence and puts it into the next occurrence:

proc nextOccData( widget_num, data, occ, context )

if ( occ < steps->max_occurrences )
{
steps[+1] = steps[+0] + 1
}
return

Relative references that use an expression for a subscript must enclose the expression in parentheses. For example steps[+(n)] refers to the nth occurrence after the current one in array steps, while steps[(+n)] refers to the array's nth occurrence.

References to Element Field Numbers

Although Panther assigns unique field numbers to elements within an array, any reference to these elements through their field number always evaluates to the array itself—that is, its first element. For example, the following array has three elements, which are numbered 1 through 3:

Given this array, the following statement puts Wilma into variable data:

data = @field_num(3)

To use field numbering to reference a given element's data, you must translate that field number into an occurrence number. For example:

proc getElemData()
vars cur_elem, i, elem_data, occ_no

// get the current element's field number
cur_elem = @screen()->fldnum

// get occurrence number of data in current element
for i = 1 while i <= @field_num(cur_elem)->array_size
{
if @field_num(cur_elem)[[i]]->fldnum = current_elem
{
occ_no = @field_num(cur_elem)->first_occurrence + i - 1
elem_data = @field_num(cur_elem)[occ_no]
}
}

Precedence of Object Types

If a named object's type is not made explicit, Panther searches for that object among the following Panther types, in this order:

  1. Local variables already declared in the current JPL procedure
  2. Variables that are global to the current JPL module
  3. Widgets or groups on the current screen
  4. Widgets in an active LDB (local data block)
  5. Global JPL variables
  6. Screens in the window stack, starting with the active screen
  7. Active LDBs

Compound Object Strings

You can join multiple object strings in a compound object string with the ! character. Compound object strings have this syntax:

object-string !object-string [ !object-string]...

For example, the following object string specifies the customer widget on the active window:

@screen("@current")!@widget("customer")

Compound object strings let you make the context of a Panther object as specific as you like and avoid possible ambiguity among different objects with the same name. For example, if two screens on the window stack—custqry.frm and custedit.frm—both have a cust_id widget, you can uniquely identify each one as follows:

custqry.frm!cust_id
custedit.frm!cust_id

You can achieve even greater specificity within a compound object string by including object modifiers. For example, all of the following object strings are variants of custqry.frm!cust_id:

custqry.frm!@widget("cust_id")
@screen("custqry.frm")!cust_id
@screen("custqry.frm")!@widget("cust_id")

Note: You can reference objects through their object IDs with the @id modifier; these unique handles provide a way to identify an object that is independent of its name and context.

Object Values

An object's value is implicit in all references to it. In practice, this applies only to widgets that can have values. For example, you can get and set the contents of a text widget or a push button's label; widgets such as lines and boxes have no equivalent values that you can access.

In the case of arrays, subscripted references return the value of the specified occurrence or element; non-subscripted references return the first element. Thus, these two statements put the same data into variable cust:

cust = @widget("customer")[[1]]
cust = @widget("customer")

You can get portions of an object's value by appending substring specifiers to the object's reference. For example, this statement gets the first eight characters from customer's second occurrence:

cust = @widget("customer")[2](1, 8)

For more information about substrings, refer to "Substring Specifiers."

Properties

All Panther objects have properties that can be accessed with this syntax:

object-spec ->property-spec

The string that you supply for property-spec contains at a minimum the JPL mnemonic for the desired property. For example, you can reference the current screen's title as follows:

@screen("@current")->title

If a property can be set to multiple values, property-spec can specify one of them; for more information, refer to "Multi-item Properties." You can also specify a portion of a string property's setting; this is described in "Property Substrings."

Property specification can include the @property modifier. For example, you can reference the current screen's title as follows

@screen("@current")->@property("title")

The @property modifier is optional if you use the property's actual mnemonic; it is typically used in order to reference a property through a variable. For example, an all-purpose procedure that changes a widget's properties at runtime might look like this:

proc change_props (widg_name, prop, value)

@widget(widg_name)->@property(prop) = value

Editor Properties

If a property is accessible through the editor, its JPL mnemonic is usually a variant of the name used in the Properties window, where all characters are in lower case, and non-alpha characters such as spaces, dashes, and slashes are replaced with underscores. For example, the Menu Name property is referenced as menu_name.

A number of exceptions exist, usually for properties that share the same label in the Properties window. For example, if you set a widget's FG Color Type and BG Col or Type properties to Basic, both properties get Color Name as a subproperty. To differentiate these two properties, their runtime names are fg_color_name and bg_color_name, respectively. For a full list of runtime property names, refer to Chapter 1, "Runtime Properties," in Quick Reference.

Note: Several properties that are visible in the Properties window are not accessible at runtime—for example, the Inherit From and Columns properties.

Runtime and Application Properties

Panther also provides access to a number of properties that are not available in the editor, either because they are accessible only at runtime or because they are application-wide (@app) properties. For example, selected is a runtime property that returns true or false for a specified occurrence in a list box or selection group; in_gui is an application property that returns true if the application is running on a GUI platform and false if in character mode.

Refer to Chapter 1, "Runtime Properties," in Quick Reference for a list of all properties and their definitions.

Multi-item Properties

Some properties have an array of values, for example, the Drop-Down Data property of combo boxes and option menus. To reference multi-item properties, specify the offset into that property's values as follows:

object-spec ->property-name [prop-item]

For example, the following code changes the selected item in an option menu that has its Drop-Down Source property set to constant data:

#replace current item with contents of "substitute"
vars count
for count = 1 \
while flavors->drop_down_data[count] != flavors
{}
flavors->drop_down_data[count] = substitute
flavors = flavors->drop_down_data[count]

To access control string assignments for a screen or for the application, use the desired logical key as the control_string property's offset. For example, the following statement gets the screen-level control string assigned to the PF5 key:

ctrlstr = @screen("@current")->control_string[PF5]
Although properties cannot have properties, you can call sm_n_num_occurs and sm_n_max_occur for property expressions. For example:
drop_down_size = sm_n_num_occurs("optmenu->drop_down_data")

Property Substrings

You can get and set a portion of a string property's value with the following syntax:

object-spec ->property-name(offset, length)
object-spec ->property-name [prop-item ](offset, length)

For example, the following code conditionally assigns the first eight characters of a widget's name to its column_title property:

if @field_num(i)->column_title = ""
@field_num(i)->column_title = @field_num(i)->name(1, 8)

Property Value Types

Properties can be grouped into three general categories according to the types of values that they take:

Literal Properties

Literal properties take any value—string, integer, or numeric, depending on the property. For example:

@widget("customers")->first_occurrence = 1
@screen("@current")->control_string[XMIT] = "^verify_acct"

Some properties have implied or explicit ranges. For example, you cannot set an array's first_occurrence property to a value greater than the number of occurrences in the array.

Logical Properties

Logical properties take a value of PV_YES (1) or PV_NO (0). For example:

@widget("salary")->focus_protection = PV_YES
Enumerated Properties

Enumerated properties can only be set to one of several predefined integer constants. For example, a widget's hidden property can be set to one of three constants: PV_YES, PV_NO, or PV_ALWAYS.

For a full listing of runtime JPL property names and valid values, refer to Chapter 1, "Runtime Properties," in Quick Reference.

Implicit Properties

All widgets that contain data have a property that let you set its initial value—Initial Text for text widgets, Label for push buttons and check boxes, and so on. For most widget types, these need not be referenced explicitly. To access a widget's data, refer to the widget itself. For example, the following statements change the labels of check boxes day1 through day7 to the values found in successive elements of array lang:

for count = 1 while count <= 7
@widget("day"##count) = @widget(lang)[count]

Table 19-4 shows which widget types are referenced directly in order to change their data, and the editor properties that set their initial data. There are accessible at runtime as implicit properties.

Table 19-4 Editor properties setting a widget's initial data

Property name Widget types

Initial Text (Format/Display)

single line and multiline text, list box, combo box, option menu

Initial Value (Input)

scale

Label (Identity)

dynamic label, push button, check box, radio button, toggle button

Notes: Graph widget data is set by its Value Source property; this multi-item property must be explicitly referenced, for example, @widget("sales")->y_value_source[1].

Properties of Elements and Occurrences

Properties of an array's occurrences and elements can be accessed by subscripting the array reference—a single pair of square brackets refer to occurrences [], double square brackets to elements [[]]. For example, this statement toggles the reverse property of an array's first element:

salaries[[1]]->reverse = !salaries[[1]]->reverse

Selection Group Data

In the editor, you can group together multiple radio buttons, check boxes or toggle buttons into a selection group. JPL identifies a selection group name as an array whose number of occurrences is equal to the number of selections from the group. Each array occurrence contains the number of the selected item: the first element contains the number of the first selected item, the second element contains the number of the next selection, and so on.

Groups can be set up to accept one, multiple, or no selections. If the group allows only one selection—its num_of_selections property is set to PV_0_OR_1 or PV_1—its corresponding JPL variable is an array with one occurrence of data, where group-name [1] contains the number of the selected item. Because single-selection groups have only one occurrence, JPL lets you omit the subscript. Thus, group-name[1] and group-name are equivalent.

For example, days is a selection group that allows multiple selections. It contains seven check box widgets with these labels:

[ ] MON  [ ] TUE  [ ] WED  [ ] THU  [ ] FRI  [ ] SAT  [ ] SUN

Panther numbers widgets in this group in order of their placement on the screen: MON has a value of 1, TUE a value of 2, and so on. If the user selects THU and SUN, days[1] has a value of 4, while days[2] has a value of 7.

You can programmatically evaluate and manipulate the contents of the group array. For example, the following code returns the number of items selected from days, then passes each selection to the routine days_off:

occurs = days->num_occurrences
for count = 1 while count <= occurs
{
call days_off( days[count] )
}

You can programmatically change group selections by setting group array occurrences to the desired values. The following code selects members 6 and 7—SAT and SUN—in group days:

days[1] = 6 
days[2] = 7

You can also use library functions sm_select and sm_deselect to change group selections. The following code is equivalent to the previous JPL:

call sm_select("days", 6)
call sm_select("days", 7)

Grid Properties

If you place a widget inside a grid widget, the widget becomes an array whose size is determined by the number of occurrences assigned to the grid widget.

At runtime, the current selection inside of a grid widget can be determined by grid_current_occ, a read-only property. The following JPL statement returns the number of the selected row in a grid named Detail:

myvar=@widget("Detail")->grid_current_occ

Traversal Properties

When you use the transaction manager, it builds a tree of all table views that are linked to the root table view. It traverses this tree to issue transaction manager commands to each table view or server view. You can query traversal properties to get information about the table views, server views, and links that are a part of the current transaction.

The following JPL procedure queries the sv property to ascertain the server view for the current widget on widget entry. It then executes the VIEW command to specify that server view:

proc get_sv_query
if K_ENTRY
{
vars value1
value1 = name->sv
call sm_tm_command("VIEW :value1")
}
return 0

If the specified property references an object that does not participate in the current transaction, Panther returns an error. For more information on traversal properties, refer to "Using Traversal Properties."

Global Variables

You can reference any JPL variable declared by the global command at any time during the application. JPL also recognizes global variables defined in Panther header files—for example, logical key names such as XMIT and EXIT, and bit mask settings such as K_EXPOSE and K_ENTRY. You can reference these variables in any JPL expression and pass them as arguments to another procedure or function.

Caution: Because Panther uses these variables internally, avoid changing their values; doing so can yield unpredictable and possibly harmful results.


Data Types, Operators, and Expressions

Data types describe how JPL uses the values of variables and constants. Operators specify what to do or how to manipulate variables and constants. Expressions combine variables and constants to produce new values.

Data Types

JPL determines the data type of a variable or expression according to its value or usage. All variable values are stored as character strings; JPL converts those values when required.

JPL recognizes four data types:

Operators

The following sections summarize JPL operators, their operands, and the data type of the value after the operation. Associativity is left to right except for exponentiation, where it is right to left.

String
JPL string operators evaluate to a string. Operands must also be strings.

()

substring specifier

##

concatenation

Numeric
Evaluate to an integer or float. Operands must be either an integer or float.

@date

date calculation

@length

string length calculation

@sum

array sum

^

exponentiation

/

division

*

multiplication

+

addition

-

subtraction

Assignment =
Evaluates to numeric or string, according to the operand types. Both operands must be of the same data type.

Relational
Evaluate to true or false; both operands must be of the same data type.

>

greater than

>=

greater than or equal to

<

less than

<=

less than or equal to

==

equal to

!=

not equal to

Logical
Evaluate to true or false; operands must be logical values.

!

NOT (unary operator)

&&

Logical AND

||

Logical OR

Bitwise
Evaluate to integer; operands must be integer types:

~

one's complement

&

bitwise AND

|

bitwise OR

Operator Precedence

JPL operators have the following precedence, in decreasing order:

() []  @date @length  @sum 
##
^
~ !
/ *
+ -
> >= < <=
== !=
&
&&
|
||
=

Conversion of Operands

Some operators require operands of specific data types. If the operand's data type is different, JPL tries to convert it; otherwise an error occurs. In the case of relational and logical operators, JPL checks whether the operand data types are the same; if they are different but compatible—for example, integer and numeric—JPL converts them to one or the other; if they are incompatible, an error occurs.

Table 19-5 shows the data type that JPL uses for operands of compatible data types in relational and logical expressions:

Table 19-5 Data type conversion in relational and logical expressions

Operand type String Float Integer Logical

String

string

error

error

logical*

Float

error

float

float

logical**

Integer

error

float

integer

logical**

Logical

logical*

logical**

logical**

logical

* A string evaluates to a logical true or false if it begins with the value of SM_YES or SM_NO.

** A numeric or integer evaluates to a logical true if it is non-zero or or to a logical false if 0.

Concatenation

Use the concatenation operator ## to join multiple values into a single string. For example, these statements concatenate the string Blue Moon into variable a.

vars a = "Blue "
vars b = "Moon"
a = a##b

Substring Specifiers

Substring specifiers let you reference any part of a string that is in a variable or property. Specify a substring with the following syntax:

obj-name (offset, length)

obj-name
The name of a JPL variable, widget, or LDB entry, or a property that takes string values.

offset
The offset of the first character of the substring to get from obj-name, where the first character in obj-name is 1. A value for offset is required, and can be an integer or integer expression.

length
An integer expression that evaluates to the substring's length. If length exceeds the substring's actual length, JPL reads only up to the last byte of data. A value for length is optional: if no argument is supplied, JPL operates on all characters from offset to the end of the string.

The following examples show some common uses for substring specifiers:

@date

The @date operator lets you compare and perform arithmetic on dates. This operator uses a date as its operand—either a widget with a date format, or a date string constant or expression. @date converts a date constant to a numeric by counting the number of days between the date constant and January 1, 1753—the standard for date calculations.

For example, if widgets order-date and ship-date have date edits, you can add 30 days to order-date's value and assign it to ship-date:

ship-date = @date(order-date) + 30

In the next example, today is a widget with the current date, and days is a variable that gets the number of days between today and 4/1/96:

days = @date("4/1/1996") - @date(today)

If an operand includes a time value—for example, 02/22/94 10:15@date ignores the time value and outputs only a date value.

@length

The @length operator counts the number of characters in one or more string arguments. You can supply string constants or variables as arguments. You can use a substring specifier on any argument that is a variable.

@length counts all characters and embedded blanks. Leading blanks in right-justified widgets and trailing blanks in left-justified widgets are ignored. In quoted string constants, leading blanks are counted but trailing blanks are ignored.

For example, the following statement gets the total number of characters in fname and lname:

vars ln
ln = @length(@widget("fname"), @widget("lname"))

@sum

The @sum operator calculates the sum of all non-blank occurrences in an array. In the next statement, quantities is an array and total is a widget that gets the sum of occurrences in quantities:

total = @sum(quantities)

Bitwise Operators

JPL provides three operators for bit manipulation: AND (&), OR (|), and one's complement (~). Bitwise operators let you examine and set the flags that are set on bit masks.

For example, this procedure tests the value of widget status flags K_ENTRY and K_EXIT to determine whether the widget is being entered or exited:

proc field_func (number, data, occ, flags)
if flags & K_ENTRY
jpl do_process
else if flags & K_EXIT
jpl do_exit_process
return

The next procedure examines the settings of K_KEYS to determine which key the user pressed to exit a widget:

proc field_func2(num, dat, occ, flags)
if (flags & K_KEYS) == K_NORMAL
return
else if (flags & K_KEYS) == K_ARROW
msg emsg "Please use the tab key to move between fields."
return

For more information on the flag settings that Panther passes into widget and screen modules and hook functions, refer to "Calls from Screens and Widgets" and Chapter 44, "Installed Event Functions."

Expressions

An expression produces a new value by combining constants, variables, and operators. In all statements, Panther's colon preprocessor evaluates colon-expand ed variables. In all expressions, JPL's statement processor replaces variable names with values. Evaluation is generally from left to right; however, you can affect order of evaluation through parentheses.

JPL evaluates an expression as one of four data types: string, numeric, bitwise, or logical. However, it is an error to combine a string assignment with a numeric assignment for a single variable within one expression. The following sections discuss these data types.

String Expressions

A string expression combines one or more quoted string constants or values of string variables. Substring specifiers and ## are string operators. The following examples are all string expressions:

'Montreal'
"Processed :i items"
fname##' '##lname
telephone(1, 3)

Numeric Expressions

A numeric expression combines variables and numeric constants with one or more of the numeric operators. The following examples are numeric expressions:

y + z
@sum(quantities)
@length(fname,lname)
x^y + y * (z^3/4 + 1) - x/2
86

If the setup variable DECIMAL_PLACES is set to a number, JPL rounds the value of a numeric expression to that number of decimal places. You can change this with a format specifier to declare the total length and the number of decimal places. Format specifiers have this syntax:

%[ t ] [m] [ .n] var-name

where m and n are integer constants or variables. m specifies the total number of characters, including leading spaces, sign, digits, and decimal place. If you omit m, or m is too small to output the variable's value, JPL uses the variable's size. n specifies the number of digits after the decimal place. If you omit n, JPL uses 2 decimal places.

For example, the following statement assigns 1.667 to i.

%6.3 i = 10/6 // rounds value to 1.667

t overrides rounding and truncates to the specified number of decimal places, if any. For example, the following statements truncate the values assigned to variables i and n:

%t1.2 i = 10/6  // truncates i to 1.66
%t1.0 n = 10/6 // truncates n to 1

Notes: Panther uses the sprintf() function to perform rounding. Because this function's behavior is compiler-specific, rounding results for 0.5 decimals can vary among different platforms.

If var-name is a widget or LDB entry, you can define its floating point precision by setting Data Formatting (data_formatting) to PV_NUMERIC and setting its Format Type property. At validation, Panther uses this property to format the widget's value.

Bitwise Expressions

A bitwise expression uses variables or constants which have the data type integer, and any of the bitwise operators. The following examples are bitwise expressions:

flag1 & flag2
x | mask

Logical Expressions

A logical expression uses logical and relational operators to evaluate variables, numeric constants, integer constants, string expressions, numeric expressions, or integer expressions. Operands must be of the same data type; otherwise, JPL tries to convert them according to Table 19-5. For example, you can compare a numeric literal to a variable or expression only if JPL can evaluate the variable or expression to a numeric. Otherwise, it displays an error message.

The following examples are logical expressions:

y
x != 7
(total * (1 + tax)) <= max_value
flag > ~flag

In contrast to C, the JPL interpreter always fully evaluates a boolean expression. In the following example, JPL calls myFunc even though the expression already evaluates to true:

vars a = 1
if ( a || myFunc() )
...

JPL Commands

All the JPL commands are fully described in Chapter 2, "JPL Command Reference," in Programming Guide. In general, command arguments can be either variables or strings. String arguments must be enclosed in single or double quotes. To use a variable's value within a string, you can append a colon to the variable (colon preprocessing). For more information, refer to "Colon Preprocessing."


Optimization

You can improve performance of JPL procedures in several ways: