Category:Polyglot:PL/I and PL/M
This programming language may be used to instruct a computer to perform a task.
See Also: |
|
---|
A Polyglot program is a program whose source is a valid program in two or more languages, producing the same results when run in the different languages.
PL/I and PL/M
Although similar, PL/I and PL/M are not the same language. However some features of the languages can be exploited to make sources that are valid in both languages - although some stylisation is required.
In this, the PL/M language as implemented by Gary Kildall's original 8080 PL/M Compiler will be considered.
8080 PL/M features of interest:
- The compiler ignores everything after column 80 of a source line.
- The compiler treats lower case letters and many "special" characters as spaces.
- PL/M has a parameterless macro facility.
- The PL/M source must end with an EOF keyword - everything after it is ignored.
- The source must start with 100H: which sets the origin for CP/M programxs.
- A PL/M program is a sequence of statements and declarations, not a main procedure.
- PL/M has no builtin I/O statements - under CP/M it is possible to call OS routines.
- MOD is an operator, AND, OR and NOT are keywords.
- The only types are BYTE and ADDRESS - unsigned 8 and 16 bit integers.
- Identifiers cannot contain underscores - the PL/M compiler treats them as spaces - dollar signs can appear but are ignored.
PL/I features of interest:
- PL/I is not (usually) case sensitive.
- PL/I has a powerful in-built pre-proessor, however this is not implemented in all PL/I compilers.
- A PL/I program consists of a main procedure which contains all declarations and statements.
- The main procedure is declared as having "options(main)".
- I/O statements are built in to the language.
- MOD is a function, &, | and ^ (or ¬) are used for AND, OR and NOT.
- PL/I has a range of types, none of which are called BYTE or ADDRESS.
- Identifiers can contain underscores and some implementations allow dollar signs.
Implementation
The following stratagy could be used:
- The program will start with
n100H: procedure options (main);
where the "(main)" starts in column 81. - The procedure header will be in lower case except for the final "H" of "100H" - there will be no digits in the procedure name, other than the final "100"
- The final "end" of the program will be labelled EOF, in upper-case.
- Code that is specific to PL/M will be commented out by having the opening "/*" of the comment appear in column 81.
- The code that is specific to PL/M will have "/* */" at the end - the "/* */" will terminate before column 81.
- Code that is specific to PL/I will generally be commented out by placing "/* */" in column 78, so that the "*/" is invisible to the PL/M compiler.
- Additionally, some PL/I code can be commented out by using a macro to add a "/*" to a PL/I keyword and following the code with "/* */".
Note that for some PL/I compilers, it may be necessary to specify a compiler option to set the margins for the code, so the source line can be up to say, 120 characters wide.
A "lowest common denominator" approach will be used with a common set of procedures providing the I/O and a limited range of types used.
As noted above PL/M only has 8 and 16 bit unsigned integers.
The PL/M BYTE type is 8 bits and can be used where a character( 1 ) or bit( 1 ) item would be used in PL/I.
Include Files
PL/I allows file inclusion via the %include
statement but the original 8080 PL/M compiler does not support file inclusion so the relevent definitions must be included in each program.
A suitable file for PL/I definitions could be:
<lang pli>/* pg.inc: PL/I definitions for "polyglot PL/I and PL/M programs" compiled with PL/I */
%replace true by '1'b, false by '0'b;
declare lower_case character( 26 ) static initial( 'abcdefghijklmnopqrstuvwxyz' ); declare upper_case character( 26 ) static initial( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' );
/* print a character */ prchar: procedure( c ); declare c character( 1 ); put edit( c )( a( 1 ) ); end prchar;
/* print a newline */ prnl: procedure; put skip; end prnl;
/* print a number in the minimum field width */ prnumber: procedure( n ); declare n binary( 15 )fixed; if n < 10 then put edit( n )( f( 1 ) ); else if n < 100 then put edit( n )( f( 2 ) ); else if n < 1000 then put edit( n )( f( 3 ) ); else if n < 10000 then put edit( n )( f( 4 ) ); else put edit( n )( f( 5 ) ); end prnumber;
/* print a "$" terminated string */ prstring: procedure( s ); declare s character( 80 )varying; declare ( p, len ) binary( 15 )fixed; declare c character( 1 ); len = length( s ); if len > 1 then do; p = 1; c = substr( s, p, 1 ); do while( p <= length( s ) & c ^= '$' ); call prchar( c ); p = p + 1; if p <= len then c = substr( s, p, 1 ); end; end; end prstring;
/* read a character from the keyboard, with a carriage-return following it */ rdchar: procedure( dummy )returns( character( 1 ) ); declare dummy binary( 15 )fixed; declare c character( 1 ); get edit( c )( a( 1 ) ); get skip; return ( c ); end rdchar;
/* allows PL/M code to say "CALL PRSTRING( SADDR( 'ABC' ) );" */ /* where SADDR is declared LITERALLY '.' */ saddr: procedure( s )returns( character( 80 )varying ); declare s character( 80 )varying; return ( s ); end saddr;
/* returns a MOD b */ modf: procedure( a, b )returns( binary( 15 )fixed ); declare ( a, b ) binary( 15 )fixed; return ( mod( a, b ) ); end modf;
toupper: procedure( c )returns( character( 1 ) ); declare c character( 1 ); return ( translate( c, upper_case, lower_case ) ); end toupper;
/* end pg.inc */</lang>
For PL/M, the following definitions would be used, with the appropiate subset cut-and-pasted into the programL <lang pli> DECLARE BINARY LITERALLY 'ADDRESS', CHARACTER LITERALLY 'BYTE';
DECLARE SADDR LITERALLY '.', BIT LITERALLY 'BYTE'; DECLARE TRUE LITERALLY '1', FALSE LITERALLY '0'; BDOSF: PROCEDURE( FN, ARG )BYTE; DECLARE FN BYTE, ARG ADDRESS; GOTO 5; END; BDOS: PROCEDURE( FN, ARG ); DECLARE FN BYTE, ARG ADDRESS; GOTO 5; END; PRSTRING: PROCEDURE( S ); DECLARE S ADDRESS; CALL BDOS( 9, S ); END; PRCHAR: PROCEDURE( C ); DECLARE C CHARACTER; CALL BDOS( 2, C ); END; PRNL: PROCEDURE; CALL PRCHAR( 0DH ); CALL PRCHAR( 0AH ); END; PRNUMBER: PROCEDURE( N ); DECLARE N ADDRESS; DECLARE V ADDRESS, N$STR( 6 ) BYTE, W BYTE; N$STR( W := LAST( N$STR ) ) = '$'; N$STR( W := W - 1 ) = '0' + ( ( V := N ) MOD 10 ); DO WHILE( ( V := V / 10 ) > 0 ); N$STR( W := W - 1 ) = '0' + ( V MOD 10 ); END; CALL BDOS( 9, .N$STR( W ) ); END PRNUMBER; RDCHAR: PROCEDURE( DUMMY )BYTE; DECLARE DUMMY ADDRESS; DECLARE C BYTE; DECLARE X BYTE; C = BDOSF( 1, 0 ); DO WHILE( C = 0DH OR C = 0AH ); CALL PRNL; C = BDOSF( 1, 0 ); END; X = C; DO WHILE( X <> 0DH AND X <> 0AH ); X = BDOSF( 1, 0 ); END; CALL PRNL; RETURN ( C ); END RDCHAR ; TOUPPER: PROCEDURE( C )BYTE; DECLARE C BYTE; IF C >= 97 AND C <= 122 THEN RETURN ( ( C - 97 ) + 'A' ); ELSE RETURN ( C ); END TOUPPER; MODF: PROCEDURE( A, B )ADDRESS; DECLARE ( A, B )ADDRESS; RETURN( A MOD B ); END MODF;</lang>
Note the lack of comments in the PL/M "include" file - this is because the definitions will be commented out for PL/I compilers by having a "/*" starting in column 81 preceeding the definitions and /* */ follow them.
See below for some examples.
Pages in category "Polyglot:PL/I and PL/M"
The following 9 pages are in this category, out of 9 total.