perlobjecttry-catchstatemarpa

Internal State Changes After try{} Block


I'm working with Marpa::R2, and am trying to catch errors using Try::Tiny. I am puzzled because when I put the parsing code into a try block, the result of the value method is undef. Here's my code:

use strict;
use warnings;
use Marpa::R2;
use Data::Dumper;
use Try::Tiny;

my $grammar = Marpa::R2::Scanless::G->new(
    {
        source => \(<<'END_OF_SOURCE'),
            :default ::= action => ::array
            :discard ~ ws
            ws ~ [\s]+
            :start ::= Name
            Name ::= Foo
            Foo ~ [\w]+
END_OF_SOURCE
    }
);
my $reader = Marpa::R2::Scanless::R->new(
    {
        grammar => $grammar,
    }
);
my $input = 'foo';
try{
    $reader->read(\$input);
}catch {
    warn "caught error: $_";
}
my $value = $reader->value;
print Dumper $value;

I was going a little nuts trying to figure out what was the grammar, and then I realized that removing the try block made the value of $reader->value non-null. Even if I assign $value within the try block, it becomes undef when the block is finished:

my $value;
try{
    $reader->read(\$input);
    $value = $reader->value;
}catch {
    warn "caught error: $_";
}
print Dumper $value;

This seems to mean that if I want to catch errors with Try::Tiny, I have to put ALL of my processing of $value into the try block, which is inconvenient.

Can anyone tell me what is causing this (I'd like an explanation of how this is possible in Perl)? And is there a way to fix it?


Solution

  • try{
        $reader->read(\$input);
    }catch {
        warn "caught error: $_";
    }                               <------ missing semicolon
    my $value = $reader->value;
    

    Keep in mind that try is not actually a control structure. try and catch are simply subs with the &@ prototype. (I imagine that catch returns some form of object.) As such, the above code is equivalent to

    &try(sub { ... }, &catch(sub { ... }, my $value = $reader->value));
    

    Notice how the assignment to $value is being done before the try?