src/mathexpr

Source   Edit  

This library is a relatively small (<500 cloc) mathematical expression evaluator library written in Nim.

The implementation is a simple recursive-descent evaluator.

It only depends on the stdlib (mostly on the math module), and works on all official Nim backends including JavaScript and in the VM

There's a lot of predefined math functions and some constants, and of course you can define custom ones.

Most of the library usage can be shown in this code example:

import mathexpr
# Create a new evaluator instance
# All custom variables and math functions are bound to this evaluator
# so you could have different evaluators with different vars/functions
let e = newEvaluator()

echo e.eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2") # 7.5
# Add some variables to our Evaluator object
e.addVars({"a": 5.0})
echo e.eval("+5^+3+1.1 + a") # 131.1
# Variables with the same name overwrite the old ones
e.addVars({"a": 1.0, "b": 2.0})
echo e.eval("a + b") # 3

# Define our custom function which returns
# 25 multiplied by all arguments it got
proc myFunc(args: seq[float]): float =
  result = 25
  for arg in args:
    result *= arg

e.addFunc("work", myFunc)
echo e.eval("work(1, 2, 3) + 5") # 25*1*2*3 + 5 = 155

# Define a custom function which only accepts two arguments
proc test(a: seq[float]): float =
  a[0] + a[1]

e.addFunc("test", test, 2)
echo e.eval("test(1, 5)") # 6

# In some places parentheses and commas are optional:
echo e.eval("work(1 2 3) + 5") # 155
echo e.eval("sqrt 100 + 5") # 15

eval can return NaN or Inf for some inputs, such as 0/0, or 1/0, see src/tests.nim for more info

What is supported?

Supported operators include +, -, /, *, %, ^, <, >, <=, >=, ==, !=

Implemented mathematical functions:

  • abs(x) - the absolute value of x
  • acos(x) or arccos(x) - the arccosine (in radians) of x
  • asin(x) or arcsin(x) - the arcsine (in radians) of x
  • atan(x) or arctan(x) or arctg(x) - the arctangent (in radians) of x
  • atan2(x, y) or arctan2(x, y) - the arctangent of the quotient from provided x and y
  • ceil(x) - the smallest integer greater than or equal to x
  • cos(x) - the cosine of x
  • cosh(x) - the hyperbolic cosine of x
  • deg(x) - converts x in radians to degrees
  • exp(x) - the exponential function of x
  • sgn(x) - the sign of x
  • sqrt(x) - the square root of x
  • sum(x, y, z, ...) - sum of all passed arguments
  • fac(x) - the factorial of x
  • floor(x) - the largest integer not greater than x
  • ln(x) - the natural log of x
  • log(x) or log10(x) - the common logarithm (base 10) of x
  • log2(x) - the binary logarithm (base 2) of x
  • max(x, y, z, ...) - biggest argument from any number of arguments
  • min(x, y, z, ...) - smallest argument from any number of arguments
  • ncr(x, y) or binom(x, y) - the the number of ways a sample of y elements can be obtained from a larger set of x distinguishable objects where order does not matter and repetitions are not allowed
  • npr(x, y) - the number of ways of obtaining an ordered subset of y elements from a set of x elements
  • rad(x) - converts x in degrees to radians
  • pow(x, y) - the x to the y power
  • sin(x) - the sine of x
  • sinh(x) - the hyperbolic sine of x
  • tg(x) or tan(x) - the tangent of x
  • tanh(x) - the hyperbolic tangent of x

Predefined constants

  • pi - The circle constant (Ludolph's number)
  • tau - The circle constant, equals to 2 * pi
  • e - Euler's number

Operators in more detail

Following table shows order of evaluation of various operators, lower the precedence - sooner it shall be evaluated.

PrecedenceOperator
1Exponentiation ^
2Unary + and -
3Multiplication/division *, /, %
4Addition/substraction +, -
5Less than/Greater than comparison <, <=, >, >=
6Exact comparison ==, !=

Notes on comparison:

  • Comparison operators return a float value, 0.0 for false and 1.0 for true
  • Every number in mathexpr is a float. Comparing floats exactly is a bit problematic due to precision, so difference less than 0.0001 makes 2 numbers equal by mathexpr's standards.

Types

Evaluator = ref object
  
Main instance of the math evaluator Source   Edit  
MathFunction = proc (args: seq[float]): float
Type of the procedure definition for custom math functions Source   Edit  

Procs

proc addFunc(e: Evaluator; name: string; fun: MathFunction; argCount = -1) {.
    ...raises: [], tags: [], forbids: [].}

Adds custom function fun with the name name to the evaluator e which will then be available inside of all following e.eval() calls.

argCount specifies the number of arguments this function is allowed to be called with. If it is -1, the function will be able to be called with one or more arguments, otherwise - only with argCount arguments.

Source   Edit  
proc addVar(e: Evaluator; name: string; val: float) {....raises: [], tags: [],
    forbids: [].}
Adds a constant with the name name to the evaluator e Source   Edit  
proc addVars(e: Evaluator; vars: openArray[(string, float)]) {....raises: [],
    tags: [], forbids: [].}
Adds all constants from the vars openArray to the evaluator e

Example:

let e = newEvaluator()
e.addVars({"a": 3.0, "b": 5.5})
assert e.eval("a ^ a + b") == 32.5
Source   Edit  
proc eval(e: Evaluator; input: string): float {....raises: [ValueError, Exception],
    tags: [RootEffect], forbids: [].}

Evaluates a math expression from input and returns result as float

Can raise an exception if input is invalid or an overflow occured

Source   Edit  
proc newEvaluator(): Evaluator {....raises: [], tags: [], forbids: [].}

Creates a new evaluator instance for evaluating math expressions

There's no limit on the number of evaluator instances, and all functions and math procedures are local to the current instance

Source   Edit  
proc removeFunc(e: Evaluator; name: string) {....raises: [], tags: [], forbids: [].}
Removes function with the name name from the evaluator e Source   Edit  
proc removeVar(e: Evaluator; name: string) {....raises: [], tags: [], forbids: [].}
Removes the specified constant with the name name from the evaluator e Source   Edit