Friday, July 29, 2022

Porting from IBM PL/I for Windows to Iron Spring PL/I

To date, most activity porting PL/I code to Iron Spring PL/I  has been mainframe code. We recently looked at porting software from IBM PL/I for Windows and OS/2 to Iron Spring PL/I for Linux. Two problems immediately stood out.

File Declarations

FIXED DECIMAL

Saturday, July 16, 2022

What is this thing?

What is this thing?

The PL/I preprocessor is a general-purpose macro processor. It was designed to add macro capability to the PL/I language, but in fact has the ability to operate on nearly any text data (exceptions noted below). The PL/I preprocessor includes functionality similar to the C preprocessor, but is much more powerful, including looping and branching absent from C.

The preprocessor is not part of the PL/I language standard, but many compilers include a version similar to IBM’s, perhaps with some differences.

Friday, July 15, 2022

Down the rabbit hole

         One pill makes you larger
        And one pill makes you small

After fifty years, PL/I still retains it's capacity to astound me.

Most recently I have been poking around in the dusty corners of the based storage class.
We all know its usefulness for, for example, list processing, allocating and traversing a linked list of items. Often it is used to allocate a string or array whose length or upper bound is not known at run time, for example the following allocates a string of length 1024.

    DECLARE size FIXED BINARY;
    DECLARE string CHARACTER(size) BASED(p);

    size = 1024;
    ALLOCATE string SET(p); 

What IS surprising, however is that the "size" of the based variable can change without reallocation. Consider the following program fragment:

[1] dcl i fixed bin(31);
[2] dcl (p,q) ptr;
[3] dcl s char(i) based;
[4] i = 8;
[5] allocate s set(p);
[6] put skip list('size of p->s ' || trim(storage(p->s)) );
[7] i = 4;
[8] allocate s set(q);
[9] put skip list('size of q->s ' || trim(storage(q->s)) );

[10] put skip list('size of p->s ' || trim(storage(p->s)) );

As expected, line 5 allocates a string of length 8, line 6 prints
    "
size of p->s 8"
Line 8 allocates a second string of length 4, line 9 prints
    "size of q->s 4"

There are now two strings of different lengths allocated. However, line 10 prints
    "size of p->s 4"

Now add the following lines:

[11] i = 16;
[12] put skip list('size of q->s ' || trim(storage(q->s)) );

What is the size of the area printed? No additional allocations have taken place.

The value is  "size of q->s 16"!

This isn't quite as much of a problem if the string length is shortened, but if it's  lengthened all kinds of bad can result.

For adjustable based data, the lengths and bounds expressions from the declaration are re-evaluated each time the variable is referenced. Who knew? Actually, the Multics PL/I language reference makes this clear, but the various IBM manuals seem to gloss over it.

[The sample program was run using IBM PL/I for Windows V2.R1.07. Don't try this with the current version of Iron Spring PL/I, where the length is being taken from the most recent allocation. This will be fixed in a future release.]

Sunday, June 5, 2022

Monday, May 23, 2022

Principle of greatest astonishment

PL/I is a very forgiving language—the programmer can code nearly every type of operand for an operation and have it compile and (sometimes) execute. For example, they can add a fixed-point numeric value, a bit string, and a character string.  [e.g. 3.5 + '10'b + '  433E5']  While not producing the best code, this will operate correctly, as long as the character string represents a valid number. Occasionally, however, PL/I can produce completely unexpected results. 

One case of this is conversion of a numeric value to a bit string. The results, although fully documented, were so surprising that I checked the code with two compilers to make sure it wasn't an error.

The two cases I tried assigned values to a string declared as BIT(8). The first case assigned a variable declared as FIXED BINARY(7), and the second case assigned a numeric constant, by definition FIXED DECIMAL.

CASE 1
         DECLARE  bt BIT(8);  
    DECLARE  fx FIXED BINARY(7);
    fixed = 3;
    bt    = fx.

The result is 6!

CASE 2:
    DECLARE bt BIT(8);
    bt = 3;

In this case, the result is 48! What is going on here?

As I said, this is completely documented.

In Case 2, "3" is a FIXED DECIMAL number with precision (1,0). Before converting it to BIT, it is first converted to FIXED BINARY. The precision of this value is CEIL( (1-0)*3.32 ), or 4. This converts to a bit string of length four, containing '0011'b. This string is then assigned, left justified—it is a string, after all—to bt, giving a result of  '0011_0000'b, hex 30 or decimal 48.

In Case 1, fx is already FIXED BINARY(7,0) with a value of 3. UNSPEC(fx) is '0000_0011'b, but aha! the sign bit is dropped, so the source bit string is '0000011'b. This is then assigned to bt, again left justified, giving a result of '0000_0110'b, hex 06 or decimal 6.

ADDENDUM:
In case 1, if fx is declared FIXED BINARY(8) UNSIGNED, then bt will be 3, since there is no need to account for a sign bit.

Enjoy! 


Thursday, August 30, 2018

Welcome

Welcome!  

Iron Spring Software is a developer of a PL/I compiler (see Wikipedia) and related software. Our current project is a PL/I preprocessor, soon to be in pre-alpha.  From time to time we will use this blog to post interesting tidbits or to provide status information.