- THE PL/I NEWSLETTER -
|- THE PL/I NEWSLETTER -|
- THE PL/I NEWSLETTER -
The tenth issue may be downloaded from:
The PL/I Newsletter, No. 10, September 2008.
The ninth issue may be downloaded from: The PL/I Newsletter, No. 9, April 2006.
The eighth issue may be downloaded from: The PL/I Newsletter, No. 8, January 2005.
The seventh issue may be downloaded from: The PL/I Newsletter, No. 7, December 2004.
The sixth issue may be downloaded from: The PL/I Newsletter, No. 6, December 2003.
The fifth issue may be downloaded from: The PL/I Newsletter, No. 5, August 2002.
The fourth issue may be downloaded from: The PL/I Newsletter, No. 4, November 2001.
The third issue may be downloaded from: The PL/I Newsletter, No. 3, June 2001.
The second issue may be downloaded from: The PL/I Newsletter, No. 2, September 2000.
The first issue may be downloaded from: The PL/I Newsletter, No. 1, July 2000.
Particulars of the English-language edition are:
E. Sturm, The New PL/I ... for PC, Workstation and Mainframe, Vieweg-Teubner, Wiesbaden, Germany, 2009. ISBN: 978-3-8348-0726-7.
Details of the German-language edition are:
E. Sturm, Das neue PL/I (fur PC, Workstations und Mainframe), 7th Ed., Vieweg-Verlag (2008) ISBN: 3-528-44792-3
A couple of years ago I put together a syntax error checking PL/I Editor
for the Eclipse framework. The package is named Pliedit, for PL/I Editor.
There was some interest, but not enough for me to pursue it at the time. I
recently decided to give it one more try rather than abandoning it totally.
I made some revisions and fixed some problems that the original version
had, including some performance issues with huge PL/I source files.
I've set up a web site for the PL/I Editor at pliedit.com. The homepage contains basic information, and there are Eclipse Update sites for Eclipse versions 3.2 through 3.5. You can download Pliedit by using the normal Eclipse software installation process (anonymously - no user information is tracked). There is also a bulletin board with discussion forums.
Because of the complexity of PL/I, both a scanner generator and parser generator were used. The colorization capabilities are very detailed, including the ability to have distinct color and font for individual keywords based on semantics rather than syntax. Currently, error checking is only at the statement level. (I'll have a go at full AST processing next time around.) There is little preprocessor support for now.
Pliedit is not a finished product but based on the 80/20 (or 90/10) rule, I think Pliedit in its current state can be useful for PL/I programmers who also use Eclipse. If you are interested, visit the pliedit.com web site. The discussion forums are at pliedit.com/bb .
PL/I offers a richer variety of basic data types than most languages. Consider just computational data. C has the floating-point types float, double, long double, integer types short [int] and long [int], and possibly char can be considered a computational data type.
PL/I computational data has base, scale, mode, and precision. Scale can be fixed or float, roughly corresponding to C. Mode can be real or complex; C has no complex data. Base can be binary or decimal; decimal data is unknown in C. Precision is the attribute that provides the most power, but also some degree of confusion. In place of C's short and long, for example, PL/I allows the programmer to specify the precision, the number of bits(binary), or digits(decimal) that are required to store the data, although the hardware can use more if appropriate. For example, a machine with a word size of 32 bits can store a fixed binary value up to FIXED BIN(31) [one bit is reserved for the sign and is not counted].
An often-unnoticed appendage to precision called the scale factor sometimes causes confusion. For fixed data precision is specified as a pair of numbers ( number-of-digits [, scale-factor ] ), where the scale-factor is assumed to be zero if not specified. Float data has no scale-factor, since by definition floating-point data includes its own scale. So far, so good. A number representing the number of items in inventory might be declared FIXED DECIMAL(8), or alternatively FIXED DECIMAL(8,0), which provides storage to handle quantities up to 10,000,000. The count must obviously be a whole number of items, and so the ",0". A number representing a bank balance might be declared FIXED DECIMAL(12,2), which would reserve storage to handle balances up to $10,000,000.00, dollars and cents. Non-US readers feel free to read this using your own currency conventions, PL/I supports them all.
The scale factor can cause some confusion when performing division as part of an arithmetic
expression. C INT data is, as the name indicates, always integer. The compiler assures
that expressions of type INT will always give integer results: in C 5/3=1. In PL/I, the scale factor is
always taken into account, even when it is zero. For addition, subtraction, and multiplication, if
all the operands have a scale factor of zero, the result will too. For division, however, PL/I always
tries to preserve the maximum possible significance of a fractional quotient. Many books on PL/I
fail to point out this fact. In practical terms, the compiler converts the dividend
(the number being divided) to a temporary value which has just enough digits to hold its declared
value, followed by zeroes to pad out the maximum possible length of that data type. The definition
from the standard is:
|m = N|
|n = N-p+q-s|
This conversion is defined by the language, not the implementation. The only possible differences among machines might be in the maximum sizes of data which can be handled (N). Here are a couple of examples for IBM S/390 architecture:
FIXED BINARY(15,0) divided by FIXED BINARY(15,0): The temporary result is FIXED BINARY(31,15). That's right, the quotient will have fifteen bits of fractional result.
FIXED DECIMAL(5,0) divided by FIXED DECIMAL(5,2): The temporary result is FIXED DECIMAL(15,8).
Most PL/I "Language Reference" or similar manuals will have a table providing formulas for results of expressions involving different types of operands like "Table 16: Results of Arithmetic Operations for Coded Arithmetic Operands" in IBM's SC26-3114.
Having gotten to this point, a natural question is "so what?" In many cases this may not matter.
For example 5/3 evaluates to 1.666.... Assigning this result to a FIXED variable with a scale factor of
zero will truncate to an integer (1), exactly as in C. Assigning the result to a FLOAT variable,
will result in the expected 1.666... to the limits of precision. Try this in C; the result will
be 1.00000..., because the expression 5/3 involves integers and evaluates to an INT before
assignment. To achieve the same result, C has to resort to casting, an art so arcane that I
had to try three times to get it correct for my test. A C cast in this case is a method of
forcing the conversion
of data during expression evaluation. C has to use a cast to force the expression
5/3 to be evaluated as FLOAT. Guess which one of these is correct C:
f = (float)(5/3); f = (float)5/3; or f = (float)(5)/3;
The other situation in which you have to be aware of what PL/I is doing is when the division is part of a larger expression. Compare the statement i = (5/3) * 3 in PL/I and C. C divides five by three and gets one (integer division), then multiplies one by three and gets the result "three". PL/I divides five by three and gets 1.6666... (scaled integer division), it then multiplies 3.00000 (three scaled to the same number of fractional digits as the result of the division) and gets 4.999.... Assigning this result to i truncates to an integer "four". This may or may not be the desired result, but in any case you'd best be aware of what's happening. Another unobvious point is that PL/I will use decimal operations, since the constants "5" and "3" are decimal constants, the exact equivalent of the C expression would be (101B/11B) * 11B, or (BINARY(5)/BINARY(3))*BINARY(3).
PL/I provides a way for the programmer to exercise further control over division by way of the DIVIDE built-in function. The syntax is DIVIDE( dividend, divisor, precision [, scale] ). Divisor and dividend are self-explanatory. Here "precision" is the required precision of the result of the division, and "scale" is the required scale factor. As usual with PL/I built-in functions, the base, scale, and mode of the result are determined by the attributes of the dividend and the divisor. If the result is FLOAT the scale must be omitted. If the result is FIXED and the scale is omitted, zero is assumed.
The result of DIVIDE(5,3,15) [or DIVIDE(5,3,15,0)] will have a no fractional digits; an integer result of "one", or the same as C's (5/3). To obtain the same result as the C expression (5/3) * 3, code: DIVIDE(5,3,15) * 3
The BOOL builtin function is typically described in language like the following from an IBM PL/I language reference:
Bool returns a bit string that is the result of a Boolean operation, specified by z,
on x and y. ... Z is converted to a bit string of length 4, if necessary. When a bit from x is
matched with a bit from y, the corresponding bit of the result is specified by a selected bit of z,
This language is an adaptation of the description of BOOL in the PL/I standard. It manages to provide an exact description of how the function operates, without providing a clue to what it is doing, or why.
In the course of implementing the BOOL builtin, I did some research that caused me to look at BOOL in a different and perhaps more useful way.
There are sixteen boolean functions of two variables, conventionally listed as follows:
|F2||X&~Y||X AND NOT Y|
|F4||~X&Y||NOT X AND Y|
|F11||X|~Y||X OR NOT Y|
|F13||~X|Y||NOT X OR Y|
Interestingly, if the 'z' bit string is thought of as an integer between 0 and 15, the result of BOOL is the result of the corresponding function listed above.
Here are a few examples:
The preprocessor can be used to make the operation clearer:
%DECLARE $FALSE CHARACTER;
%DECLARE $AND CHARACTER;
%DECLARE $AND_NOT CHARACTER;
%DECLARE $X CHARACTER;
%DECLARE $NOT_AND CHARACTER;
%DECLARE $Y CHARACTER;
%DECLARE $XOR CHARACTER;
%DECLARE $OR CHARACTER;
%DECLARE $NOR CHARACTER;
%DECLARE $XNOR CHARACTER;
%DECLARE $NOT_Y CHARACTER;
%DECLARE $OR_NOT CHARACTER;
%DECLARE $NOT_X CHARACTER;
%DECLARE $NOT_OR CHARACTER;
%DECLARE $NAND CHARACTER;
%DECLARE $TRUE CHARACTER;
%$FALSE = '''0000''B';
%$AND = '''0001''B';
%$AND_NOT = '''0010''B';
%$X = '''0011''B';
%$NOT_AND = '''0100''B';
%$Y = '''0101''B';
%$XOR = '''0110''B';
%$OR = '''0111''B';
%$NOR = '''1000''B';
%$XNOR = '''1001''B';
%$NOT_Y = '''1010''B';
%$NOT_X = '''1011''B';
%$NOT_OR = '''1100''B';
%$NAND = '''1101''B';
%$TRUE = '''1110''B';
%$FALSE = '''1111''B';
r = BOOL(x,y,$AND);
r = BOOL(x,y,$OR);
... etc ...
This material is excerpted from an IBM research report:
"Thirty Years Later: Lessons from the Multics Security Evaluation" by Paul A. Karger and Roger R. Schell. At the time of writing, the complete document is available from The IBM Thomas J. Watson Research Center. The report consists of two papers to be presented at the 18th Annual Computer Security Applications Conference (ACSAC), December 9-13, 2002. Following the conference, the papers should be available from ACSAC.
[Here's the direct link to the paper. -- Ed]
One of the most common types of security penetrations today is buffer overflow. However, when you look at the published history of Multics security problems, you find essentially no buffer overflows. Multics generally did not suffer from buffer overflows, both because of the choice of implementation language and because of the use of several hardware features. ...It has long been argued that one of the chief causes of software flaws is the use of the C language. Hopefully the current movement toward more secure software will make developers pay more attention to the influence of language choice on software reliability.
Although PL/I had some influence on the development of C, the differences in the handling of varying length data structures between the two languages can be seen as a major cause of buffer overflows. In C, the length of all character strings is varying and can only be determined by searching for a null byte. By contrast, PL/I character strings may be either fixed length or varying length, but a maximum length must always be specified, either at compile time or in an argument descriptor or in another variable using the REFER option. When PL/I strings are used or copied, the maximum length specifications are honored by the compiled code, resulting in automatic string truncation or padding, even when full string length checking is not enabled. The net result is that a PL/I programmer would have to work very hard to program a buffer overflow error, while a C programmer has to work very hard to avoid programming a buffer overflow error. [italics mine]
PL/I also provides richer features for arrays and structures. While these differences are not as immediately visible as the character string differences, an algorithm coded in PL/I will have less need for pointers and pointer arithmetic than the same algorithm coded in C. [again my italics]
The PL/I macro SAY is helpful for programmers.
The macro SAY offers some service around the PL/I command 'PUT SKIP LIST'. At first it is just easier to write SAY in the source than PUT SKIP LIST, especially in case of IF-cascades or nested DOs. This can be done by a simple pre-processor variable like this:
Now the macro looks like a PL/I-command with the following syntax:
Here now is the macro SAY:
I have been reading about the OO ideas of code/data encapsulation to reduce some tightly coupled monolithic code that I have to something I can understand in detail, and I wondered how sympathetic PL/I would be to the use of those techniques. RTFM I hear your murmur! None such ever existed! I am also disposed to the ideas of Test-Driven development and wondered if the use of Junit and Java might be better than writing my own PL/I test handler. Two ways to do that: ask someone who has done it, and do it oneself. Option two is open to me.
My chosen sample code on which to experiment was the creation of a single control block (a resource anchor to which many linked lists are chained, named RCOMMON) and a linked list of data control blocks (list has an unknown number of elements, list is not ordered, named DCOMMON). The current code base has create DCOMMON code anywhere it fancies, data elements in the DCOMMON block are read or updated directly without necessarily any validation.
The first steps on the PL/I side are to bring the data and logic within a single procedure, and to permit data element read/write only through a defined interface. One procedure for the RCOMMON and one procedure for the DCOMMON. The encapsulating procedure would be the only resource allowed to update data directly, access to data items is via published "getters" and "setters". I can see how this enforces validation, but I do have some concerns about the cost. I want to raise that at the end, hoping that someone with more knowledge that I will be able to answer.
The working code allows for initial creation of block, and get/set for data elements.
I did something similar with DCOMMON
Look at the code for RCOMMON.
Look at the code for DCOMMON.
I created a short test main program to verify the code, then I created some
"test orchestration" code so that I could just call "run test 1" "run test2"
with predictable results that I might test.
Look at the PL/I test program which calls the functions which have been bound in by linkedit.
Look at the PL/I test program which calls the DLL.
Look at the DLL test orchestration code.
The PL/I brings the DLL into memory by
My original code was title('dllCommon/T01common'); I spent a whole evening puzzling why that did not work. I still do not really understand why, but Eberhard Sturm's The New PL/I book told me what to do. It had arrived only recently and I was not going to open it until I had finished rereading "Refactoring" by Martin Fowler. Stupid Boy.
Moving on now to making the PL/I code available to Junit and Java, on the basis that the ANT/Junit testing suites contain a lot of thought and good work by many developers. The test orchestration code that interfaces to Java needs language conversion
routines. I have used two tiers of Java to achieve this, the top level runs the tests, the lower level defines the Java native Interface (JNI) that calls the PL/I DLL directly. In this case there may be no need, but I have trialed cases where the PLI returns
binary data or arrays, the lower level Java deals with this, maybe making some result to pass back to the higher level to test with an "assert" condition. In this way you can write a range of tests, and the entire suite of code can be compiled and run and any
failures highlighted, the test is run by running a single BAT from a Windows DOS prompt. The tests I have here compiled and ran to completion in less that one second (my environment is Win2000 with VA PLI_2.1.14 on a dual-core AMD machine).
Look at the Java "wrapper" which calls the DLL.
View the Java-DLL test orchestration code.
I need to point out some difficulties with the PROC statement. The external clause needs to define an ID which Java can recognise, in this case
If your Java code is within a package then the ID will need to reflect that, this is not documented in the IBM literature that I could see, and you have no idea how long it took me to understand that! Now the ID becomes
In the customisation of the BAT environment I use ...
I have attempted to answer my own question about how painful or otherwise is it to create and run an automated test suite of PLI code using Junit. The answer is \'93not too bad at all\'94 once the mechanics of it are laid out. I have not used ANT as yet, but I believe that it allows some parts of a build/test to continue even if one leg fails.
The question of efficiency remains, run time efficiency that is.
I always thought that setting a variable with something like
Updating the same field using a setter interface, as in
A colleague who had coerced me into looking seriously at encapsulation and Junit thought that I should have the main code calling a "linked list orchestration interface" which coordinated the manipulation of Rcommon and Dcommon and all the other linked lists I have. Then the Dcommon routine would have no need to issue a _setRcommon call, it would be handled by the higher interface.
You can see:
This description in RTF format
This description in PDF format
Continuing our series about built-in functions, we look at some string functions specifically for WIDECHAR strings.
Included are PL/I maintenance tools, tools to develop and test
web services, CICS, and a configurable editor.
Enhancements to the editor include syntax checking while typing.
documentation for Rational Developer for System z.
Look here and download the PL/I Language Reference, and related manuals.
This LRM is for Rational Developer for System z PL/I for Windows, version 7.6, PL/I for z/OS, and for PL/I for AIX.
Both English and Japanese versions of the manuals are available.