Technical Description

The different parts in this chapter are:

Syntax

The generic call interface is part of the SAA package of REXX. It has an equivalent meaning of the function RXFUNCADD. It defines a function or subroutine by use of the following call:

RXFUNCDEFINE(REXXName, Library, LibraryFunction, DefinitionStem)
REXXName
This is the name of the function or procedure which can be used from within the REXX interpreter. The value must follow the rules of function or procedure names. The value will be uppercased by the REXX interpreter.
Library
This is the name of the library. The value is case-sensitive. The name may be abbreviated following the rules of the system. A path may be omited too.
LibraryFunction
This is the name of the function or procedure within the library which shall be invoked. This value must usually be the same as given within the library. The value is case-sensitive. Ordinal values may or may not been accepted depending on the interpreter and the system.
DefinitionStem
This is the heart of RXFUNCDEFINE. Its value must be a stems name. A period may or may not follow immediately after the value. The stem must contain special values which describe the parameters of the function or procedure. The structure is described below.

The return value returns the defined values as for RXFUNCADD and a special value indicating errors in the structure.

Example:

stem = 'setlocale'
err = RxFuncDefine('SetCountry', 'libc', 'setlocale', stem) 

In this example the variables setlocale.0 and others describe a function setlocale in the library libc.

The call itself is done by using another stem and the defined function name.

The return value of the created generic function is the empty string. The return value may be changed by a special parameter to reflect the true return value of the underlying function. A normal usage is shown in the following example:

stem = 'setlocale'
call SETCOUNTRY stem 

The Definition Stem's Syntax and Semantic

The stem itself must follow special requirements. It is a tree-based structure with the description of the parameters, the return value, and the calling convention. Thus, a description stem has the following elements:

  1. calltype
  2. return
  3. 0
  4. 1, ...

Modified element names can be choosen.

calltype
This value of this variable defines the calling convention. The variable may be omitted if a standard calling convention exists. The value is not case-sensitive. Typical values may be cdecl, pascal, etc depending on possible values for the current machine and system.

Immediately following this value there are two optional tokens delimited by at least one whitespace. These tokens are as function and with parameters. These tokens change the usage of the generic function as shown below.

return
This variable is a stem itself which describes the content of the value returned by the library call. The structure is defined below in the section Definition Parameter's Syntax and Semantic.
0
This value of this variable is a non-negative whole number declaring the number of parameters. For each parameter a different numbered entry beginning with 1 exists. A value of 0 is possible if no arguments exist.
1, ...
These variables will be examined if the value of the variable 0 contains a number greater or equal to this variable's name. The variable with the name 1 describes the left-most parameter of the function call.
Each of this variables is a stem describing the kind of the parameter. The syntax of the stem is described below in the section Definition Parameter's Syntax and Semantic.

The interpreter will build a data structure for each parameter in the background. The values of the parameter stem will be copied to the internal data structure on call. The library function or procedure is then called. After the return, each parameter and the return value is filled back from the internal data structure to allow the modification of the parameters by the library call. This only happens when indirect or a return value is used, of course.

Definition Parameter's Syntax and Semantic

The return value and the parameters of a function share the same syntax. Both variables are stems describing the structure and type of it. Each stem element describes a new part of the parameter.

A part contains the following elements:

  1. type
  2. name
  3. 0, ...

Modified element names can be choosen.

type
The value of this variable defines the type of the parameter or part of the parameter. It may be prefixed by the string indirect followed by the basetype.
Possible values for the basetype are described below.
name
This variable identifies a parameter or a part of a parameter. The use is optional and its purpose is a higher readability.
0
The value of this variable is a positive whole number declaring the number of container elements or the number of array elements.
1, ...
These variables are available only if the type describes a container or an array. In case of a container, the variable 0 indicates the number use used elements. In case of an array, only the first element 1 is used.

Possible values for type vary and depend on the system and machine. The interpreter should support at least the values:

The complete structure is evaluated in depth-first order. This allows nesting structures. The content itself is aligned in depth-first order, too; indirected values are separated in position, though.

The memory allocation block of the folling example should be allocated as follows.

stem.type = container
stem.0 = 3
stem.1 = indirect float64
stem.2 = indirect integer8
stem.2 = integer32
will lead to the following allocation block (assuming a pointer size of 32 bit).
Byte
  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
  indirect  | indirect  | integer32 |        float64        |integer8
  to float  | to integer|           |                       |

The interpreter must respect alignments only for indirections. Parts of a container will be considered as correctly aligned in all cases, it is the user's responsibility to assure this. The following example explains this.

stem.type = container
stem.0 = 3
stem.2 = integer8
stem.2 = indirect integer16
stem.1 = indirect float64
will lead to the following allocation block (assuming a pointer size of 32 bit).
Byte
       0| 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
integer8| indirect  | indirect  | s|int16|         float64
        | to integer| to float  | s|     |

Spare bytes are marked by a "s".

Call Stem's Syntax and Semantic

A function defined by RXFUNCDEFINE will always accept only one parameter if neither as function nor with parameters are given in the calltype. The meaning of these special values are explained some paragraphs further.

The single parameter to a stem is formed very similar to the definition stem.

The interpreter will reassign all changed values of every argument to the values after the function call. It is a tree-based structure with the values of the parameters and the return value. Thus, a call stem has the following elements:

  1. return
  2. 1, ...

Modified element names can be choosen.

return
This variable is a stem itself which holds both the input as the output values passed to and returned by the library call. The structure is defined below in the section Call Parameter's Syntax and Semantic.
1, ...
These variables will be examined as defined in the Definition Stem. The variable with the name 1 describes the left-most parameter's values of the function call.
Each of this variables is a stem describing the values of the parameter. The syntax of the stem is described below in the section Call Parameter's Syntax and Semantic.

The interpreter will set a variable 0 after each call to reflect the parameter count. This is done at the very end of operation and can be used as a flag for success.

The values of the parameter stem will be copied to the internal data structure on call. The library function or procedure is then called. After the return, each parameter and the return value is filled back from the internal data structure to allow the modification of the parameters by the library call. This only happens when indirect or a return value is used, of course.

If as function is given in the calltype, the return value of the the underlying function call is passed back as the return value of the generic function call. It is the content of the value variable of the return part.

The interpreter may or may not accept this value in case of a compound return value. It depends on the interpreter's ability to return stems.
No error is thrown if the called object has no return value, e.g. is a procedure.

The return part of the Call Stem isn't set if this parameter is given.

If with parameters is given in the calltype, the different parameters to the underlying function are passed directly to the generic function call as normal parameters in the defined order. This parameter implies as function if the return definition isn't the empty string. Thus, every parameter to the generic function call is passed to the underlying function.

Missing parameters are treated as NULL-pointers in case of indirect values. Other interpretations are not possible. On return, NULL-pointers lead to a drop of the value variable or to a recursive drop of every value variable of the whole stem's branch in case of arrays or containers. Other interpretations are not possible.

Parameters are passed in a modification allowing fashion. This is called pass by reference. A copy of the value is used if the value is a constant.

The interpreter may or may not accept this value. The number of parameters must not be more than 10.

Call Parameter's Syntax and Semantic

The return value and the parameters of a function share the same syntax. Both variables are stems holding values for the function call. Each stem element describes a new part of the parameter.

A part contains the following elements:

  1. value
  2. name
  3. 1, ...

Modified element names can be choosen.

value
The value of this variable is the value of the corresponding entry in the Definition Stem. This variable is the number of of elements in case of a type of container or array, but see below.
name
This variable identifies a parameter or a part of a parameter. The use is optional and its purpose is a higher readability.
1, ...
These variables are used only if the type describes a container or an array. In case of a container, these variables are recursively used structures like this.
In case of an array, each entry contains the value itself without any value after the index name.

Values may be omitted. This is allowed for indirect values only. A NULL value is passed in this case. Consequently, each variable's value is dropped if the called function sets the corresponding variable to NULL. In case of an indirected CONTAINER or ARRAY however, the value must be set to the elements of the container or array to reflect that this object shall not be set to NULL. No further elements of the container or array will be examinated otherwise; on return from the called function, value will be set to the number of elements, but not if the container or array is an indirection with an actual value of NULL.

The interpreter will signal a NOVALUE condition in all cases of missing values where missing values are not allowed. The user has to install or deinstall an appropriate handler.

The interpreter will signal a SYNTAX condition if structual parts are missing or the wrong number or parameters are passed. The interpreter will signal this condition if numbers can't be represented or strings are too long. Other errors are passed back as an error string with one exception: A function with a calling convention defined as as function will always raise a SYNTAX condition in case of errors.

The interpreter will not signal a LOSTDIGITS condition if numbers can't be converted without rounding the least significant digit. Instead, the interpreter assumes an infinite precision when writing back values ignoring the NUMERIC DIGITS value. The user shall either call the FORMAT builtin function or has to add a zero. This allows the reusage of intermediate values in cases where it is needed.

Direct Operating System Call

The REXX interpreter may allow the user to call the operating system directly. The user must omit all the arguments to RXFUNCDEFINE except the internal function name and the stem. The interpreter may require the user to use a special name as the internal function name.

Moveover the user must use predefined values like register names to be passed to the kernel. The interpreter's documentation will explain what variables of which type must be used. Systems without a dynamic link interface should include the Direct Operating System Call feature.

Name modifications of leaf names

Because of the tail expansion of names in stems it is possible to select a special character as a prefix for tail names. This can change a variable name from stem.return.type to stem.!return.!type.

The change happens immediately after selection and affects both definition stems as call stems. The function for changing the prefix is:

GCIPREFIXCHAR(Prefix)
Prefix
This parameter may be omitted. In this case the current prefix is returned as usual. Otherwise the prefix needs to be a string with no or one character which defines the new prefix of tail names.

An empty prefix or a prefix containg a blank or D2C(0) is treated same; a prefix isn't used any longer in this case. Otherwise a character of !?_#$@ must be used.

Please note that only the first three characters are definitely defined as valid by the ANSI standard, and some interpreters won't accept anything else.

return value
The previous value of the prefix is returned. An empty string is used in case of a previously assigned prefix of a blank character.

Never use the Generic Call Interface

The generic call interface is highly dangerous. The Direct Operating System Call will be the most dangerous feature. So why shall this be implemented? The detour of a separate wrapper library won't reduce the chances of a program crash or a system crash. Indeed, using RXFUNCADD is as dangerous as using RXFUNCDEFINE because one may always run in a fault.

It is best programming practice to use RXFUNCDEFINE only when no other chance of the wanted functionality exists.