Skip to content

Instantly share code, notes, and snippets.

@perlancar
Created June 15, 2016 04:30
Show Gist options
  • Save perlancar/9ff816448e6e9d4bfd1573a26388c0b6 to your computer and use it in GitHub Desktop.
Save perlancar/9ff816448e6e9d4bfd1573a26388c0b6 to your computer and use it in GitHub Desktop.
#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;
my ($opr, %res);
sub parse {
state $RE =
qr{
(?&TOP)
(?{
$res{top} = $^R;
})
(?(DEFINE)
(?<TOP>
^\s* (?&EXPR) \s*$
)
(?<EXPR>
((?&MULT_EXPR))
(?{
$res{add} = $^R
})
(?: \s* ([+-])
(?{
$opr = $^N;
})
\s* ((?&MULT_EXPR))
(?{
$res{add} = $opr eq '+' ? $res{add} + $^R : $res{add} - $^R;
})
)*
)
(?<MULT_EXPR>
((?&POW_EXPR))
(?{
$res{mult} = $^R;
})
(?: \s* ([*/])
(?{
$opr = $^N;
}) \s*
((?&POW_EXPR))
(?{
$res{mult} = $opr eq '*' ? $res{mult} * $^R : $res{mult} / $^R;
})
)*
)
(?<POW_EXPR>
((?&TERM))
(?{
$res{pow} = [$^R];
})
(?: \s* \*\* \s* ((?&TERM))
(?{
unshift @{$res{pow}}, $^R;
})
)*
(?{
# because ** is right-to-left, we collect first then
# apply from right to left
my $res = $res{pow}[0];
for (1..$#{$res{pow}}) {
$res = $res{pow}[$_] ** $res;
}
$res;
})
)
(?<TERM>
\( \s* (?&EXPR)
(?{
$^R;
})
\s* \)
| (?&LITERAL)
(?{
$^R;
})
)
(?<LITERAL>
(-?(?:\d+|\d*\.\d+))
(?{
$^N;
})
)
)
}x;
%res = ();
$_[0] =~ $RE or return undef;
$res{top};
}
for (@ARGV) {
say "$_: ", parse($_);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment