C to PL/I PL/1 PL1 Project

A C to PL/I PL/1 PL1 project is under way. The purpose is to produce a source translator that will help in converting a C source program to a PL/I source program.

At present, the program converts a subset of C to PL/I.

The development is proceeding apace. In November 2002, the following statements or functions or other facilities were added or enhanced: multiple assignment, assert, while, fread, printf, initializaton, Question-mark expressions.

In February 2002, code to recognize C names differing only in case was completed. Thus the C statement
int he, He, hE, HE;
is transformed to DECLARE (he, He0001$, hE0002$, HE0003$) FIXED BINARY;
Computational statements are simularly transformed.
In the week to 25th July 1999, typedef statements were added, and embedded ++ and -- operators were added to assignment statements and the printf procedure calls. Work is progressing on pointers. Added are translations for "free" and for allocating storage for structures.
In the week of 9 July 1999, SWITCH statements and enumerated types were added, types for functions were extended to the full range, and OPEN/CLOSE statements were improved. Pretty printing of the PL/I code was also dramatically improved.

In August 2003, function declarations were added, along with typedef enum (a, b, c);

The implementation of the C SWITCH statement has been to a fully-structured PL/I form, using a SELECT statement. This is a vast improvement over the C equivalent, provided that the C program uses a BREAK statement at the end of each CASE.

Depending on demand, a conversion of the C SWITCH statement to an unstructured PL/I form can also be provided, which exactly mimics the unstructured C form, to handle those cases where execution of a CASE "falls" though to the next CASE. This would be implemented using PL/I's computed GO TO statement: GO TO Lab(K).


Facilities implemented/recognized include: Feature C example: becomes: _______ _________ _______ DEFINE #define D = 1234 %DECLARE D CHARACTER; %D = 1234; #define DELTA(a, b) a - b %DELTA: PROCEDURE (a, b); DECLARE (a, b) CHARACTER; RETURN (a || ' - ' || b); %END DELTA; %ACTIVATE DELTA; UNDEF #undef D %DEACTIVATE D; INCLUDE #include <fred.h> %INCLUDE fred; References to standard files such as stdio.h and math.h are removed, as such facilities are provided either by the normal PL/I library or by the supplied PL/I library. PRAGMA Recognized only. __LINE__ SOURCELINE () __FILE__ SOURCEFILE () __DATE__ handled by function reference __TIME__ ditto __TIMESTAMP__ ditto __STDC__ 1 typedef typedef int INTEGER ) typedef struct Node A_Node; } Nil; translation achieved typedef struct Node ) on-the-fly {int k; char *p}; typedef struct Node (int K; char *p} A_Node; DECLARE A_Node LIKE Node; typedef enum (a, b, c) class; DEFINE STRUCTURE class (a, b, c); typedef enum (a=3,b=5) class; DEFINE STRUCTURE class (a VALUE (3), b VALUE (5)); structures struct S { DECLARE 1 S, int p; 3 p FIXED BINARY, float q; } 3 q FLOAT DECIMAL; struct T { DECLARE 1 T, unsigned int u : 1; 3 u BIT (1) UNALIGNED, unsigned int v : 2; 3 v BIT (2) UNALIGNED, unsigned int w : 0; 3 w BIT (5) UNALIGNED; } Note that a zero field is filled out to a byte boundary as shown. enum enum months (JAN, FEB, MAR) DEFINE ORDINAL months (JAN, FEB, MAR); enum color (red=3, yellow, DEFINE ORDINAL color blue=7) red VALUE (3), yellow, blue VALUE (7)) enum color subcolor; DECLARE subcolor ORDINAL color; arrays all types including: char c[10]; DECLARE c(0:10-1) CHARACTER (1); float x[10][10] DECLARE x (0:10-1,0:10-1) FLOAT; x[J][K] = A + B; x(J,K) = A + B; Initialization is supported for scalars and arrays, except that array initialization must be for the entire array; row initial- ization is not supported yet. for statement for (j = 1; j++; j <= n) DO j = 1 TO n BY 1; for (j = n; j--; j >= 1) DO j = n TO 1 BY -1; for (j = 1; j++; j < n) DO j = 1 TO n-1 BY 1; for (j = k-1; j++; j < n) j = k-1; DO FOREVER; IF ^(j < n) THEN LEAVE; [loop body] j = j + 1; END; for (;;) DO FOREVER; do do { a = b; ... } DO FOREVER; a = b; ... while (p > q); IF ^(p>q) THEN LEAVE; END; while while ( a < b ) c = d; DO WHILE (a < b); c = d; END; while ( a > b ) { c=d; e=f; } DO WHILE (a > b); c = d; e = f; END; switch switch (e) SELECT (e) {case (1) ... WHEN (1) DO; ... END; case (3) ... WHEN (3) DO; ... END; default ... OTHERWISE DO; ... END; } END; case break default continue printf printf ("\nalpha=%7d", W); PUT EDIT ('alpha=') (SKIP, A) (EDIT_VALUE(W,7,0)) (A); printf ("\nalpha=%15.6e, beta=%7i", P, Q); PUT EDIT ('alpha=') (SKIP, A) (P) (E(15,6)) (', beta=') (A) (EDIT_VALUE(Q,7,0)) (A); puts same as printf without a data list. fread fread (*buffer, 4, count, DCL Buff(30) CHARACTER (1); FILE *stream); GET EDIT ((Buff(I) DO count= 0 to 4*count-1)) (A(1)); STRING(buffer)=STRING(Buff); count = count/4; fprintf fprintf(out, "\nalpha=%7d", W); PUT FILE (OUT) EDIT ('alpha=') (SKIP, A) (EDIT_VALUE(W,7,0)) (A); sprintf as for fprintf, using STRING(out). Note: some conversions for PRINTF, FPRINTF, and SPRINTF are handled by function references so as to guarantee identical implementation of C conversions (of an "expanding" field width). scanf scanf ("%d", &W); GET EDIT (W) (F(5)); fopen fopen ("mydata.dat"); OPEN FILE (mydata) TITLE ('mydata.dat'); fopen inf = fopen("mydata.dat"); ON UNDEFINEDFILE (File_inf) BEGIN; inf = '1'B; GO TO L000001; END; inf = '0'B; OPEN FILE (File_inf) TITLE ('mydata.dat'); L00001: REVERT UNDEFINEDFILE(File_inf); inf=fopen("mydata.dat", "r") As above, except for: OPEN FILE (File_inf) TITLE ('mydata.dat') INPUT; fclose fclose ("mydata.txt"); CLOSE FILE (mydata); fclose inf = fclose ("mydata.txt"); ON TRANSMIT (File_inf) BEGIN; inf = '1'B; GO TO L00002; END; inf = '0'B; CLOSE FILE (File_inf); L00002: REVERT TRANSMIT (File_in); FILE FILE *inf, *outf; DECLARE File_inf FILE, File_outf FILE; goto if if (a > b) c=d; IF (a > b) THEN c=d; if (a > b) c=d; else e=f; IF (a > b) THEN c=d; ELSE e=f; return return; RETURN; return a + b; RETURN (a + b); block is recognized in procedures, replaced with BEGIN; ... END; do, if, while, etc. or DO; ... END; as appropriate. FUNCTION int fun (int a, KIND b, char c); DECLARE fun ENTRY (FIXED BINARY, DECLARATON TYPE KIND, CHARACTER VARYING) RETURNS (FIXED BINARY); functions int sub (float x, int y) SUB: PROCEDURE (X, Y) RETURNS (FIXED BINARY) RECURSIVE OPTIONS (REORDER); DECLARE x FLOAT, y FIXED BINARY; void sub2 (int t) SUB2: PROCEDURE (t) RECURSIVE OPTIONS (REORDER); DECLARE t FIXED BINARY; char *fun (char *s, *t) FUN: { char f[200]; PROCEURE (s_var, t_var) strcat (s, t); RETURNS (CHARACTER strcpy (f, s); (32767) VARYING RECURSIVE; return f } DECLARE s_var CHARACTER (*) VARYING; DECLARE t_var CHARACTER (*) VARYING; DECLARE f CHARACTER (200) VARYING; s_var = s_var || t_var; f = s_var; RETURN (f); END FUN; int fun (int a, KIND b, char c); fun: PROCEDURE (a, b, c) RETURNS (FIXED BINARY) RECURSIVE; DECLARE a FIXED BINARY; DECLARE b TYPE KIND; DECLARE c CHARACTER (*) VARYING; multiple assign a=b=c=d=0; a,b,c,d=0; if the types are the same; otherwise: d=0; c=d; b=c; a=b; ? expressions c = (d > e) ? f : g IF (d > e) THEN c = f; ELSE c = g; Note: embedded ? expressions are implemented in printf etc for the i, d, e, E, and s format codes, otherwise they are not handled yet. int int a, b, c; DECLARE (a, b, c) FIXED BINARY; int a=5, b=7; DECLARE (a INITIAL (5), b INITIAL (7)) FIXED BINARY; float long double double d, e; DECLARE (d, e) FLOAT (15); unsigned signed char char t [] = "abcd" DECLARE t(0:3) CHARACTER (1) INITIAL ('a', 'b', 'c', 'd'); string string s = 'pqrstu'; DECLARE (s INITIAL ('pqrstu')) CHARACTER (6); void malloc p = malloc(sizeof(struct H)); ALLOCATE H SET (p); free free (p); FREE p->p_str_; The above two constructs are typically used in the maintanance of linked lists. string functions assignment statements compound assignments operators ++ -- * / + - << >> < <= > >= == != & ^ | && || = += -= *= /* <<= >>= %= &= ^= |= -> ? (as explained above for assignment statements and in printf etc function references.)

Notes:

The shift operators << and >> are replaced by calls to the PL/I functions ISLL and ISRL respectively, and the % operator is replaced by a call to the PL/I MOD built-in function.

The pow function is replaced with the ** operator, via the macroprocessor.

Many of the functions are implemented using PL/I's macro facilities, which replace C calls with an equivalent PL/I usage. If desired, the user then compiles the raw PL/I source code and saves the macro file from the compiler for a source translation that eliminates C usage and C function names. Alternatively, the user retains the raw PL/I source output from the translator, using the macro facility in conjunction with the raw PL/I file produced.

A few of the C function references are satisfied by providing a PL/I procedure, obtained from the below-mentioned library.

The macro procedures are contained in a library of PL/I macro definitions and functions.

Floating-point C constants are padded out to 6 or 14 digits according to whether they are float or double/long. Floating-point constants are made explicit by appending an explicit exponent of zero ("E0").

Decimal integer constants with a suffix (U and/or L) are explicitly converted to FIXED BINARY. A precision is included in the explicit conversion.

Casts are preserved, and are converted to an equivalent PL/I built-in function reference. In the case of floating-point, precision is specified according to the type being float/double/long.
The special case of (int) c is recognized and is translated to UNSPEC(c) when c is unsigned char or char. When c is signed char, sign extension occurs.

Expressions are converted to fully-parenthesized form so as to preserve the C priority order, and to ensure unambiguity for the PL/I reader. They also assist in the conversion of such operators as %, <<, and >> to the corresponding PL/I built-in function references MOD, ISLL, ISRL, and in the application of explicit type conversions.

Note: some conversions for PRINTF, FPRINTF, and SPRINTF are handled by function references so as to guarantee identical implementation of C conversions (of an "expanding" field width).
If there is any demand, straight conversion without using functions could be provided via a translator option, e.g. printf ("%7d", H); could be converted to PUT EDIT (H) (F(7));

In the case of the C SWITCH statement, a structured PL/I form is produced. The unstructured C form is not retained. If the C code "falls through" from one CASE to the next, the user will have to duplicate the relevant code via an editor, and make any other appropriate changes.
If there is any demand, translation to an unstructured form could be provided via a translator option. Such would use PL/I's label arrays.

Some progress has been made in implementing structure and pointer references. In particular, the construct (*ptr).value is translated to ptr->value, and qualified references such as a.b are recognized. The arrow operator ( -> ) is implemented. The "&" operator is recognized and is translated to ADDR.

Names in C may differ only in case. C2PLI recognizes such names and generates different names in the finished PL/I output.


A sample C program and the corresponding PL/I program produced by the converter are shown below:

Sample C program.

#include <stdio.h> #define SIZE 5 int main (void) { float prices[SIZE] = {1.41, 1.50, 3.75, 5.00, .86 }; float total; int i; for (i = 0; i < SIZE; i++) { printf("price = $%.2f\n", prices[i]); } printf("\n"); total = 0; for (i = 0; i < SIZE; i++) { total = total + prices[i] * 1.05; printf("\nnew price = $%.2f", prices[i] * 1.05); } printf("\ntotal = $%.2f\n", total); }

Corresponding PL/I Program

(FIXEDOVERFLOW, SIZE): MAIN: PROCEDURE OPTIONS (MAIN, REORDER); %DECLARE SIZE CHARACTER; %SIZE = '5'; DECLARE (prices(0:SIZE-1) STATIC INITIAL (1.41000000000000E0, 1.50000000000000E0, 3.75000000000000E0, 5.00000000000000E0, .860000000000000E0) ) FLOAT DECIMAL; DECLARE (total) FLOAT DECIMAL; DECLARE (i) FIXED BINARY; i=0; DO FOREVER; IF ^(i<SIZE) THEN LEAVE;; PUT EDIT ('price = $') (A) ( EDIT_VALUE(prices(i),1,2)) (A); PUT SKIP;; i = i+1; END ; ; PUT SKIP; total = 0; i=0; DO FOREVER; IF ^(i<SIZE) THEN LEAVE;; total = (total+(prices(i)*1.05000000000000E0)); PUT EDIT ('new price = $') (SKIP, A) ( EDIT_VALUE((prices(i)*1.05000000000000E0),1,2)) (A); i = i+1; END ; PUT EDIT ('total = $') (SKIP, A) ( EDIT_VALUE(total,1,2)) (A); PUT SKIP;; END MAIN; The converted PL/I program (above) uses the afore-mentioned PL/I library to produce results in the usual C form, as follows: price = $1.41 price = $1.50 price = $3.75 price = $5.00 price = $0.86 new price = $1.48 new price = $1.58 new price = $3.94 new price = $5.25 new price = $0.90 total = $13.15

For further information, contact email: robin_v@bigpond.com
If you can spare a short segment of C for testing (30-100 lines) it would be appreciated.

To try an early version of the translator download the C to PL/I translator here. You'll need the PL/I library. This executable file is for OS/2. If you link the object module, you'll need the STACK option. I use the link command:
LINK386 C2PLI /STACK:6000000 /CO;
Don't forget that pointers are not implemented. Constructs such as embedded K++ are not implemented everywhere. However, embedded --K, ++K, K++, and K-- are implemented in assignment statements and printf.
Extensions over ANSI won't be handled well.
Don't expect much. It's still somewhat mickey-mouse at present.

First announcement: 27th May 1999.
Updated: 25th December 1999, 3 February 2002.
Updated: 27 November 2002, 28 August 2003.