Iron Spring PL/I Compiler

Programming Guide

Beta Version 0.8c

This is beta software. Some PL/I features are still missing from this version. A partial list is provided under restrictions below.


Introduction

PL/I is a powerful programming language suitable for a wide range of problems. PL/I has been used to write operating systems and compilers, simulations, real-time programs, mathematical software, spreadsheets, GUI programs, and for general computing.

PL/I is widely used on mainframes and midrange systems today, but since the demise of Digital Research, Inc. has not been easily available on personal computers.

This is the second beta version of Iron Spring PL/I. Version 0.8c is still missing some features of full PL/I, but their addition should not affect features already working. The section Restrictions lists many of the missing features.

Modifications to Guide for 0.8c

Modifications to Guide for 0.8b
Modifications to Guide for 0.8a
Modifications to Guide for 0.7b
Editorial changes only.

Modifications to Guide for 0.7a

Modifications to Guide for 0.6a

Trademarks
eComStation is a trademark of Serenity Systems International.
EMX is a trademark of Eberhard Mattes.
Gnu is a trademark of the Free Software Foundation.
IBM is a trademark of IBM, Inc.
Intel is a trademark of Intel Corporation.
Iron Spring is a trademark of Iron Spring Software.
Linux is a registered trademark of Linus Torvalds.
VAX is a trademark of Hewlett-Packard, Inc.
WATCOM is a trademark of Sybase, Inc. and its subsidiaries.


Restrictions

This is a list of major current restrictions of the compiler.

The compiler doesn't yet support building Presentation Manager applications. Only text-mode applications are allowed.

PL/I procedures can call functions coded in other languages as long as they use the "SYSTEM" calling convention. The "OPTLINK" and other calling conventions are not supported. Programs in other languages can call PL/I procedures with restrictions. The main program must be PL/I. See Program Linkage for more information.

Major features not implemented

Preprocessor
In the current version of the compiler the only preprocessor statements fully implemented are %INCLUDE and the listing control statements %PAGE, %SKIP[(n)], %PRINT, %NOPRINT, and %REPLACE. %PROCESS (*PROCESS) is implemented incompatibly from the IBM compilers.
All other preprocessor statements will generate a diagnostic.
See Preprocessor for more information.
Tasking
TASK and EVENT data, the WAIT statement, and the tasking builtins PRIORITY, COMPLETION, and STATUS.
GENERIC attribute
The GENERIC attribute.
DEFAULT
The DEFAULT statement.
DISPLAY
The REPLY and EVENT options of the DISPLAY statement.
Input/Output
The I/O statements DELETE and UNLOCK are not implemented due to differences in Input/Output of the PC vs. the mainframe. This is a permanent restriction.
The COPY option of the GET statement, the ONKEY and SAMEKEY builtin functions.
FETCH and RELEASE
The FETCH and Release statements are not currently implemented.
Array Handling builtins
The following array handling builtin functions are not implemented in the current version of the compiler: ALL, ANY, POLY, PROD, and SUM.
Mathematical builtins
The following mathematical builtin functions are not implemented in the current version of the compiler:
ACOS   ASIN ATAN ATAND
ATANH COSH SINH TANH
ERF ERFC EXP
Arithmetic builtins
The following arithmetic builtin functions are not implemented in the current version of the compiler:
CONJGROUND TRUNC
Builtins PLICKPT, PLICANC, PLIREST, PLITEST
These builtins are not implemented. This is a permanent restriction.
PLISRTx
The sort builtin functions PLISRTA, PLISRTB, PLISRTC, and PLISRTD are not currently implemented. It is anticipated that versions of these functions that interface to one or more sort products will be added in the future.
Aggregate Results
Builtin functions will not return agregate results in the current version. This is similar to Subset G support for builtins.
AREA
The AREA attribute, AREA data. and the EMPTY builtin function are not currently supported.
GRAPHIC data
GRAPHIC data support will not be included in this compiler. This is a permanent restriction. A future version will include WIDECHAR data for 16-bit Unicode character support. The features not supported include:
REFER
The REFER option for string lengths and array bounds.
LIKE
The LIKE attribute is implemented with one restriction. If a structure is declared as DECLARE a LIKE b; and some elements of "b" have the INITIAL attribute, the initialization is not performed for "a". This will be fixed in a future version.
UNION
The UNION attribute may not be used in adjustable structures.

Other features
The compiler error message 995 ("Unimplemented feature xxx") and the run-time condition "UNIMPLEMENTED" are used to flag unimplemented features. Except for "permanent restrictions" noted above, these should be removed in future releases. Some infrequently-used conversions may be only partially implemented.

Compiler Limits
Maximum number of dimensions15
Maximum number of levels in a structure 15
Maximum level number in a structure255
Maximum number of picture characters in a picture
(after expanding all repetition factors)
511
Maximum length of a CHAR or BIT string
(after expanding all repetition factors)
~32000
Maximum nesting depth of %INCLUDE files4
Maximum precision of FIXED DECIMAL data18
Default precision of FIXED DECIMAL data5
Maximum precision of FIXED BINARY data31
Default precision of FIXED BINARY data15
Maximum precision of FLOAT DECIMAL data20
Default precision of FLOAT DECIMAL data6
Maximum precision of FLOAT BINARY data64
Default precision of FLOAT BINARY data52
Minimum/maximum scale factor-128 / 128
Maximum length of an internal or
external label
31


Documentation

Except as noted under
Compiler Differences and Restrictions this compiler is compatible with The IBM "PL/I for MVS and VM Compiler 1.1". Complete HTML documentation will be available as part of a future release. In the meantime, documentation for the IBM compiler is available online.

Compiler Differences

The reference implementation for this compiler is IBM PL/I for MVS and VM 1.1. Most of the differences are in the area of Input/Output. See the section
Input and Output for a description of the input/output facilities.

See Restrictions for information on major language features that are not currently implemented.

Character representation
Multiple alternate characters can be used to represent OR and NOT. Either the single or double quote can be used as a string delimiter.
DECIMAL FLOAT
DECIMAL FLOAT data is simulated using BINARY FLOAT. This is similar to S/370 PL/I implementations that simulate both DECIMAL FLOAT and BINARY FLOAT using the hardware Hexadecimal floating point formats.
FIXED BINARY
FIXED BINARY data with precision 7 or less is stored in a single byte.
E Format
E Format output will print four digits for exponent instead of two. Output field widths may need to be adjusted.
STRING
The STRING builtin function and pseudovariable restrict the range of data allowed as arguments. This should not affect most normal use of STRING. See the section Builtin Functions and Pseudovariables for the definition of STRING.
ONCODE
The ONCODE builtin function returns only the base value of the error code. For example, IBM PL/I can return codes 600 to 639 for CONVERSION. This compiler will return 600 in all cases. This is a permanent restriction. See list of oncodes.
Additional builtins
The following additional builtin functions are supported:
ALLOCATE, BYTE, COLLATE, COMPARE, COPY, PLIFILL, PLIFREE, PLIMOVE, RANK.
See the section Builtin Functions and Pseudovariables for the definitions.
ATTENTION
The ATTENTION condition is enabled by default. It is raised by pressing ^C while running a VIO application or otherwise sending XCPT_SIGNAL_INTR to the PL/I process. If no ON-Unit is established for ATTENTION, the implicit action is the same as for ERROR: print an error message and raise the FINISH condition. On normal return from an established ON-Unit, execution continues unaffected. Note that signal interrupts are sent only to thread 1 (the major task). Due to OS/2 restrictions, a backtrace printed as a result of 'ON ATTENTION SNAP ...' may be incomplete.
RETURNS keyword
Procedure and Entry statements (not ENTRY declarations) may specify "RETURNS( CHARACTER(*) [VARYING] )", or "RETURNS( BIT(*) [VARYING] )". The length of the returned string is determined by the entry declaration in the calling program, e.g.: "DECLARE a ENTRY RETURNS( CHARACTER(20) );". IBM compilers require an explicit length and will not allow '*'.
EXTERNAL Attribute and Program Linkage
The EXTERNAL attribute has been enhanced to allow the provision of a non-PL/I name visible to procedures outside the PL/I program. The syntax is:
DECLARE x EXTERNAL( 'environment_name' )
See Program Linkage. for more details.


Running the Compiler

The "PLIC" command is used to invoke the compiler.

The syntax of the PLIC command is shown. The case of the option switches is significant. Lower-case options have parameters, upper-case do not.


   PLIC [<options>] <input files> [-o <output file>]
        <options> = <output option> [<include options>] [<listing options>]
                    [<source margins>] [<character substitutions>] [<version info>]
                    [<error option>] [<misc options>]
        (options may be entered in any order).

        <output option> = -S | -C | -L
                          -S = generate assembler (symbolic) output.
                          -C = generate compiled (object) output.
                          -L = generated linked (EXE or DLL) output.
                               (this option not currently implemented.}

        <include options> = -i<directory>
                            where <directory> is the absolute or relative path 
                            to a directory to be searched for %INCLUDE files.
                            This option may be used more than once on the command line,
                            and directories will be searched in the order listed.

        <listing options> =  -l[siaxgm]
                            one or more of [siaxgm] may be entered, in any order.
                            -ls = list source
                            -li = list insource
                            -la = list attributes
                            -lx = list cross-reference
                            -lg = list aggregates
                            -lm = list generated code in a format similar to an assembly listing.
                            Currently, the insource and aggregate listings are not available.

        <source margins> = -m(start[,end])
                           This option defines the first and last positions of each
                           input line that contain input for the compiler.  If this
                           option is omitted the source is assumed to be the entire line.
                           This is included for compatibility with mainframe compilers
                           which would use, for example, -m(2,72).  

        <character substitutions> = -cn(<list>) and/or
                                    -co(<list>)
                            This option defines up to four characters each to be used as
                            substitutions for the NOT(¬) [-cn()] and/or OR(|) [-co()]
                            operator IN ADDITION TO the defaults.  The caret (^) is
                            a metacharacter for the OS/2 command processor; if the
                            caret is to be used, code two consecutive carets,
                            for example -cn(^^).

        <version info> = -V
                         The compiler prints version and copyright information on stderr.

        <error option> = -e<option>
                         This option sets the errorlevel returned by the compiler for warning
                         and error messages.  Normally compiler returns 4 if only warnings were
                         issued, and 8 for any errors.
                         -ew tells the compiler to return 0 if only warning messages were issued.
                         -es tells the compiler to return 0 if any errors or warnings were issued.
                         This option is useful when the compiler is run from a script or makefile.

        <misc options> = -d<option>
                         <option> is a character string, with or without enclosing quotes.
                         -dLIB tells the compiler it is compiling a standard run-time library procedure.
                         -dELF causes the compiler to generate ELF object files.
                         -dOMF causes the compiler to generate OMF object files.
                         The default is to generate the standard object format for the host OS;
                         for OS/2 -dOMF is the default.

        <input files> and <output files> are absolute or relative path names.  Only one input and one
                         one output file are currently allowed.  If the output file is omitted the name
                         is generated.  For example, PLIC -S abc.pli will create a file named abc.asm.

Compiler input

Compiler input is a standard OS/2 text file [ENV(LF) or ENV(CRLF)]. Conventionally, the source program might have the extension ".pli" or ".pl1", but this is not required. Lines are delimited by Newline characters ('0A'x). There is no limitation on line length, although individual tokens such as character-strings are limited to approximately 32,000 characters. Other than newlines, characters lower in the collating-sequence than spaces ('20'x) are ignored. This means, therefore, that at the present time they should not be coded in character string constants. To compile a character string constant containing one or more of these characters concatenate the hexadecimal value, for example:

declare two_line_constant char(24) static initial( ('Line one' || '0D0A'x || 'Line two.') );
This type of expression is resolved at compile-time and does not generate any additional code.

The default representations for the OR and NOT characters are '|' and '¬' (U+004F and U+00AA respectively. 'AA'x is ALT-1-7-0 on the numeric keypad.) As indicated above under Running the compiler, the options -cn and -co can be used to indicate additional encodings used to represent these characters.

String data can be delimited by either the single (') or double (") quote characters. The same character must be used to begin and end the string. The character not used as a delimiter may be part of the string. If portability is a concern, use only the single-quote to delimit strings. The character used as a delimiter can be included in the string by specifying it twice in succession, for example, 'Let''s dance' will be compiled as Let's dance, """Fine"", he said" as "Fine", he said.

Source lines are logically continued from the right margin of one line to the left margin of the next, with no whitespace assumed between the two character positions. (see the -m compiler option). Any characters outside the margins, as well as any characters lower than space in the collating sequence, are ignored. For example:


         Left Margin            Right Margin                  Line End
             |                       |                            |
             "This is a continued charsome stuff outside the margin
 extra stuff acter string"
will be compiled as This is a continued character string.

If a line ends with a statement keyword with no trailing blank, and another keyword begins in the left margin of the next line the two keywords will be considered a single keyword token. A future release will cause words and numbers, but not strings, operators, etc., to break at the end of a line.

INCLUDE files are identical in format to the source file. Currently INCLUDE files must have either no extension or the extension ".inc" or ".cpy". The compiler option -i, which can be repeated multiple times, provides a list of directories to be searched, in the order written, to locate an INCLUDE file. The current working directory is always searched last if the file is not found sooner. INCLUDE files use the same list of alternate characters as the source, and must use the same margins, although a future release will allow these to be overridden for each individual file.


Compiler output

Compiler output currently consists of the optional
source, assembler, and attribute, and cross-reference listings, and an optional object file or assembler file of the compiled program.

Source listing:
If the compiler option "-ls" is specified, compiling a program xxx.pli will create a source listing file named xxx.lst. Here are a few lines from a listing: The letters in blue are a key to the descriptions below, and are not part of the listing.


A   B  C
  340 
  341  %include DSA;
  342
  D     E   F 
341 (INF104)Processing include file DSA.
G

    1  /********************************************************************/
    2  /*                                                                  */

'B' is the line number. The source file and each %include file are numbered starting from line one. A future compiler version will also provide a file number for each include file to be used on error messages.

'C', the next 100 characters of the listing, show the input line. If the line is longer than 100 characters additional unnumbered lines will display the overflow.

The line following line 342 above shows the format of error, warning, and information messages. 'D' is the source line number. 'E' is the severity and message number (xxxyyy) 'xxx' is 'INF' for information-only messages as shown, 'WRN' for warnings, and 'ERR' for errors. 'yyy' is a unique message identifier for this error. 'F' is the message text.

Currently messages generated by the parser appear intermixed in the source listing, while code generator messages appear at the end of the source, or intermixed in the object code listing if one is produced. In a future version all messages will appear at the end.

The listing is paginated with imbedded formfeed characters 'G', currently every fifty-six lines.

Attribute and cross-reference listings:
If the compiler options "-la' and/or "-lx" are specified, the attribute list (symbol table) and/or cross-refreence listing is printed as shown below. Variables are listed in alphabetic order. The variable names always appear, "-la" causes the variable attributes to be printed, "-lx" causes the cross-reference listing to be generated. The example below shows the results of specifying "-lax".


H                    I    J                                                    K
ADDR                 124  Builtin
BSW                  309  Entry Unaligned                                       < Code+'13C0'x >
                            265 
BYTE                 332* Builtin
                            737   738 
C                     77  Char(1) Unaligned                                     < DSA-'51'x(1) >
                            266   278   300   321   346   362   370   446   452   453 
                            478   507   510   512   514   516   531   553   557   566 
C4                   109  Char(4) Unaligned Based                               < +'00'x(4) >
                            297   298   533   615   679 
C4                   327  Char(4) Unaligned Parameter                           < Loc @DSA+'08'x(4) >
                            723   724   725   726   727 
CONDS                 97  (9) Char(6) Var Unaligned Static Init()               < Static+'12'x(8) >
                            407   635 
DSA_BELOW_EBP         27  Unaligned Structure In(PLI_DSA)                       < +'00'x(32) >
                            635   636   642   643   644   646   647 
DSA_CHC               33  Ptr Aligned In(PLI_DSA.DSA_BELOW_EBP)                 < +'08'x(4) >
                            643 
PLI_DSA              340  Unaligned Based Structure Level(1)                    < +'00'x(40) >
'H' is the name of the data element. If the name is longer than twenty characters it will appear on a line by itself, with the remaining data shown on the next line. 'I' is the line number of the declaration of this item. Undeclared data, like 'BYTE' above show the line number where this element is first used, followed by '*'.

'J' lists the data attributes, which should be self-explanatory. This field may occupy more than one line.

'K' shows the address of the item in < >. The addresses are displayed as follows:

Code labels
Code+'oooo'x. 'oooo' is the hexadecimal offset from the label '_pli_code'. See 'BSW' above.
STATIC data
Static+'oooo'x(ll). 'oooo' is the offset from the label '_pli_data. 'll' is the (decimal) length of the data element. If this was an UNALIGNED BIT element, the length would be shown as (x.y) where 'x' is the number of whole bytes, and 'y' is the number of additional bits. For an array, 'll' is the length of one member. See 'CONDS' above.
AUTOMATIC data
DSA-oooo'x(ll). 'oooo' is the offset from the start of the DSA containing the data. Register EBP always points to the current DSA. Lexically containing DSAs (static scope) are chained from offset -8 in the contained DSA. See the file 'DSA.INC' distributed with the library. 'C' above is an example of AUTOMATIC data.
BASED data
+'oooo'x(ll). 'oooo' shows the offset from the beginning of the BASED variable or structure. See the first occurrence of 'C4' above.
Data addressed via locator
Loc @DSA±'oooo'(ll). This is the address format for parameters, adjustable data, and CONTROLLED data. 'oooo' is the offset of the locator/descriptor which points to and describes the data. See the second 'C4' above.

Assembler listing:
If the -lm option was specified, a listing of the generated code will appear between the source listing and the attribute list. This is similar in format to the listing generated by ALP. The major difference is that ALP displays immediate operands in big-endian order. This listing will always display immediate operands as they are stored in memory, i.e. little-endian.

Here is a small sample of the object code listing:


                                        ; Stmt 2, Line 3 (PUT)
                                         .code
00000025 83 EC 40                        sub esp,64
00000028 66:C7 04/24 0100                mov word ptr 0[esp],1
0000002E 89 6C/24 [0C]                   mov dword ptr 12[esp],ebp
00000032 C7 44/24 [10] 00000000          mov dword ptr 16[esp],0
0000003A 0F BF 75 [E6]                   movsx esi,word ptr -26[ebp]
0000003E 66:89 74/24 [2C]                mov word ptr 44[esp],si
00000043 66:C7 44/24 [02] 1500           mov word ptr 2[esp],21
0000004A C7 44/24 [04] 00000000:R        mov dword ptr 4[esp],offset _SYSPRINT
00000052 C7 44/24 [08] 04000000:R        mov dword ptr 8[esp],offset _pli_data+04h
0000005A C7 44/24 [14] C0001000          mov dword ptr 20[esp],001000C0h
                                         extern _pli_Put:near

If there were any errors or warnings, a message showing the number of them appears at the end of the listing.

Object output:
Compiling a program xxx.pli with the -C option will create an object file xxx.obj. The program contains a single code segment 'CODE32', a single static data segment 'DATA32', and zero or more external data segments as described under
Assembler output. No BSS segment or symbolic debug information is generated.

Assembler output:
Compiling a program xxx.pli with the -S option will create an assembler file xxx.asm in a format suitable for the IBM ALP assembler. Other assemblers will most likely not be able to assemble this file correctly.

The compiler generates assembler labels for level-1 EXTERNAL data and for each EXTERNAL ENTRY. Most OS/2 assemblers have a list of reserved words which cannot be used for labels, typically including all register names (e.g. EAX), all instruction mnemonics (LEA), and most pseudo-ops (ORG). Using any of these as PL/I external labels will cause an assembly error.

A few lines of assembler output are shown:


; Stmt 2, Line 3 (PUT)
 .code
 sub esp,64 
 mov word ptr 0[esp],1 
 mov dword ptr 12[esp],ebp
The source statement and line number that generated each piece of code, along with the statement type is shown as indicated. The line number ties this code to the source listing.
Program Structure
The generated program consists of a single code segment identified by the label '_pli_code', a single static data segment identified by '_pli_data', an unnamed stack segment, and zero or more external data segments. No BSS segment is currently generated. PL/I programs typically do not contain large amounts of uninitialized static data that would go into a BSS segment, although it is possible one will be added later.

External segments are generated for each variable or structure declared EXTERNAL, as well as for each EXTERNAL FILE. If the variable has the INITIAL attribute the data is always generated, otherwise the declared amount of storage is reserved. Here is the generated code for an uninitialized external structure named 'abc':


ABC segment dword common 'ABC'
_ABC equ $
 org _ABC+0Ch
ABC ends
Here is the code for the initialized external segment 'sysprint':

SYSPRINT segment dword common 'SYSPRINT'
_SYSPRINT equ $
 dd 0
 dd 0
 dd _SYSPRINT+12
 dw 8
 db 'SYSPRINT'
SYSPRINT ends
The linker combines identically-named external segments. The resulting length will be the length of the largest such segment. The data values will be taken from the first initialized segment encountered.

All declarations of EXTERNAL data should be identical in length, and, if one or more of the declarations contains the INITIAL attribute, the initial values should be the same. It is permissible to have only one declaration of EXTERNAL data have the INITIAL attribute, and all procedures declaring it will reference that data.

Internal Data Representations
FIXED BIN, precision 7 or lessBYTE
FIXED BIN, precision 15 or lessWORD
FIXED BIN, precision 31 or lessDWORD
FIXED DEC, any precision
Intel x87 BCD format
TBYTE
FLOAT BIN, precision 23 or less
Intel x87 short floating-point
REAL4
FLOAT BIN, precision 52 or less
Intel x87 long floating-point
REAL8
FLOAT BIN, precision>52
Intel x87 extended floating-point
REAL10
FLOAT DEC, precision 5 or lessREAL4
FLOAT DEC, precision 14 or lessREAL8
FLOAT DEC, precision>14REAL10
CHARACTER(n)n bytes
BIT(n) ALIGNED(n+7)/8 bytes
BIT(n) UNALIGNEDn bits
PTR, OFFSETNEAR PTR

Work file:
The compiler creates a temporary work file named PLI-xxxxxxxx-yyyyyyyy-zzzzzzzz.tmp in the current working directory. Normally this file is deleted at the end of compliation. Errors in the beta version of the compiler causing certain traps may prevent automatic deletion of this file. If it not automatically removed it may be deleted manually.


Input and Output Statements

In the following discussion, delimiter signifies either a linefeed character (ASCII '0A'x), or a carriage-return/linefeed combination (ASCII '0D0A'x).

Iron Spring PL/I can read and write:

  1. Standard OS/2 text files, with lines of text terminated by delimiters.
  2. Files of fixed-length records with or without delimiters.
  3. Files of variable-length records where the length of each record is indicated by a two-byte binary prefix, with or without delimiters.
  4. Files containing arbitrary streams of binary data with no record boundaries.

File Declarations
The general form of a file declaration is shown below. As usual, keywords can be specified in any order. Many keywords may be specified either in the declaration or in the OPEN statement for the file. If they are specified in both, they must not conflict. For example, a file declared INPUT cannot be opened as OUTPUT.

>>-DECLARE-name-+------+-+--------+-+--------+-+----------------------------------+-+------------------+-><
                |      | |        | |        | |                                  | |                  |
                +-FILE-+ +-RECORD-+ +-INPUT -+ +-ENVIRONMENT(environment options)-+ +-other attributes-+
                         |        | |        |
                         +-STREAM-+ +-OUTPUT-+
                                    |        |
                                    +-UPDATE-+

RECORD and STREAM Files
  • STREAM indicates that the file is a continuous stream of data items in character form. STREAM files are read or written without regard to record boundaries. The PL/I statements GET and PUT are used to process STREAM files.
  • RECORD indicates that the file is logically divided into records which are composed of one or more data items in any form. A record is always read or written as a unit. The PL/I statements READ, WRITE, REWRITE, and LOCATE are used to process RECORD files.

INPUT, OUTPUT, and UPDATE
  • INPUT indicates that the file is to be read only. The file must exist or the UNDEFINEDFILE condition will be raised when the file is opened.
  • OUTPUT indicates that the file is to be written only. If the file does not exist, it will be created when opened. If it does exist, it will be deleted and a new file created, unless the ENVIRONMENT option APPEND is specified.
  • UPDATE indicates that the file is going to be both read and written.

ENVIRONMENT Options
The ENVIRONMENT attribute allows specification of implementation-dependent options for a file. Iron Spring PL/I allows coding of all ENVIRONMENT attributes allowed by IBM PL/I for MVS and VM. Only attributes actually used are described here.
  • Record format keywords: D, DB, F, FB, FS, FBS, V, VB, VS, VBS, U. One of these keywords is coded to indicate the type of records to be processed. Only the D, F, V, and U are significant, the others are provided for compatibility. F indicates that the file consists of records having all the same length. V or D indicate that the records in the file can have different lengths. The options CRLF, LF, and VARLS provide additional information about format F, V, and D files.

    Record format U allows reading and writing files without regard to formatting or record boundaries. When reading INTO a character string, the amount of data available up to the maximum length of the string is returned. ENDFILE is raised only on the next read after the last byte has been returned. See the sample program 'readu.pli' for an example of using ENV(U) to read a file with no predetermined maximum record length. Writing FROM a character string writes n bytes of data from the string, where n is the current or constant length of the string.

  • BLKSIZE(n) specifies the size of the buffer used to transfer data to or from the file or device. If this keyword is not specified, a default value is used.
  • RECSIZE(n) specifies the uniform record length for F files, or the maximum record length for V or D files. If this keyword is not specified, the default is LINESIZE. The RECSIZE value describes only the data portion of the record, exclusive of delimiters, length prefixes, etc.
  • CRLF or LF specifies the presence of record delimiters. If neither is specified, no delimiters will be written on output or expected on input. LF indicates that a single newline character (ASCII '0A'x) delimits records in the file (Unix convention). CRLF indicates that a carriage-return/linefeed combination (ASCII '0D0A'x) is used as a delimiter (OS/2, DOS convention). The delimiters will be written as specified on output, but either is always acceptable for input.
  • VARLS specifies that records in the file will be written with a FIXED BINARY(15) prefix containing the length of the following data.
  • APPEND specifies that an INPUT or UPDATE file will have data appended to the end if it already exists. When the file is opened, the current file position will be set to the end of the file.

Other Attributes
  • Storage attributes ALIGNED, UNALIGNED, AUTOMATIC, BASED, DEFINED, STATIC, INTERNAL, and EXTERNAL may be used with declarations of file variables. INTERNAL and EXTERNAL are also applicable to file constants. The default for a file constant is EXTERNAL.
  • BUFFERED or UNBUFFERED are used to control the amount of storage that will be used for buffers for this file. BUFFERED requests that a storage area of length BLKSIZE is to be used to handle transfers to or from the file or device, minimizing interaction with the Operating System. UNBUFFERED specifies that each record is read or written immediately, and no additional storage is used.
  • KEYED indicates that keys may be used to select a particular record to be read or written, bypassing normal sequential processing. A key for this implementation is a CHARACTER(12) data item which uniquely identifies a record in the file. This is not intended to be user-modifiable data, except that key of (12)'00'x indicates the beginning of the file, and (12)'FF'x indicates the end. The KEY, KEYFROM, and KEYTO keywords may be used on I/O statements to receive or supply the identifier of the record.
  • PRINT is an additional attribute for STREAM OUTPUT files indicating that this file is ultimately destined to be printed. The compiler keeps track of the current "page" and "line" in the file, and raises the ENDPAGE condition when starting a new page. PRINT also enables print-related processing of the PAGE option on the PUT statement and the PAGE format item.
  • VARIABLE indicates that this declaration is a file variable rather than a file constant. File variables can be assigned the value of a file constant at run time, for example, to pass a file as a parameter to a procedure, but cannot be used for I/O unless a value has been assigned.

STREAM files default to ENV(V CRLF) unless other attributes are specified. RECORD ENV(V) files default to ENV(V VARLS). Specifying PAGESIZE(0) on the OPEN statement will suppress page breaks for PRINT files.

Standard Files
Unless overridden by ENVIRONMENT or OPEN, the standard files SYSIN and SYSPRINT default to stdin and stdout with the following attributes respectively.
SYSINENV(V CRLF RECSIZE(120)) LINESIZE(120)
SYSPRINTENV(V CRLF RECSIZE(120)) LINESIZE(120) PAGESIZE(60)
If stdin and stdout are the current VIO window, these files default to ENV( BLKSIZE(0) ).

Input/Output Statements
OPEN and CLOSE apply to both STREAM and RECORD files. The GET and PUT statements operate on STREAM files. The READ, WRITE, REWRITE, and LOCATE statements operate on RECORD files. The assumption is made that the reader is familiar with, or has access to a reference for, the I/O statements of IBM PL/I for MVS and VM. Only the syntax accepted by Iron Spring PL/I is shown here, no explanation is provided.

OPEN statement.

         +----------------------- , -------------------------------------+
         v                                                               |
>>-OPEN--.---FILE(file_ref)-+------------------+----+---------------+----+---- ; ----><
                            |                  |    |               |
                            +-|stream-options|-+    +-TITLE(string)-+  
                            |                  |
                            +-|record-options|-+


|stream-options| 

              +-INPUT ---------------------------+
              |                                  |   
|----STREAM---+----------------------------------+-+-------------+-----|
              |                                  | |             |                  
              +OUTPUT ---+-----------------------+ +-LINESIZE(m)-+                                   
                         |                       |                           
                         +-PRINT-+-------------+-+                           
                                 |             |                  
                                 +-PAGESIZE(n)-+                  

|record-options|

              +-INPUT -+
              |        |
|---RECORD----+--------+-+------------+-+-------+------| 
              |        | |            | |       |                    
              +-OUTPUT-+ +-BUFFERED---+ +-KEYED-+   
              |        | |            |                                      
              +-UPDATE-+ +-UNBUFFERED-+                                      
                

TITLE
The TITLE(string) option provides the OS/2 "filespec" of the file to be read or written. "String" is any valid character string expression. If the TITLE is omitted, the file name used by OS/2 will be the declared PL/I filename in uppercase, except for SYSIN and SYSPRINT which default to stdin and stdout. The TITLE may be a full filespec [TITLE('C:/test.dat')], an OS/2 device name [TITLE('LPT1:')], or a relative filespec. Unlike IBM OS/2 PL/I, file attributes may not be specified in the title string.

CLOSE statement.

          +----- , ---------+
          v                 |
>>-CLOSE--.--FILE(file_ref)-+-- ; --><


STREAM Input/Output

GET statement.

>>-GET-FILE(file_ref)-+-----------------+-+----------------------+- ; --><
                      |                 | |                      |
                      +-SKIP----------+-+ +-|data_specification|-+
                             |        |
                             +-(expr)-+ 
PUT statement.

>>-PUT-FILE(file_ref)-+---------------------+-+----------------------+- ; --><
                      |                     | |                      |
                      +-PAGE-+------------+-+ +-|data_specification|-+
                      |      |            | |
                      |      +-LINE(expr)-+ |
                      |                     |
                      +-SKIP-+--------+-----+
                      |      |        |     |
                      |      +-(expr)-+     |
                      |                     |
                      +-LINE(expr)----------+

Data Specification


|--+------+---( |data-list| )---------------------+----|
   |      |                                       |           
   +-LIST-+                                       |
   |                                              |
   +-DATA---+-----------------+-------------------+
   |        |                 |                   |
   |        +-( |data-list| )-+                   |
   |                                              |
   |        +-----------------------------------+ | 
   |        v                                   | |
   +-EDIT---.-( |data-list| )-( |format-list| }-+-+  

SKIP[(expr)]
The SKIP option causes a new line to be started in the data stream prior to transmitting any data from the current or subsequent GET or PUT statement. The (expr) is converted to an integer n. On output, n newlines (linefeed or carriage-return/linefeed) are inserted. On input the remainder of the current line and the n-1 following lines are skipped. If (expr) is omitted or not greater than zero, 1 is used for the value of n. For PRINT files, the ENDPAGE condition may be raised if PAGESIZE is exceeded.

PAGE
The PAGE option is valid only for PRINT files. It causes a page eject (ASCII '0C'x) to be inserted in the output stream prior to transmitting any data from the current or subsequent PUT statement. If both PAGE and LINE are present, the PAGE option is applied first.

LINE(expr)
The LINE option is valid only for PRINT files. It causes a new line to be started in the output stream prior to transmitting any data from the current or subsequent PUT statement. The (expr) is converted to an integer n. If n or more lines have been written on the current page, or if n exceeds PAGESIZE, a new page is started. newlines are inserted in the output strean to position the next data written at line n. If (expr) is less than or equal to zero, 1 is used for the value of n.


RECORD Input/Output

READ statement.

>>-READ-FILE(file_ref)-+-----------------------------------+----------- ; --><
                       |                                   |          
                       +-INTO(ref)-+-------------+---------+
                       |           |             |         |
                       |           +-KEY(expr)---+         |
                       |           |             |         |
                       |           +-KEYTO(ref)--+         |
                       |                                   | 
                       +-SET(pointer-ref)-+-------------+--+
                       |                  |             |  |
                       |                  +-KEY(expr)---+  |
                       |                  |             |  |
                       |                  +-KEYTO(ref)--+  | 
                       |                                   |
                       +-IGNORE(expr)----------------------+

WRITE statement.

>>-WRITE-FILE(file_ref)--FROM(ref)--+-------------------------+----------- ; --><
                                    |                         |
                                    +-KEYFROM(expr)-----------+
                                    |                         |
                                    +-KEYTO(ref)--------------+

LOCATE statement.

>>-LOCATE--based-variable--FILE(file-ref)--+------------------+-------------------+---- ; --><
                                           |                  |  |                |
                                           +-SET(pointer-ref)-+  +-KEYFROM(expr)--+             

REWRITE statement.

>>-REWRITE-FILE(file_ref)--+-----------+--+-----------+----------- ; --><
                           |           |  |           |
                           +-FROM(ref)-+  +-KEY(expr)-+


DISPLAY statement

The DISPLAY statement writes text to stderr. The syntax is:

>>-DISPLAY (expression)--+-----------------+--+-------------------+----------- ; --><
                         |                 |  |                   |
                         +-REPLY(char-ref)-+  +-EVENT(event-ref)--+

"expression" is converted to a character string and written to stderr. If the "reply" option is not specified, this is followed by a newline. Otherwise the "reply" and "event" options are currently ignored.


Builtin Functions and Pseudovariables

This section lists only the differences between Iron Spring PL/I and IBM PL/I for MVS and VM. The definitions are taken from IBM PL/I for OS/2 [OS2], IBM Enterprise PL/I [ENT], or VAX PL/I [VAX].

ALLOCATE(n) ALLOCATE allocates n bytes of storage and returns a pointer to the first byte. [OS2][ENT]
BYTE(n)BYTE returns a character string of length one equivalent to the following:
SUBSTR( COLLATE(), MOD(n,256)+1, 1 ) [ENT][VAX]
COLLATECOLLATE Returns a character string of length 256 containing all 256 possible character values. [OS2][ENT][VAX]
COMPARE(x,y,z)COMPARE compares "z" bytes at locations pointed to by "x" and "y". [OS2][ENT]
COPY(x,y)COPY returns a string consisting of "y" concatenated copies of string "x". [OS2][ENT][VAX]
HEXIMAGE(x,y[,z])HEXIMAGE returns a character string that is the hexadecimal representation of 'y' bytes of storage at location 'x'. If 'z' is omitted, the length of the returned string is 2*y. If 'z' is present it must be CHARACTER(1) nonvarying, and the value of 'z' is inserted between every set of eight characters in the output string. In this case the length of the returned string is (2*y) + floor((y-1)/4). [OS2][ENT]
PLIFILL(x,y,z)PLIFILL moves "z" copies of a single byte "y" to a location "x" with no conversion, padding, or truncation. [OS2][ENT]
PLIFREE(p)PLIFREE frees storage at location "p" allocated by the ALLOCATE builtin. [OS2][ENT]
PLIMOVE(x,y,z)PLIMOVE moves "z" bytes to the location pointed to by "x" from the location pointed to by "y". [OS2][ENT]
RANK(c)RANK returns a FIXED BINARY(15) result. "c" is a character string of length one. RANK(c) is equivalent to the following:
INDEX( COLLATE(), c ) - 1 [ENT][VAX]
STRING(x)STRING concatenates the elements of array or structure "x" and returns the result. "x" is any reference that is suitable for string-overlay defining. Bit strings should have no gaps between elements caused by alignment. The string returned is a nonvarying character or bit string depending on the type of "x". The length of the string is the total number of characters or bits in "x". [ENT][VAX]


Program Linkage

PROCEDURE and ENTRY statements
PROCEDURE statement.

PROCEDURE-+--------------------+-+-----------------+-+-----------------+-+-------------+-+------------------+-- ; ><
          |                    | |                 | |                 | |             | |                  |
          +-| ( parameters ) |-+ +-| returns-opt |-+ +-| options-opt |-+ +- RECURSIVE -+ +-| external-opt |-+
ENTRY statement.

ENTRY--+--------------------+-+-----------------+-+-----------------+-+-------------+-+------------------+- ; ><
       |                    | |                 | |                 | |             | |                  |
       +-| ( parameters ) |-+ +-| returns-opt |-+ +-| options-opt |-+ +- RECURSIVE -+ +-| external-opt |-+ 
Other keywords defined by The IBM PL/I for MVS and VM compiler 1.1 and various other compilers are accepted for source compatibility, but are otherwise ignored.


|parameters|

  +------- , ----------------+
  v                          |
|-.- parameter-description --+----|
Each parameter-description supplies the attributes for one parameter, therefore the entry expects as many arguments as there are items in the list. A parameter-description is a list of data attributes that describes that parameter. For example: FIXED BINARY(7), CHARACTER(8), POINTER, or ENTRY would all be valid parameter descriptions.


|returns-opt|

|-- RETURNS ( returns-description ) ---|
The returns-description describes the attributes of the data which will be returned by this entry if it is invoked as a function. If the returns-opt is omitted the attributes are determined by the first character name of the first or only name prefix: I-N=FIXED BINARY(15), other=FLOAT DECIMAL(6).


|options-opt|

                +--- , --------+
                |              |
                +--------------+
                v              |
|---- OPTIONS ( . entry-option +  ) ---|
Each entry-option is one of the following:
FROMALIEN
Indicates that this entry is called from a non-PL/I program. A FROMALIEN procedure should be considered to be non-nested. That is, it may not access any AUTOMATIC data in the containing procedures, if any. Also, any condition-handling established in a previously-called procedure will be ignored on entry to the FROMALIEN procedure.

LINKAGE( linkage-type )
'linkage-type' indicates the type of calling sequence with which this entry will be invoked. If LINKAGE is omitted, a standard PL/I convention will be used. (see Run-time Considerations). Currently the only valid linkage type is SYSTEM. This can be used to call OS/2 API functions, but is also a valid convention among PL/I procedures, with some restrictions.
For source compatibility with IBM PL/I for OS/2, specify 'OPTIONS( BYVALUE LINKAGE(SYSTEM) )'. BYVALUE is assumed by this compiler and the keyword is ignored, but IBM PL/I defaults to BYADDR unless otherwise specified.

LINKAGE(OPTLINK) will be implemented in a future version to specify the "Optlink" calling convention that uses registers for some arguments.

MAIN
Indicates that this is the initial or 'main' procedure. Iron Spring PL/I allows only one OPTIONS(MAIN) procedure to be included in an executable program.

REENTRANT
This is included for compatibility only. All code compiled by Iron Spring PL/I is reentrant.

RECURSIVE is supported for compatibility only. It indicates that this entry may call itself, either directly or indirectly through a chain of calls. Iron Spring PL/I assumes all entries are potentially recursive.


|external-opt|

|-- EXTERNAL --+----------------------+---|
               |                      |
               +- ( external-name ) --+
The external-name is a character string, enclosed in quotes, representing the name by which this entry will be known to external callers. External 'external-name's beginning with the string "_pli" are reserved for use by the run-time library. If the 'external-name' is not specified, the default external name is an uppercase translation of the first label on the external procedure or entry name. For example, x: y: PROCEDURE; will use "X" as the external name for the linker.

ENTRY Declarations
The following attributes can be used in the declaration of an external entry:

DECLARE-- decl-name --ENTRY-+--------------------+-+-----------------+-+-----------------+-+------------------+-- ; ><
                            |                    | |                 | |                 | |                  |
                            +-| ( parameters ) |-+ +-| returns-opt |-+ +-| options-opt |-+ +-| external-opt |-+
Parameters, returns-opt, and external-opt are described under "PROCEDURE and ENTRY Statements" above.

|options-opt|

                +--- , --------+
                |              |
                +--------------+
                v              |
|---- OPTIONS ( . entry-option +  ) ---|
The following options are valid for entry declarations:
ASSEMBLER (abbreviated ASM)
Indicates that the entry is a non-PL/I procedure (assembler, C, etc.) The compiler generates code to save and restore the x87 Floating-point control word, since programs in different languages may change this unpredictably.
LINKAGE( linkage-type )
'linkage-type' is as described above under "PROCEDURE and ENTRY Statements".


Run-time considerations

Run-time messages
All run-time messages are written to stderr:

Parameter of MAIN Procedure
The MAIN procedure is called with one parameter: A CHARACTER(*) VARYING string containing the [possibly edited] image of the command line that invoked the program. A future release will allow programs to permit run-time options such as heap size to be specified on the command line. These options will be edited out of the text pased to the program.

The sample program 'numwrd.pli' is an example of a program that uses the command-line.

Memory utilization
Generated PL/I code does not modify STATIC storage except during program initialization. All code is reentrant unless the user program modifies STATIC storage in such a way as to invalidate this.

PL/I programs use the stack for activation records for procedures and BEGIN-blocks, including all AUTOMATIC storage. The heap is used for all allocated BASED and CONTROLLED storage, for control information for error-handling, and for file information and buffers.

The stack size is taken from the value stored with the executable, either by the linker, by EXEHDR, or a default minimal value of 16K bytes. Stack storage is allocated when the program is loaded, but is not committed until actually used. Stack probes are employed to prevent traps when more than 4K is requested at one time.

The heap size is currently fixed at 1MB (1024K), although a future release will allow this value to be respecified at run time. In the interim, if a larger heap is required, the library procedure INIT can be re-assembled specifying a larger value for the data item 'def_heap_size'.

The layout of the stack frame for one procedure or Begin-block is illustrated below.


                    ^
                    | |                                          |
                  +08 |  Parameters (see program linkage below)  |  Higher addresses
                      +------------------------------------------+        |
                  +04 |  Calling program's EIP                   |        |
                      +------------------------------------------+        |
               EBP+00 |  Calling program's EBP                   |        |
                      +------------------------------------------+        |
                      |  PL/I control information                |        |
                      |  [see library include file 'dsa.inc'     |        |
                      |   for layout (subject to change)]        |        |
                      |           ...                            |        v
                      +------------------------------------------+  Lower addreses
                      |  AUTOMATIC non-adjustable data           |
                      |  for this block                          |
                      |           ...                            |
                      +------------------------------------------+
                      |  AUTOMATIC adjustable data               |
                      |           ...                            |
                      +------------------------------------------+
                      |  Temporaries, argument lists             |
                                  ...

Program Linkage
The 'SYSTEM' and 'OPTLINK' calling conventions are similar: all arguments are pushed on the stack right to left (leftmost arguments in lower addresses). Returned values from function procedures may be returned on the stack [or possibly in EAX or ST(0) for 'OPTLINK']. The caller cleans up the argument list following return.

For both conventions, the called program is responsible for saving registers EBP, and EBX, ESI, and EDI. It is the caller's responsibility to save any other registers used. If the called entry is specified with OPTIONS(ASM), the caller saves and restores the 80x87 FPU control register. The calling program passes the size of the argument list in DWORDs in AL.

The standard PL/I calling sequence passes all arguments by reference, that is by passing their addresses and, optionally, the addresses of their descriptors. This means that changes to the values of parameters in the called procedure are reflected back to the caller. Descriptors are passed for structure, array, and string arguments. Normally descriptors are not passed for arithmetic arguments, although many PL/I runtime procedures require a descriptor. The library include file 'desc.inc' shows the layout of descriptors for various types of data [subject to change in future releases]. When the argument is a constant, an expression, or the data type doesn't match the corresponding parameter, a dummy argument is created and placed on the stack; the called procedure is passed the address of the dummy. In this case, changes to the values of parameters change the dummy argument and don't modify the actual argument.

The "SYSTEM" calling sequence passes arguments by value. All arguments, normally limited to arithmetic data, pointers, and nonvarying strings, are evaluated and the values are passed to the called procedure; descriptors are not used. Changes to the values of parameters do not cause changes in the values of the corresponding arguments.

Descriptors for non-adjustable data are normally stored in STATIC storage, descriptors for adjustable data are created during block initialization and stored in the current stack frame.

Two other pieces of information are also passed to a called PL/I procedure. The static backchain is passed in ESI, and the address of the Program Global Table (PGT) is passed in EDI. These are passed for both "system" and PL/I linkage unless the called entry is specified with OPTIONS(ASM). The PGT is a vector table containing the addresses of some required runtime routines. The static backchain is the EBP value of the block which lexically contains the called procedure. This allows the called procedure to reference automatic data belonging to containing blocks. This is not necessarily the same as the EBP value of the caller. For example, consider the following:


  A: PROC;
    CALL B;
  B: PROC;
    CALL B;
  C: BEGIN;
    END;
    END B;
    END A;
A is always the 'containing' block of B, regardless of whether B is called from A or from itself recursively. B is the containing block of block C. Using the static backchain, C can address AUTOMATIC data from B and A, and so on.

The stack layout for a standard PL/I call is illustrated below.

 

                      +--------------//--------------------------+
                      |  Space for returned value                |
                      +------------------------------------------+ <--------+
                                                                            |
 (optional, locator/  +------------------------------------------+          |
  descriptor pair     |  Address of descriptor                   | --->     |
  for returned value) +------------------------------------------+          |
                      |  Address of data                         | ---------+ 
                      +------------------------------------------+

 (optional)           +-------------//---------------------------+
                      |  Dummy arguments                         |
                      |            ...                           |
                      +------------------------------------------+

 (optional, locator/  +------------------------------------------+
  descriptor pair     |  Address of descriptor                   | --->
  for arguments       +------------------------------------------+
  requiring a         |  Address of data                         | --->
  descriptor -        +------------------------------------------+ <------+
  one per argument)                                                       |
                                                                          |
 (optional)           +-------------//---------------------------+        |
                      |  Saved values of 'live' registers        |        |
                      |  not saved by the called procedure       |        |
                      |             ...                          |        |
                      +------------------------------------------+        |
                                                                          |
                      +------------------------------------------+        |
                      | Argument list, one address per argument. |        |
                      | This address points to either the data   |        |
                      | or the address of the locator/           | -------+
                      | descriptor pair for the data.            |
                      | The last (highest addressed) argument    |   -or-  
                      | identifies the returned value.           | -------> argument
         ESP ----->   +------------------------------------------+
         

EXTERNAL data (not EXTERNAL ENTRY) is not shared between executable and DLL code, nor among procedures in different DLLs. For example, data declared DECLARE a EXTERNAL POINTER; will identify different "a"s in procedures linked into in the executable, in DLL "1", and DLL "2". Within the executable or any one of the DLLs all occurrences of that declaration will identify the same "a". Each level-1 external data declaration generates a segment for the linker.


Oncodes


                  Oncode
Condition         Value   
finish              4
error               9
name               10
record             20
transmit           40
key                50
endfile            70
undefinedfile      80
endpage            90
pending           100
stringsize        150
overflow          300
fixedoverflow     310
zerodivide        320
underflow         330
size              340
stringrange       350
area              360
attention         400
condition         500
check             510
subscriptrange    520
conversion        600


Preprocessor

The current implementation is limited to a subset of the IBM preprocessor statements. The listing control statements %PAGE, %SKIP, %PRINT, and %NOPRINT, the %INCLUDE statement, and the %REPLACE statement are described below.

%INCLUDE: The %INCLUDE statement instructs the compiler to read an external file of text and insert its contents in place of the %INCLUDE. The syntax is: "%INCLUDE <filespec>;".
<filespec> is any valid OS/2 file specification. If no file extension is coded the compiler will search for files having the extensions ".cpy", ".inc", or no extension. The search path for the file is specified on the PLIC command (see Running the compiler).

Examples of the %INCLUDE statement are:


%INCLUDE ABC; [will include "ABC", "ABC.CPY", or "ABC.INC" in your search path]
%INCLUDE "def.pli"; [will include "DEF.PLI" in your search path]
%INCLUDE "c:\pli\includes\ghi.pl1"; [will include the named file only]

Nested includes are allowed, that is, the included file may itself contain %INCLUDE statements. The maximum depth of nesting is four levels.

%REPLACE: The %REPLACE preprocessor statement provides limited text substitution capability. The syntax is "%REPLACE <identifier> BY <constant>;".
<identifier> is any word which would be valid as an identifier in a PL/I program. PL/I keywords are not allowed. Multiple %REPLACE statements may specify the same identifier, with the most recently-occurring one being in effect.
<constant> is any valid character-string, bit-string, or arithmetic constant.

The scope of the %REPLACE statement is between its occurrence in the input stream to the end of the source program, including any included files. The effect is to substitute the value of <constant> in the program anywhere <identifier> appears.

For example, the following sequence of statements:

 %REPLACE abc by 1;
 put edit(3+abc)(f(5));
will result in the value 4 being output by the PUT statement.

The %REPLACE statement above is equivalent to the following, but does not require a full preprocessor scan:

 %DECLARE abc CHARACTER;
 %abc = '1';

%PRINT: The %PRINT statement turns on (enables) printing of the source listing. The %PRINT statement itself will not be printed if the listing was previously disabled.

%NOPRINT: The %NOPRINT statement turns off (disables) printing of the source listing. The %NOPRINT statement itself will be printed if the listing was previously enabled.

%PAGE: The %PAGE statement causes a page eject in the source listing after printing of the %PAGE statement, if the listing is enabled.

%SKIP: The %SKIP[(n)] statement will print 'n' blank lines (n=1 to 9) in the source listing following the printing of the %SKIP statement, if the listing is enabled. If 'n' is omitted, the default is one line.


Efficient Programming

  1. Avoid use of the ENTRY statement. If you do have a procedure with multiple entries, try to minimize the number of RETURN statements.
  2. Any use of the GET and PUT statements, including GET STRING and PUT STRING, in a statically-linked program causes library conversion and format-processing code to be included. If there are only have one or two GET or PUT statements, consider using record I/O and formatting the data yourself. The PL/I runtime library itself does not use any stream I/O.
  3. Read and write as much data as possible with a single GET or PUT statement. Instead of:
    
    PUT LIST(a);
    PUT LIST(b);
    
    use:
    
    PUT LIST(a,b);
    
  4. Use "adjustable" strings and arrays only when necessary.
  5. The SUBSCRIPTRANGE condition is intended for debugging and should not be used in a production program.
  6. Use SIZE only when necessary as it can generate a lot of code.
  7. When possible provide your own checks to prevent computational conditions such as FIXEDOVERFLOW for FIXED DECIMAL data rather than relying on an ON-unit.

User-callable PL/I Library Procedures

Most of the procedures in the PL/I runtime library (prf.lib) are for use by the compiler and are not user-callable. The following, however, may be useful:
osdelete deletes a file by name.
DECLARE osdelete ENTRY( CHAR(*) VAR )
                          RETURNS( FIXED BIN(31) )
                          EXT( '_pli_OSDelete' );
DECLARE rc FIXED BIN(31);
RC = osdelete( filename );
filename
a character string variable or constant containing the name of the file to be deleted.
rc
a FIXED BIN(31) data element which will contain the OS/2 error code from DosDelete.
0 indicates no error.
2 indicates that the file was not found.
for other errors see the OS/2 Operating System Control Program Guide and Reference.

tempnam generates a unique name for a temporary file.

DECLARE tempnam ENTRY( CHAR(*) VAR, CHAR(*) VAR, CHAR(*) VAR )
                         RETURNS( CHAR(260) VARYING)
                         EXT( '_pli_Tempnam' );
DECLARE temp_filename CHAR(260) VARYING;
temp_filename = tempnam( sDir, sPfx, sSfx );
sDir
a VARYING CHARACTER string identifying the directory in which the file is to be located. If this is the null string the file will be located in the current working directory.
sPfx, sSfx
VARYING CHARACTER strings containing the prefix (sPfx) and the suffix (sSfx) to be attached to the generated name. Either or both may be the null string. The generated name will be:
   'sPfxpppppppp-tttttttt-uuuuuuuusSfx'
where 'pppppppp' is the hex value of the current process id (pid), 'tttttttt' is the hex value of the current thread id (tid), and 'uuuuuuuu' is a unique number within this process. If another file with this name already exists, the unique number is incremented until the generated name does not confilct with an existing file.
temp_filename
In the example this represents a VARYING CHARACTER string which will contain the generated unique name.


Debugging PL/I programs

Debuggers
The WATCOM® debugger may be used to debug PL/I programs.
Source-level debugging is not yet available, but since the compiler can generate assembly-language output, debugging at the assembly level is straightforward.

The following steps are not required, but will simplify debugging.

To get the debugger to stop at the beginning of the PL/I program, make the following modifications to "setup.dbg", normally found in the <WATCOM>\binw directory.
elseif _dbg@dbg$os == 12 && ?@main {Following this
go/until/noflip @main
}
elseif ?@@_PLI_MAIN { Insert these three lines
go/until/noflip @@_PLI_MAIN
}
After this, you will also need to link your program with a module definition file that exports the symbol "_PLI_Main" so that it is visible to the debugger. The "samples" directory contains an example .def file and linker command to do this.

The IBM "ASDT" debugger is also usable.

Sometimes it may be desirable to compile-in a breakpoint to be activated when a specific location is reached or condition occurs. The following statement inserted in your source will activate a breakpoint when it is reached when running under control of a debugger:

 *PROCESS DBG(INT3); 
If the resulting executable is not run under a debugger, the interrupt will be ignored. Nevertheless, INT3 traps should not be left in a production program.

SNAP traceback
The PL/I statement "ON CONDITION(xxx) SNAP..." will generate a traceback on stderr when the specified condition is raised. Beginning with the procedure generating the traceback (_pli_Trace) and working backwards, the trace lists the address of the active entry point of the procedure, the address of the statement in the calling procedure that called that entry, and the name of the entry point if available.

PLIDUMP
The PL/I statement "CALL PLIDUMP( "<options>", "<title>" ); will generate debugging output on stderr when executed.
"title" is a character string used as the
title of the dump.
"options" provide one or more dump options:
'S' = terminate after dump
'C' = continue after dump
'T' = print traceback
'B' = dump automatic storage.

Future versions will be extended to dump PL/I STATIC storage ('H'), and information about opened files ('F').

Options may be combined and may appear in any order, for example "TCB" means: print trace, continue after snap, and dump all automatic storage.

The stack is dumped from the current procedure (_pli_Dump) [lowest address] to the PL/I startup code (_pli_Init) [highest address]. Storage for each procedure is dumped in three sections (see sample segment of PLIDUMP output below): Temporaries (argument lists and storage used during expression evaluation), Data (AUTOMATIC variables and other non-temporary user data in the stack frame), and DSA (system data in stack frame). The layout of the DSA is given in lib\include\dsa.inc. This is subject to change in future releases.

If the dump is issued as result of an OS/2 system exception, trap information will appear immmediately before the DSA of the procedure in which the exception occurred.

Each data line includes the actual address of the first byte, the offset (±EBP for DSA and data, +ESP for temporaries), up to sixteen bytes of data in hex as actually stored in memory (no byte-swapping), and the ASCII representation of the same data.

The DSA display provides additional formatted information regarding the ON-conditions enabled for this block and identifies runtime-library procedures.

sample PLIDUMP output

***error dump***

Address  Caller   Entry name
           ... omitted from the example ...

OS/2 System Trap Information
  System Code=C0000005 XCPT_ACCESS_VIOLATION
  Exception Address=000100D7
  EAX 44000B00  EBX 00020000  ECX 00000000  EDX 44000B00
  ESI 00000000  EDI F8070200
  DS  0053      ES  0053      FS  150B      GS  0000    
  CS:EIP 005B:000100D7  SS:ESP 0053:00048600  EBP 00048628
  EFLAGS 00012216

0001868A 00027402 FTPRO
 
Temporaries at 003C80C4
003C80C4 ( 000000) 74823C00 5C863C00 98823C00 3C813C00 |t.<.\.<...<.<.<.|
003C80D4 ( 000010) 53003C00 F4803C00 5C863C00 A8A2021C |S.<...<.\.<.....|
           ... omitted from the example ...
003C83B4 ( 0002F0) 3C843C00 00000000 3C843C00 C4833C00 |<.<.....<.<...<.|
003C83C4 ( 000300) 64140500                            |d...|            

Data at 003C83C8
003C83C8 (-000074) 786D0500 00843C00 3E350400 E4C34300 |xm....<.>5....C.|
003C83D8 (-000064) 00000000 30823C00 0083E180 B8813C00 |....0.<.......<.|
003C83E8 (-000054) 0000E181 E4C34300 00000000 B5570400 |......C......W..|
003C83F8 (-000044) 31000000 10843C00 786D0500 80853C00 |1.....<.xm....<.|
003C8408 (-000034) 12720200 600F3C00 01001500 00001B00 |.r..`.<.........|
003C8418 (-000024) E0320500                            |.2..|            

DSA at 003C841C
Enabled: CONV FOFL OFL UFL ZDIV 
003C841C (-000020) C8833C00 0000E180 E4C34300 00000000 |..<.......C.....|
003C842C (-000010) 8A860100 31000000 48843C00 786D0500 |....1...H.<.xm..|
003C843C ( 000000) 80853C00 02740200                   |..<..t..|        

00026C79 00010123 PH10
 
Temporaries at 003C8444
003C8444 ( 000000) 48843C00 11000000 42000000          |H.<.....B...| 
           ... omitted from the example ...



version 0.8c, 17 Oct, 2009
http://www.iron-spring.com