in reply to Playing with (macro/source-filter) fire

sleepingsquirrel,

Intruiging idea. I am a fan of LISP style (runtime code expansion) more than C style (text substitution) macros myself. So this node grabbed my attention. But I tried running your code, which worked, but when I added a few print statments to see the guts of it, it looked to me that nothing is really happening.

Here is the output I got:

MACRO (NOT-COMPILED): for ($var1=$cons2;$var3<$cons4;$var5++) {$body6} -e syntax OK MACRO (COMPILED & DEPARSED): while ($var3 < $cons4) { $body6; } continue { ++$var5 } SOURCE (NOT-COMPILED): #use Recur_unroll; my $x; for($x=0;$x<8;$x++) { my $f=fact($x); print "factorial($x)=$f\n"; } sub fact { return(1) if ($_[0] <= 0); return $_[0]*fact($_[0]-1); } 1; -e syntax OK SOURCE (COMPILED & DEPARSED): my $x; while ($x < 8) { my $f = fact($x); print "factorial($x)=$f\n"; } continue { ++$x } sub fact { $_[0] <= 0 and return 1; return $_[0] * fact($_[0] - 1); } '???'; REORDER: $var3 = $1;$cons4 = $2;$body6 = $3;$var5 = $4; REG-EXED MACRO: while \((\$[a-zA-Z][a-zA-Z0-9]*) < (\d+)\) \{ ([^}]+); \} continue \{ \+\+(\$[a-zA-Z][a-zA-Z0-9]*) \} FINAL SOURCE: my $x; while ($x < 8) { my $f = fact($x); print "factorial($x)=$f\n"; } continue { ++$x } sub fact { $_[0] <= 0 and return 1; return $_[0] * fact($_[0] - 1); } '???'; MACRO (NOT-COMPILED): for ($var1=$cons2;$var3<$cons4;$var5++) {$body6} -e syntax OK MACRO (COMPILED & DEPARSED): while ($var3 < $cons4) { $body6; } continue { ++$var5 } SOURCE (NOT-COMPILED): SOURCE (COMPILED & DEPARSED): REORDER: $var3 = $1;$cons4 = $2;$body6 = $3;$var5 = $4; REG-EXED MACRO: while \((\$[a-zA-Z][a-zA-Z0-9]*) < (\d+)\) \{ ([^}]+); \} continue \{ \+\+(\$[a-zA-Z][a-zA-Z0-9]*) \} FINAL SOURCE: factorial()=1 factorial(1)=1 factorial(2)=2 factorial(3)=6 factorial(4)=24 factorial(5)=120 factorial(6)=720 factorial(7)=5040
And here is the altered Loop_unroll module:
package Loop_unroll; use strict; use Filter::Simple; FILTER { my $unroll_limit = 10; #max unrolling depth my ($var1, $cons2, $var3, $cons4, $var5, $body6); #This is the macro replacement pattern we're looking for... my $macro = 'for ($var1=$cons2;$var3<$cons4;$var5++) {$body6}'; print "MACRO (NOT-COMPILED): $macro\n"; #run the macro snippet through B::Deparse (better way to grab outp +ut?) my $compiled_macro=`perl -MO=Deparse,-x9 -e'$macro'`; #screws with + ', FIX ME. print "MACRO (COMPILED & DEPARSED): $compiled_macro\n"; print "SOURCE (NOT-COMPILED): $_\n"; #compile the main script my $source = `perl -MO=Deparse,-x9 -e'$_'` if $_; #same proble +m, Bad dog. print "SOURCE (COMPILED & DEPARSED): $source\n"; #These are crude regex to try to match perl variables and integers $var1 = $var3 = $var5 = '(\$[a-zA-Z][a-zA-Z0-9]*)'; $cons2 = $cons4 = '(\d+)'; $body6 = '([^}]+)'; #barf, should be something nicer, no nested bl +ocks #deswizzle variable order (mangled by compile) #variables should be guarenteed to occur only once because of side +-effects my $i; my $reorder; while ($compiled_macro=~/(var1|cons2|var3|cons4|var5|body6)/g) { $reorder.="\$$1 = \$".++$i.";"; # map vars to capturing parens } print "REORDER: $reorder\n"; $compiled_macro=~s/([|(){}\^*+?.\[\]])/\\\\$1/g; #de-meta stuff, n +ot $ (ick) my $regexed_macro; eval '$regexed_macro=qq/'."$compiled_macro".'/'; #interpolate patt +ern match junk print "REG-EXED MACRO: $regexed_macro\n"; $source =~ s{$regexed_macro} { eval $reorder; # better method? ($var1 eq $var3 and $var1 eq $var5 and $cons4 - $cons2 < $unroll_limit) ? "$var1 = $cons2;".("{$body6;} $var1++;") x ($c +ons4-$cons2) : $&; #Lazy like a fox. }eg; $_ = $source; print "FINAL SOURCE: $source\n"; }; 1;

Am I missing something?
(BTW - I am on Mac OS X (10.3.2) with Perl v5.8.0)

-stvn

Replies are listed 'Best First'.
Re: Re: Playing with (macro/source-filter) fire
by sleepingsquirrel (Chaplain) on Jan 23, 2004 at 20:52 UTC
    Hmmm. I don't see what went wrong, but here's the output of the filter I get with use Unroll_loop after uncommenting the last print STDERR $source;
    my $x; $x = 0;{my $f = fact($x); print "factorial($x)=$f\n";} $x++;{my $f = fact($x); print "factorial($x)=$f\n";} $x++;{my $f = fact($x); print "factorial($x)=$f\n";} $x++;{my $f = fact($x); print "factorial($x)=$f\n";} $x++;{my $f = fact($x); print "factorial($x)=$f\n";} $x++;{my $f = fact($x); print "factorial($x)=$f\n";} $x++;{my $f = fact($x); print "factorial($x)=$f\n";} $x++;{my $f = fact($x); print "factorial($x)=$f\n";} $x++;sub fact { $_[0] <= 0 and return 1; return $_[0] * fact($_[0] - 1); }
    And here's the result of Recur_unroll...
    my $x; $x = 0; while ($x < 8) { my $f = fact($x); print "factorial($x)=$f\n"; } continue { ++$x } sub fact { $_[0] <= 0 and return 1; return $_[0] * eval{local @_=$_[0] - 1; $_[0] <= 0 and return 1; return $_[0] * fact($_[0] - 1); }; }

      Still not working for me. I tried downloading your code again (in case it was me), and ran both the Loop and Recurse filters. I have Filter::Simple version 0.79 (i upgraded this too), which uses Text::Balanced which I have version 1.89. Again, Mac OS X 10.3.2 with the default installed Perl 5.8.0. Should I upgrade anything?

      What platform are you on? I mean, the code looks like it should work. I dont see why its not though.

      -stvn
strange differences...
by sleepingsquirrel (Chaplain) on Jan 23, 2004 at 21:34 UTC
    After running your version of the code on my machine I notice a couple of differences between your results and mine. When you print out the results of the compiled and deparsed macro, you get...
    MACRO (COMPILED & DEPARSED): while ($var3 < $cons4) { $body6; } continue { ++$var5 }
    while I get...
    MACRO (COMPILED & DEPARSED): $var1 = $cons2; while ($var3 < $cons4) { $body6; } continue { ++$var5 }
    ...I notice that the first line $var1 = $cons2; is missing in your report. And strangely enough there's a line is missing from your compilied and deparsed source code ...
    SOURCE (COMPILED & DEPARSED): my $x; while ($x < 8) { my $f = fact($x); print "factorial($x)=$f\n"; } continue { ++$x } sub fact { $_[0] <= 0 and return 1; return $_[0] * fact($_[0] - 1); } '???';
    vs. my machine
    SOURCE (COMPILED & DEPARSED): my $x; $x = 0; while ($x < 8) { my $f = fact($x); print "factorial($x)=$f\n"; } continue { ++$x } sub fact { $_[0] <= 0 and return 1; return $_[0] * fact($_[0] - 1); }
    ...the $x = 0; has gone missing. So this causes $reorder to differ...
    REORDER: $var3 = $1;$cons4 = $2;$body6 = $3;$var5 = $4;
    vs.
    REORDER: $var1 = $1;$cons2 = $2;$var3 = $3;$cons4 = $4;$body6 = $5;$va +r5 = $6;
    and is probably what causes the substitution match failure and thus you get the original code back. I don't know why lines would suddenly dissapear. Is it a buffering problem with the back-tick operator?

    update:I'd try just running...

    perl -MO=Deparse,-x9 -e'for($i=0;$i<10;$i++){$x++}'

    ...all by itself and see if it matches...

    $i = 0; while ($i < 10) { ++$x; } continue { ++$i }
    That's straight from 3rd edition camel p. 480. Anyone else have any ideas? BTW, I'm running perl 5.6.1 under Slackware Linux

      Hmm, I re-downloaded all your code, tried it again, no go. My guess is that B::Deparse on OS X is different that Linux. My version of Deparse is one behind the latest, so that might play a role in this.

      Cool stuff none the less, thanks for showing your output, so I could see the guts of what it was doing though.

      -stvn