Category:Polyglot:PL/I and PL/M: Difference between revisions

m
(Created stub page for Polyglot:PL/I and PL/M)
 
 
(17 intermediate revisions by the same user not shown)
Line 1:
{{stub}}{{language|Polyglot:PL/M and PL/I}}
 
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.
<br><br>
===PL/I and PL/M===
Although similar, [[PL/I]] and [[PL/M]] are not the same language. A relatively simple pre-compiler could probably handle the differences for simple programs, but why write a pre-compiler when fetures of the languages/compilers can be exploited to make sources that are valid in both PL/I and PL/M ( even if some stylisation is required ) ?
<p>
In this, the PL/M language as implemented by Gary Kildall's original [[8080 PL/M Compiler]] will be considered.
<br><br>
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.
* Keywords are reserved in PL/M.
<br><br>
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.
* Keywords are not reserved in PL/I.
 
===Arrays===
In PL/I when an array is declared, the lower boiund can be omitted and defaults to 1. The upper bound is the dimension specified in the declaration.<br>
E.g. <code>declare a ( 100 ) fixed binary;</code> declares an array of 100 integers, the subscripts range from 1 to 100.<br><br>
In PL/M when an array is declared, the lower bound cannot be specified and is always 0. The upper bound is one less than the dimension specified in the declaration.<br>
E.g. <code>DECLARE A( 101 ) ADDRESS;</code> declares an array of 101 integers, the subscripts range form 0 to 100.
<br><br>
PL/M only allows arrays with a single subscript to be declared. PL/I allows multi-dimensional arrays.
<br><br>
 
===Implementation===
The following stratagy could be used:
<br><br>
* The program will start with <code>n100H: procedure options (main);</code> 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 "/* */".
<br><br>
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.
<br><br>
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.
<br>
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 <code>%include</code> statement but the original 8080 PL/M compiler does not support file inclusion so the relevent definitions must be included in each program.
<br><br>
A suitable file for PL/I definitions could be:
<br>
<syntaxhighlight 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;
 
/* returns not p */
not: procedure( p )returns( bit( 1 ) );
declare p bit( 1 );
return( ^ p );
end not;
 
toupper: procedure( c )returns( character( 1 ) );
declare c character( 1 );
return ( translate( c, upper_case, lower_case ) );
end toupper;
 
 
/* end pg.inc */</syntaxhighlight>
 
For PL/M, the following definitions would be used, with the appropiate subset cut-and-pasted into the program:
<syntaxhighlight lang=pli>
DECLARE BINARY LITERALLY 'ADDRESS', CHARACTER LITERALLY 'BYTE';
DECLARE FIXED LITERALLY ' ', BIT LITERALLY 'BYTE';
DECLARE STATIC LITERALLY ' ', RETURNS LITERALLY ' ';
DECLARE FALSE LITERALLY '0', TRUE LITERALLY '1';
DECLARE HBOUND LITERALLY 'LAST', SADDR LITERALLY '.';
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;
MIN: PROCEDURE( A, B ) ADDRESS;
DECLARE ( A, B ) ADDRESS;
IF A < B THEN RETURN( A ); ELSE RETURN( B );
END MIN;
MAX: PROCEDURE( A, B ) ADDRESS;
DECLARE ( A, B ) ADDRESS;
IF A > B THEN RETURN( A ); ELSE RETURN( B );
END MAX;</syntaxhighlight>
 
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.
3,022

edits