On Sunday, Jul 11, 2004 "Andrew A. Chen" said:

> Hello-
> I'm just starting out with PRD here, but I think I'm on the right track. 
> I'm attempting to write a parser that evaluates a language similar to 
> SQL's WHERE syntax.  I'd also like to evaluate the result of this boolean 
> expression with some given input data.  The language is a reduced version 
> of SQL's WHERE syntax.  Here are some valid expressions (and they function 
> as you would imagine):  nb: `=' is comparison, not assignment.
> 
> (has_access = 1 && is_disabled = 0) || has_override = 1
> has_override = 1
> likes_pie = 1 && likes_apples = 1
> (num_oranges > 5 && num_apples > 5) || (num_grapes < 2 && num_berries < 3)
> 
> The item to the left of the operand is always the variable and the item to 
> the right is always some integer or floating point value.  Here's the 
> grammar I've come up with:
> 
> --snip--
> start       : coupon(s /(?:&&|\|\|)/) /^\Z/
> 
> coupon      : expression
>              | '(' expression ')'
> expression  : statement boolean statement
>              | statement
> statement   : variable comparison number
> 
> boolean     : /(?:&&|\|\|)/
> comparison  : /(?:=|!=|>|>=|<|<=)/
> number      : /[-+]?\d+(\.\d+)?/
> 
> variable    : /[a-z_]+/i
>                | '.'
> --snip--
> 
> It appears to properly parse things, so I'm pretty happy about that.  The 
> question here is that I'd like to be able to evaluate the above statements 
> by passing in some values for num_oranges, num_apples, has_override, etc. 
> Idealy, I'd like to be able to give it a hashref with all of these 
> variables and then have PRD spit out true or false for the final result. 
> I was thinking about using the $arg facility and calling it as such:
>     my $result = $parser->start($expression, 1, $parameters);
> but passing $arg around seems to be a fairly messy way to do this.  In 
> fact, do I even want to be attempting to evaluate this from within PRD, or 
> should I be building a parsetree and doing it separately?  Some guidance 
> would be appreciated.  Thanks.

I think it is better to create an "executable" and then execute it.  I have 
done something like this and an executable was my approach.  One problem you 
may or may not run into is that depending on your syntax, things may or may 
not have side effects, and preventing those side effects can get tricky.  
Using a run-time environment avoids this.

Try the following:

use strict;
use Data::Dumper;
use Parse::RecDescent;
use Date::Parse;
$RD_HINT = 1;
$RD_TRACE = 1;
my $parser=Parse::RecDescent->new(q(
start       : expression(s) /^\Z/
{
    $return=bless([$item[1],{}],'QWERT');
}
expression  : statement boolean statement
{
    $return=sub{eval $item[1]->(@_).$item[2].$item[3]->(@_)?'1':'0'}
}
             | statement
{
$return=sub {$item[1]->(@_)};
}
statement   : variable comparison number
{
    $return=sub{eval $item[1]->(@_).$item[2].$item[3]->(@_)?'1':'0'}
}
| '(' expression ')'
{
$return=sub {$item[2]->(@_)};
}

boolean     : /(?:&&|\|\|)/
{
    $return=$item[1];
}
comparison  : /(?:=|!=|>|>=|<|<=)/
{
    $return=$item[1] eq '='?'==':$item[1];
}
number      : /[-+]?\d+(\.\d+)?/ 
{
$return=sub {$item[1]};
}

variable    : /[a-z_]+/i
{
$return=sub {$_[0]->get($item[1])}
}

                                    ));
my $exe=$parser->start('(has_access = 1 && is_disabled = 0) || has_override = 
1');
sub QWERT::get {
    my $self=shift;
    my $name=shift;
    $self->[1]{$name};
}
sub QWERT::set {
    my $self=shift;
    my $name=shift;
    $self->[1]{$name}=shift;
}
sub QWERT::execute_exp {
    my $self=shift;
    $self->[0][0]->($self)   

}
$exe->set('is_disabled',0);
    $exe->set('has_access',0);
    $exe->set('has_override',1);
    print "return is: '" ,$exe->execute_exp,"'\n";
$exe->set('is_disabled',1);
    $exe->set('has_access',0);
    $exe->set('has_override',0);
    print "return is: '" ,$exe->execute_exp,"'\n";
$exe->set('is_disabled',1);
    $exe->set('has_access',1);
    $exe->set('has_override',0);
    print "return is: '" ,$exe->execute_exp,"'\n";
$exe->set('is_disabled',0);
    $exe->set('has_access',1);
    $exe->set('has_override',0);
    print "return is: '" ,$exe->execute_exp,"'\n";
1;
__END__

--
 Intel, Corp.
 5000 W. Chandler Blvd.
 Chandler, AZ 85226

-- 
 Intel, Corp.
 5000 W. Chandler Blvd.
 Chandler, AZ  85226


Reply via email to