I have this method in a class that's throwing a Cannot bind attributes in a Nil type object. Did you forget a '.new'?
method parse() {
grammar FindHeaders {
token TOP { [<not-header> | <header>]+ $ }
token not-header { ^^ <![#]> \N* \n }
token header { ^^ '#'{ 1 .. 6 } <content> \n }
token content { \N+ }
}
class HeaderActions {
method content($match) {
return if $match ~~ m/^^\#\s+<[A..Z]>+e*s/ || $match !~~ m/<[a..z]>/;
return if $match ~~ m/\|/ && ( $match ~~ m:i/project/ || $match ~~ m:i/\+\w/ );
my $tc = Lingua::EN::Titlecase.new($match);
my $new_title = $tc.title;
make($new_title);
}
}
my $t = $!content;
FindHeaders.parse($t, actions => HeaderActions.new);
}
As far as I can tell, this code matches what's in the official documentation. So not sure why I'm getting this error. I have no idea what attribute or Nil object the compiler is referring to. If I comment out the line with the make
method, everything works fine.
method content($match) {
There's a reason that action methods typically use $/
as the argument name: because the make
function looks for $/
in order to associate the provided object to it. You can use $match
, but then need to call the make
method on that instead:
$match.make($new_title);
The mention of Nil
is because the failed match earlier in the action method resulted in $/
being set to Nil
.
I guess you avoided the more idiomatic $/
as the parameter of the action method because it gets in the way of doing further matching in the action method. Doing further matching in action methods means that the text is being parsed twice (once in the grammar and again the action), which is not so efficient, and usually best avoided (by moving the parsing work into the grammar).
As a final style point, declaring grammars and action classes in a method
is neat encapsulation if they are only used there, but it would be wise to my
scope them (my grammar FindHeaders { ... }
), otherwise they shall end up installed in the nearest enclosing package anyway.