README.md in dentaku-0.2.14 vs README.md in dentaku-1.0.0
- old
+ new
@@ -3,12 +3,10 @@
[](http://badge.fury.io/rb/dentaku)
[](https://travis-ci.org/rubysolo/dentaku)
[](https://codeclimate.com/github/rubysolo/dentaku)
-http://github.com/rubysolo/dentaku
-
DESCRIPTION
-----------
Dentaku is a parser and evaluator for a mathematical and logical formula
language that allows run-time binding of values to variables referenced in the
@@ -17,100 +15,155 @@
EXAMPLE
-------
This is probably simplest to illustrate in code:
- calculator = Dentaku::Calculator.new
- calculator.evaluate('10 * 2')
- => 20
+```ruby
+calculator = Dentaku::Calculator.new
+calculator.evaluate('10 * 2')
+=> 20
+```
Okay, not terribly exciting. But what if you want to have a reference to a
variable, and evaluate it at run-time? Here's how that would look:
- calculator.evaluate('kiwi + 5', :kiwi => 2)
- => 7
+```ruby
+calculator.evaluate('kiwi + 5', kiwi: 2)
+=> 7
+```
You can also store the variable values in the calculator's memory and then
evaluate expressions against those stored values:
- calculator.store(:peaches => 15)
- calculator.evaluate('peaches - 5')
- => 10
- calculator.evaluate('peaches >= 15')
- => true
+```ruby
+calculator.store(peaches: 15)
+calculator.evaluate('peaches - 5')
+=> 10
+calculator.evaluate('peaches >= 15')
+=> true
+```
For maximum CS geekery, `bind` is an alias of `store`.
Dentaku understands precedence order and using parentheses to group expressions
to ensure proper evaluation:
- calculator.evaluate('5 + 3 * 2')
- => 11
- calculator.evaluate('(5 + 3) * 2')
- => 16
+```ruby
+calculator.evaluate('5 + 3 * 2')
+=> 11
+calculator.evaluate('(5 + 3) * 2')
+=> 16
+```
A number of functions are also supported. Okay, the number is currently five,
but more will be added soon. The current functions are
`if`, `not`, `round`, `rounddown`, and `roundup`, and they work like their counterparts in Excel:
- calculator.evaluate('if (pears < 10, 10, 20)', :pears => 5)
- => 10
- calculator.evaluate('if (pears < 10, 10, 20)', :pears => 15)
- => 20
+```ruby
+calculator.evaluate('if (pears < 10, 10, 20)', pears: 5)
+=> 10
+calculator.evaluate('if (pears < 10, 10, 20)', pears: 15)
+=> 20
+```
`round`, `rounddown`, and `roundup` can be called with or without the number of decimal places:
- calculator.evaluate('round(8.2)')
- => 8
- calculator.evaluate('round(8.2759, 2)')
- => 8.28
+```ruby
+calculator.evaluate('round(8.2)')
+=> 8
+calculator.evaluate('round(8.2759, 2)')
+=> 8.28
+```
`round` and `rounddown` round down, while `roundup` rounds up.
If you're too lazy to be building calculator objects, there's a shortcut just
for you:
- Dentaku('plums * 1.5', {:plums => 2})
- => 3.0
+```ruby
+Dentaku('plums * 1.5', plums: 2)
+=> 3.0
+```
BUILT-IN OPERATORS AND FUNCTIONS
---------------------------------
Math: `+ - * / %`
+
Logic: `< > <= >= <> != = AND OR`
+
Functions: `IF NOT ROUND ROUNDDOWN ROUNDUP`
EXTERNAL FUNCTIONS
------------------
-See `spec/external_function_spec.rb` for examples of how to add your own functions.
+I don't know everything, so I might not have implemented all the functions you
+need. Please implement your favorites and send a pull request! Okay, so maybe
+that's not feasible because:
-The short, dense version:
+1. You can't be bothered to share
+2. You can't wait for me to respond to a pull request, you need it `NOW()`
+3. The formula is the secret sauce for your startup
-Each rule for an external function consists of three parts: the function's name,
-a list of tokens describing its signature (parameters), and a lambda representing the
-function's body.
+Whatever your reasons, Dentaku supports adding functions at runtime. To add a
+function, you'll need to specify:
-The function name should be passed as a symbol (for example, `:func`).
+* Name
+* Return type
+* Signature
+* Body
-The token list should consist of `:numeric` or `:string` if a single value of the named
-type should be passed; `:non_group` or `:non_group_star` for grouped expressions.
+Naming can be the hardest part, so you're on your own for that.
-> (what's the difference? when would you use one instead of the other?)
+`:type` specifies the type of value that will be returned, most likely
+`:numeric`, `:string`, or `:logical`.
-The function body should accept a list of parameters. Each function body will be passed
-a sequence of tokens, in order:
+`:signature` specifies the types and order of the parameters for your function.
-1. The function's name
-2. A token representing the opening parenthesis
-3. Tokens representing the parameter values, separated by tokens representing the commas between parameters
-4. A token representing the closing parenthesis
+`:body` is a lambda that implements your function. It is passed the arguments
+and should return the calculated value.
-It should return a token (either `:numeric` or `:string`) representing the return value.
+As an example, the exponentiation function takes two parameters, the mantissa
+and the exponent, so the token list could be defined as: `[:numeric,
+:numeric]`. Other functions might be variadic -- consider `max`, a function
+that takes any number of numeric inputs and returns the largest one. Its token
+list could be defined as: `[:non_close_plus]` (one or more tokens that are not
+closing parentheses.
-Rules can be set individually using Calculator#add_rule, or en masse using Calculator#add_rules.
+Functions can be added individually using Calculator#add_function, or en masse using
+Calculator#add_functions.
+
+Here's an example of adding the `exp` function:
+
+```ruby
+> c = Dentaku::Calculator.new
+> c.add_function(
+ name: :exp,
+ type: :numeric,
+ signature: [:numeric, :numeric],
+ body: ->(mantissa, exponent) { mantissa ** exponent }
+ )
+> c.evaluate('EXP(3,2)')
+=> 9
+> c.evaluate('EXP(2,3)')
+=> 8
+```
+
+Here's an example of adding the `max` function:
+
+```ruby
+> c = Dentaku::Calculator.new
+> c.add_function(
+ name: :max,
+ type: :numeric,
+ signature: [:non_close_plus],
+ body: ->(*args) { args.max }
+ )
+> c.evaluate 'MAX(5,3,9,6,2)'
+=> 9
+```
THANKS
------