dentaku 1.2.5 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.pryrc +2 -0
- data/README.md +2 -1
- data/dentaku.gemspec +1 -0
- data/lib/dentaku/bulk_expression_solver.rb +75 -0
- data/lib/dentaku/calculator.rb +5 -17
- data/lib/dentaku/version.rb +1 -1
- data/spec/bulk_expression_solver_spec.rb +51 -0
- data/spec/calculator_spec.rb +19 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3007daa3af2e8cf5e738f4d98d046f23422004e3
|
4
|
+
data.tar.gz: 7994b1e07961a589580275cfb14978a9fb6ab091
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5287d0c47550c976e373a1b291526e79da3fae5aa4e78ebd6e479e3ee467a701f577b45d9acacf9949466186f429f6b9d0ec211917a776e0e29230bba6075d7
|
7
|
+
data.tar.gz: f7ac853d45ed2a50b3b5d0b4ee8cab110db7adea2dce84f291a328051a19da1c955e058d2d68d23ea1cb367deac0387b94b6039b836f7310a625a7a599861236
|
data/.gitignore
CHANGED
data/.pryrc
ADDED
data/README.md
CHANGED
@@ -247,11 +247,12 @@ contributors:
|
|
247
247
|
|
248
248
|
* [0xCCD](https://github.com/0xCCD)
|
249
249
|
* [AlexeyMK](https://github.com/AlexeyMK)
|
250
|
-
* [CraigCottingham](https://github.com/CraigCottingham)
|
251
250
|
* [antonversal](https://github.com/antonversal)
|
252
251
|
* [arnaudl](https://github.com/arnaudl)
|
253
252
|
* [bernardofire](https://github.com/bernardofire)
|
254
253
|
* [brixen](https://github.com/brixen)
|
254
|
+
* [CraigCottingham](https://github.com/CraigCottingham)
|
255
|
+
* [glanotte](https://github.com/glanotte)
|
255
256
|
* [jasonhutchens](https://github.com/jasonhutchens)
|
256
257
|
* [jmangs](https://github.com/jmangs)
|
257
258
|
* [mvbrocato](https://github.com/mvbrocato)
|
data/dentaku.gemspec
CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
|
19
19
|
s.add_development_dependency('rake')
|
20
20
|
s.add_development_dependency('rspec')
|
21
|
+
s.add_development_dependency('pry')
|
21
22
|
|
22
23
|
s.files = `git ls-files`.split("\n")
|
23
24
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'dentaku/calculator'
|
2
|
+
require 'dentaku/dependency_resolver'
|
3
|
+
require 'dentaku/exceptions'
|
4
|
+
require 'dentaku/expression'
|
5
|
+
|
6
|
+
module Dentaku
|
7
|
+
class BulkExpressionSolver
|
8
|
+
def initialize(expression_hash, memory)
|
9
|
+
self.expression_hash = expression_hash
|
10
|
+
self.memory = memory
|
11
|
+
end
|
12
|
+
|
13
|
+
def solve!
|
14
|
+
solve(&raise_exception_handler)
|
15
|
+
end
|
16
|
+
|
17
|
+
def solve(&block)
|
18
|
+
error_handler = block || return_undefined_handler
|
19
|
+
results = load_results(&error_handler)
|
20
|
+
|
21
|
+
expression_hash.each_with_object({}) do |(k, _), r|
|
22
|
+
r[k] = results[k.to_s]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_accessor :expression_hash, :memory
|
29
|
+
|
30
|
+
def return_undefined_handler
|
31
|
+
->(*) { :undefined }
|
32
|
+
end
|
33
|
+
|
34
|
+
def raise_exception_handler
|
35
|
+
->(ex) { raise ex }
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_results(&block)
|
39
|
+
variables_in_resolve_order.each_with_object({}) do |var_name, r|
|
40
|
+
begin
|
41
|
+
r[var_name] = evaluate!(expressions[var_name], r)
|
42
|
+
rescue Dentaku::UnboundVariableError, ZeroDivisionError => ex
|
43
|
+
r[var_name] = block.call(ex)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def dependencies(expression)
|
49
|
+
Expression.new(expression, memory).identifiers
|
50
|
+
end
|
51
|
+
|
52
|
+
def expressions
|
53
|
+
@expressions ||= Hash[expression_hash.map { |k,v| [k.to_s, v] }]
|
54
|
+
end
|
55
|
+
|
56
|
+
def expression_dependencies
|
57
|
+
Hash[expressions.map { |var, expr| [var, dependencies(expr)] }]
|
58
|
+
end
|
59
|
+
|
60
|
+
def variables_in_resolve_order
|
61
|
+
@variables_in_resolve_order ||=
|
62
|
+
DependencyResolver::find_resolve_order(expression_dependencies)
|
63
|
+
end
|
64
|
+
|
65
|
+
def evaluate!(expression, results)
|
66
|
+
expr = Expression.new(expression, memory.merge(expressions))
|
67
|
+
raise UnboundVariableError.new(expr.identifiers) if expr.unbound?
|
68
|
+
calculator.evaluate!(expression, results)
|
69
|
+
end
|
70
|
+
|
71
|
+
def calculator
|
72
|
+
@calculator ||= Calculator.new.store(memory)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/dentaku/calculator.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'dentaku/bulk_expression_solver'
|
1
2
|
require 'dentaku/evaluator'
|
2
3
|
require 'dentaku/exceptions'
|
3
4
|
require 'dentaku/expression'
|
@@ -40,24 +41,11 @@ module Dentaku
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def solve!(expression_hash)
|
43
|
-
|
44
|
-
|
45
|
-
# expression_hash: { variable_name: "string expression" }
|
46
|
-
# TSort thru the expressions' dependencies, then evaluate all
|
47
|
-
expression_dependencies = Hash[expressions.map do |var, expr|
|
48
|
-
[var, dependencies(expr)]
|
49
|
-
end]
|
50
|
-
|
51
|
-
variables_in_resolve_order = DependencyResolver::find_resolve_order(
|
52
|
-
expression_dependencies)
|
53
|
-
|
54
|
-
results = variables_in_resolve_order.each_with_object({}) do |var_name, r|
|
55
|
-
r[var_name] = evaluate!(expressions[var_name], r)
|
56
|
-
end
|
44
|
+
BulkExpressionSolver.new(expression_hash, @memory).solve!
|
45
|
+
end
|
57
46
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
47
|
+
def solve(expression_hash, &block)
|
48
|
+
BulkExpressionSolver.new(expression_hash, @memory).solve(&block)
|
61
49
|
end
|
62
50
|
|
63
51
|
def dependencies(expression)
|
data/lib/dentaku/version.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'dentaku/bulk_expression_solver'
|
2
|
+
|
3
|
+
RSpec.describe Dentaku::BulkExpressionSolver do
|
4
|
+
describe "#solve!" do
|
5
|
+
it "evaluates properly with variables, even if some in memory" do
|
6
|
+
expressions = {
|
7
|
+
weekly_fruit_budget: "weekly_apple_budget + pear * 4",
|
8
|
+
weekly_apple_budget: "apples * 7",
|
9
|
+
pear: "1"
|
10
|
+
}
|
11
|
+
memory = {apples: 3}
|
12
|
+
solver = described_class.new(expressions, memory)
|
13
|
+
expect(solver.solve!)
|
14
|
+
.to eq(pear: 1, weekly_apple_budget: 21, weekly_fruit_budget: 25)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "lets you know if a variable is unbound" do
|
18
|
+
expressions = {more_apples: "apples + 1"}
|
19
|
+
expect {
|
20
|
+
described_class.new(expressions, {}).solve!()
|
21
|
+
}.to raise_error(Dentaku::UnboundVariableError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "lets you know if the result is a div/0 error" do
|
25
|
+
expressions = {more_apples: "1/0"}
|
26
|
+
expect {
|
27
|
+
described_class.new(expressions, {}).solve!()
|
28
|
+
}.to raise_error(ZeroDivisionError)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#solve" do
|
33
|
+
it "returns :undefined when variables are unbound" do
|
34
|
+
expressions = {more_apples: "apples + 1"}
|
35
|
+
expect(described_class.new(expressions, {}).solve())
|
36
|
+
.to eq(more_apples: :undefined)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "allows passing in a custom value to an error handler when a variable is unbound" do
|
40
|
+
expressions = {more_apples: "apples + 1"}
|
41
|
+
expect(described_class.new(expressions, {}).solve() { :foo })
|
42
|
+
.to eq(more_apples: :foo)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "allows passing in a custom value to an error handler when there is a div/0 error" do
|
46
|
+
expressions = {more_apples: "1/0"}
|
47
|
+
expect(described_class.new(expressions, {}).solve() { :foo })
|
48
|
+
.to eq(more_apples: :foo)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/spec/calculator_spec.rb
CHANGED
@@ -87,6 +87,25 @@ describe Dentaku::Calculator do
|
|
87
87
|
result = with_memory.solve!(total_fruit: "Apples + pears", pears: 10)
|
88
88
|
expect(result[:total_fruit]).to eq 13
|
89
89
|
end
|
90
|
+
|
91
|
+
it "lets you know if a variable is unbound" do
|
92
|
+
expect {
|
93
|
+
calculator.solve!(more_apples: "apples + 1")
|
94
|
+
}.to raise_error(Dentaku::UnboundVariableError)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'solve' do
|
99
|
+
it "returns :undefined when variables are unbound" do
|
100
|
+
expressions = {more_apples: "apples + 1"}
|
101
|
+
expect(calculator.solve(expressions)).to eq(more_apples: :undefined)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "allows passing in a custom value to an error handler" do
|
105
|
+
expressions = {more_apples: "apples + 1"}
|
106
|
+
expect(calculator.solve(expressions) { :foo })
|
107
|
+
.to eq(more_apples: :foo)
|
108
|
+
end
|
90
109
|
end
|
91
110
|
|
92
111
|
it 'evaluates a statement with no variables' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dentaku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Solomon White
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
description: |2
|
42
56
|
Dentaku is a parser and evaluator for mathematical formulas
|
43
57
|
email:
|
@@ -47,6 +61,7 @@ extensions: []
|
|
47
61
|
extra_rdoc_files: []
|
48
62
|
files:
|
49
63
|
- ".gitignore"
|
64
|
+
- ".pryrc"
|
50
65
|
- ".travis.yml"
|
51
66
|
- Gemfile
|
52
67
|
- README.md
|
@@ -54,6 +69,7 @@ files:
|
|
54
69
|
- dentaku.gemspec
|
55
70
|
- lib/dentaku.rb
|
56
71
|
- lib/dentaku/binary_operation.rb
|
72
|
+
- lib/dentaku/bulk_expression_solver.rb
|
57
73
|
- lib/dentaku/calculator.rb
|
58
74
|
- lib/dentaku/dependency_resolver.rb
|
59
75
|
- lib/dentaku/evaluator.rb
|
@@ -68,6 +84,7 @@ files:
|
|
68
84
|
- lib/dentaku/version.rb
|
69
85
|
- spec/benchmark.rb
|
70
86
|
- spec/binary_operation_spec.rb
|
87
|
+
- spec/bulk_expression_solver_spec.rb
|
71
88
|
- spec/calculator_spec.rb
|
72
89
|
- spec/dentaku_spec.rb
|
73
90
|
- spec/evaluator_spec.rb
|
@@ -106,6 +123,7 @@ summary: A formula language parser and evaluator
|
|
106
123
|
test_files:
|
107
124
|
- spec/benchmark.rb
|
108
125
|
- spec/binary_operation_spec.rb
|
126
|
+
- spec/bulk_expression_solver_spec.rb
|
109
127
|
- spec/calculator_spec.rb
|
110
128
|
- spec/dentaku_spec.rb
|
111
129
|
- spec/evaluator_spec.rb
|