padre, the perl ide, has a plugin system, allowing people to extend it to suit their needs. plugins are written in perl (not in a dumbed down language), and thus one can leverage the whole cpan to extend padre. and since there is a parrot plugin, one can even write plugins in any language supported by the parrot virtual machine. but i digress...
to manage those plugins, and enable / disable them at will, padre is shipping a plugin manager. until now, here's how it looked like:
not really sexy - in fact, it was pretty ugly. and the screenshot has not been taken with all padre plugins available - in this case, the window grows absurdely and expands way beyond the screen edges. not to mention that current plugin manager incarnation is not really useful to know what a plugin really does...
so i worked on it and totally revamped the plugin manager. after much fighting with wxwidgets (and i did not win every time... :-|), here's the result, as available in trunk:
as you can see, the available plugins are now in a list, scaling up quite well with the number of padre plugins that we expect to see in the future. moreover, the plugin status icons show immediately which plugins are currently enabled. finally, there's a space on the right of the window to display the plugin documentation.
what's not visible in above screenshot is that one can sort the plugins on their version and status. and that plugins can now provide their own icon, instead of the default one. now that it has been implemented in the plugin manager, i expect plugins to start using this opportunity quite soon to stand apart...
all in all, i'm quite happy with the new plugin manager. it will be available in next padre release, which should arrive soon. and if you have new wishes or remarks, i invite you to report those bugs / requests to padre's ticket center.
2009-03-30
polyglot: ain't your brain fucked yet?
reminder: this article is part of a serie.
it's becoming harder and harder to follow. your brain may start to melt, so let's be proactive, and play with brainfuck! :-)
brainfuck is yet another esoteric language. it is a minimalist programming, language, where each character is an instruction (same as befunge). it contains only 8 instructions yet is turing complete. and anyone who tried to program with it definitely knows why this name was chosen. we're going to use aidbf for our tests.
since esoteric languages are hard to program with, we'll start once again by writing the brainfuck version without messing with our polyglot program. here's my version:
interesting, uh? :-) that took me quite some time to write it... (you're welcome to come up with your own version if you want to) so, now let's insert this gem in our polyglot program. but where? well, brainfuck has this interesting property of ignoring all the unknown instructions. so let's try to insert it somewhere... the end of the file is our place of choice for this:
however, this does not work since some brainfuck instructions are used before, and therefore mess up the data pointer and the value under the data pointer. sigh. and we cannot cheat regarding this, since the characters used by brainfuck are quite common. so, should we abandon? no, we won't let that affect us! let's use a brainfuck while, which will skip the whole program till the actual brainfuck program. in brainfuck, the while is noted [ ... ] (that is, square brackets). it will repeat everything between the square brackets till the value pointed by the data pointer is not null. since the values pointed by the data pointer are initially null, the while loop will be skipped directly. so, let's just add a [ at the beginning of our program, and a ] just before our brainfuck program. but in order not to disturb befunge, we insert it before the caret - and we don't forget to line up the bottom of the file:
note the [ at the end of the first line, and the ] just before our brainfuck program. and this time, the fibonacci numbers are appearing... but the program never stops! why? well, once again there are some brainfuck instructions trailing after the brainfuck program. but we now know the recipe, and just skip them with a while loop happily skipped:
and our tests are now all passing:
that was easy, finally. and since we're at it, let's just insert a new language derived from brainfuck: ook! (named after terry pratchett's discworld librarian) ook is bijective with brainfuck: each brainfuck instruction is translated as a ook! ook! instruction. of course, unknown instruction are skipped too... so let's
just create our test file (using Language::Ook interpreter), translate our brainfuck program, and insert it. our program now looks like:
and everything is running smoothly:
that is, 8 different languages... ok, 3 of them are esoteric, but that's quite an achievement nevertheless! now is the time to get back to more mainstream languages... but this will be in another post...
it's becoming harder and harder to follow. your brain may start to melt, so let's be proactive, and play with brainfuck! :-)
brainfuck is yet another esoteric language. it is a minimalist programming, language, where each character is an instruction (same as befunge). it contains only 8 instructions yet is turing complete. and anyone who tried to program with it definitely knows why this name was chosen. we're going to use aidbf for our tests.
since esoteric languages are hard to program with, we'll start once again by writing the brainfuck version without messing with our polyglot program. here's my version:
++++++++++>[-]++++++++++>+>+<<[->[>>+>+>+<<<<-]>[<
+>>>>+<<<-]>>>[<<<+>>>-]<>+++++++++<[>>>+<<[>+>[-]
<<-]>[<+>-]>[<<++++++++++>>>+<-]<<-<-]>>>>[<<<<+>>
>>-]<<<<>[-]<[++++++++++++++++++++++++++++++++++++
++++++++++++.-------------------------------------
-----------[<---------->-]]<++++++++++++++++++++++
++++++++++++++++++++++++++.-----------------------
-------------------------<<<<.>>>>[-]<<<]
interesting, uh? :-) that took me quite some time to write it... (you're welcome to come up with your own version if you want to) so, now let's insert this gem in our polyglot program. but where? well, brainfuck has this interesting property of ignoring all the unknown instructions. so let's try to insert it somewhere... the end of the file is our place of choice for this:
[...]
end
* ++++++++++>[-]++++++++++>+>+<<[->[>>+>+>+<<<<-]>[<+>>>>+<<<-]>>>[<<<+>>>-]<>++
* +++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+<-]<<-<-]>>>>[<<<<+>>>>-]<<
* <<>[-]<[++++++++++++++++++++++++++++++++++++++++++++++++.---------------------
* ---------------------------[<---------->-]]<++++++++++++++++++++++++++++++++++
* ++++++++++++++.------------------------------------------------<<<<.>>>>[-]<<<]
[...]
however, this does not work since some brainfuck instructions are used before, and therefore mess up the data pointer and the value under the data pointer. sigh. and we cannot cheat regarding this, since the characters used by brainfuck are quite common. so, should we abandon? no, we won't let that affect us! let's use a brainfuck while, which will skip the whole program till the actual brainfuck program. in brainfuck, the while is noted [ ... ] (that is, square brackets). it will repeat everything between the square brackets till the value pointed by the data pointer is not null. since the values pointed by the data pointer are initially null, the while loop will be skipped directly. so, let's just add a [ at the beginning of our program, and a ] just before our brainfuck program. but in order not to disturb befunge, we insert it before the caret - and we don't forget to line up the bottom of the file:
(*foo /*bar#[^
[...]
* ]++++++++++>[-]++++++++++>+>+<<[->[>>+>+>+<<<<-]>[<+>>>>+<<<-]>>>[<<<+>>>-]<>++
* +++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+<-]<<-<-]>>>>[<<<<+>>>>-]<<
* <<>[-]<[++++++++++++++++++++++++++++++++++++++++++++++++.---------------------
* ---------------------------[<---------->-]]<++++++++++++++++++++++++++++++++++
* ++++++++++++++.------------------------------------------------<<<<.>>>>[-]<<<]
[...]
*n 1:86*+,a,86*+,a,11884pv >
[...]
*/
#define fubar *)
note the [ at the end of the first line, and the ] just before our brainfuck program. and this time, the fibonacci numbers are appearing... but the program never stops! why? well, once again there are some brainfuck instructions trailing after the brainfuck program. but we now know the recipe, and just skip them with a while loop happily skipped:
[...]
* ]++++++++++>[-]++++++++++>+>+<<[->[>>+>+>+<<<<-]>[<+>>>>+<<<-]>>>[<<<+>>>-]<>++
* +++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+<-]<<-<-]>>>>[<<<<+>>>>-]<<
* <<>[-]<[++++++++++++++++++++++++++++++++++++++++++++++++.---------------------
* ---------------------------[<---------->-]]<++++++++++++++++++++++++++++++++++
* ++++++++++++++.------------------------------------------------<<<<.>>>>[-]<<<][
[...]
*/
#define fubar ]*)
and our tests are now all passing:
$ prove -l t
t/bash.........ok
t/befunge......ok
t/brainfuck....ok
t/c............fibonacci.c:1: warning: data definition has no type or storage class
t/c............ok
t/fortran......ok
t/pascal.......ok
t/perl.........ok
All tests successful.
Files=7, Tests=7, 0 wallclock secs ( 0.03 usr 0.01 sys + 0.44 cusr 0.08 csys = 0.56 CPU)
Result: PASS
that was easy, finally. and since we're at it, let's just insert a new language derived from brainfuck: ook! (named after terry pratchett's discworld librarian) ook is bijective with brainfuck: each brainfuck instruction is translated as a ook! ook! instruction. of course, unknown instruction are skipped too... so let's
just create our test file (using Language::Ook interpreter), translate our brainfuck program, and insert it. our program now looks like:
(*foo /*bar^#[
*1337#) 2>/dev/null;i=0; a=1; b=1;echo $a;while test $i -lt 9;do c=$((a+b));a=$b;b=$c;echo $a;i=$((i+1));done;exit
*0) if 0; sub C () {} # */ );
#include <stdio.h>
#include <stdlib.h>
#define C
#define $ /*
C ; "*/
C ; main () { /*"; { # */
C ; int $ i;
C ; int $ n1;
C ; int $ n2;
C ; int $ n3;
C ; $ i = 0;
C ; $ n1 = 1;
C ; $ n2 = 1;
C ; printf( "%d\n", $ n1 );
C ; while ( $ i < 9 ) {
C ; $ n3 = $ n1 + $ n2;
C ; $ n1 = $ n2;
C ; $ n2 = $ n3;
C ; printf( "%d\n", $ n1 );
C ; $ i++;
C ; }
C ; }
#define foo /*
C ; __END__
*) program foo; (*
*) var i, n1, n2, 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.(*
integer i, n1, n2, n3
n1 = 1
n2 = 1
print '(I0)', n1
do 10 i = 1, 9
n3 = n1 + n2
n1 = n2
n2 = n3
print '(I0)', n1
10 continue
end
* ]++++++++++>[-]++++++++++>+>+<<[->[>>+>+>+<<<<-]>[<+>>>>+<<<-]>>>[<<<+>>>-]<>++
* +++++++<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<++++++++++>>>+<-]<<-<-]>>>>[<<<<+>>>>-]<<
* <<>[-]<[++++++++++++++++++++++++++++++++++++++++++++++++.---------------------
* ---------------------------[<---------->-]]<++++++++++++++++++++++++++++++++++
* ++++++++++++++.------------------------------------------------<<<<.>>>>[-]<<<][
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook? Ook! Ook? Ook! Ook! Ook? Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook. Ook. Ook?
* Ook. Ook. Ook? Ook. Ook? Ook. Ook! Ook? Ook! Ook! Ook. Ook? Ook! Ook? Ook. Ook? Ook. Ook?
* Ook. Ook. Ook. Ook? Ook. Ook. Ook. Ook? Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook.
* Ook! Ook! Ook? Ook! Ook. Ook? Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook. Ook?
* Ook. Ook? Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook! Ook! Ook? Ook! Ook. Ook? Ook. Ook?
* Ook. Ook? Ook! Ook? Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook. Ook?
* Ook! Ook! Ook? Ook! Ook? Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook. Ook! Ook? Ook. Ook? Ook. Ook? Ook. Ook?
* Ook. Ook. Ook? Ook. Ook? Ook. Ook! Ook? Ook. Ook? Ook. Ook. Ook. Ook? Ook! Ook? Ook! Ook!
* Ook? Ook! Ook? Ook. Ook? Ook. Ook! Ook! Ook? Ook! Ook. Ook? Ook! Ook? Ook? Ook. Ook. Ook.
* Ook. Ook? Ook! Ook! Ook? Ook! Ook. Ook? Ook! Ook? Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook?
* Ook. Ook? Ook. Ook? Ook. Ook. Ook? Ook. Ook! Ook! Ook? Ook! Ook? Ook. Ook? Ook. Ook! Ook!
* Ook? Ook. Ook! Ook! Ook? Ook! Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook! Ook? Ook? Ook.
* Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook! Ook!
* Ook? Ook! Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook? Ook! Ook? Ook! Ook! Ook? Ook!
* Ook? Ook. Ook! Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook? Ook? Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook! Ook? Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
* Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
* Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook? Ook. Ook? Ook. Ook? Ook.
* Ook? Ook. Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook! Ook? Ook! Ook! Ook? Ook!
* Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook!
*n 1:86*+,a,86*+,a,11884pv >
*$ +,>a,94g\84g1-:84p!#@_>:94p+:a`#v_:'0 ::
* ^ ,+*68-*+55/+55::,+*+338/+55:<
*/
#define fubar ]*)
and everything is running smoothly:
$ prove -l t
t/bash.........ok
t/befunge......ok
t/brainfuck....ok
t/c............fibonacci.c:1: warning: data definition has no type or storage class
t/c............ok
t/fortran......ok
t/ook..........ok
t/pascal.......ok
t/perl.........ok
All tests successful.
Files=8, Tests=8, 0 wallclock secs ( 0.04 usr 0.01 sys + 0.49 cusr 0.10 csys = 0.64 CPU)
Result: PASS
that is, 8 different languages... ok, 3 of them are esoteric, but that's quite an achievement nevertheless! now is the time to get back to more mainstream languages... but this will be in another post...
2009-03-23
polyglot: the almighty befunge
reminder: this article is part of a serie.
till now, we support 5 languages. plain, boring, production languages. it's time to add fun languages - and which is funnier than befunge? this topological, stack-based language on a 2D lahey space really is interesting to study. for this polyglot effort, we're going to use the -98 version of befunge, and use the interpreter supplied by Language::Befunge (shameless plug). so, let's create our test script, and let's roll!
in befunge, every character is an instruction. and the instruction pointer can move in whatever direction one wants: from left to right (as other languages), but also right to left, top to bottom or bottom to top. add to this that code and data share the same space, and you will understand that befunge introduces a whole new dimension to coding (and obfuscation). :-)
before trying to mix befunge with our polyglot program, let's first try to write the program itself. here's my version:
note that spaces are important: if chars are not lined up, then your program changes semantics!
so, how can we include that in our polyglot beast? befunge starts at the top-left corner, with a left-to-right velocity. therefore, we need to insert it at the top of the file... which is not really doable, given the other languages. some things worth knowing wrt befunge:
unlucky us, the opening paren is a valid befunge 98 instruction: it loads a library (yes, befunge supports libraries). but we cannot insert any library name before the (, therefore it will try to load a non-existant library. which is an error - bingo! we reverse. we just need to insert a caret ^ (protected by a hash for perl, and without impact on other languages) that will change befunge velocity to bottom-to-top. and since we're at the top of file, it will wrap to the end of file! it misses the space between bar and the star at the end of the file, but * being a stack multiplication in befunge, the star of the end of pascal comment is not a problem. so, let's just use a > to put it back on a left-to-right velocity (protected by a * comment for fortran). note how the ^ and > are lined up:
we can now paste our befunge code, protected by * fortran comments. but since we want befunge to execute as if it were at the top-left corner of the file, we need to clear the stack first, in case some instructions filled it. this is done by inserting a n instruction (n clears the stack in befunge) just after the comment:
but when we run our test suite, we don't get the expected output for befunge! when thinking about it, it's obvious: the second line of our befunge program wraps from the right (after the 0) to the beginning of the line. and when our plain befunge program was hitting a + instruction (addition), it now hits a * instruction, which, as you remember, performs a multiplication. which totally ruins our stack! so we need to somehow ignore this instruction - but we cannot remove it. so let's just add 2 numbers on the stack (with eg : which duplicates the top of stack), let befunge hit the * and then remove the top of stack (instruction $ which pops the stack). we can then continue with our regular befunge program:
you can see the whole program:
which now passes all our tests:
6 languages supported, that's not bad. but we won't stop here! to be continued...
till now, we support 5 languages. plain, boring, production languages. it's time to add fun languages - and which is funnier than befunge? this topological, stack-based language on a 2D lahey space really is interesting to study. for this polyglot effort, we're going to use the -98 version of befunge, and use the interpreter supplied by Language::Befunge (shameless plug). so, let's create our test script, and let's roll!
in befunge, every character is an instruction. and the instruction pointer can move in whatever direction one wants: from left to right (as other languages), but also right to left, top to bottom or bottom to top. add to this that code and data share the same space, and you will understand that befunge introduces a whole new dimension to coding (and obfuscation). :-)
before trying to mix befunge with our polyglot program, let's first try to write the program itself. here's my version:
1:86*+,a,86*+,a,11884pv
+,>a,94g\84g1-:84p!#@_>:94p+:a`#v_:'0
^ ,+*68-*+55/+55::,+*+338/+55:<
note that spaces are important: if chars are not lined up, then your program changes semantics!
so, how can we include that in our polyglot beast? befunge starts at the top-left corner, with a left-to-right velocity. therefore, we need to insert it at the top of the file... which is not really doable, given the other languages. some things worth knowing wrt befunge:
- when the instruction pointer hits a border, it goes back to the opposite (i simplify a bit, but for this program you can ignore the details).
- when hitting an unknown instruction, befunge reverses the direction. this is also the case on error cases.
unlucky us, the opening paren is a valid befunge 98 instruction: it loads a library (yes, befunge supports libraries). but we cannot insert any library name before the (, therefore it will try to load a non-existant library. which is an error - bingo! we reverse. we just need to insert a caret ^ (protected by a hash for perl, and without impact on other languages) that will change befunge velocity to bottom-to-top. and since we're at the top of file, it will wrap to the end of file! it misses the space between bar and the star at the end of the file, but * being a stack multiplication in befunge, the star of the end of pascal comment is not a problem. so, let's just use a > to put it back on a left-to-right velocity (protected by a * comment for fortran). note how the ^ and > are lined up:
(*foo /*bar#^
[...]
* >
*/
#define bar *)
we can now paste our befunge code, protected by * fortran comments. but since we want befunge to execute as if it were at the top-left corner of the file, we need to clear the stack first, in case some instructions filled it. this is done by inserting a n instruction (n clears the stack in befunge) just after the comment:
[...]
*n 1:86*+,a,86*+,a,11884pv >
* +,>a,94g\84g1-:84p!#@_>:94p+:a`#v_:'0
* ^ ,+*68-*+55/+55::,+*+338/+55:< */
#define bar *)
but when we run our test suite, we don't get the expected output for befunge! when thinking about it, it's obvious: the second line of our befunge program wraps from the right (after the 0) to the beginning of the line. and when our plain befunge program was hitting a + instruction (addition), it now hits a * instruction, which, as you remember, performs a multiplication. which totally ruins our stack! so we need to somehow ignore this instruction - but we cannot remove it. so let's just add 2 numbers on the stack (with eg : which duplicates the top of stack), let befunge hit the * and then remove the top of stack (instruction $ which pops the stack). we can then continue with our regular befunge program:
[...]
*n 1:86*+,a,86*+,a,11884pv >
*$ +,>a,94g\84g1-:84p!#@_>:94p+:a`#v_:'0 ::
* ^ ,+*68-*+55/+55::,+*+338/+55:<
[...]
you can see the whole program:
(*foo /*bar#^
*1337#) 2>/dev/null;i=0; a=1; b=1;echo $a;while test $i -lt 9;do c=$((a+b));a=$b;b=$c;echo $a;i=$((i+1));done;exit
*0) if 0; sub C () {} # */ );
#include <stdio.h>
#include <stdlib.h>
#define C
#define $ /*
C ; "*/
C ; main () { /*"; { # */
C ; int $ i;
C ; int $ n1;
C ; int $ n2;
C ; int $ n3;
C ; $ i = 0;
C ; $ n1 = 1;
C ; $ n2 = 1;
C ; printf( "%d\n", $ n1 );
C ; while ( $ i < 9 ) {
C ; $ n3 = $ n1 + $ n2;
C ; $ n1 = $ n2;
C ; $ n2 = $ n3;
C ; printf( "%d\n", $ n1 );
C ; $ i++;
C ; }
C ; }
#define foo /*
C ; __END__
*) program foo; (*
*) var i, n1, n2, 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.(*
integer i, n1, n2, n3
n1 = 1
n2 = 1
print '(I0)', n1
do 10 i = 1, 9
n3 = n1 + n2
n1 = n2
n2 = n3
print '(I0)', n1
10 continue
end
*n 1:86*+,a,86*+,a,11884pv >
*$ +,>a,94g\84g1-:84p!#@_>:94p+:a`#v_:'0 ::
* ^ ,+*68-*+55/+55::,+*+338/+55:<
*/
#define bar *)
which now passes all our tests:
$ prove -l t
t/bash.......ok
t/befunge....ok
t/c..........fibonacci.c:1: warning: data definition has no type or storage class
t/c..........ok
t/fortran....ok
t/pascal.....ok
t/perl.......ok
All tests successful.
Files=6, Tests=6, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.38 cusr 0.09 csys = 0.50 CPU)
Result: PASS
6 languages supported, that's not bad. but we won't stop here! to be continued...
2009-03-17
polyglot: throwing fortran in the mix
reminder: this article is part of a serie.
our polyglot program now supports perl, c, pascal and bash. it wouldn't be complete without some maths. so, let's try to shoe-horn fortran in it, with the help of our test snippet, of course! (note: we're going to use fortran 77, with the help of gfortran 4.3.2)
fortran has some strict formatting rules (at least the -77 version): everything after the 72th column will be ignored. so, an easy way to achieve our result would be to insert 72 spaces in the beginning of each line, and fit our fortran program somewhere where it will be ignored by all the languages. it's not really difficult, as you can see in the resulting program. the most difficult part was to come up with the integer format io so that fortran doesn't prepend spaces.
but it's not really satisfying from an artistic point of view. so let's revert this, and try to be more creative! to that end, we're going to use the standard fortran comment, which is a C character in the first column. that's a better challenge... of course, we're still going to use the 72 columns trick in some cases (the cpp instructions for example, so let's just move them and forget about them), but non-fortran code will also be able to use the first columns too.
so now, let's focus on helping the languages ignore the leading C of each line. let's decoy it: in c, a simple #define will do the trick, while in perl, we're going to define an empty sub after this name, with an empty prototype.
we can now prepend "C ; " in front of each line. but we cannot do that between the main keyword and the opening curly brace, since only the function arguments can be declared in here... so let's just move the opening brace at 2 different places (one for perl, one for c).
now we can prepend the fortran comment before all the mixed perl & c code, including the __END__ token (which, contrary to popular belief, does not need to be at the beginning of a line):
ok, the middle of the code has been fortran-ized. let's now focus on the top of our file. to please pascal, we don't have much choice than moving the opening comment in column 73. but next lines can be commented out in fortran quite easily - indeed, a star in column 0 also acts as a comment. so instead of dividing by 1337, we'll multiply (and bash will glob in current directory instead of looking at filesystem root). and since the line just after is perl only, we can multiply by 0 to achieve the same result:
(note: we could have used the C-form comment, by adding some math operators in the mix, but let's vary our weapons! :-) )
so, we're stuck with the pascal part of our file. let's just open a comment in column 73, and close it at column 0 on the following line - since the *) will conveniently open a fortran comment. of course, some parts of the program will be spaces, so let's tighten our program to have fewer, longer lines.
and we just have the end of the file which needs to be made compatible with fortran. this is not difficult using above trick, while creating some space that will be ignored by pascal and c, with some clever comments. we get this final program:
program which passes all out tests:
see? finally, it's not so hard to mess with programming languages, isn't it? i guess you're now capable to continue this exercise - but if you bear with me, i'll continue adding some more! :-)
our polyglot program now supports perl, c, pascal and bash. it wouldn't be complete without some maths. so, let's try to shoe-horn fortran in it, with the help of our test snippet, of course! (note: we're going to use fortran 77, with the help of gfortran 4.3.2)
fortran has some strict formatting rules (at least the -77 version): everything after the 72th column will be ignored. so, an easy way to achieve our result would be to insert 72 spaces in the beginning of each line, and fit our fortran program somewhere where it will be ignored by all the languages. it's not really difficult, as you can see in the resulting program. the most difficult part was to come up with the integer format io so that fortran doesn't prepend spaces.
but it's not really satisfying from an artistic point of view. so let's revert this, and try to be more creative! to that end, we're going to use the standard fortran comment, which is a C character in the first column. that's a better challenge... of course, we're still going to use the 72 columns trick in some cases (the cpp instructions for example, so let's just move them and forget about them), but non-fortran code will also be able to use the first columns too.
so now, let's focus on helping the languages ignore the leading C of each line. let's decoy it: in c, a simple #define will do the trick, while in perl, we're going to define an empty sub after this name, with an empty prototype.
[...]
) if 0; sub C () {} # */ );
#define C
[...]
we can now prepend "C ; " in front of each line. but we cannot do that between the main keyword and the opening curly brace, since only the function arguments can be declared in here... so let's just move the opening brace at 2 different places (one for perl, one for c).
[...]
"*/
main () { /*"; { # */
int $ i;
[...]
now we can prepend the fortran comment before all the mixed perl & c code, including the __END__ token (which, contrary to popular belief, does not need to be at the beginning of a line):
[...]
C ; "*/
C ; main () { /*"; { # */
C ; int $ i;
C ; int $ n1;
C ; int $ n2;
C ; int $ n3;
C ; $ i = 0;
C ; $ n1 = 1;
C ; $ n2 = 1;
C ; printf( "%d\n", $ n1 );
C ; while ( $ i < 9 ) {
C ; $ n3 = $ n1 + $ n2;
C ; $ n1 = $ n2;
C ; $ n2 = $ n3;
C ; printf( "%d\n", $ n1 );
C ; $ i++;
C ; }
C ; }
#define foo /*
C ; __END__
[...]
ok, the middle of the code has been fortran-ized. let's now focus on the top of our file. to please pascal, we don't have much choice than moving the opening comment in column 73. but next lines can be commented out in fortran quite easily - indeed, a star in column 0 also acts as a comment. so instead of dividing by 1337, we'll multiply (and bash will glob in current directory instead of looking at filesystem root). and since the line just after is perl only, we can multiply by 0 to achieve the same result:
(*foo /*bar
*1337#) 2>/dev/null;i=0; a=1; b=1;echo $a;while test $i -lt 9;do c=$((a+b));a=$b;b=$c;echo $a;i=$((i+1));done;exit
*0) if 0; sub C () {} # */ );
[...]
(note: we could have used the C-form comment, by adding some math operators in the mix, but let's vary our weapons! :-) )
so, we're stuck with the pascal part of our file. let's just open a comment in column 73, and close it at column 0 on the following line - since the *) will conveniently open a fortran comment. of course, some parts of the program will be spaces, so let's tighten our program to have fewer, longer lines.
[...]
*) program foo; (*
*) var i, n1, n2, 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.
[...]
and we just have the end of the file which needs to be made compatible with fortran. this is not difficult using above trick, while creating some space that will be ignored by pascal and c, with some clever comments. we get this final program:
(*foo /*bar
*1337#) 2>/dev/null;i=0; a=1; b=1;echo $a;while test $i -lt 9;do c=$((a+b));a=$b;b=$c;echo $a;i=$((i+1));done;exit
*0) if 0; sub C () {} # */ );
#include <stdio.h>
#include <stdlib.h>
#define C
#define $ /*
C ; "*/
C ; main () { /*"; { # */
C ; int $ i;
C ; int $ n1;
C ; int $ n2;
C ; int $ n3;
C ; $ i = 0;
C ; $ n1 = 1;
C ; $ n2 = 1;
C ; printf( "%d\n", $ n1 );
C ; while ( $ i < 9 ) {
C ; $ n3 = $ n1 + $ n2;
C ; $ n1 = $ n2;
C ; $ n2 = $ n3;
C ; printf( "%d\n", $ n1 );
C ; $ i++;
C ; }
C ; }
#define foo /*
C ; __END__
*) program foo; (*
*) var i, n1, n2, 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.(*
integer i, n1, n2, n3
n1 = 1
n2 = 1
print '(I0)', n1
do 10 i = 1, 9
n3 = n1 + n2
n1 = n2
n2 = n3
print '(I0)', n1
10 continue
end
*/
#define bar *)
program which passes all out tests:
$ prove -l t
t/bash.......ok
t/c..........fibonacci.c:1: warning: data definition has no type or storage class
t/c..........ok
t/fortran....ok
t/pascal.....ok
t/perl.......ok
All tests successful.
Files=5, Tests=5, 1 wallclock secs ( 0.03 usr 0.00 sys + 0.25 cusr 0.06 csys = 0.34 CPU)
Result: PASS
see? finally, it's not so hard to mess with programming languages, isn't it? i guess you're now capable to continue this exercise - but if you bear with me, i'll continue adding some more! :-)
2009-03-13
autoformat within padre
one of the things i care about in an editor is the ability to reformat a paragraph. it's really handy if you update your file here and there, and you're left with lines of 15 (or 100) characters.
vim has a nice builtin paragraph-reformatting tool (with gqip), and so does emacs (alt+q iirc). but padre (the perl ide) lacks one...
so i decided to create a plugin that would bring this ability to padre. moreover, it was the opportunity to discover padre's plugin subsystem.
writing the plugin was incredibly easy. especially since i'm standing on the shoulders of giants: i'm reusing the formidable text::autoformat module. in fact, adding the standard build stuff & boilerplate was really what took most of my time... ok, that and fighting a bit with wx stc, which provides everything but the methods i wanted. :-)
all in all, padre::plugin::autoformat is now propagating on cpan. to use it:
vim has a nice builtin paragraph-reformatting tool (with gqip), and so does emacs (alt+q iirc). but padre (the perl ide) lacks one...
so i decided to create a plugin that would bring this ability to padre. moreover, it was the opportunity to discover padre's plugin subsystem.
writing the plugin was incredibly easy. especially since i'm standing on the shoulders of giants: i'm reusing the formidable text::autoformat module. in fact, adding the standard build stuff & boilerplate was really what took most of my time... ok, that and fighting a bit with wx stc, which provides everything but the methods i wanted. :-)
all in all, padre::plugin::autoformat is now propagating on cpan. to use it:
- install it using your favorite method
- open padre
- activate the plugin (plugins / plugin manager / autoformat / enable). you only need to do this once, since padre will remember the enabled plugins.
- open a file
- type ctrl+shift+j to autoformat either current selection (or current paragraph if nothing is selected).
2009-03-12
polyglot: bash me harder
reminder: this article is part of a serie.
remember when we inserted the opening pascal comment in previous episode? we used characters that are also valid in bash... (uh, sorry, i meant this bash :-) ) indeed, ( starts a subshell, and * is of course the globbing operator. there definitely is potential to sneak in some bashism! especially since bash is interpreted, and thus we can exit in the middle of the file without even needing to have the rest of the file be bash-compliant!
ok, so how can we achieve that? well, *foo will either report an error if no program matching *foo exist in the current directory, or run the program matching *foo if there is at least one. this is not what we want, but we have an asset: we can discard everything produced, including the errors, by trapping stdout and stderr! and then, we can run our real commands...
but before doing that, we need to be cautious with the c program. so the first thing that we're going to do is to somehow comment a portion of code wrt c (which will be understood as command arguments by bash). note that we cannot use a cpp instruction here, since the hash sign is also used to comment in bash. so, let's open a plain c comment, without forgetting to close it before the closing parens:
however, this breaks perl. we need to come up with something... well, let's take things in order: / is the division in perl. dividing a glob doesn't make much sense, but we just want to quiet the syntax errors - we don't care about the semantics. * is the glob operator, but it is missing a varname - let's fix that:
but now, perl continues to parse, using the * as a multiplication operator, and the / as a regex opening. but then, a closing paren is not valid, since a regex needs to have balanced parens. that's not good, so let's cheat and do some hocus pocus comment thingy:
we're not using any more the same closing paren in perl and c - but who cares? perl compiles correctly, but we have an "illegal division by zero" during runtime. sigh. indeed, *bar is understood as $bar (scalar context). and $bar in numeric context is 0, since it's undef. man, this is getting annoying... let's use perl builtin optimiser to really skip this instruction:
there! finally, we're back to a working c + pascal + perl program. and this time, we may have some room to grow bash instructions... but before that, we need to close the subshell and discard its output - knowing that we should hide it from perl:
alas, this does not work since # also hides the line to bash. so let's just start an instruction (which includes the bang), and this time the closing paren will be seen. however, we need to have something also valid from the perl point of view - remember that we're inside a mathematical expression. so we must have a math operator be part of the command. and which operator is better than the division, since it's the path separator in unix? there, we'll divide by the leet number for perl while calling non-existant program /1337# (hmm, there should exist such a program):
our tests pass, and bash doesn't throw errors anymore! let's just insert the bash fibonacci instructions between the /dev/null redirection and the exit. (who said we're limited to 80 columns? :-) ) our program is now complete:
and it passes our internal tests, including the new bash one:
pfeww, is it me or is it getting harder? :-) but don't worry, we're not finished yet! but this will have to wait a bit...
remember when we inserted the opening pascal comment in previous episode? we used characters that are also valid in bash... (uh, sorry, i meant this bash :-) ) indeed, ( starts a subshell, and * is of course the globbing operator. there definitely is potential to sneak in some bashism! especially since bash is interpreted, and thus we can exit in the middle of the file without even needing to have the rest of the file be bash-compliant!
ok, so how can we achieve that? well, *foo will either report an error if no program matching *foo exist in the current directory, or run the program matching *foo if there is at least one. this is not what we want, but we have an asset: we can discard everything produced, including the errors, by trapping stdout and stderr! and then, we can run our real commands...
but before doing that, we need to be cautious with the c program. so the first thing that we're going to do is to somehow comment a portion of code wrt c (which will be understood as command arguments by bash). note that we cannot use a cpp instruction here, since the hash sign is also used to comment in bash. so, let's open a plain c comment, without forgetting to close it before the closing parens:
(*foo /* */
);
[...]
however, this breaks perl. we need to come up with something... well, let's take things in order: / is the division in perl. dividing a glob doesn't make much sense, but we just want to quiet the syntax errors - we don't care about the semantics. * is the glob operator, but it is missing a varname - let's fix that:
(*foo /*bar */
);
[...]
but now, perl continues to parse, using the * as a multiplication operator, and the / as a regex opening. but then, a closing paren is not valid, since a regex needs to have balanced parens. that's not good, so let's cheat and do some hocus pocus comment thingy:
(*foo /*bar
); # */ );
[...]
we're not using any more the same closing paren in perl and c - but who cares? perl compiles correctly, but we have an "illegal division by zero" during runtime. sigh. indeed, *bar is understood as $bar (scalar context). and $bar in numeric context is 0, since it's undef. man, this is getting annoying... let's use perl builtin optimiser to really skip this instruction:
(*foo /*bar
) if 0; # */ );
[...]
there! finally, we're back to a working c + pascal + perl program. and this time, we may have some room to grow bash instructions... but before that, we need to close the subshell and discard its output - knowing that we should hide it from perl:
(*foo /*bar
#) 2>/dev/null;exit
) if 0; # */ );
[...]
alas, this does not work since # also hides the line to bash. so let's just start an instruction (which includes the bang), and this time the closing paren will be seen. however, we need to have something also valid from the perl point of view - remember that we're inside a mathematical expression. so we must have a math operator be part of the command. and which operator is better than the division, since it's the path separator in unix? there, we'll divide by the leet number for perl while calling non-existant program /1337# (hmm, there should exist such a program):
(*foo /*bar
/1337#) 2>/dev/null;exit
) if 0; # */ );
[...]
our tests pass, and bash doesn't throw errors anymore! let's just insert the bash fibonacci instructions between the /dev/null redirection and the exit. (who said we're limited to 80 columns? :-) ) our program is now complete:
(*foo /*bar
/1337#) 2>/dev/null;i=0; a=1; b=1;echo $a;while test $i -lt 9;do c=$((a+b));a=$b;b=$c;echo $a;i=$((i+1));done;exit
) if 0; # */ );
#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 it passes our internal tests, including the new bash one:
$ prove -l t
t/bash......ok
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=4, Tests=4, 1 wallclock secs ( 0.02 usr 0.00 sys + 0.20 cusr 0.04 csys = 0.26 CPU)
Result: PASS
pfeww, is it me or is it getting harder? :-) but don't worry, we're not finished yet! but this will have to wait a bit...
2009-03-11
parrot revived on mandriva
i just took ownership of parrot rpm on mandriva. it was indeed left over for a long time, since only version 0.4.17 was available! after some time fighting with it, i'm happy to report that mandriva cooker (the development version of mandriva) now features latest and greatest parrot 0.9.1. enjoy...
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:
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:
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:
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:
now we can insert the pascal code, which is a straightforward port of the algorithm in this new language. the final program is thus:
and the obligatory test run:
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...
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...
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:
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.
things are shaping up. let's not forget to include some common c headers. no problem with cpp instructions:
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:
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:
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:
our tests are now passing completely:
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...
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...
2009-03-02
polyglot: introducing our test environment
reminder: this article is part of a serie.
before we start, we need to set up a working environment. this environment should provide us with two things:
the version control is needed to be sure that we get some points where we can go back in time. since we're going to mess our program quite hard, we'll be happy to check out a version that was working at a given time, and start messing in another direction.
the easiest way to go is of course to go with git. the following simple steps will create a git repository, and start tracking our polyglot program:
note: for easier reference, i will publish the changes to an online git repository. of course, i will only push the parts needed for a given blog entry, so don't expect to see all commits supporting all languages! :-)
that being done, we'll now create a test suite to make sure that our modifications won't break the languages that were previously working.
perl is well-known for its test automation. it has a strong history in regression testing: perl itself comes with over 200.000 internal tests, and the various distributions available on cpan almost all come each with a comprehensive test suite. i'll therefore use the standard test modules to come up with the polyglot test suite.
the simplest way to build our test suite is to use prove. each covered language will have a test file, emitting some tap. prove will harness those test files and report their status. each test will do, in order:
this fibo_check() function really implements the steps described above. the only things worth noting are:
is really dumbed-down, and will look like the following:
running the test suite is as simple as:
running a single language test will be achieved with:
of course, let's not forget to commit this in git:
and for even better checkpoint, let's add a git tag, named after the implemented language:
our working environment is now ready, and we can now go wild - knowing that we have a safety belt and a control mechanism. let the fun begin!
before we start, we need to set up a working environment. this environment should provide us with two things:
- a version control mechanism
- a test environment with some non-regression tests
the version control is needed to be sure that we get some points where we can go back in time. since we're going to mess our program quite hard, we'll be happy to check out a version that was working at a given time, and start messing in another direction.
the easiest way to go is of course to go with git. the following simple steps will create a git repository, and start tracking our polyglot program:
$ git init
$ vi fibonacci.txt
[fill in the perl program mentioned in the introduction]
$ git add fibonacci.txt
$ git ci -m 'first language: perl'
note: for easier reference, i will publish the changes to an online git repository. of course, i will only push the parts needed for a given blog entry, so don't expect to see all commits supporting all languages! :-)
that being done, we'll now create a test suite to make sure that our modifications won't break the languages that were previously working.
perl is well-known for its test automation. it has a strong history in regression testing: perl itself comes with over 200.000 internal tests, and the various distributions available on cpan almost all come each with a comprehensive test suite. i'll therefore use the standard test modules to come up with the polyglot test suite.
the simplest way to build our test suite is to use prove. each covered language will have a test file, emitting some tap. prove will harness those test files and report their status. each test will do, in order:
- create a clean directory where the test will take place
- copy the program in this directory. since some languages are picky on the extension, the destination will have a file extension matching the language.
- run the appropriate command, capturing stdout / stderr
- compare the output with what was expected
- remove the test directory
package FiboTest;
use File::Copy;
use File::Path qw{ mkpath rmtree };
use FindBin qw{ $Bin };
use Test::More tests => 1;
use parent qw{ Exporter };
our @EXPORT = qw{ fibo_check };
# fibo_check( $lang, $ext, $cmd )
sub fibo_check {
my ($lang, $ext, $cmd) = @_;
# clean room for the test
my $dir = "$Bin/tmp/$lang";
rmtree($dir);
mkpath($dir);
# get expected result
my $want = do { local $/; <DATA> };
# run the test
copy( "$Bin/../fibonacci.txt", "$dir/fibonacci.$ext");
my $command = sprintf "cd $dir; $cmd 2>&1", "fibonacci.$ext";
my $have = qx{ $command };
is( $have, $want, "language $langtest");
# clean after ourselve
rmtree($dir) unless $ENV{POLYGLOT_DEBUG};
}
1;
__DATA__
1
1
2
3
5
8
13
21
34
55
this fibo_check() function really implements the steps described above. the only things worth noting are:
- the fact that we're storing the expected output in the data section of the module
- the last line, that will _not_ remove the test environment if the environment variable POLYGLOT_DEBUG is set.
is really dumbed-down, and will look like the following:
use strict;
use warnings;
use FiboTest;
fibo_check('perl', 'pl', "perl %s");
running the test suite is as simple as:
$ prove -l t
t/perl....ok
All tests successful.
Files=1, Tests=1, 0 wallclock secs ( 0.02 usr 0.00 sys + 0.03 cusr 0.00 csys = 0.05 CPU)
Result: PASS
running a single language test will be achieved with:
$ prove -l t/perl.t
t/perl....ok
All tests successful.
Files=1, Tests=1, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.03 cusr 0.00 csys = 0.06 CPU)
Result: PASS
of course, let's not forget to commit this in git:
$ git add lib
$ git ci -m 'test helper module'
$ git add t
$ git ci -m 'test file for perl'
and for even better checkpoint, let's add a git tag, named after the implemented language:
$ git tag perl
our working environment is now ready, and we can now go wild - knowing that we have a safety belt and a control mechanism. let the fun begin!
Subscribe to:
Posts (Atom)