> I've an alternation where some subrules are mandatory but others are
> optional (Subrule statement). 

This doesnt make much sense to me.  An alternation is a rule that can be
matched by any _one_ of a selection of rules.  So that means you can't say
that some of those alternations are _mandatory_ because that would mean you
are implying that more than one of the options must be matched, and since
that is impossible you have a problem... :-)
 
> $grammar = << 'EOGRAMMAR';
>     contact   : statement(s)
>     statement : email | name |  phone | fax
>     email     : 'email' '=' value  # mandatory
>     name      : 'name' '=' value   # mandatory
>     phone     : 'phone' '=' value  # optional
>     fax       : 'fax' '=' value    # optional
>     value     : /".*?"/
> EOGRAMMAR
> 
> for example email and name will be required and phone or fax  are
optional.
> In the moment I have to deal with action code working with sets. That
means
> I have an additional layer of grammar in the action code.

Hmm, if you mean the "additional layer of grammer" is the logic that ensures
that contact is not sucessful unless it contains both an email and a name
then I can see what you mean but you have to realize that you _can_ do this
with a normal context-free-grammer, but you probably dont want to.

contact         : email name fax(?) phone(?)
                | email name phone(?) fax(?)
                | email phone(?) name fax(?)
                | phone(?) email name fax(?)
                | email fax(?) name phone(?)
                | email fax(?) phone(?) name
                | email phone(?) fax(?) name
                | phone(?) email fax(?) name
                | fax(?) email name phone(?)
                | fax(?) email phone(?) name
                | fax(?) phone(?) email name
                | phone(?) fax(?) email name
                | name email fax(?) phone(?)
                | name email phone(?) fax(?)
                | name phone(?) email fax(?)
                | phone(?) name email fax(?)
                | name fax(?) email phone(?)
                | name fax(?) phone(?) email
                | name phone(?) fax(?) email
                | phone(?) name fax(?) email
                | fax(?) name email phone(?)
                | fax(?) name phone(?) email
                | fax(?) phone(?) name email
                | phone(?) fax(?) name email

Since its a permutation (Algorithym::FastPermute rocks!) this probably isnt
the optimal way to proceed.

To me the core issue that you face is that the general case of what you want
is not possible using a context-free grammar without explicitly listing
every possible permutation.  So since its context sensitive behaviour you
want, and no parser can handle such behaviour directly you need to consider
that what you want to do should _not_ be part of the grammar anyway.
(Consider that this is a relatively common state of affairs.  Its not
possible for instance using a context-free-grammar to ensure that variables
are declared before they are used.)

What I would do is create a small utility function that can handle your
needs.  Anyway heres what I did to your example...

use Parse::RecDescent;
#use diagnostics;

$::RD_AUTOACTION = q { $item[1] }; # this allows check_mandatory to be
simplified....
$grammar = << 'EOGRAMMAR';

{
        sub check_mandatory {
                my ($mand_array,$item_array)=@_;

                # make sure we dont overwrite any keys
                my %counts;
                # Transform list of mandatory items into hash
                my %mand=map {($_=>1)} @$mand_array;
                # We will convert the LOL that we recieve into a hash
                my %ret;
                foreach my $elem (@$item_array) {
                        my $name=$elem->[0];
                        delete $mand{$name} if $mand{$name};
                        if ($counts{$name}) {
                                $name.=$counts{$name};
                        }
                        $counts{$name}++;
                        $ret{ $name }=$elem->[1];
                }
                return !(scalar keys %mand) ? \%ret : undef;
        }
}

     contact   : statement(s)       {
                                     $return=check_mandatory([
'email','name' ],$item{statement});
                                     $return
                                    }
     statement : mandatory
               | optional
     mandatory : email | name
     optional  : phone | fax
     email     : 'email' '=' value  { [$item[0],$item{value}]}# mandatory
     name      : 'name'  '=' value   { [$item[0],$item{value}]}# mandatory
     phone     : 'phone' '=' value  { [$item[0],$item{value}]}# optional
     fax       : 'fax'   '=' value    { [$item[0],$item{value}]}# optional
     value     : /"[^"]*"/
EOGRAMMAR

$parser = new Parse::RecDescent ($grammar) or die "Bad grammar!\n";# acquire
$text

my $ret=$parser->contact(<<'EOTEST') or print "Bad text!\n";
email = "[EMAIL PROTECTED]"
name  = "Joe Camel"
phone = "(555)-555-5555"
fax   = "(555)-555-1111"
EOTEST

use Data::Dumper;
print Dumper $ret;

Reply via email to