- THE PL/I NEWSLETTER -

- THE PL/I NEWSLETTER -

- THE PL/I NEWSLETTER -

ISSUE NO. 5 – AUGUST 2002

Copyright © 2002 by Robin Vowels. All rights reserved for the authors.
IN THIS ISSUE:
  1. Editorial
  2. Command-Line arguments, by Robin Vowels
  3. Decimal Division
  4. The new Built-in Functions of IBM VisualAge PL/I - Part B
  5. An update on DR PL/I, from Brian Hood
  6. Usefulle Webbe Lynx ...
  7. IBM PL/I Offerings in the U.S.
  8. Multics, from Tom Van Vleck
  9. Language Suggestions
  10. Cool Codes - Improve Debugging
  11. Brain Teasers, by Eberhard Sturm

  1. Editorial

    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 first issue may be downloaded from: The PL/I Newsletter, No. 1, July 2000.
    The second issue may be downloaded from: The PL/I Newsletter, No. 2, September 2000.
    The third issue may be downloaded from: The PL/I Newsletter, No. 3, June 2001.
    The fourth issue may be downloaded from: The PL/I Newsletter, No. 4, September 2001.


  2. COMMAND-LINE ARGUMENTS

    by Robin Vowels
    Command-line arguments may be processed easily by use of the use of GET STRING and data-directed input, for example: CLA: PROCEDURE (Args) OPTIONS (MAIN); DCL Args CHARACTER (100) VARYING; GET STRING (Args || ';') DATA; ... END CLA; The argument list would take the same format as required for DATA-directed input. An OS/2 command line would look like > SUN /A=123,B=456,C=789 As the NAME condition is not raised for this use of GET, an ERROR ON-unit should be included to recover control and to write out an error message (in the event of a typing error on the command line).
    The following segment will allow any or all of the variables A, B, and C to be set from the command line. Should any or all values be omitted from the command line, default values can be taken, as shown. CLA: PROCEDURE (Args) OPTIONS (MAIN); DCL Args CHARACTER (100) VARYING; ON ERROR BEGIN; ON ERROR SYSTEM; PUT SKIP LIST ('Error in command-line arguments. The arguments are:'); PUT SKIP LIST (Args); STOP; END; A, B, C = 20; /* Default values for the arguments. */ GET STRING (Args || ';') DATA (A, B, C); REVERT ERROR; ... END CLA; Don't forget to include the statement REVERT ERROR; or else include another ON ERROR statement immediately after the GET.

    If a plain list of values is supplied as the command-line arguments,
    e.g., 16, 71, -14 or 15 163 924
    then GET LIST may be used:

    GET STRING (Args || ' ') LIST (A, B, C); will do the trick. Don't forget to append a blank to Args, so that GET will read in the final value correctly.
  3. Decimal Division


    by Robin Vowels

    Recent discussion about the decimal division drew attention to using it.
    Extra care is needed when using fixed-point arithmetic including FIXED DECMAL and FIXED BINARY.
    In general, PL/I attempts to accommodate all digits of the result, whether the operation be addition, subtraction, multiplication, or division.

    Consider the addition of A and B, where A and B are defined in
    DECLARE (A, B) FIXED (4,2);
    The precision of A + B is (5,2), which accommodates all possible results without overflowing, including the addition of such values as 99.99 and 99.99, which gives 199.98

    Multiplication of A and B produces a result of precision (9,4) [A and B are assumed to be defined as above] which acommodates the product of the smallest values 0.01 and 0.01, giving 0.0001
    It also accommodates the product A*B of the largest possible values 99.99 and 99.99, giving 9998.0001

    Division is another kettle of fish. Division always produces a result having the maximum number of digits permitted by the installation.
    The number of digits after the decimal point depends on the precisions of the operands.
    Bear in mind that PL/I will attempt to produce the greatest number of digits after the decimal point, consistent with the aim of producing an accurate and sensible result when the largest number is divided by unity. For the result, the number of digits before the point depends on the number of digits before the point in the dividend. The result can overflow when the largest value is divided by a value smaller than unity.
    Consider DECLARE (A, B) FIXED DECIMAL (5, 2);
    The precision of A/B is (15,10).
    If, however, the declarations are changed to:
    DECLARE (A, B) FIXED DECIMAL (15, 8);
    Then the precision of A/B is (15, 0) -- that is, an integer.
    There are no fraction digits.
    This outcome is governed by the maximum number of digits permitted by the installation, namely 15.
    If the maximum number of digits permitted is raised to 31, an entirely different precision is obtained.
    A/B then yields a result of precision (31, 16), which is a consistent with that obtained above when A and B are defined with precision (5,2).
    This "phenomenon" occurs when the number of digits in the dividend exceeds half the maximum permitted.
    No doubt the maximum number of digits (15) in the early S/360 implementations was influenced by hardware considerations.
    However, with the VisualAge PL/I compiler it is possible to specify the maximum number of digits as 31. The Kednos compiler (Compaq) already has this limit.
    The VisualAge PL/I compiler option is LIMITS (FIXEDDEC(31)), specified on the PROCESS statement thus:
    %PROCESS LIMITS (FIXEDDEC(31));

    Just for the record, the precision of the result of division is (M, q),
    where q = M - p1 + q1 - q2.
    M = maximum number of digits permitted;
    The precision of the dividend is (p1, q1),
    The precision of the divisor is (p2, q2).

    Observation
    If decimal division is required in a simple statement, D = A/B may be adequate, provided that p1 < M/2.
    If the division is only part of the operations of an expression, it would be better to use the DIVIDE built-in function to ensure that fraction digits are not lost.
    In any case, when execution time is important, DIVIDE should be used to avoid generating unnecessary result digits. DIVIDE can also be used to accommodate the division of the largest possible value of dividend by the smallest possible value of divisor.

    Similar considerations apply when the operands are FIXED BINARY, when scaled results are permitted.

    Conclusion
    When division is used in an expression, it would be advisable to use the DIVIDE built-in function to produce a result having a precision consistent with the other components in the expression. In particular, specify the number of digits after the decimal/binary point desired by the user.


  4. The (new) Built-in Functions of IBM VisualAge PL/I


    by Robin Vowels

    In issue No. 3 of The PL/I Newsletter, we looked at some of the (new) built-in functions of IBM VisualAge PL/I. Here we discuss some more of them.

    • ALLOCATE(n) allocates storage of size n and returns a pointer to that storage.
    • AVAILABLEAREA(x) returns the size of the largest contiguous area of memory that would be available for allocation from the area x.
    • BITLOCATION(x) returns the location (in the range 0 to 7) of x within the byte where x is stored. (x is UNALIGNED BIT.)
    • CURRENTSIZE. CURRENTSIZE(T) returns the number of bytes in the variable or structure T.
    • EDIT(Value, picture_pattern) returns the value in the form specified by the picture pattern.
      e.g., EDIT (12.345, '9999.999') returns '0012.345'
    • ENDFILE(filename) returns '1'B if the file is positioned at the end, or '0'B otherwise.
      If the file is not open, the ERROR condition is raised.
    • EPSILON(X) returns a floating-point value that is the difference between x and the next representable positive value when x is 1.0
    • EXPONENT(X) returns the exponent E of the floating-point value X as an integer. E is specified by the relation r**(E-1) <= |X| <= r**E, where r is the radix of X.
    • FILEOPEN(filename) returns '1'B if the file filename is open, and '0'B otherwise.
    • GAMMA(X) returns an approximation to the Gamma function of X.
    • HANDLE. HANDLE(T) returns a handle to the typed structure T.
    • HEX Returns the hexadecimal form of the character string argument. HEX('abc') returns '616263' on an ASCII machine, and '818283' on an EBCDIC machine. HEX(S, ' ') is the same as HEX(S), except that blanks are inserted in the returned hexadecimal string - after every eighth character.
    • HEXIMAGE is like HEX. HEXIMAGE(S, n) returns n characters at the address S (as a hexadecial string). HEXIMAGE(S, n, c) is the same as HEXIMAGE(S, n) except that the character c is inserted after every eighth character of the hexadecimal string.
    • TALLY: Need to count the number of times that a given letter appears in a sentence? or the number of times a word appears, or any given group of characters? Then TALLY is for you!
      TALLY(S, T) counts the number of times that string T appears in string S. S and T can be CHARACTER, BIT, or GRAPHIC.

  5. Usefulle Webbe Lynx ...

    Peter Flass is building a resource at: Peter Flass's PL/I 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.
    Of particular interest is the use of PL/I as a programming language for the Common Gateway Interface (CGI).

    The PL/I Connection newsletter.

    The PL/I Connection Newsletter No. 11 , December 1997.

    The PL/I Connection Newsletter No. 10 , April 1997.


  6. PL/I WORKSHOPS

    Three Workshops will be offered in Northeast Brasil at CESV without payment for the Education for Professional Programming Education with assistance of IBM: Centro de Educacao de Säo Vicente/RN Rua Boa Sorte 2001 59340-000 Säo Vicente/RN Brasil Organisation Sra Professora Neci Olieira - Phone: 0055-84-4360210 VA PLI - Date: Feb and Sep 2002 (3 Days) Pagamento:Gratuido Liguagem: Portugues/Engles Contens: Practical Programming in IBM VisualAge PL/I - Workshop for

    OS/390 V2R10 and z/OS

    VA COBOL - Date: Feb and Sep 2002 (3 Days) Pagamento: Gratuido Liguagem: Portugues/Engles Contens: Practical Programming in IBM VisualAge COBOL-Workshop for

    OS390 V2R10 and z/OS

    HLASM - Date: Feb and Sep 2002 Pagamento: Gratuido Liguagem: Portugues/Engles Contens: Practical Programming in IBM HLASM V1R4-Workshop for

    OS/390 V2R10 and z/OS

    For more Information about Sertäo -> www.prefeituradecurraisnovos.com.br or http://www.curraisnovos.com.br Best Regards Presidente CESV Wilhelm Kopschek Date sent: Wed, 04 Jul 2001 11:42:37 +0200 From: "Wilhelm.Kopschek" <Wilhelm.Kopschek@helvetia.de> Organization: Helvetia-Versicherungen To: robin_v@bigpond.com, wolfgang.fieseler@nuernberger.de, h.ramdorf@provinzial.de
  7. 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.

    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:

    • remote edit/compile
    • redevelopment tools
    • cross platform development
    • year 2000 assessment strategies, find and fix, and test capabilities

    Also included as an extra bonus offering is VisualAge CICS® Enterprise Application Development, which enables CICS host application development on the workstation.

    • Product number 04L6564: VisualAge PL/1 Enterprise V2.1
    • 04L6565: VisualAge PL/I Enterprise V2.1 Upgrade from Prior IBM PL/I Workstation or Competitive Products Program Package CD-ROM
    • 04L6567: VisualAge PL/I Enterprise V2.1 Upgrade from Professional V2.0 Program Package CD-ROM
    • 04L6566: VisualAge PL/I Enterprise V2.1 Upgrade from Standard V2.0 Package CD-ROM

  8. Multics

    From: Tom Van Vleck , Multicians Date: Tue, 23 Apr 2002 13:35:42 GMT

    An HTML version of "The Multics PL/1 Compiler" by Bob Freiburghouse from the 1969 FJCC has been added to the Multics website at http://www.multicians.org/pl1-raf.html with Bob's permission.

    This paper describes the Multics version 1 PL/I compiler's implementation.


  9. Language Suggestions


    by Robin Vowels

    Built-in functions are generic, and typically operate on any data type and for an array of any number of dimensions (up to the maximum permitted by the compiler).
    If a user attempts to write a generic function, it is necessary to provide the procedures to perform the desired operation on each of the data types that he wants.
    As well, if the argument(s) can be arrays, the user must also provide procedures to perform the desired operation on arrays of any number of dimensions. Each procedure dealing with an array must also be duplicated to deal with each of the different types that the user wants.
    Thus, if there are four data types (integer, decimal, float, float (16)), and the compiler supports arrays having 10 dimensions, then 40 procedures must be provided.
    It would be better to have a new attribute GENERIC_ALL which specifies that any procedure in the family is assumed to apply to scalars and arrays of any dimension. e.g.,

    DECLARE S GENERIC_ALL (S1 WHEN FIXED BINARY, S2 WHEN FIXED DECIMAL, S3 WHEN FLOAT, S4 WHEN FLOAT (16));
    Such a facility would require the user to write just four procedures (instead of forty in the above example).
  10. Cool Codes


    by Robin Vowels

    To improve debugging, incorporate the following in your procedures.

    DECLARE (PROCNAME, SOURCELINE) BUILTIN; CALL S (PROCNAME, SOURCELINE, A, B); If your subroutine writes out an error message, this will enable you to include both the line number and name of the procedure that called the current one, thus: S: PROCEDURE (Caller, Line, E, F) DECLARE Caller CHARACTER (31) VARYING, Line FIXED BINARY (31); ... PUT SKIP LIST ('... called from procedure ' || Caller || ' at line ' || Line); ... END; Also available is the built-in function SOURCEFILE that allows you to print the name of the file from which any portion of code is compiled. Handy when there is a number of INCLUDE statements in the program.
  11. Brain Teasers: Eberhard's PL/I Problem


    by Eberhard Sturm For an explanation of the solution of the previous problem I only have to present the one given by Tony Shediak from Australia: WHAT: PROC OPTIONS (MAIN); DCL 1 A, 2 A, 3 A FIXED BIN INIT (1), 3 B FIXED BIN INIT (2); DCL B FIXED BIN INIT (3); CALL WHAT (B, B, (B)); /* stand-alone B is the one */ PUT (B); /* 3 3 3 */ WHAT: PROC (X, Y, Z); /* (B) generates a dummy arg, hence Z is a copy of B */ DCL (X, Y, Z) FIXED BIN; DCL 1 A, 2 B FIXED BIN INIT (4), 2 A FIXED BIN INIT (5); A = A + B; /* A.B = A.B + A.B = 4 + 4 = 8 */ /* A.A = A.A + A.B = 5 + 8 = 13*/ Z = X + A.A; /* Z = 3 + 13 = 16 */ X = Z + A.B; /* X = 16 + 8 = 24 */ Y = Y + A.A.A; /* Y = 24 + 1 = 25 (X & Y are one and the same) */ END WHAT; END WHAT; So the output is 25. Things to note:
    • (B) generates a dummy argument hence Z is a COPY of B. When Z is changed B is NOT changed
    • Structure arithmetic in A = A + B (add B to every element of A).
    • X and Y and B (stand-alone) are the same area of storage - the argument B is passed by address.
    • When resolving the reference to B in the call stmt - the compiler always chooses the stand-alone variable, not the element of the structure, as both are declared in the same block.
    • The call What stmt is NOT recursive - it is OK to have a subroutine what contained within and called from procedure what
    • Last but not least - I hope I've got his right as I've been teaching PL/I for 14 years.
    Thank you, Tony, for your excellent explanation. My comment on this problem:
    • PL/I has pitfalls when you don't choose variable names carefully.
    • Always use the compiler option "rules(nolaxqual"! Then you are not allowed to specify only B (at least one error less).
    • Don't pass the same variable to a procedure more than once. You perhaps don't know what the procedure assigns to the respective parameters.
    • Let the compiler help you protect "input" parameters being assigned values by specifying the "nonassignable" attribute.

    This month's problem

    Whereas the previous problem presented a situation which you hopefully never will experience, the new problem asks for a solution you probably have wanted sometimes. In the following code fragment you have to replace the ellipses by some statements in order to being able to use array constants:

    Wanted: package; %ARRAY: procedure ... ; ... %end; %act ARRAY; Main: procedure options (main); call Sub (ARRAY((1, 2, 3))); call Sub (ARRAY((17, 200, 3, 40, 150, 98))); end Main; Sub: procedure (X); dcl ... /* X */; dcl ... /* Y */; put (Y); end Sub; end Wanted; As you may have guessed, a constant array of any dimension should be passed to a subroutine which in turn wants to use it as an array Y. ARRAY is not a builtin function but a preprocessor procedure you have to provide, too. The extra pair of parentheses is necessary when using the MACRO preprocessor. To keep the code small I allow you to give the array Y the attribute "fixed bin (8) unsigned" :-)! In this way you may give any number of elements to ARRAY, the value of each between 0 and 255. If you've found the solution I've in mind, you can start thinking about "fixed bin (15) signed"!

    Please send your solutions to Eberhard Sturm at: sturm@UNI-MUENSTER.DE


Any comments and contributions for the next newsletter to email: robin_v@bigpond.com (Team PL/I) please.

PL/I PL/I PL/I