Following my previous post (Running in the compile time), it'll be very helpful if you know what's going to try next when it doesn't work as expected.

Here are some notes that I concluded and found to be helpful:

  • import is executed in compile time
  • perl -MO=Deparse is your friend
  • perl -MO=Concise too
  • Devel::Declare::toke_skipspace helps peaking the next line of code
  • lineseq op gives you a chance to see every lines of code when it is compiling
  • Search ~flora for goodies

import is executed in compile time

This is only true when the module is included with use, but not reuquire. Since use Foo is the same as saying:

BEGIN {
    require Foo;
    Foo->import;
}

This means that B::Hooks::Parser::get_linestr and friends are all useful in import.

perl -MO=Deparse is your friend

It prints the code again in a form that perl understands it, and it helps you understand how perl interprets your code. For a simple example:

> perl -MO=Deparse -e 'print 42'
print 42;
-e syntax OK

Notice that the semi-colon is added. For a weird example:

> perl -MO=Deparse -e 'print 42; 42'
print 42;
'???';
-e syntax OK

Perl thinks that latter 42 is just the same as string '???'. That's true, because constants evaluated void context can be simply discard because it's not used anywhere else. However, see this:

> perl -MO=Deparse -e 'sub { print 42; 42 }'
sub {
    print 42;
    42;
}
;
-e syntax OK

If the same code lives in a sub {}, the later 42 is then meaningful because that's the return value of the sub.

I also use this tool to write a JAPH, see here and here.

perl -MO=Concise too

This is harder to read but quite useful sometimes. It gives you the syntax tree of your code:

> perl -MO=Concise -e 'sub { print 42; 42 }' 
6  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 2 -e:1) v:{ ->3
5     <1> refgen vK/1 ->6
-        <1> ex-list lKRM ->5
3           <0> pushmark sRM ->4
4           <$> anoncode[CV ] lRM ->5
-e syntax OK

Each line represents a node, which is always an OP of different type. You can read the name of them there: leave, enter, nexstate, refgen ... The number in the beginning and the end is the execution order, which is not necessarily the same as the order of tree nodes because the evaluation of expressions of leave nodes usually happens first.

Devel::Declare::toke_skipspace helps peaking the next line of code

I suggest reading the documentation of Devel::Declare for understanding how it works. Also the t/ directory of it, lots of gems in there.

lineseq op gives you a chance to see every lines of code when it is compiling

Well, almost. I wrote a example program that prints the body when it compiles.

You'll have to use the value of $offset to know the currently parsed position in the $line.

Search ~flora for goodies

Yes yes yes.