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 %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
#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
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.