hesabu 0.1.5 → 0.1.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/Gemfile.lock +1 -3
- data/README.md +2 -11
- data/bin/hesabucli +0 -0
- data/hesabu.gemspec +0 -2
- data/lib/hesabu/equation_cleaner.rb +11 -0
- data/lib/hesabu/errors.rb +8 -10
- data/lib/hesabu/solver.rb +26 -104
- data/lib/hesabu/version.rb +1 -1
- data/lib/hesabu.rb +4 -9
- metadata +4 -24
- data/lib/hesabu/interpreter.rb +0 -25
- data/lib/hesabu/parser.rb +0 -75
- data/lib/hesabu/types/float_lit.rb +0 -9
- data/lib/hesabu/types/fun_call.rb +0 -123
- data/lib/hesabu/types/indentifier_lit.rb +0 -9
- data/lib/hesabu/types/int_lit.rb +0 -9
- data/lib/hesabu/types/operation.rb +0 -42
- data/lib/hesabu/types/string_lit.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 431f06d479e890cf49a1cf8fb1f9a166b66a88aee4b6200be1c540996fecf4fd
|
4
|
+
data.tar.gz: e6d8a83f61a8e986658b6c574c0b51c5365787819f0fb2bff9331b7cdf3ba07b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c22ea9e78df743ee0ea74d8fb3fc22291fe12fdd1cd7235d94e608c72627c665d9982c2e803eede67320cf42e453741682313d8a4aa87f59a449638d37187e8d
|
7
|
+
data.tar.gz: 96761de748fed74701bc6aa799ec99ff309c77da03521111cd0a4960874552d7018bd5998c883cb2a41a6d826d2ec8fa3415b0ad70ff419c41c93f373370ddcd
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
hesabu (0.1.
|
5
|
-
parslet
|
4
|
+
hesabu (0.1.5)
|
6
5
|
|
7
6
|
GEM
|
8
7
|
remote: https://rubygems.org/
|
@@ -34,7 +33,6 @@ GEM
|
|
34
33
|
parallel (1.12.1)
|
35
34
|
parser (2.5.1.0)
|
36
35
|
ast (~> 2.4.0)
|
37
|
-
parslet (1.8.2)
|
38
36
|
path_expander (1.0.2)
|
39
37
|
powerpack (0.1.1)
|
40
38
|
pronto (0.9.5)
|
data/README.md
CHANGED
@@ -25,13 +25,7 @@ The expressions can be more complex (excel like), see the supported functions [h
|
|
25
25
|
|
26
26
|
Currently the solver is case sensitive (except function names)
|
27
27
|
|
28
|
-
Nb: Hesabu is swahili word for
|
29
|
-
|
30
|
-
## Technical background
|
31
|
-
|
32
|
-
* https://tomassetti.me/guide-parsing-algorithms-terminology/
|
33
|
-
* https://github.com/PhilippeSigaud/Pegged/wiki/PEG-Basics
|
34
|
-
* https://github.com/kschiess/parslet
|
28
|
+
Nb: Hesabu is swahili word for arithmetic.
|
35
29
|
|
36
30
|
## Alternatives
|
37
31
|
|
@@ -62,10 +56,7 @@ chmod 0600 ~/.gem/credentials
|
|
62
56
|
|
63
57
|
|
64
58
|
```
|
65
|
-
gem bump
|
66
|
-
gem build hesabu.gemspec
|
67
|
-
gem push hesabu-x.x.x.gem
|
68
|
-
|
59
|
+
gem bump --tag --release
|
69
60
|
```
|
70
61
|
|
71
62
|
|
data/bin/hesabucli
ADDED
Binary file
|
data/hesabu.gemspec
CHANGED
@@ -23,8 +23,6 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
|
26
|
-
spec.add_dependency "parslet"
|
27
|
-
|
28
26
|
spec.add_development_dependency "bundler", "~> 1.16"
|
29
27
|
spec.add_development_dependency "rake", "~> 10.0"
|
30
28
|
spec.add_development_dependency "rspec", "~> 3.0"
|
data/lib/hesabu/errors.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
|
2
2
|
module Hesabu
|
3
3
|
class Error < StandardError
|
4
|
+
attr_accessor :errors
|
5
|
+
def iniatialize(message)
|
6
|
+
super(message)
|
7
|
+
@errors = errors
|
8
|
+
end
|
4
9
|
end
|
5
10
|
class ArgumentError < Error
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
class CalculationError < Error
|
10
|
-
end
|
11
|
-
class DivideByZeroError < Error
|
12
|
-
end
|
13
|
-
class CyclicError < Error
|
14
|
-
end
|
15
|
-
class UnboundVariableError < Error
|
11
|
+
def iniatialize(message)
|
12
|
+
super(message)
|
13
|
+
end
|
16
14
|
end
|
17
15
|
end
|
data/lib/hesabu/solver.rb
CHANGED
@@ -1,127 +1,49 @@
|
|
1
|
-
module Hesabu
|
2
|
-
class Solver
|
3
|
-
include TSort
|
4
1
|
|
5
|
-
Equation = Struct.new(:name, :evaluable, :dependencies, :raw_expression)
|
6
|
-
EMPTY_DEPENDENCIES = [].freeze
|
7
|
-
FakeEvaluable = Struct.new(:eval)
|
8
2
|
|
3
|
+
module Hesabu
|
4
|
+
class Solver
|
9
5
|
def initialize
|
10
|
-
@parser = ::Hesabu::Parser.new
|
11
|
-
@interpreter = ::Hesabu::Interpreter.new
|
12
6
|
@equations = {}
|
13
|
-
@bindings = {}
|
14
7
|
end
|
15
8
|
|
16
9
|
def add(name, raw_expression)
|
17
10
|
if raw_expression.nil? || name.nil?
|
18
11
|
raise Hesabu::ArgumentError, "name or expression can't be nil : '#{name}', '#{raw_expression}'"
|
19
12
|
end
|
20
|
-
|
21
|
-
if ::Hesabu::Types.as_numeric(raw_expression)
|
22
|
-
add_numeric(name, raw_expression)
|
23
|
-
else
|
24
|
-
add_equation(name, raw_expression)
|
25
|
-
end
|
13
|
+
@equations[name] = EquationCleaner.clean(raw_expression.to_s)
|
26
14
|
end
|
27
15
|
|
28
16
|
def solve!
|
29
|
-
|
30
|
-
|
17
|
+
result = nil
|
18
|
+
IO.popen(HESABUCLI, mode = "r+") do |io|
|
19
|
+
io.write @equations.to_json
|
20
|
+
io.close_write # let the process know you've given it all the data
|
21
|
+
result = io.read
|
31
22
|
end
|
32
|
-
solution =
|
33
|
-
|
34
|
-
to_numerics(solution)
|
35
|
-
rescue StandardError => e
|
36
|
-
log_and_raise(e)
|
37
|
-
end
|
23
|
+
solution = JSON.parse(result)
|
24
|
+
exit_status = $CHILD_STATUS.exitstatus
|
38
25
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
raise Hesabu::CyclicError, "There's a cycle between the variables : " + e.message[25..-1]
|
43
|
-
end
|
44
|
-
|
45
|
-
def tsort_each_node(&block)
|
46
|
-
@equations.each_key(&block)
|
47
|
-
end
|
48
|
-
|
49
|
-
def tsort_each_child(node, &block)
|
50
|
-
equation = @equations[node]
|
51
|
-
raise UnboundVariableError, unbound_message(node) unless equation
|
52
|
-
equation.dependencies.each(&block)
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def to_numerics(solution)
|
58
|
-
solution.each_with_object({}) do |kv, hash|
|
59
|
-
hash[kv.first] = Hesabu::Types.as_numeric(kv.last) || kv.last
|
60
|
-
end
|
26
|
+
log_everything(exit_status, result) if ENV["HESABU_DEBUG"] || exit_status != 0
|
27
|
+
handle_error(solution) if exit_status != 0
|
28
|
+
solution
|
61
29
|
end
|
62
30
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
def log_error(e)
|
69
|
-
log "Error during processing: #{$ERROR_INFO}"
|
70
|
-
log "Error : #{e.class} #{e.message}"
|
71
|
-
log "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
|
72
|
-
end
|
73
|
-
|
74
|
-
def evaluate_equation(equation)
|
75
|
-
raise "not evaluable #{equation.evaluable} #{equation}" unless equation.evaluable.respond_to?(:eval, false)
|
76
|
-
begin
|
77
|
-
@bindings[equation.name] = equation.evaluable.eval
|
78
|
-
rescue StandardError => e
|
79
|
-
raise CalculationError, "Failed to evaluate #{equation.name} due to #{e.message} in formula #{equation.raw_expression}"
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def log(message)
|
84
|
-
puts message
|
85
|
-
end
|
86
|
-
|
87
|
-
def add_numeric(name, raw_expression)
|
88
|
-
@equations[name] = Equation.new(
|
89
|
-
name,
|
90
|
-
FakeEvaluable.new(::Hesabu::Types.as_bigdecimal(raw_expression)),
|
91
|
-
EMPTY_DEPENDENCIES,
|
92
|
-
raw_expression
|
93
|
-
)
|
94
|
-
end
|
95
|
-
|
96
|
-
def add_equation(name, raw_expression)
|
97
|
-
expression = raw_expression.gsub(/\r\n?/, "")
|
98
|
-
ast_tree = begin
|
99
|
-
@parser.parse(expression)
|
100
|
-
rescue Parslet::ParseFailed => e
|
101
|
-
log(raw_expression)
|
102
|
-
log_error(e)
|
103
|
-
raise ParseError, "failed to parse #{name} := #{expression} : #{e.message}"
|
104
|
-
end
|
105
|
-
var_identifiers = Set.new
|
106
|
-
interpretation = @interpreter.apply(
|
107
|
-
ast_tree,
|
108
|
-
doc: @bindings,
|
109
|
-
var_identifiers: var_identifiers
|
110
|
-
)
|
111
|
-
if ENV["HESABU_DEBUG"]
|
112
|
-
log expression
|
113
|
-
log JSON.pretty_generate(ast_tree)
|
114
|
-
end
|
115
|
-
@equations[name] = Equation.new(name, interpretation, var_identifiers, raw_expression)
|
116
|
-
end
|
31
|
+
def handle_error(solution)
|
32
|
+
puts
|
33
|
+
error = solution["errors"].first
|
34
|
+
message = "In equation #{error['source']} " + error["message"] + " #{error['source']} := #{error['expression']}"
|
117
35
|
|
118
|
-
|
119
|
-
|
120
|
-
|
36
|
+
err = Hesabu::Error.new(message)
|
37
|
+
err.errors = solution["errors"]
|
38
|
+
raise err
|
121
39
|
end
|
122
40
|
|
123
|
-
def
|
124
|
-
|
41
|
+
def log_everything(exit_status, result)
|
42
|
+
puts ["**************",
|
43
|
+
"exit_status:#{exit_status}",
|
44
|
+
@equations.to_json,
|
45
|
+
"=> ",
|
46
|
+
result].join("\n")
|
125
47
|
end
|
126
48
|
end
|
127
49
|
end
|
data/lib/hesabu/version.rb
CHANGED
data/lib/hesabu.rb
CHANGED
@@ -1,17 +1,12 @@
|
|
1
1
|
require "hesabu/version"
|
2
|
-
|
2
|
+
|
3
3
|
require_relative "./hesabu/errors"
|
4
|
-
require_relative "./hesabu/parser"
|
5
4
|
require_relative "./hesabu/types/numeric"
|
6
|
-
require_relative "./hesabu/types/float_lit"
|
7
|
-
require_relative "./hesabu/types/fun_call"
|
8
|
-
require_relative "./hesabu/types/indentifier_lit"
|
9
|
-
require_relative "./hesabu/types/int_lit"
|
10
|
-
require_relative "./hesabu/types/string_lit"
|
11
|
-
require_relative "./hesabu/types/operation"
|
12
5
|
|
13
|
-
require_relative "./hesabu/
|
6
|
+
require_relative "./hesabu/equation_cleaner"
|
14
7
|
require_relative "./hesabu/solver"
|
15
8
|
|
16
9
|
module Hesabu
|
10
|
+
HESABUCLI = File.expand_path("../bin/hesabucli", File.dirname(__FILE__))
|
11
|
+
puts "************** HESABU cli location : " + HESABUCLI
|
17
12
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hesabu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stéphan Mestach
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: parslet
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: bundler
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,20 +157,14 @@ files:
|
|
171
157
|
- Rakefile
|
172
158
|
- bin/console
|
173
159
|
- bin/fast
|
160
|
+
- bin/hesabucli
|
174
161
|
- bin/setup
|
175
162
|
- hesabu.gemspec
|
176
163
|
- lib/hesabu.rb
|
164
|
+
- lib/hesabu/equation_cleaner.rb
|
177
165
|
- lib/hesabu/errors.rb
|
178
|
-
- lib/hesabu/interpreter.rb
|
179
|
-
- lib/hesabu/parser.rb
|
180
166
|
- lib/hesabu/solver.rb
|
181
|
-
- lib/hesabu/types/float_lit.rb
|
182
|
-
- lib/hesabu/types/fun_call.rb
|
183
|
-
- lib/hesabu/types/indentifier_lit.rb
|
184
|
-
- lib/hesabu/types/int_lit.rb
|
185
167
|
- lib/hesabu/types/numeric.rb
|
186
|
-
- lib/hesabu/types/operation.rb
|
187
|
-
- lib/hesabu/types/string_lit.rb
|
188
168
|
- lib/hesabu/version.rb
|
189
169
|
homepage: https://github.com/BLSQ/hesabu
|
190
170
|
licenses:
|
data/lib/hesabu/interpreter.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module Hesabu
|
2
|
-
class Interpreter < Parslet::Transform
|
3
|
-
rule(plist: sequence(:arr)) { arr }
|
4
|
-
rule(plist: "()") { [] }
|
5
|
-
rule(l: simple(:left),
|
6
|
-
r: simple(:right),
|
7
|
-
o: simple(:op)) do
|
8
|
-
Hesabu::Types::Operation.new(left, op, right)
|
9
|
-
end
|
10
|
-
rule(identifier: simple(:id)) { id.to_s }
|
11
|
-
rule(variable: simple(:variable)) do |d|
|
12
|
-
d[:var_identifiers]&.add(d[:variable])
|
13
|
-
Hesabu::Types::IdentifierLit.new(d[:variable], d[:doc])
|
14
|
-
end
|
15
|
-
rule(fcall: { name: simple(:name), varlist: sequence(:vars) }) do
|
16
|
-
Hesabu::Types::FunCall.new(name, vars)
|
17
|
-
end
|
18
|
-
rule(str: subtree(:str)) do
|
19
|
-
Hesabu::Types::StringLit.new(str.map { |char| char.values.first.str }.join)
|
20
|
-
end
|
21
|
-
|
22
|
-
rule(integer: simple(:integer)) { Hesabu::Types::IntLit.new(integer) }
|
23
|
-
rule(float: simple(:float)) { Hesabu::Types::FloatLit.new(float) }
|
24
|
-
end
|
25
|
-
end
|
data/lib/hesabu/parser.rb
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
module Hesabu
|
2
|
-
class Parser < Parslet::Parser
|
3
|
-
def cts(atom_arg)
|
4
|
-
atom_arg >> space?
|
5
|
-
end
|
6
|
-
|
7
|
-
# simple things
|
8
|
-
rule(:lparen) { str("(") >> space? }
|
9
|
-
rule(:rparen) { str(")") >> space? }
|
10
|
-
rule(:comma) { str(",") >> space? }
|
11
|
-
rule(:space) { match["\s"] | match["\t"] | match["\n"] }
|
12
|
-
rule(:spaces) { space.repeat }
|
13
|
-
rule(:space?) { spaces.maybe }
|
14
|
-
|
15
|
-
rule(:nonquote) { str("'").absnt? >> any }
|
16
|
-
rule(:quote) { str("'") }
|
17
|
-
rule(:string) { quote >> nonquote.as(:char).repeat(1).as(:str) >> quote >> space? }
|
18
|
-
|
19
|
-
rule(:identifier) do
|
20
|
-
cts((match["a-zA-Z"] >> match["a-zA-Z0-9_"].repeat).as(:identifier))
|
21
|
-
end
|
22
|
-
|
23
|
-
rule(:separator) { str(";") }
|
24
|
-
|
25
|
-
rule(:digit) { match["0-9"] }
|
26
|
-
|
27
|
-
rule(:integer) do
|
28
|
-
cts((str("-").maybe >> match["1-9"] >> digit.repeat).as(:integer) | str("0").as(:integer))
|
29
|
-
end
|
30
|
-
|
31
|
-
rule(:float) do
|
32
|
-
cts((str("-").maybe >> digit.repeat(1) >> str(".") >> digit.repeat(1)).as(:float)) |
|
33
|
-
cts((str(".") >> digit.repeat(1)).as(:float))
|
34
|
-
end
|
35
|
-
|
36
|
-
# arithmetic
|
37
|
-
|
38
|
-
rule(:expression) { iexpression | variable | pexpression }
|
39
|
-
rule(:pexpression) { lparen >> expression >> rparen }
|
40
|
-
|
41
|
-
rule(:variable) { identifier.as(:variable) }
|
42
|
-
rule(:sum_op) { match("[+-]") >> space? }
|
43
|
-
rule(:mul_op) { match("[*/]") >> space? }
|
44
|
-
rule(:comparison_op) do
|
45
|
-
(
|
46
|
-
str("<=") | str(">=") | str("==") |
|
47
|
-
str("!=") | str("<") | str("=") |
|
48
|
-
str(">") | str("AND")
|
49
|
-
) >> space?
|
50
|
-
end
|
51
|
-
|
52
|
-
rule(:atom) { string | pexpression | float | integer | fcall.as(:fcall) | variable }
|
53
|
-
|
54
|
-
rule(:iexpression) do
|
55
|
-
infix_expression(atom,
|
56
|
-
[mul_op, 3, :left],
|
57
|
-
[sum_op, 2, :left],
|
58
|
-
[comparison_op, 1, :left])
|
59
|
-
end
|
60
|
-
|
61
|
-
# lists
|
62
|
-
rule(:varlist) { expression >> (comma >> expression).repeat }
|
63
|
-
rule(:pvarlist) { (lparen >> varlist.repeat >> rparen).as(:plist) }
|
64
|
-
|
65
|
-
# functions
|
66
|
-
rule(:fcall) { identifier.as(:name) >> pvarlist.as(:varlist) }
|
67
|
-
|
68
|
-
# root
|
69
|
-
rule(:command) do
|
70
|
-
iexpression | expression | atom
|
71
|
-
end
|
72
|
-
rule(:commands) { commands.repeat }
|
73
|
-
root :command
|
74
|
-
end
|
75
|
-
end
|
@@ -1,123 +0,0 @@
|
|
1
|
-
module Hesabu
|
2
|
-
module Types
|
3
|
-
class Function
|
4
|
-
def divide(num, denum)
|
5
|
-
num / denum
|
6
|
-
end
|
7
|
-
end
|
8
|
-
class IfFunction < Function
|
9
|
-
def call(args)
|
10
|
-
raise "expected args #{name} : #{args}" unless args.size != 2
|
11
|
-
condition_expression = args[0]
|
12
|
-
condition = condition_expression.eval
|
13
|
-
condition ? args[1].eval : args[2].eval
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class SumFunction < Function
|
18
|
-
def call(args)
|
19
|
-
values = args.map(&:eval)
|
20
|
-
values.reduce(0, :+)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class ScoreTableFunction < Function
|
25
|
-
def call(args)
|
26
|
-
values = args.map(&:eval)
|
27
|
-
target = values.shift
|
28
|
-
matching_rules = values.each_slice(3).find do |lower, greater, result|
|
29
|
-
greater.nil? || result.nil? ? true : lower <= target && target < greater
|
30
|
-
end
|
31
|
-
matching_rules.last
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class AvgFunction < Function
|
36
|
-
def call(args)
|
37
|
-
values = args.map(&:eval)
|
38
|
-
values.inject(0.0) { |acc, elem| acc + elem } / values.size
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
class SafeDivFunction < Function
|
43
|
-
def call(args)
|
44
|
-
eval_denom = args[1].eval
|
45
|
-
if eval_denom == 0
|
46
|
-
0
|
47
|
-
else
|
48
|
-
eval_num = args[0].eval
|
49
|
-
eval_denom.zero? ? 0 : (eval_num / eval_denom)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class MinFunction
|
55
|
-
def call(args)
|
56
|
-
values = args.map(&:eval)
|
57
|
-
values.min
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class MaxFunction
|
62
|
-
def call(args)
|
63
|
-
values = args.map(&:eval)
|
64
|
-
values.max
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
class RandbetweenFunction
|
69
|
-
def call(args)
|
70
|
-
values = args.map(&:eval)
|
71
|
-
rand(values.first..values.last)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
class AbsFunction
|
76
|
-
def call(args)
|
77
|
-
raise "expected args #{self.class.name} : #{args}" if args.size != 1
|
78
|
-
args.first.eval.abs
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
class AccessFunction
|
83
|
-
def call(args)
|
84
|
-
values = args.map(&:eval)
|
85
|
-
array = values[0..-2]
|
86
|
-
index = values[-1]
|
87
|
-
array[index]
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
class RoundFunction
|
92
|
-
def call(args)
|
93
|
-
raise "expected args #{self.class.name} : #{args}" if args.size > 2 || args.empty?
|
94
|
-
values = args.map(&:eval)
|
95
|
-
decimals = args.size == 2 ? values[1] : 0
|
96
|
-
values.first.round(decimals)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
FUNCTIONS = {
|
101
|
-
"if" => IfFunction.new,
|
102
|
-
"sum" => SumFunction.new,
|
103
|
-
"avg" => AvgFunction.new,
|
104
|
-
"min" => MinFunction.new,
|
105
|
-
"max" => MaxFunction.new,
|
106
|
-
"safe_div" => SafeDivFunction.new,
|
107
|
-
"randbetween" => RandbetweenFunction.new,
|
108
|
-
"score_table" => ScoreTableFunction.new,
|
109
|
-
"abs" => AbsFunction.new,
|
110
|
-
"access" => AccessFunction.new,
|
111
|
-
"round" => RoundFunction.new
|
112
|
-
}.freeze
|
113
|
-
|
114
|
-
FunCall = Struct.new(:name, :args) do
|
115
|
-
def eval
|
116
|
-
function_name = name.strip.downcase
|
117
|
-
function = FUNCTIONS[function_name]
|
118
|
-
raise "unsupported function call : #{function_name} only knows #{FUNCTIONS.keys.join(', ')}" unless function
|
119
|
-
function.call(args)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
data/lib/hesabu/types/int_lit.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
module Hesabu
|
2
|
-
module Types
|
3
|
-
Operation = Struct.new(:left, :operator, :right) do
|
4
|
-
def eval
|
5
|
-
op = operator.str.strip
|
6
|
-
result(op, left.eval, right.eval)
|
7
|
-
end
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
def result(op, leftval, rightval)
|
12
|
-
case op
|
13
|
-
when "+"
|
14
|
-
leftval + rightval
|
15
|
-
when "-"
|
16
|
-
leftval - rightval
|
17
|
-
when "*"
|
18
|
-
leftval * rightval
|
19
|
-
when "/"
|
20
|
-
raise DivideByZeroError, "division by 0 : #{leftval}/0" if rightval.zero?
|
21
|
-
leftval / rightval
|
22
|
-
when ">"
|
23
|
-
leftval > rightval
|
24
|
-
when "<"
|
25
|
-
leftval < rightval
|
26
|
-
when ">="
|
27
|
-
leftval >= rightval
|
28
|
-
when "<="
|
29
|
-
leftval <= rightval
|
30
|
-
when "=", "=="
|
31
|
-
leftval == rightval
|
32
|
-
when "!="
|
33
|
-
leftval != rightval
|
34
|
-
when "AND"
|
35
|
-
leftval && rightval
|
36
|
-
else
|
37
|
-
raise "unsupported operand : #{op} : #{left} #{operator} #{right}"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|