2009-03-05

polyglot: adding c language

reminder: this article is part of a serie.

now that our working environment is set up, it's time to get (finally) serious. the first language that we're going to add is the c language. i'm going to use gcc 4.3.2 - not that it matters a lot, since we won't use any recent feature: we'll stick to ansi c.

the first thing to do (after creating the test file for the c language) is of course to define the main() function, which is required for any c program. we're going to open a c comment within a preprocessor instruction (which is conveniently a perl comment), this comment being used to start a lonely perl string. are you following me? this string, containing the main declaration, will be a statement of its own in perl, which is valid but yields a warning. however, we run perl without warnings, so this is ok in our case. to finish the perl string, we use more or less the same trick, and start a c comment embedding the final quote. take some time to understand this:

#define whatever /*
"*/
main () /*"; # */
[...]


at this point, the perl test still passes. however, the c version is still far from working. of course, we need to add the curly braces that will hold the function definition. that's kind of easy using k&r indentation style, with the opening brace on the beginning of the line. in perl, this just opens an anonymous block, and is thus valid.

#define whatever /*
"*/
main () /*"; # */
{
[...]
}


things are shaping up. let's not forget to include some common c headers. no problem with cpp instructions:

#include <stdio.h>
#include <stdlib.h>
[...]


now, let's focus on the content of the main() function... the first thing to change is of course the variables: $ is not supposed to be part of a varname in c. so we will rely on a perl parser idiosyncracy: everybody knows that spaces are not significant while parsing perl (except for litteral strings of course, and in some dark corner cases) - but not everyone knows that this is also true for the variable sigil. yes, this means that "$foo" can be accessed with "$ foo" (note the additional space between the sigil and the varname). so, by redefining in c the $ string to be nothing (with a cpp instruction), and adding a space between the $ and the varname, perl and c can now share their variables:

[...]
#define $ /*
"*/
main () /*"; # */
{
$ i = 0;
[...]
}


well, of course the variables still need to be defined first in c... luckily, our variables are all integers, defined with the int keyword - which is also a function part of the perl core, and rounds a number to its integer part. perl happily accepts useless statements, provided we're running without warnings (which is the case). therefore, we can please c while still keeping perl happy with the following:

[...]
#define $ /*
"*/
main () /*"; # */
{
int $ i;
int $ n1;
int $ n2;
$ i = 0;
[...]
}


we're getting close... now, we just need to adapt the code. we can use printf (valid in both perl and c) instead of print, while loops are ok, auto-incrementation too... double assignment is not allowed, and thus splitted accross the lines. so, here's the final program:

#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++;
}
}


our tests are now passing completely:

$ prove -l t
t/c.......ok
t/perl....ok
All tests successful.
Files=2, Tests=2, 1 wallclock secs ( 0.02 usr 0.01 sys + 0.11 cusr 0.04 csys = 0.18 CPU)
Result: PASS


so, that's one additional language (two if you consider that it's also working with c++). that was quite interesting, wasn't it? of course, perl and c languages are quite close, which allowed us to really interlace the two. next languages won't be that easy - but shhh! that will be covered in other posts...

No comments:

Post a Comment