Showing posts with label befunge. Show all posts
Showing posts with label befunge. Show all posts

2009-06-11

redefining exported subs in perl

i've explained in a previous post that i changed the way i was logging debug statements within language::befunge. i mentioned that i applied some tricks and promised to explain them - so here are the explanations.

the goal is to minimize time spent for debug statements. previously, i was doing:
$interpreter->debug(@stuff);
and debug was a method defined as:
sub debug {
my ($self, @stuff) = @_;
return unless $self->debug_mode;
warn @stuff;
}

so, to log a debug message, i was doing:
  • a method call on $interpreter
  • a second method call to check an attribute
  • finally the actual logging (skipped if we're not in debug mode)
this is bad, especially since method calls cannot be resolved at compile time by perl, and thus are actually resolved during run-time. but what's worse is that this always happens, even if we're not in debug mode (which is around 99% of the time).

so, one obvious way to improve was to move from a method to a plain sub. this would remove the run-time cost of resolving the method. the debug mode can be stored as a package scalar instead of an attribute.

but we can do even better. knowing that:
  • perl optimizes out calls to empty subs
  • we are not in debug mode most of the time
we can define the debug sub as an empty sub!

here's our code at that point:
package Language::Befunge::Debug;

use 5.010;
use strict;
use warnings;

use base qw{ Exporter };
our @EXPORT = qw{ debug };

sub debug {}
of course, we need to provide a way to activate debugging. a naive approach would be to redefine our debug() sub in our debug package:
sub enable {
*debug = sub { warn @_; };
}
alas, this won't work. well, it will work for calls such as:

Language::Befunge::Debug::debug(@stuff);
but calls using exported debug() will still log nothing. indeed, it's important to understand that exporter installs a copy of exported sub in the package. therefore, changing the definition of the original does not change the exported copies.

so, to redefine exported subs, one is forced to walk the symbol table of all packages and redefine subs on the fly. here's what i ended up doing:

my %redef;
sub enable {
%redef = ( debug => sub { warn @_; } );
_redef();
}

sub disable {
%redef = ( debug => sub {} );
_redef();
}

my %orig; # original subs
sub _redef {
my $parent = shift;
if ( not defined $parent ) {
$parent = '::';
foreach my $sub ( keys %redef ) {
$orig{ $sub } = \&$sub;
}
}
no strict 'refs';
no warnings 'redefine';
foreach my $ns ( grep /^\w+::/, keys %{$parent} ) {
$ns = $parent . $ns;
_redef($ns) unless $ns eq '::main::';
foreach my $sub (keys %redef) {
next # before replacing, check that...
unless exists ${$ns}{$sub} # ... named sub exist...
&& \&{ ${$ns}{$sub} } == $orig{$sub}; # ... and refer to the one we want to replace
*{$ns . $sub} = $redef{$sub};
}
}
}
there, it will redefine my sub in all packages, even the ones that hold an exported copy.

now, do you think this would warrant a sub::redefine module on cpan? after all, i found nothing on cpan that would achieve that. otoh, i'm not sure it's that common to do this kind of things... so tell if you're interested, and i'll turn that in a cpan module for your own use.

2009-06-05

some befunge love

i took some time to review my befunge modules. trying to speed up things is always fun, so thanks to devel::nytprof, i saw that i was spending quite some time on my debug statements.

it should be noted that those statements were method calls on the main language::befunge::interpreter object. and the method then was outputing things depending on the value of a debug attribute of the interpreter. the interpreter was not used outside of this.

knowing that method calls are expensive (since perl doesn't know until run-time where to find the method), i therefore created a language::befunge::debug module that exports a debug() sub. 2 other subs are provided (but not exported) to turn on/off the debug. (there's a trick here, that i will explain in another post).

net result? around 20% speedup (a bit more in fact). not bad for one hour spent on the subject. :-)

other than that, language::befunge tests got sanitized (using test::more, test::output and test::exception everywhere instead of crafting stuff by hand). part of this code was not touched since 2002...

finally, language::befunge got some new extensions, still passing all mycology tests. you can now enjoy the following in jqbef98:
  • CPLI - complex numbers extension
  • DIRF - directory operations
  • FILE - file i/o operations
  • FIXP - fixed point operations
  • STRN - string operations
  • SUBR - subroutines extension
  • TIME - date/time operations
some of them were pretty difficult to get right, if you forget some befunge basis (note to self: the storage offset is here for a reason, dammit!).

which leaded me to update language::befunge::debugger to load mycology correctly, with a new option to run without delay till the next breakpoint. using it, things were easier to get right. still not perfect, but already more than usable...

so, enjoy language::befunge 4.11 and language::befunge::debugger 0.3.6, now available on cpan!

2009-04-08

polyglot: html / javascript for the win!

reminder: this article is part of a serie.

still ready for more polyglot action? then let's continue. today's language is a bit special, since it's targetting the browser. we're indeed going to craft a mix of html and javascript, the goal being that firefox renders the file as the 10 first fibonacci numbers. it's not that difficult, using the onload argument of the body tag. here's the html / javascript snippet:


<html><script language="javascript">
function foo_it () {
var i = 0
var n1 = 1
var n2 = 1
document.writeln(n1)
while (i < 9) {
n3=n1+n2
n1=n2
n2=n3
document.write("<br>",n1)
i++
}
}
</script><body onload="foo_it()"></body></html>


the nice thing is that if the html is well written, everything outside the <html> tags will be ignored. so, once again, let's put this at the end of our file. but we have a problem: we cannot have * fortran comments in the middle of our javascript code! and we cannot use the /* */ comments, since we're already inside a c comment... a first solution would be to put everything on a single line - but we can do better. since * is the multiply operator in javascript, let's create a temp variable foo that will be multiplied in place with *=. and we can then insert our code (compacted on 4 lines):


[...]
* <html><script language="javascript">function foo_it () { var foo; foo
*= 2; var i=0; var n1=1; var n2=1; document.writeln(n1); foo
*= 3; while ( i<9 ) { n3=n1+n2; n1=n2; n2=n3; document.write("<br>",n1); foo
*= 4; i++ } } </script><body onload="foo_it()"></body></html>
[...]


our tests still pass, and opening this file in firefox (after renaming it to html, in order for firefox to render it as html, not as plain text) yields the famous sequence. unfortunately, this test cannot be scripted, because of javascript...

the latest version of the program can be seen here, and now supports 9 languages. on a parallel note, is it me or are those blog entries getting smaller and smaller? :-)

anyway, that was today's entry...

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:


[...]
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:


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-01-17

how to shave 10% speed?

contributing to other projects is good (not counting the fact that it's fun):
  • you help open-source as a whole
  • you gain some knowledge on the projects you help
  • ... and you discover some new stuff, techniques, tricks
so recently i've been helping the padre team, where i discovered the module class::xsaccessor. reading the pod, it seemed quite good, but the question was: is it really that interesting?

so, i decided to try that on language::befunge, which currently uses a mix of class::accessor::fast and hand-crafted accessors (man, some part of the code is untouched since 2002!). who recalls of using the following in his/her code:
BEGIN {
my @attrs = qw{ attr1 attr2 attr3 };
foreach my $attr ( @attrs ) {
my $code = qq[ sub get_$attr { return \$_[0]->{$attr} } ];
$code .= qq[ sub set_$attr { \$_[0]->{$attr} = \$_[1] } ];
eval $code;
}
}

anyway, i created a new git branch in langage::befunge's repository, and ported all my classes to use class::xsaccessor. and then i've run both the module's test suite and mycology:
  • current language::befunge: tests = 5.28s, mycology = 20.48s
  • using class::xsaccessor: tests = 5.12s, mycology = 18.18s
(everything ran once to warm cache, and then averaging three passes. all output directed to /dev/null in order not to pollute cpu measures with io)

that is, around 5% saved (a bit less) for the tests, but 10% (a bit more) for mycology. knowing that the tests are not representative of real befunge workload, this means a 10% speedup in befunge programs... neat!

needless to say that i merged this temp branch (ain't git cool?) to master, and language::befunge 4.09 is on its way to cpan!

conclusion: use class::xsaccessor, it fulfils its promises... and contribute to other projects, at least you'll find some ideas for your own projects!

2009-01-10

befunge on parrot fix for "sgml spaces" error in mycology

my bad - mycology was right, i was wrong. when spotting an error in my befunge interpreters, i should trust matti and look in my code. :-)

the sgml spaces mycology error mentioned previously came from the instruction ` which is greater than, not greater than or equal.

so i fixed it, and befunge on parrot now passes completely the befunge 93 mycology tests.

2009-01-09

befunge on parrot interpreter now working (again)

it all started with a discussion on #parrot:
Dec 28 19:17:27 <kj> don't think zcode is still being maintained
Dec 28 19:19:16 <rurban> Looks like so, yes.
Dec 28 19:19:27 <rurban> befunge being the worst.
(funny thing is that i wasn't even here at that time, cognominal reported it to me later on)

as befunge on parrot author, it hurted my pride. i could stand that befunge wasn't the best parrot language implemented, but being the worst... i had to do something!

so, after some time to compile parrot (didn't did that for 3 years, it's now way easier!), getting up to speed to groak parrot error messages, i started porting befunge on recent parrot.

i took the opportunity to move from pasm to pir, using neat addons such as subs (even with parameters and return values!), named vars, random pmc (instead of pseudo random stuff using mod and chicken sacrifying) and other welcome stuff that were not available in 2002 when i first implemented befunge on parrot.

the result is a working befunge-93 implementation on top of parrot, and befunge now passes its tests:
$ make test
../../parrot -o befunge.pbc befunge.pir
cd .. && /usr/bin/perl5.10.0 t/harness --languages=befunge
befunge/t/basic....ok
All tests successful.
Files=1, Tests=1, 1 wallclock secs ( 0.06 cusr + 0.01 csys = 0.07 CPU)

i also checked this implementation with mycology, and it seems to work:
  • plain mycology: reports a failure on sgml-mode, but that's a bug in mycology
  • mycouser: success, before jumping to befunge-98 library semantics, where it fails (of course)
  • mycorand: success

so, all in all, befunge on parrot is once again alive and kicking!