Math::Calc::Parser - Parse and evaluate mathematical expressions
- SYNOPSIS
- DESCRIPTION
- FUNCTIONS
- ATTRIBUTES
- METHODS
- OPERATORS
- DEFAULT FUNCTIONS
- CAVEATS
- BIGNUM CAVEATS
- BUGS
- AUTHOR
- COPYRIGHT AND LICENSE
- SEE ALSO
SYNOPSIS
use Math::Calc::Parser 'calc';
use utf8; # for π in source code
my $result = calc '2 + 2'; # 4
my $result = calc 'int rand 5'; # Random integer between 0 and 4
my $result = calc 'sqrt -1'; # i
my $result = calc '0xff << 2'; # 1020
my $result = calc '1/0'; # Division by 0 exception
# Class methods
my $result = Math::Calc::Parser->evaluate('2 + 2'); # 4
my $result = Math::Calc::Parser->evaluate('3π^2'); # 29.608813203268
my $result = Math::Calc::Parser->evaluate('0.7(ln 4)'); # 0.970406052783923
# With more advanced error handling
my $result = Math::Calc::Parser->try_evaluate('rand(abs'); # undef (Mismatched parentheses)
if (defined $result) {
print "Result: $result\n";
} else {
print "Error: ".Math::Calc::Parser->error."\n";
}
# Or as an object for more control
my $parser = Math::Calc::Parser->new;
$parser->add_functions(triple => { args => 1, code => sub { $_[0]*3 } });
$parser->add_functions(pow => { args => 2, code => sub { $_[0] ** $_[1] });
$parser->add_functions(one => sub { 1 }, two => sub { 2 }, three => sub { 3 });
my $result = $parser->evaluate('2(triple one)'); # 2*(1*3) = 6
my $result = $parser->evaluate('pow(triple two, three)'); # (2*3)^3 = 216
my $result = $parser->try_evaluate('triple triple'); # undef (Malformed expression)
die $parser->error unless defined $result;
$parser->remove_functions('π', 'e');
$parser->evaluate('3π'); # Invalid function exception
# Arbitrary precision calculations - use only in a controlled environment
$parser->bignum(1);
my $result = $parser->evaluate('30!'); # 265252859812191058636308480000000
my $result = $parser->evaluate('atan pi'); # 1.262627255678911683444322083605698343509
# Rational number calculations - use only in a controlled environment
$parser->bigrat(1);
my $result = $parser->evaluate('3 / 9'); # 1/3
my $result = $parser->evaluate('3 >> 2'); # 3/4
DESCRIPTION
Math::Calc::Parser is a simplified mathematical expression evaluator with support for complex and trigonometric operations, implicit multiplication, and perlish "parentheses optional" functions, while being safe for arbitrary user input. It parses input strings into a structure based on Reverse Polish notation (RPN), and then evaluates the result. The list of recognized functions may be customized using "add_functions" and "remove_functions".
FUNCTIONS
calc
use Math::Calc::Parser 'calc';
my $result = calc '2+2';
$ perl -MMath::Calc::Parser=calc -E 'say calc "2+2"'
$ perl -Math -e '2+2'
Compact exportable function wrapping "evaluate" for string expressions. Throws an exception on error. See ath for easy compact one-liners.
ATTRIBUTES
These attributes can only be set on instantiated objects.
bignum
my $bool = $parser->bignum;
$parser = $parser->bignum($bool);
Enable support for arbitrary precision numbers using Math::BigInt and Math::BigFloat. This will avoid losing precision when working with floats or large integers, but see "BIGNUM CAVEATS".
bigrat
my $bool = $parser->bigrat;
$parser = $parser->bigrat($bool);
Enable support for precise rational numbers using Math::BigRat. This will avoid losing precision when working with integer divison and similar operations, and will result in output like 3/7
where possible, but see "BIGNUM CAVEATS".
METHODS
Aside from add_functions
and remove_functions
, all methods can be called as class methods, and will act on a singleton object with the default functions available.
new
my $parser = Math::Calc::Parser->new;
my $parser = Math::Calc::Parser->new(bignum => 1);
Creates a new Math::Calc::Parser object.
parse
my $parsed = Math::Calc::Parser->parse('5 / e^(i*pi)');
my $parsed = $parser->parse('3pi');
Parses a mathematical expression. On success, returns an array reference representation of the expression in RPN notation which can be passed to "evaluate". Throws an exception on failure.
evaluate
my $result = Math::Calc::Parser->evaluate($parsed);
my $result = Math::Calc::Parser->evaluate('log rand 7');
my $result = $parser->evaluate('round 13/3');
Evaluates a mathematical expression. The argument can be either an arrayref from "parse" or a string expression which will be passed to "parse". Returns the result of the expression on success or throws an exception on failure.
try_evaluate
if (defined (my $result = Math::Calc::Parser->try_evaluate('floor 2.5'))) {
print "Result: $result\n";
} else {
print "Error: ".Math::Calc::Parser->error."\n";
}
if (defined (my $result = $parser->try_evaluate('log(5'))) {
print "Result: $result\n";
} else {
print "Error: ".$parser->error."\n";
}
Same as "evaluate" but instead of throwing an exception on failure, returns undef. The "error" method can then be used to retrieve the error message. The error message for the most recent "try_evaluate" call can also be retrieved from the package variable $Math::Calc::Parser::ERROR
.
error
my $result = Math::Calc::Parser->try_evaluate('(i');
die Math::Calc::Parser->error unless defined $result;
my $result = $parser->try_evaluate('2//');
die $parser->error unless defined $result;
Returns the error message after a failed "try_evaluate".
add_functions
$parser->add_functions(
my_function => { args => 5, code => sub { return grep { $_ > 0 } @_; } },
other_function => sub { 20 },
bignum_function => { args => 1, code => sub { 2 ** $_[0] }, bignum_code => sub { Math::BigInt->new(2)->bpow($_[0]) } },
);
Adds functions to be recognized by the parser object. Keys are function names which must start with an alphabetic character and consist only of word characters. Values are either a hashref containing args
and code
keys, or a coderef that is assumed to be a 0-argument function. args
must be an integer greater than or equal to 0
. code
or the passed coderef will be called with the numeric operands passed as parameters, and must either return a numeric result or throw an exception. Non-numeric results will be cast to numbers in the usual perl fashion, and undefined results will throw an evaluation error.
Alternate implementations to be used when "bignum" or "bigrat" is enabled can be passed as bignum_code
and bigrat_code
respectively. bignum_code
will also be used for "bigrat" calculations if bigrat_code
is not separately defined; it is not common that these will need separate implementations.
remove_functions
$parser->remove_functions('rand','nonexistent');
Removes functions from the parser object if they exist. Can be used to remove default functions as well as functions previously added with "add_functions".
OPERATORS
Math::Calc::Parser recognizes the following operators with their usual mathematical definitions.
+, -, *, /, %, ^, !, <<, >>
Note: +
and -
can represent both binary addition/subtraction and unary negation.
DEFAULT FUNCTIONS
Math::Calc::Parser parses several functions by default, which can be customized using "add_functions" or "remove_functions" on an object instance.
- abs
-
Absolute value.
- acos
- asin
- atan
-
Inverse sine, cosine, and tangent.
- atan2
-
Two-argument inverse tangent of first argument divided by second argument.
- ceil
-
Round up to nearest integer.
- cos
-
Cosine.
- e
-
Euler's number.
- floor
-
Round down to nearest integer.
- i
-
Imaginary unit.
- int
-
Cast (truncate) to integer.
- ln
-
Natural log.
- log
-
Log base 10.
- logn
-
Log with arbitrary base given as second argument.
- pi
-
π
- π
-
π (this must be the decoded Unicode character)
- rand
-
Random value between 0 and 1 (exclusive of 1). Uses Math::Random::Secure if installed.
- round
-
Round to nearest integer, with halfway cases rounded away from zero. Due to bugs in Math::BigRat, precision may be lost with "bigrat" enabled.
- sin
-
Sine.
- sqrt
-
Square root.
- tan
-
Tangent.
CAVEATS
While parentheses are optional for functions with 0 or 1 argument, they are required when a comma is used to separate multiple arguments.
Due to the nature of handling complex numbers, the evaluated result may be a Math::Complex object. These objects can be directly printed or used in numeric operations but may be more difficult to use in comparisons.
Operators that are not defined to operate on complex numbers will return the result of the operation on the real components of their operands. This includes the operators <<
, >>
, %
, and !
.
BIGNUM CAVEATS
The Math::BigInt, Math::BigFloat, and Math::BigRat packages are useful for working with numbers without losing precision, and can be used by this module by setting the "bignum" or "bigrat" attributes, but care should be taken. They will perform significantly slower than native Perl numbers, and can result in an operation that does not terminate or one that uses up all your memory.
Additionally, similar to when using the bignum or bigrat pragmas, the auto-upgrading and downgrading behavior of these modules can only be set globally, so enabling these options will affect all other uses of these modules in your program. For the same reason, it is not recommended to enable both "bignum" and "bigrat" in the same program.
The evaluated result may be a Math::BigInt, Math::BigFloat, Math::BigRat, or other similar type of object. These objects can be printed and behave normally as numbers.
Math::BigFloat defaults to rounding values at 40 digits in division. This can be controlled by setting the global "ACCURACY AND PRECISION" in Math::BigFloat, but may have a large impact on performance and memory usage.
Complex math is incompatible with "bignum" and "bigrat" and will likely result in NaN.
BUGS
Report any issues on the public bugtracker.
AUTHOR
Dan Book, [email protected]
COPYRIGHT AND LICENSE
Copyright 2015, Dan Book.
This library is free software; you may redistribute it and/or modify it under the terms of the Artistic License version 2.0.