kalculator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/kalculator.gemspec +27 -0
- data/lib/kalculator.rb +23 -0
- data/lib/kalculator/data_sources.rb +23 -0
- data/lib/kalculator/evaluator.rb +74 -0
- data/lib/kalculator/lexer.rb +25 -0
- data/lib/kalculator/parser.rb +37 -0
- data/lib/kalculator/version.rb +3 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a18c933b621aa811fba3ca081470b348175529ea
|
4
|
+
data.tar.gz: 6ed07d4ffee1aa412db11c5e4d0eaa78316165b3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f5c54366fb9740454b8fb110436a4e4c6c36927e9858549bd7be94c93feb3db12840d5dd2f9a603625f6a7a331e0f1111ac74a024b90bd1723f5a05e179e7615
|
7
|
+
data.tar.gz: e065a88e4cc6ebe6f944693fd4a55623fbe4213a96b4e39536d4fa56778e62cb7afa577aba6e1b63c48459b35138a7fb50d82b17c898f4ecb544103c0367eba9
|
data/kalculator.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "kalculator/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "kalculator"
|
7
|
+
spec.version = Kalculator::VERSION
|
8
|
+
spec.authors = ["Michael Ries"]
|
9
|
+
spec.email = ["michael@riesd.com"]
|
10
|
+
|
11
|
+
spec.summary = "A calculator that can safely and quickly interpret user-input"
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/mmmries/kalculator"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = ["kalculator.gemspec"] + Dir["lib/**/*"]
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "rltk", "~> 3.0"
|
22
|
+
|
23
|
+
spec.add_development_dependency "benchmark-ips", "~> 2.7"
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
end
|
data/lib/kalculator.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "rltk"
|
2
|
+
require "kalculator/data_sources"
|
3
|
+
require "kalculator/evaluator"
|
4
|
+
require "kalculator/lexer"
|
5
|
+
require "kalculator/parser"
|
6
|
+
require "kalculator/version"
|
7
|
+
|
8
|
+
class Kalculator
|
9
|
+
def self.evaluate(formula, data_source = {})
|
10
|
+
new(formula).evaluate(data_source)
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :ast, :string
|
14
|
+
|
15
|
+
def initialize(string)
|
16
|
+
@ast = Kalculator::Parser.parse(Kalculator::Lexer.lex(string))
|
17
|
+
@string = string
|
18
|
+
end
|
19
|
+
|
20
|
+
def evaluate(data_source = {})
|
21
|
+
Kalculator::Evaluator.new(data_source).evaluate(ast)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Kalculator
|
2
|
+
class DataSources
|
3
|
+
def initialize(*sources)
|
4
|
+
@sources = sources
|
5
|
+
end
|
6
|
+
|
7
|
+
def key?(name)
|
8
|
+
ret = false
|
9
|
+
@sources.each do |source|
|
10
|
+
break ret = true if source.key?(name)
|
11
|
+
end
|
12
|
+
ret
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](name)
|
16
|
+
ret = nil
|
17
|
+
@sources.each do |source|
|
18
|
+
break ret = source[name] if source.key?(name)
|
19
|
+
end
|
20
|
+
ret
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class Kalculator
|
2
|
+
class Evaluator
|
3
|
+
def initialize(data_source)
|
4
|
+
@data_source = data_source
|
5
|
+
end
|
6
|
+
|
7
|
+
def evaluate(ast)
|
8
|
+
send(ast.first, *ast)
|
9
|
+
end
|
10
|
+
|
11
|
+
def +(_, left, right)
|
12
|
+
evaluate(left) + evaluate(right)
|
13
|
+
end
|
14
|
+
|
15
|
+
def -(_, left, right)
|
16
|
+
evaluate(left) - evaluate(right)
|
17
|
+
end
|
18
|
+
|
19
|
+
def *(_, left, right)
|
20
|
+
evaluate(left) * evaluate(right)
|
21
|
+
end
|
22
|
+
|
23
|
+
def /(_, left, right)
|
24
|
+
evaluate(left) / evaluate(right)
|
25
|
+
end
|
26
|
+
|
27
|
+
def >(_, left, right)
|
28
|
+
evaluate(left) > evaluate(right)
|
29
|
+
end
|
30
|
+
|
31
|
+
def >=(_, left, right)
|
32
|
+
evaluate(left) >= evaluate(right)
|
33
|
+
end
|
34
|
+
|
35
|
+
def <(_, left, right)
|
36
|
+
evaluate(left) < evaluate(right)
|
37
|
+
end
|
38
|
+
|
39
|
+
def <=(_, left, right)
|
40
|
+
evaluate(left) <= evaluate(right)
|
41
|
+
end
|
42
|
+
|
43
|
+
def ==(_, left, right)
|
44
|
+
evaluate(left) == evaluate(right)
|
45
|
+
end
|
46
|
+
|
47
|
+
def boolean(_, boolean)
|
48
|
+
boolean
|
49
|
+
end
|
50
|
+
|
51
|
+
def if(_, condition, true_clause, false_clause)
|
52
|
+
if evaluate(condition)
|
53
|
+
evaluate(true_clause)
|
54
|
+
else
|
55
|
+
evaluate(false_clause)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def number(_, number)
|
60
|
+
number
|
61
|
+
end
|
62
|
+
|
63
|
+
def sum(_, array)
|
64
|
+
evaluate(array).inject(0){|sum, num| sum + num}
|
65
|
+
end
|
66
|
+
|
67
|
+
def variable(_, names)
|
68
|
+
names.inject(@data_source) do |source, name|
|
69
|
+
raise "undefined variable #{names.join(".")} (could not find #{name} in #{source}" unless source.key?(name)
|
70
|
+
source[name]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Kalculator
|
2
|
+
class Lexer < RLTK::Lexer
|
3
|
+
rule(/\s/)
|
4
|
+
rule(/\(/) { :LPAREN }
|
5
|
+
rule(/\)/) { :RPAREN }
|
6
|
+
rule(/,/) { :COMMA }
|
7
|
+
rule(/\+/) { :PLUS }
|
8
|
+
rule(/-/) { :SUB }
|
9
|
+
rule(/\*/) { :MUL }
|
10
|
+
rule(/\//) { :DIV }
|
11
|
+
rule(/>/) { :GT }
|
12
|
+
rule(/>=/) { :GTE }
|
13
|
+
rule(/</) { :LT }
|
14
|
+
rule(/<=/) { :LTE }
|
15
|
+
rule(/==/) { :EQ }
|
16
|
+
rule(/\d+/) { |t| [:NUMBER, t.to_i] }
|
17
|
+
rule(/\.\d+/) { |t| [:NUMBER, t.to_f] }
|
18
|
+
rule(/\d+\.\d+/) { |t| [:NUMBER, t.to_f] }
|
19
|
+
rule(/if/) { |t| :IF }
|
20
|
+
rule(/sum/) { |t| :SUM }
|
21
|
+
rule(/true/) { |t| :TRUE }
|
22
|
+
rule(/false/) { |t| :FALSE }
|
23
|
+
rule(/[A-Za-z][A-Za-z0-9\._]*/) { |t| [:IDENT, t] }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Kalculator
|
2
|
+
class Parser < RLTK::Parser
|
3
|
+
#Left 200 '&&' '||'.
|
4
|
+
left :GT, :GTE, :LT, :LTE, :EQ
|
5
|
+
left :PLUS, :SUB
|
6
|
+
left :MUL, :DIV
|
7
|
+
#Left 600 '.'.
|
8
|
+
|
9
|
+
production(:expression) do
|
10
|
+
clause('IF LPAREN expression COMMA expression COMMA expression RPAREN') do |_, _, condition, _, true_clause, _, false_clause, _|
|
11
|
+
[:if, condition, true_clause, false_clause]
|
12
|
+
end
|
13
|
+
clause('SUM LPAREN expression RPAREN') do |_, _, e0, _|
|
14
|
+
[:sum, e0]
|
15
|
+
end
|
16
|
+
clause('LPAREN expression RPAREN') { |_, expression, _| expression }
|
17
|
+
|
18
|
+
clause('NUMBER') { |n| [:number, n] }
|
19
|
+
clause('IDENT') { |n| [:variable, n.split(".")] }
|
20
|
+
clause('TRUE') { |n| [:boolean, true] }
|
21
|
+
clause('FALSE') { |n| [:boolean, false] }
|
22
|
+
|
23
|
+
clause('expression GT expression') { |e0, _, e1| [:>, e0, e1] }
|
24
|
+
clause('expression GTE expression') { |e0, _, e1| [:>=, e0, e1] }
|
25
|
+
clause('expression LT expression') { |e0, _, e1| [:<, e0, e1] }
|
26
|
+
clause('expression LTE expression') { |e0, _, e1| [:<=, e0, e1] }
|
27
|
+
clause('expression EQ expression') { |e0, _, e1| [:==, e0, e1] }
|
28
|
+
|
29
|
+
clause('expression PLUS expression') { |e0, _, e1| [:+, e0, e1] }
|
30
|
+
clause('expression SUB expression') { |e0, _, e1| [:-, e0, e1] }
|
31
|
+
clause('expression MUL expression') { |e0, _, e1| [:*, e0, e1] }
|
32
|
+
clause('expression DIV expression') { |e0, _, e1| [:/, e0, e1] }
|
33
|
+
end
|
34
|
+
|
35
|
+
finalize()
|
36
|
+
end
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kalculator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Ries
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rltk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: benchmark-ips
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.16'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.16'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
description: A calculator that can safely and quickly interpret user-input
|
84
|
+
email:
|
85
|
+
- michael@riesd.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- kalculator.gemspec
|
91
|
+
- lib/kalculator.rb
|
92
|
+
- lib/kalculator/data_sources.rb
|
93
|
+
- lib/kalculator/evaluator.rb
|
94
|
+
- lib/kalculator/lexer.rb
|
95
|
+
- lib/kalculator/parser.rb
|
96
|
+
- lib/kalculator/version.rb
|
97
|
+
homepage: https://github.com/mmmries/kalculator
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.6.14
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: A calculator that can safely and quickly interpret user-input
|
121
|
+
test_files: []
|