perl_learner_2016 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Dear All,

Happy New Year!

I am trying to evaluate a logical expression and find its value

My code looks like below:

#!/usr/bin/perl use strict; use warnings; my $when = '(!(C)&T)&!Q'; my %pinValsH = (); my @pinsA = qw (C T Q); foreach my $pin (@pinsA) { $pinValsH{$pin} = 0b1; } &evalLogicExpr($when, \%pinValsH); sub evalLogicExpr { #my $self = shift; my $whenCond = shift; my $pinValsHRef= shift; my $newExpr = $when; $newExpr =~ s/\&+/\&\&/g; $newExpr =~ s/\|+/\|\|/g; foreach my $pin (keys %{$pinValsHRef}) { printf "%s %s %b\n", "NAME ", $pin,$pinValsHRef->{$pin}; if($newExpr =~ /(.*)($pin)(.*)$/) { $newExpr =~ s/(.*)($pin)(.*)$/$1$pinValsHRef->{$pin}$3/g; } } print "EXPR = ",$newExpr,"\n"; printf "%b\n", $newExpr; }

I saved the above code into a file called  nBool.pl When I run it, I get the following:

NAME Q 1 NAME T 1 NAME C 1 EXPR = (!(1)&&1)&&!1 Argument "(!(1)&&1)&&!1" isn't numeric in printf at ./nBool.pl line 26 +. 0

Please tell me what am I doing wrong.

Thanks

Andy

Replies are listed 'Best First'.
Re: Logic expression evaluation not working. What am I doing wrong?
by Cristoforo (Curate) on Jan 04, 2016 at 02:20 UTC
    Add an eval to that printf, printf "%b\n", eval $newExpr;.

    As AnonymousMonk says, care should be taken when using 'eval'. You should check as he does that only valid characters are allowed, especially if eval'ing a string from an unknown source. The unknown string could have malicious system commands, for example, to wipe out your hard drive.

Re: Logic expression evaluation not working. What am I doing wrong?
by GrandFather (Saint) on Jan 04, 2016 at 07:06 UTC

    An interesting way to do it is to use a full blown parser. Marpa is a relatively easy way to get the job done:

    use strict; use warnings; use Marpa::R2; package DoStuff; my %vars; sub doAnd { my @params = @_; return $params[1] && $params[3]; } sub doOr { my @params = @_; return $params[1] || $params[3]; } sub doVarValue { my @params = @_; return $vars{$params[1]}; } sub doAssign { my @params = @_; $vars{$params[1]} = $params[3]; return; } sub doVal { my @params = @_; my $val = 'ARRAY' eq ref $params[1] ? $params[1][1] : $params[1]; return $val; } sub doNVal { my @params = @_; my $val = 'ARRAY' eq ref $params[2] ? $params[2][1] : $params[2]; return $val ? 0 : 1; } sub doResult { my @params = @_; return $params[-1]; } package main; my $syntax = <<'SYNTAX'; lexeme default = latm => 1 test ::= assignments expression action => doResult | expression action => doResult assignments ::= assignment assignments | assignment assignment ::= var '=' constant action => doAssign expression ::= expression '|' term action => doOr | term action => ::f +irst term ::= term '&' factor action => doAnd | factor action => ::first factor ::= negatable action => doVal | '!' negatable action => doNVal negatable ::= '(' expression ')' action => [values] | value action => +::first value ::= var action => doVarValue | constant action => ::first var ~ [A-Z] constant ~ [10] :discard ~ spaces spaces ~ [\s]+ SYNTAX my $grammar = Marpa::R2::Scanless::G->new({source => \$syntax}); for my $inputs ([0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1]) { my $input = <<INPUT; C = $inputs->[0] Q = $inputs->[1] T = $inputs->[2] (!(C)&T)&!Q INPUT my $result = $grammar->parse(\$input, 'DoStuff'); printf "${input}Result: %d\n\n", $$result; }

    Prints:

    C = 0 Q = 0 T = 0 (!(C)&T)&!Q Result: 0 C = 0 Q = 0 T = 1 (!(C)&T)&!Q Result: 1 C = 0 Q = 1 T = 1 (!(C)&T)&!Q Result: 0 C = 1 Q = 0 T = 1 (!(C)&T)&!Q Result: 0
    Premature optimization is the root of all job security

      my %vars; ...

      Is that closure the only option?

        It's a package variable, not a closure. In a sense it's a global variable, but notionally private to the package. There are other ways it could be done, but for a "simple" example it is expedient and not totally terrible.

        Premature optimization is the root of all job security
Re: Logic expression evaluation not working. What am I doing wrong?
by AnomalousMonk (Archbishop) on Jan 04, 2016 at 04:27 UTC

    Building on AnonyMonk's approach above (and this actually works):

    c:\@Work\Perl>perl -wMstrict -MData::Dump -le "use constant OPS => qw{ ( ( ) ) ! ! | || & && }; ;; sub compile { my ($expr, $hr_vars) = @_; ;; my %xlate = (OPS, %$hr_vars); ;; $expr =~ s{ (.) } { exists $xlate{$1} or die qq{bad: '$1' at offset $-[1] in '$expr'}; $xlate{$1}; }xmsge; ;; return $expr; } ;; my %vars = qw(Q 0 T 1 C 0); dd \%vars; ;; my $expr = '(!(C)&T)&!Q'; print qq{initial expr: '$expr'}; ;; my $final_expr = compile($expr, \%vars); print qq{ final expr: '$final_expr'}; ;; my $result = eval $final_expr; if ($@) { print qq{eval of '$final_expr' failed: $@} } else { print qq{'$final_expr' -> '$result'}; } " { C => 0, Q => 0, T => 1 } initial expr: '(!(C)&T)&!Q' final expr: '(!(0)&&1)&&!0' '(!(0)&&1)&&!0' -> '1'
    Some thoughts:
    • Caution: Because it uses string eval, this code is not guaranteed to be safe, whatever your definition of "safe" may be. Caveat programmor.
    • Play with this. E.g., are the outputs of  '(x!(C)&T)&!Q' or  '(!(C(&T)&!Q' acceptable?
    • The expression  !1 evaluates to  '' (the empty string), which can propagate through subsequent logical expressions to produce an empty string as output, so after all you may want the bitwise  | & operators, which can produce  0 1 outputs.
    • This code works under ActiveState 5.8.9 and should work, but has not been tested, under any higher Perl version.


    Give a man a fish:  <%-{-{-{-<


      "The expression !1 evaluates to ''" I tried the following:
      #!/usr/bin/perl use strict; use warnings; printf "%b\n", !1;
      which output the following:
      0
      on my Linux machine. So, not sure under what circumstances, like you say that it outputs '' empty string.
      Can you elaborate a bit for novices like myself please?
      TIA.

        I was thinking of the following contexts | examples:

        c:\@Work\Perl>perl -wMstrict -le "print '>', !1, '<';" >< c:\@Work\Perl>perl -wMstrict -le "print '>', !1 && 1, '<';" ><
        In numeric contexts such as that supplied by the  %b printf format specifier, !1 will, I think, always evaluate to numeric 0 — but it would be best to do some research on this!


        Give a man a fish:  <%-{-{-{-<

Re: Logic expression evaluation not working. What am I doing wrong?
by Anonymous Monk on Jan 04, 2016 at 02:29 UTC
    safety first
    my %allowed = qw{ ( ( ) ) ! !! & && }; my $finalExpr = map { $allowed{$_} || die "not allowed '$_'" } split ' +', $expr; my $result = eval $finalExpr;
      #from Anonymous Monk code > my %allowed = qw{ ( ( ) ) ! !! & && }; > my $finalExpr = map { $allowed{$_} || die "not allowed '$_'" } split + '', $expr;

      The %allowed hash doesn't look right and it isn't, among other things because it forgets digits [0-9]. A solution along these lines might do something like using taint mode for the script and then:

      print "EXPR = ",$newExpr,"\n"; # don't know how to execute shell commands without [A-Za-z] letters $newExpr =~ /^([\d\s()&|!]+)$/; printf "%b\n", eval $1; }
      Ron
        The %allowed hash doesn't look right ...

        I would be more concerned about the map statement:

        c:\@Work\Perl>perl -wMstrict -le "my $expr = '(!(C)&T)&!Q'; my %allowed = qw{ ( ( ) ) ! !! & && C 1 T 1 Q 1 }; my $finalExpr = map { $allowed{$_} || die qq{not allowed '$_'} } spli +t '', $expr; print qq{'$finalExpr'}; " '11'


        Give a man a fish:  <%-{-{-{-<

        Why would boolean logic need more than 1 and 0?

        If using %allowed all the values are already untainted