2009-03-09

polyglot: pimping up pascal

reminder: this article is part of a serie.

so, c being added, we need another victim^Wtarget language. pascal being a boring language, let's spice up its life by blessing it as a new polyglotter. for this challenge, we'll use free pascal compiler v2.2.2. of course, we won't begin without adding the pascal test file first.

when trying to add a new language, we need to consider the way it declares comments. comments are indeed the polyglotism corner stone, and we rely heavily on them to build our program. in pascal, comments are enclosed within curly braces, which are really easy to introduce in a perl language. however, this doesn't work in c: there's no such thing as anonymous block / function - it needs an identifier first. but pascal also accepts (* comment *) as a substitue to curly braces embedded comments. let's see whether perl and c can cope with this scheme.

in c, one can declare (global) identifiers outside a function. * is used to take a pointer on a given identifier - so far, so good. and it's not uncommon to use parens to help disambiguate a complex identifier declaration (such as a pointer to an array of functions returning an array of character pointer - pfew). in that case, (* is a valid c construction, but we need an identifier after it - and the instruction should be finished with a semi-colon. therefore, the following will start a pascal comment, while still being a valid c statement:


(*foo);
[...]


note that it does produce a warning when compiling the program. but it does not impact the runtime at all, so we won't bother.

but what about perl? well, in perl, *foo is the typeglob for foo. enclosing it in parens does not change this fact, and adding a final semi-colon just finishes the statement. and since we already mentioned that perl accepts useless statements (although with a warning), it means that adding this statement at the top of our file does not change the perl semantics.

so, by adding this sole line, we have opened a pascal comment without disturbing our previous languages. let's just close this comment with *) at the end of the file, while still keeping perl & c compatibility. piece of cake, we just use a cpp instruction / perl comment, to get the following:


(*foo);
[...]
#define bar *)


there, our polyglot program is now a valid pascal program - but it still does nothing. it's time to fix this...

we therefore need to get some space that would be ignored by both perl and c. conveniently, perl proposes a special token __END__ that mark the end of the program. anything after this token will be ignored by perl. however, this token is not valid in c: we have to bury it in a comment. but we know how to achieve that - we've already used this trick of embedding comments within cpp instructions. nothing magic, thus, and we end up with:


(*foo);
[...]
#define foo /*
__END__
*/
#define bar *)


everything between __END__ and */ will be ignored by perl and c. but this is not enough, since it will also be ignored by pascal (still in the pascal comment). but we can safely close the pascal comment in this space, put our pascal code in there, and then re-open a pascal comment, that will be closed by the current end of comment:


(*foo);
[...]
#define foo /*
__END__
*)
<insert pascal code here>
(* */
#define bar *)



now we can insert the pascal code, which is a straightforward port of the algorithm in this new language. the final program is thus:


(*foo);
#include <stdio.h>
#include <stdlib.h>

#define $ /*
"*/
main () /*"; # */
{
int $ i;
int $ n1;
int $ n2;
int $ n3;
$ i = 0;
$ n1 = 1;
$ n2 = 1;
printf( "%d\n", $ n1 );
while ( $ i < 9 ) {
$ n3 = $ n1 + $ n2;
$ n1 = $ n2;
$ n2 = $ n3;
printf( "%d\n", $ n1 );
$ i++;
}
}

#define foo /*
__END__
*)

program foo;
var
i : integer;
n1 : integer;
n2 : integer;
n3 : integer;

begin
i := 0;
n1 := 1;
n2 := 1;
writeln(n1);
while i < 9 do
begin
n3 := n1 + n2;
n1 := n2;
n2 := n3;
writeln(n1);
i := i + 1;
end;
end.

(* */
#define bar *)


and the obligatory test run:


$ prove -l t
t/c.........fibonacci.c:1: warning: data definition has no type or storage class
t/c.........ok
t/pascal....ok
t/perl......ok
All tests successful.
Files=3, Tests=3, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.16 cusr 0.04 csys = 0.23 CPU)
Result: PASS


we can see the warning when compiling the c program, but the test remains correct. this ends today's exercise. contrary to what's been done with c, we had to somehow declare a private pascal space where we stuffed the new program. this is less statisfying than a true interlaced program, but pascal and perl differ too much to allow this kind of trickery.

until next time...

1 comment:

  1. Just for your interest I am working on an implementation of pascal for parrot:

    http://code.google.com/p/porcupinepascal/

    Rob

    ReplyDelete