I am parsing cfengine class definitions, which have a pretty simple syntax:
a.(b|!c) means a and ( b or (not c)) The logical true/false value of a class is defined by whether it exists as a key in the global hash %classes. I basically should get a boolean back from eval_tree. I wanted, however, that P::RD do the evaluation of the expressions for me as well. I tried to do in the actions, e.g. "or" would do { $item[1] || $item[3] } but that would not work as the parser would give up in disgust. The actions are too tightly integrated with the parsing to be reused for evaluating, it seems. Maybe I'm not clear on the documentation. I ended up doing the following, which can be seen as either moderately clever or horribly convolved, depending on your frame of mind and the number of hours you have gone without sleep trying to finish it. I used one of the demos to get me started. The demos and previous P:RD discussions were not enlightening, so I'm wondering if anyone has better ways to do this sort of self-evaluation. I must be missing something fairly obvious. I have seen Damian's code in http:[EMAIL PROTECTED]/msg00029.html but that separates the grammar from the evaluation engine. I prefer to have them integrated, as the example below shows. Jeffrey Franks also mentioned he might try to use the Class::Visitor module, but after looking at that module I think the effort of adapting to its API is hardly worthwhile. Thanks Ted ------------------------------------------------------ # local scope for the autoaction, { local $::RD_AUTOACTION = q{ [ sub{ $_[1]; }, $item[1] ] }; $parser = new Parse::RecDescent(q{ input : or or : and /\|+/ or { [ sub{ $_[1] || $_[2] }, $item{and}, $item{or} ] } | and and : unary /\.+/ and { [ sub{ $_[1] && $_[2] }, $item{unary}, $item{and} ] } | unary unary : not | '(' input ')' { [ sub{ $_[1] }, $item{input} ] } | atom not : '!'/\w+/ { [ sub{ my $classes = shift; my $atom = shift; # print "not_atom($atom) "; return !exists $classes->{$atom}; }, $item[2] ] } atom : /\w+/ { [ sub{ my $classes = shift; my $atom = shift; # print "atom($atom) "; return exists $classes->{$atom}; }, $item[1] ] } }); } # {{{ eval_tree: evaluate nested parse tree sub eval_tree { my $branch = shift @_; my $depth = shift @_ || 0; return undef unless defined $branch; my $code = shift @$branch; return $code unless scalar @$branch; foreach my $key (@$branch) { if (ref $key eq 'ARRAY') { $key = eval_tree($key, $depth+1); } } return $code->(\%classes, @$branch); } # }}} # example usage print eval_tree($parser->input("a.b|(c)"));