Raku grammar action throwing "Cannot bind attributes in a Nil type object. Did you forget a '.new'?" error when using "make"
Asked Answered
D

2

6

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.

Damalus answered 25/3, 2022 at 20:46 Comment(2)
I have a hunch make may be trying to access the class the parse method is in. Maybe a bug?Damalus
"If I comment out the line with the make method, everything works fine." Your code doesn't include use of a .make method. "As far as I can tell, this code matches what's in the official documentation." From the official doc for make: "The sub form operates on the current Match $/". So the "Nil object the compiler is referring to" is the "value" (a Nil is a value representing a lack of a value) that's bound to $/ when the make sub call is evaluated.Reactionary
N
8

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.

Nishanishi answered 25/3, 2022 at 21:31 Comment(1)
Yes, my big reason for not using $/ was it was throwing wrenches in my regex tests. According to the docs, it says you can use $match instead so that's what I did. At any rate, your solution worked. Thanks for that and the extra tips!Damalus
P
1

Err - bit of a guess here but looks like this error is generated during creation of a new object. That points to the line my $tc = Lingua::EN::Titlecase.new($match). I wonder if you want to pass a Str into this function call e.g. with "$match" or ~$match...

Peavey answered 25/3, 2022 at 21:25 Comment(1)
Unfortunately, that's not it. The code works fine with the make line commented out. And If I do $tc = Lingua::EN::Titlecase.new('blah') instead, there is no difference in behavior. Same error.Damalus

© 2022 - 2024 — McMap. All rights reserved.