Keisan
Keisan (計算, to calculate) is a Ruby library for parsing equations into an abstract syntax tree. This allows for safe evaluation of string representations of mathematical/logical expressions.
Installation
Add this line to your application's Gemfile:
gem 'keisan'
And then execute:
$ bundle
Or install it yourself as:
$ gem install keisan
Usage
Calculator class
The functionality of keisan
can be demonstrated by using the Keisan::Calculator
class. The evaluate
method evaluates an expression by parsing it into an abstract syntax tree (AST), then evaluating any member functions/variables given.
calculator = Keisan::Calculator.new
calculator.evaluate("15 + 2 * (1 + 3)")
#=> 23
Specifying variables
Passing in a hash of variable (name
, value
) pairs to the evaluate
method defines variables
calculator.evaluate("3*x + y**2", x: -2.5, y: 3)
#=> 1.5
It will raise an error if an variable is not defined
calculator.evaluate("x + 1")
#=> Keisan::Exceptions::UndefinedVariableError: x
Specifying functions
Just like variables, functions can be defined by passing a Proc
object as follows
calculator.evaluate("2*f(1+2) + 4", f: Proc.new {|x| x**2})
#=> 22
It will raise an error if a function is not defined
calculator.evaluate("f(2) + 1")
#=> Keisan::Exceptions::UndefinedFunctionError: f
Lists
Just like in Ruby, lists can be defined using square brackets, and indexed using square brackets
calculator.evaluate("[2, 3, 5, 8]")
#=> [2, 3, 5, 8]
calculator.evaluate("[[1,2,3],[4,5,6],[7,8,9]][1][2]")
#=> 6
They can also be concatenated using the +
operator
calculator.evaluate("[3, 5] + [x, x+1]", x: 10)
#=> [3, 5, 10, 11]
Logical operations
keisan
understands basic boolean logic operators, like <
, <=
, >
, >=
, &&
, ||
, !
, so calculations like the following are possible
calculator.evaluate("1 > 0")
#=> true
calculator.evaluate("!!!true")
#=> false
calculator.evaluate("x >= 0 && x < 10", x: 5)
#=> true
There is also a useful ternary if
function defined
calculator.evaluate("2 + if(1 > 0, 10, 29)")
#=> 12
Bitwise operations
The basic bitwise operations, NOT ~
, OR |
, XOR ^
, and AND &
are also available for use
calculator.evaluate("2 + 12 & 7")
#=> 6
String
keisan
also can parse in strings, and access the characters by index
calculator.evaluate("'hello'[1]")
#=> "e"
Binary, octal, and hexadecimal numbers
Using the prefixes 0b
, 0o
, and 0x
(standard in Ruby) indicates binary, octal, and hexadecimal numbers respectively.
calculator.evaluate("0b1100")
#=> 12
calculator.evaluate("0o775")
#=> 504
calculator.evaluate("0x1f0")
#=> 496
Random numbers
keisan
has a couple methods for doing random operations, rand
and sample
. For example,
calculator.evaluate("rand(10)")
#=> 3
calculator.evaluate("sample([2, 4, 6, 8])")
#=> 8
If you want reproducibility, you can pass in your own Random
object to the calculator's context.
calculator1 = Keisan::Calculator.new(Keisan::Context.new(random: Random.new(1234)))
calculator2 = Keisan::Calculator.new(Keisan::Context.new(random: Random.new(1234)))
5.times.map {calculator1.evaluate("rand(1000)")}
#=> [815, 723, 294, 53, 204]
5.times.map {calculator2.evaluate("rand(1000)")}
#=> [815, 723, 294, 53, 204]
Builtin variables and functions
keisan
includes all standard methods given by the Ruby Math
class.
calculator.evaluate("log10(1000)")
#=> 3.0
Furthermore, the following builtin constants are defined
calculator.evaluate("pi")
#=> 3.141592653589793
calculator.evaluate("e")
#=> 2.718281828459045
calculator.evaluate("i")
#=> (0+1i)
This allows for simple calculations like
calculator.evaluate("e**(i*pi)+1")
=> (0.0+0.0i)
Adding custom variables and functions
The Keisan::Calculator
class has a single Keisan::Context
object in its context
attribute. This class is used to store local variables and functions. As an example of pre-defining some variables and functions, see the following
calculator.define_variable!("x", 5)
#=> 5
calculator.evaluate("x + 1")
#=> 6
calculator.evaluate("x + 1", x: 10)
#=> 11
calculator.evaluate("x + 1")
#=> 6
Notice how when passing variable values directly to the evaluate
method, it only shadows the value of 5 for that specific calculation. The same thing works for functions
calculator.define_function!("f", Proc.new {|x| 3*x})
#=> #<Keisan::Function:0x005570f935ecc8 @function_proc=#<Proc:0x005570f935ecf0@(pry):6>, @name="f">
calculator.evaluate("f(2)")
#=> 6
calculator.evaluate("f(2)", f: Proc.new {|x| 10*x})
#=> 20
calculator.evaluate("f(2)")
#=> 6
Development
After checking out the repository, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/project-eutopia/keisan. If there is any functionality you would like (e.g. new functions), feel free to open a new issue.