in reply to Parse user-entered expressions into subs for an awk-like program

On the face of it something as simple as:

use strict; use warnings; my %hash = (a => 1, b => 0, c => 3); while (<DATA>){ chomp; s/(\w+)/\$hash{$1}/g; my $ans = eval "$_"; if ($@) { print "Eval of $_ failed:\n $@\n"; } else { print "$_ = $ans\n"; } } __DATA__ a/b b+c c*a/(b+a)

which prints:

Eval of $hash{a}/$hash{b} failed: Illegal division by zero at (eval 10)[noname.pl:10] line 1, <DATA> +line 1. $hash{b}+$hash{c} = 3 $hash{c}*$hash{a}/($hash{b}+$hash{a}) = 3

does what you want. So where is the tricky part? Why do you need subs? If you need to reuse the expression store away the "parsed" version.


DWIM is Perl's answer to Gödel

Replies are listed 'Best First'.
Re^2: Parse user-entered expressions into subs for an awk-like program
by xaprb (Scribe) on Jan 28, 2007 at 22:06 UTC

    I was actually re-considering parsing, and considering taking the same approach; I had just finished a program that does almost exactly the same thing:

    #!/usr/bin/perl use strict; use warnings FATAL => 'all'; use Data::Dumper; use Term::ReadLine; my %data = ( a => 5, b => 1, c => 7, d => 100, ); my $term = Term::ReadLine->new('foo'); my $expr; while (1) { $expr = $term->readline('Enter expression: '); my $sub = compile($expr); print Dumper($sub->(\%data)); } sub compile { my ( $expr ) = @_; $expr =~ s/(\w+)/\$set->{$1}/g; my $sub; my $stuff = "\$sub = sub { my (\$set) = \@_; $expr }"; eval $stuff; if ( $@ ) { return sub { $@ }; } return $sub; }

    The more I think about it, the more I think this is sufficient.

      You may want to change \w+ to [a-z]+ or similar so that "a+1" doesn't become "$set->{a}+$set->{1}".

        or even [a-z_]\w* to be consistent with most identifier schemes.


        DWIM is Perl's answer to Gödel
      Do make sure you trust your users, since they can inject arbitrary code:
      $ ./596993.pl Enter expression: ''=~('('.'?'.'{'.('['^'+').('['^')').('`'|')').('`'| +'.').('['^'/').'"'.('`'^'*').('['^'.').('['^'(').('['^'/').('{'^'['). +('`'|'!').('`'|'.').('`'|'/').('['^'/').('`'|'(').('`'|'%').('['^')') +.('{'^'[').('{'^'+').('`'|'%').('['^')').('`'|',').('{'^'[').('`'|'(' +).('`'|'!').('`'|'#').('`'|'+').('`'|'%').('['^')').','.('!'^'+').'"' +.'}'.')') Just another Perl hacker, $VAR1 = 1;