- THE PL/I NEWSLETTER - |
- THE PL/I NEWSLETTER - |
- THE PL/I NEWSLETTER - |
We welcome your contributions to this newsletter. It will
continue only through your efforts. Thank you in advance.
Please send any comments and contributions for the next newsletter to
email: robin_v@bigpond.com
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.
The IBM PL/I SORT procedures, generically PLISRTx (x=A,B,C,D), provide easy access to powerful sorting capabilities. However I have always felt them to lack flexibility, particularly compared with the COBOL SORT verb. Any change to the layout of the data to be sorted seems to require changes to several points in the program, which is an invitation to mistakes.
Recently I wrote a program using PLISRTD which caused me to rethink this in the hope of simplifying the process. With a few simple "tweaks" to IBM's sample logic, I believe I have accomplished this. I'm using PL/I for MVS & VM 1.1. The sort interface is documented in the Programming Guide (SC26-3113) beginning on page 348. I believe this logic should be applicable to other IBM PL/I compilers. The sample program as written requires the compiler option 'LANGLVL(SPROG)' to allow pointer arithmetic, but the same results could be achieved using the PTRADD, PTRVALUE and BINVALUE builtin functions. Line number references in [] in the text refer to the sample program below.
The first step was to define the record to be sorted [lines 3-8]. I defined this as a BASED structure, essentially a template, to facilitate reuse throughout the program. The 'BASED( SYSNULL() )' is not strictly required, but allows me to reference the structure without locator qualification.
The second step was to define SORT's SORT and RECORD statements. The
problem with all of IBM's examples is that they show these statements
hard-coded as constant character strings. As constants these statements have to be
changed every time the sort record length or sort key information changes.
Instead, using the structure defined for the sort record I dynamically built
these statements [lines 13-17]. Many changes to the sort record format now
don't require changes.
I show only one - character - sort key, but the same logic
applies to multiple keys in varying formats. Only adding a sort key or changing
its format requires a change to the SORT statement logic. The position of
each 'sort_key' is computed as "ADDR(sort_key) - ADDR(sort_rec) + 1", and its length
is "STG(sort_key)". For this example I print out the values of the SORT
and RECORD statements. The results are as follows:
SORT FIELDS=(11,10,CH,A)
RECORD TYPE=F,LENGTH=(80)
Once this was done the next step was to tweak the Sort Exit E15 and E35 (sort input and sort output) procedures. PLISRTA uses no exits, PLISRTB and PLISTRC use one each, but the same general discussion applies as in the example, where PLISRTD uses both exits.
Both procedures access the sort records as a character string. IBM's examples define the value returned by the E15 procedure as 'CHAR(x)' where x is the length of the sort record -- another changeable item. In an ideal world it would be defined as 'CHAR( STG(sort_rec) )', but this isn't allowed. Instead I found that 'CHAR(32767) VARYING' would apparently work [line 24]. I am not especially happy with this solution, and would like to research it farther to see what is actually going on behind the scenes1.
Exit E15 builds sort records one at a time and passes them to the sort via the
RETURN statement. In this case my data is all character and
'RETURN( STRING(card) )' works.
If the sort record is not all character it will be necessary here -
as well as IBM's examples - to define an overlay and return that.
Something like
'RETURN( SUBSTR(ADDR(record)->overlay, 1, STG(sort_rec) )'
should work, where overlay is a BASED character string long enough
to overlay the entire sort record, although PL/I doesn't check unless
STRINGRANGE is enabled.
Exit E35 is easier. The sorted record passed by the sort can be declared as 'CHAR(*)' [line 35]. The data can be accessed by overlaying the definition of sort_rec on this string [line 39].
Procedure itod [lines 43-68] is a small formatting routine I've found useful. It formats an unscaled fixed binary number as a VARYING character string to exactly the length required, without leading or trailing blanks.
I didn't attempt any tests on variable-length sort records. Presumably all or most of these ideas would be applicable there also.
Most of this code violates all suggested portability guidelines. However use of PLISRTx itself is not particularly portable. In addition, using one declaration of the sort record throughout the program and declaring character strings in terms of 'STG(sort_rec)' should mitigate some of the potential problems.
1.Presumably PLISRTx allocates storage for the string returned from exit E15 using the length from the RECORD statement and allowing additional storage for a possible VARYING string prefix. The RETURN statement may truncate (or pad, if fixed-length string) to this length. The determination of VARYING vs. NONVARYING is made by examination of the descriptor for the returned value.
Matrix arithmetic in PL/I ?
Commands available include:
The 5th edition of Eberhard Sturm's book (in German)
There's a new section about the WIDECHAR attribute and a chapter about
"Interfaces to the World", dealing with interfacing to C, REXX, and
Java. An example shows starting a Java Virtual Machine, creating
objects, and calling methods for asking an internet time server for
the current time. Other examples demonstrate CGI programming and using
the PLISAX builtin function for XML parsing. (A nice idea of the
publisher: a SAXophonist is shown on the cover :-)
You can find the preface ("Vorwort"), a table of contents
("Inhaltsverzeichnis"), and the examples ("Beispiele") on Eberhard's web page:
Eberhard Sturm's web page
Sometimes data has to be read in and stored in an array, but the
number of elements to be read is unknown. This situation arises when
data has been prepared by an automatic recording machine.
This latter method is illustrated below. We use a CONTROLLED array.
A CONTROLLED array has the feature that storage for the array is not obtained
until an ALLOCATE statement is executed. If an ALLOCATE statement for the same
array is executed, a second generation of the array is created, and is now accessible.
The previous allocation is "stacked", and is not accessible until a FREE
statement is executed for the current generation, in which case the first
generation becomes visible or accessible again. There may be many
generations for a given array. To assist in the use of the array,
the built-in function ALLOCATION tells us how many generations there are at
any particular moment.
This method is more efficient than the linked list method in terms of time and
storage. Extra storage for approximately
2*n elments must be available (where n is the number of
elements in the array).
He also has installed PL/I
on an OpenVMS test drive system that HP
operates. They haven't as yet made it onto their home
page.
The spring 2000 edition of the
COBOL and PL/I newsletter
has some topics about PL/I. The specific PL/I
topics may be viewed separately, or the entire newsletter in PDF
format may be downloaded.
The PL/I
Connection
newsletter.
The PL/I Connection Newsletter No. 11 , December 1997.
The PL/I Connection Newsletter No. 10 , April 1997.
VisualAge PL/I Enterprise Edition Version 2.1 combines the two separate
offerings from the previous release of VisualAge PL/I (Standard and
Professional) into a single offering available on both the OS/2 and
Windows NT platforms. By doing so, it provides these productivity
features:
Also included as an extra bonus offering is VisualAge CICS® Enterprise
Application Development, which enables CICS host application development
on the workstation.
When it is necessary to check whether a given character is one of
many, the VERIFY function may be used.
This code is considerably more efficient than:
Sample PL/I sort program
1 /* Sample Sort Program for PL/I Newsletter */
2 sortsamp: proc options(main);
3 dcl 1 sort_rec based( sysnull() ),
4 2 sort_stuff1 char(10),
5 2 sort_key,
6 3 sort_key1 char(4),
7 3 sort_key2 char(6),
8 2 sort_stuff2 char(60);
9 dcl rc fixed bin(31);
10 dcl (sort_stmt,rcrd_stmt)char(100) varying;
11 dcl max_mem fixed bin(31) init(1000000);
12
13 sort_stmt = ' SORT FIELDS=(' ||
14 itod( addr(sort_key) - addr(sort_rec) + 1 ) ||
15 ',' || itod( stg(sort_key) ) || ',CH,A) ';
16 rcrd_stmt = ' RECORD TYPE=F,LENGTH=(' ||
17 itod( stg(sort_rec) ) || ') ';
18 put edit( sort_stmt, rcrd_stmt )(skip,a);
19 call PLISRTD( sort_stmt, rcrd_stmt, max_mem, rc,
20 E15, E35 );
21 call PLIRETC(rc);
22 return;
23
24 E15: proc returns( char(32767) varying );
25 dcl card char(80);
26 dcl eof bit(1) static init( '0'b );
27 on endfile(sysin) eof='1'b;
28 get edit( card )(a(80));
29 if eof then call PLIRETC(8);
30 else call PLIRETC(12);
31 return( string(card) );
32 end E15;
33
34 E35: proc(data);
35 dcl data char(*);
36 dcl my_data like sort_rec based( addr(data) );
37 dcl cnt fixed dec static init(0);
38 cnt = cnt+1;
39 put skip edit( cnt, string(my_data) )(p'zzzz9B',a);
40 call PLIRETC(4);
41 end E35;
42
43 itod: proc(iFB31) returns( char(11) varying );
44 dcl iFB31 fixed bin(31);
45 dcl w fixed bin(31);
46 dcl c char(11) varying;
47 dcl s char(1) varying init('');
48
49 w = iFB31;
50 if w<0 then do;
51 s='-';
52 w=-w;
53 end;
54 select;
55 when( w>999999999 ) put string(c) edit(w)(p'(10)9');
56 when( w>99999999 ) put string(c) edit(w)(p'(9)9');
57 when( w>9999999 ) put string(c) edit(w)(p'(8)9');
58 when( w>999999 ) put string(c) edit(w)(p'(7)9');
59 when( w>99999 ) put string(c) edit(w)(p'(6)9');
60 when( w>9999 ) put string(c) edit(w)(p'(5)9');
61 when( w>999 ) put string(c) edit(w)(p'(4)9');
62 when( w>99 ) put string(c) edit(w)(p'(3)9');
63 when( w>9 ) put string(c) edit(w)(p'(2)9');
64 otherwise put string(c) edit(w)(p'(1)9');
65 end; /* select */
66
67 c=s||c;
68 return(c);
69
70 end itod;
71
72 end sortsamp;
MATRIX OPERATIONS IN PL/I
Copyright © 2002 by R. A. Vowels.
How would you like to be able to write
C = A x B; for matrix multiplication?
or C = A transpose; for the transpose?
This PL/I program enables matrix operations to be specified simply and
concisely.
Matrix operations may be specified in appropriate terms.
For example, to form the sum of corresponding elements of two
matrices A and B, write:
C = A + B;
To form the inverse of matrix A, write: C = A inverse;
To form the transpose of matrix A, write: C = A transpose;
To solve a set of linear simultaneous equations, write: C = A solve;
(the right-hand sides are included with the coefficients in matrix A).
To form matrix multiplication of two matrixes A and B, write C = A x B;
These statements may be intermixed with normal PL/I statements to make a
useful program.
For example, to read in a matrix, print it, and to compute the inverse,
and to print it, write:
It is not necessary to define (declare) the matrices A and C, nor is it
necessary to be concerned with the allocation of storage, nor with the specification
and checking of array dimensions, and so on. The declarations are formed
automatically, and the code to allocate and free storage is
generated automatically.
In fact, checks are performed to ensure that a matrix has been allocated
and initialized prior to its being used. The dimensions of each array
are checked before an operation is performed, in order to ensure that
the matrices involved have consistent dimensions for that operation.
Thus, it will be seen that the implementation is a form of object-oriented
programming.
To implement the feature, extensive use of CONTROLLED storage is used.
Normal PL/I statements may be intermixed with matrix statements.
Some of the matrix statements appear to be exactly like normal PL/I
assignmens statements (for example, C = A + B;). However, there is more
work implied in the matrix form.
For example, the matrix statement C = A + B; is translated as follows:
First the bounds are checked, to ensure that each matrix has the same number of
rows and columns.
Next, the matrix C is checked that it has not been allocated. If it has been,
storage is freed.
Next, A and B are checked that they have been allocated (that is, they have been assigned values).
Finally, a normal PL/I array assignment C = A + B; is executed.
The invisible (generated) code looks like this:
It should be noted that this is not an interpretive scheme. The detailed PL/I
code is generated by the pre-processor, and is compiled by the PL/I compiler.
Procedures for performing any task may be included by the programmer,
and are called via the mechanism described.
New PL/I Book
Reading the Unknown
Copyright © 2002 by R. A. Vowels.
There are several ways in which this problem may be tackled in PL/I.
This method is simplest, and is a legitimate solution where neither time
nor storage space is important. Such would be the case where the number
of elements is, say, of the order of 1000.
New PL/I site
Tom Linden has set up a web site for Kednos PL/I for Compaq
(late Digital) PL/I compilers for Tru64 unix.
There you will find manuals and details of the compiler,
and even a hobbyist licence.
Kednos Corporation PL/I
You may contact Tom at:
tom@kednos.com
To use the compiler you need to register by following
the link from his site ( www.kednos.com ) to HP's.
Once you are logged in, you can type HELP PLI. User
documentation you can get off Kednos' web site.
Usefulle Webbe Lynx ...
Peter Flass is building a resource at:
Peter Flass's PL/I home page.
Of particular interest is the use of PL/I as a programming
language for the Common Gateway Interface (CGI).
Current IBM PL/I offerings in the U.S.
IBM has enhanced VisualAge® PL/I with powerful features to increase
development productivity, simplify the maintenance of your legacy code,
and provide seamless portability from your host to your workstation. It
also provides the tools needed to identify your Year 2000 date-related
fields on both OS/2 and Windows NT.
Current IBM PL/I offerings in Australia.
Personal PL/I for Windows is also available online from
IBM Australia
for $330.
Cool Codes
by Robin Vowels
For example, to check whether a given character is a letter, write:
QUUTE QUESTIONS
The first allows 8 byte integers, but will use 4 byte integers in all
expressions except those that contain at least one 8 byte integer.
Expressions that contain an 8 byte integer will be carried out using 63 bit
precision.
The second will cause all binary integer arithmetic to be carried out to 63
bits.
Subject: PUT DATA format question