The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl
# Copyright 2015 Jeffrey Kegler
# This file is part of Marpa::R2.  Marpa::R2 is free software: you can
# redistribute it and/or modify it under the terms of the GNU Lesser
# General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
#
# Marpa::R2 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser
# General Public License along with Marpa::R2.  If not, see
# http://www.gnu.org/licenses/.

# Based on Github issue #254 -- constructor invoked
# on per-parse argument, which should not happen.

# _The Computer Journal_, Vol. 45, No. 6, pp. 620-630,
# in its "NNF" form

use 5.010;
use strict;
use warnings;

use English qw( -no_match_vars );
use Test::More tests => 12;
use lib 'inc';
use Marpa::R2::Test;
use Marpa::R2;
use Data::Dumper;

package Class_Actions;

sub new {
    my ( $class ) = @_;
    return bless { ctor_desc => 'class ctor' }, $class;
}

sub do_A {
    my ( $self, $letter ) = @_;
    my $ctor_desc = $self->{ctor_desc} // 'no ctor';
    return join ';', $ctor_desc, "class method", "letter=$letter";
}

package Package_Actions;

sub new {
    my ( $class ) = @_;
    return bless { ctor_desc => 'package ctor' }, $class;
}

sub do_A {
    my ( $self, $letter ) = @_;
    my $ctor_desc = $self->{ctor_desc} // 'no ctor';
    return join ';', $ctor_desc, "package method", "letter=$letter";
}

package main;

my $grammar =
    Marpa::R2::Scanless::G->new( { source => \q(A ::= 'a' action => do_A) } );

my @tests = ();
for my $recce_arg_desc ( 'semantics_package', 'no semantics_package' ) {
    PPO:
    for my $ppo_desc ( 'no', 'unblessed', 'same blessed', 'other blessed' ) {
        my $recce_arg   = {};
        my $ctor_desc   = 'no ctor';
        my $method_desc = undef;
        if ( $recce_arg_desc eq 'semantics_package' ) {
            $recce_arg   = { semantics_package => 'Package_Actions' };
            $ctor_desc   = 'package ctor';
            $method_desc = 'package method';
        }
        my $ppo = undef;
        SET_PPO_PARMS: {
            last SET_PPO_PARMS if $ppo_desc eq 'no';
            if ( $ppo_desc eq 'unblessed' ) {
                $ppo = { desc => $ppo_desc };
                $ctor_desc = 'no ctor';
                last SET_PPO_PARMS;
            }
            if ( $ppo_desc eq 'same blessed' ) {
                $ppo         = bless { desc => $ppo_desc }, 'Package_Actions';
                $ctor_desc   = 'no ctor';
                $method_desc = 'package method' if not defined $method_desc;
                last SET_PPO_PARMS;
            } ## end if ( $ppo_desc eq 'same blessed' )
            if ( $ppo_desc eq 'other blessed' ) {
                $ppo         = bless { desc => $ppo_desc }, 'Class_Actions';
                $ctor_desc   = 'no ctor';
                $method_desc = 'class method' if not defined $method_desc;
                last SET_PPO_PARMS;
            } ## end if ( $ppo_desc eq 'other blessed' )
            die;
        } ## end SET_PPO_PARMS:
        next PPO if not defined $method_desc;
        my $value = join ';', $ctor_desc, $method_desc, 'letter=a';
        my $desc = "$recce_arg_desc; $ppo_desc ppo";
        push @tests, [ $recce_arg, $ppo, $value, 'Parse OK', $desc ];
    } ## end PPO: for my $ppo_desc ( 'no', 'unblessed', 'same blessed',...)
} ## end for my $recce_arg_desc ( 'semantics_package', 'no semantics_package')

TEST:
for my $test_data (@tests) {
    my ( $recce_arg, $ppo, $expected_value, $expected_result,
        $test_name )
        = @{$test_data};
    my ( $actual_value, $actual_result ) =
        my_parser( $grammar, $recce_arg, $ppo );
    Test::More::is(
        Data::Dumper::Dumper( \$actual_value ),
        Data::Dumper::Dumper( \$expected_value ),
        qq{Value of $test_name}
    );
    Test::More::is( $actual_result, $expected_result,
        qq{Result of $test_name} );
} ## end TEST: for my $test_data (@tests_data)

sub my_parser {
    my ( $grammar, $recce_arg, $ppo ) = @_;

    my $recce = Marpa::R2::Scanless::R->new( { grammar => $grammar }, $recce_arg );

    if ( not defined eval { $recce->read( \'a' ); 1 } ) {
        # say $EVAL_ERROR
        my $abbreviated_error = $EVAL_ERROR;
        chomp $abbreviated_error;
        return 'No parse', $abbreviated_error;
    } ## end if ( not defined eval { $recce->read( \$string ); 1 ...})
    my $value_ref;
    if (not defined eval { $value_ref = $recce->value($ppo); 1 } ) {
        # say $EVAL_ERROR
        my $abbreviated_error = $EVAL_ERROR;
        chomp $abbreviated_error;
        return 'value() failure', $abbreviated_error;
    }
    if ( not defined $value_ref ) {
        return 'No parse', 'Input read to end but no parse';
    }
    return ${$value_ref}, 'Parse OK';
} ## end sub my_parser

# vim: expandtab shiftwidth=4: