5

マルパ初心者です。文法で 0 個以上の用語のリストを記述するいくつかの方法を試しましたが、複数の解析ツリーを避けたいと考えています。

私の言語には、ちょうど 1 つのコンポーネントがあり、その後に 0+ サブコンポーネントが続きます。

package => component-rule [subcomponent-rule ...]

私が最初に試したのはこれでした:

{ lhs => 'Package', rhs => [qw/component-rule subcomponents/] },
{ lhs => 'subcomponents', rhs => [qw/subcomponent-list/] },
{ lhs => 'subcomponent-list', rhs => [qw/subcomponent-rule/], action => 'do_subcomponent_list' },
{ lhs => 'subcomponent-list', rhs => [qw/subcomponent-list subcomponent-rule/], action => 'do_subcomponent_list' },
{ lhs => 'subcomponent-list', rhs => [qw//], action => 'do_subcomponent_empty_list' },
{ lhs => 'subcomponent-rule', rhs => [qw/subcomponent subcomponent-name/], action => 'do_subcomponent' },

(投稿の最後に完全なコード。)

ここに私の入力があります:

$recce->read( 'component', );
$recce->read( 'String', 'MO Factory');
$recce->read( 'subcomponent', );
$recce->read( 'String', 'Memory Wipe Station');
$recce->read( 'subcomponent', );
$recce->read( 'String', 'DMO Tour Robot');

2 つの解析ツリーが得られます。1 つ目は望ましくない undef で、2 つ目は私が好むものです。どちらもリストを本質的にツリーとして返します。

$VAR1 = [
          {
            'Component' => 'MO Factory'
          },
          [
            [
              {
                'Subcomponent' => undef
              },
              {
                'Subcomponent' => 'Memory Wipe Station'
              }
            ],
            {
              'Subcomponent' => 'DMO Tour Robot'
            }
          ]
        ];
$VAR2 = [
          {
            'Component' => 'MO Factory'
          },
          [
            {
              'Subcomponent' => 'Memory Wipe Station'
            },
            {
              'Subcomponent' => 'DMO Tour Robot'
            }
          ]
        ];

subcomponent-list の nullable ルールは、0 個のサブコンポーネントのケースを許可することでしたが、代替解析である 1+ サブコンポーネントのリストの前に null 要素を導入します。(マルパは一度だけサイクルを下ります、ありがたいことに。)

私の他のアイデアは、subcomponent-list を null 非許容にし、0 または 1 つの subcomponent-lists である中間ルールを導入することでした:

{ lhs => 'subcomponents', rhs => [qw//] },
{ lhs => 'subcomponents', rhs => [qw/subcomponent-list/] },

これにより、少なくとも複数の解析が排除されましたが、まだサイクルがあり、圧縮するための厄介なネストされたツリーがあります。

長さ 0+ のリストを作成する、またはシンボルをオプションにするより直接的な方法はありますか?

完全なサンプル コード:

#!/usr/bin/perl

use Marpa::R2;
use Data::Dumper;

my $grammar = Marpa::R2::Grammar->new(
    {   start   => 'Package',
        actions => 'My_Actions',
        default_action => 'do_what_I_mean',
        rules => [
        { lhs => 'Package', rhs => [qw/component-rule subcomponents/] },
        { lhs => 'component-name', rhs => [qw/String/] },
        { lhs => 'component-rule', rhs => [qw/component component-name/], action => 'do_component' },
        { lhs => 'subcomponent-name', rhs => [qw/String/] },
        { lhs => 'subcomponent-rule', rhs => [qw/subcomponent subcomponent-name/], action => 'do_subcomponent' },
        { lhs => 'subcomponents', rhs => [qw//] },
        { lhs => 'subcomponents', rhs => [qw/subcomponent-list/] },
        { lhs => 'subcomponent-list', rhs => [qw/subcomponent-rule/], action => 'do_subcomponent_list' },
        { lhs => 'subcomponent-list', rhs => [qw/subcomponent-list subcomponent-rule/], action => 'do_subcomponent_list' },
#       { lhs => 'subcomponent-list', rhs => [qw//], action => 'do_subcomponent_empty_list' },
#       { lhs => 'subcomponent-list', rhs => [qw//],  },
        ],
    }
);

$grammar->precompute();

my $recce = Marpa::R2::Recognizer->new( { grammar => $grammar } );

$recce->read( 'component', );
$recce->read( 'String', 'MO Factory');

if (1) {
$recce->read( 'subcomponent', );
$recce->read( 'String', 'Memory Wipe Station');
$recce->read( 'subcomponent', );
$recce->read( 'String', 'DMO Tour Robot');
$recce->read( 'subcomponent', );
$recce->read( 'String', 'SMO Break Room');
}


my @values = ();
while ( defined( my $value_ref = $recce->value() ) ) {
    push @values, ${$value_ref};
}

print "result is ",Dumper(@values),"\n";

sub My_Actions::do_what_I_mean {

    print STDERR "do_what_I_mean\n";

    # The first argument is the per-parse variable.
    # At this stage, just throw it away
    shift;

    # Throw away any undef's
    my @children = grep { defined } @_;

    # Return what's left
    return scalar @children > 1 ? \@children : shift @children;
}

sub My_Actions::do_component {
    my ( undef, $t1 ) = @_;
    print STDERR "do_component $t1\n";
    my $href = { 'Component' => $t1 };
    return $href;
}
sub My_Actions::do_subcomponent{
    my ( undef, $t1 ) = @_;
    print STDERR "do_subcomponent $t1\n";
    my $href = { 'Subcomponent' => $t1 };
    return $href;
}

sub My_Actions::do_subcomponent_empty_list
{
    print STDERR "do_subcomponent_empty_list\n";
    my $href = { 'Subcomponent' => undef };
    return $href;
}

sub My_Actions::do_subcomponent_list{
    # The first argument is the per-parse variable.
    # At this stage, just throw it away
    shift;

    # Throw away any undef's
    my @children = grep { defined } @_;

    print STDERR "do_subcomponent_list size ",scalar(@children),"\n";
# Do this to collapse recursive trees to a list:
#    @children = map { ref $_ eq "ARRAY" ? @{$_} : $_; } @children;

    return scalar @children > 1 ? \@children : shift @children;
}
4

1 に答える 1