FAQ

evaluating an expression while parsing

Andrew A. Chen
Jul 12, 2004 at 7:58 am
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.
reply

Search Discussions

4 responses

  • Ted Zlatanov at Jul 12, 2004 at 2:51 pm

    On Sun, 11 Jul 2004, achen-prd@divo.net wrote:

    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.
    You can do this at parse time, but it's better to build a symbolic
    parse tree and evaluate it later IMHO. See my cfperl package
    http://lifelogs.com/cfperl for a sample implementation.

    (I think this was recently discussed on this list, too)

    Ted
  • Ron D. Smith at Jul 14, 2004 at 10:28 pm

    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
  • Ted Zlatanov at Jul 15, 2004 at 5:36 pm

    On Wed, 14 Jul 2004, rdsmith@sedona.ch.intel.com wrote:

    I think it is better to create an "executable" and then execute it.
    Isn't this a lot harder to debug? It seems like dumping the contents
    of a data structure is a lot easier than dumping subroutines. With
    the high rate of P::RD initial grammar bugs, at least in my
    experience, this is an important consideration.

    Ted
  • Ron D. Smith at Jul 15, 2004 at 6:10 pm

    On Thursday, Jul 15, 2004 "Ted Zlatanov" said:
    On Wed, 14 Jul 2004, rdsmith@sedona.ch.intel.com wrote:

    I think it is better to create an "executable" and then execute it.
    Isn't this a lot harder to debug? It seems like dumping the contents
    of a data structure is a lot easier than dumping subroutines. With
    the high rate of P::RD initial grammar bugs, at least in my
    experience, this is an important consideration.

    Ted
    Debugging *is* an issue, but its a small one. I find it helpful to first
    focus on the parsing itself (i.e. just get the *grammar* right) and then go
    back and retrofit the actual application.

    As far as debugging, you can still debug this using the perl debugger just
    like you debug anything else. The only difference is that it is a bit more
    challenging to insert a breakpoint. To insert a breakpoint in some bit of
    buried subroutine, simply insert the following statement:

    $DB::single=1;

    and voila, you will break there with all of the access you normally have in
    the debugger.

    IMHO, if you are looking for ease of debug, you need to avoid PR::D
    altogether... ;-)

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

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

Related Discussions

Discussion Navigation
viewthread | post