kalc 1.2.0 → 1.7.0
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 +5 -5
- data/Gemfile.lock +17 -19
- data/LICENSE +1 -1
- data/kalc.gemspec +6 -8
- data/lib/kalc/ast.rb +21 -23
- data/lib/kalc/environment.rb +4 -8
- data/lib/kalc/grammar.rb +139 -147
- data/lib/kalc/interpreter.rb +30 -21
- data/lib/kalc/repl.rb +11 -12
- data/lib/kalc/transform.rb +58 -59
- data/lib/kalc/version.rb +1 -1
- data/spec/grammar_spec.rb +1 -1
- data/spec/interpreter_spec.rb +14 -5
- data/spec/stdlib_spec.rb +1 -2
- metadata +6 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c6b57e87fb3af0e7410e84529380e3b6e6b37d79ded5ba5956807803dfe4e9b1
|
4
|
+
data.tar.gz: 280bbed579405d13d7c31f0931ddd4d63904bb5759f0468409307b040ba4b4fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22a28f8e0fc573736bf356a6e0744eef81a6e9d0ea7710ee1559094ffe1549d9ef01e4f9ca221dd93ff9d1f3813284b7e35cbfb79c63cbc18dc71c4a7d28b4ca
|
7
|
+
data.tar.gz: 70332cf06859be1a6ceb52ad2951acd5c5553bde9239d24210cec7a0ef142fe74bfa666ace03dc58cb3487039a76a0f3d0f9faec92ed154a002ed6a7515b54fa
|
data/Gemfile.lock
CHANGED
@@ -1,30 +1,28 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
kalc (1.
|
5
|
-
parslet (~> 1.
|
4
|
+
kalc (1.7.0)
|
5
|
+
parslet (~> 1.8.2)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
rspec-
|
17
|
-
|
18
|
-
rspec-
|
19
|
-
rspec-
|
20
|
-
rspec-support (~> 3.5.0)
|
21
|
-
rspec-expectations (3.5.0)
|
10
|
+
diff-lcs (1.3)
|
11
|
+
parslet (1.8.2)
|
12
|
+
rake (12.1.0)
|
13
|
+
rspec (3.6.0)
|
14
|
+
rspec-core (~> 3.6.0)
|
15
|
+
rspec-expectations (~> 3.6.0)
|
16
|
+
rspec-mocks (~> 3.6.0)
|
17
|
+
rspec-core (3.6.0)
|
18
|
+
rspec-support (~> 3.6.0)
|
19
|
+
rspec-expectations (3.6.0)
|
22
20
|
diff-lcs (>= 1.2.0, < 2.0)
|
23
|
-
rspec-support (~> 3.
|
24
|
-
rspec-mocks (3.
|
21
|
+
rspec-support (~> 3.6.0)
|
22
|
+
rspec-mocks (3.6.0)
|
25
23
|
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
-
rspec-support (~> 3.
|
27
|
-
rspec-support (3.
|
24
|
+
rspec-support (~> 3.6.0)
|
25
|
+
rspec-support (3.6.0)
|
28
26
|
|
29
27
|
PLATFORMS
|
30
28
|
java
|
@@ -36,4 +34,4 @@ DEPENDENCIES
|
|
36
34
|
rspec
|
37
35
|
|
38
36
|
BUNDLED WITH
|
39
|
-
1.
|
37
|
+
1.16.2
|
data/LICENSE
CHANGED
data/kalc.gemspec
CHANGED
@@ -1,25 +1,23 @@
|
|
1
|
-
|
2
|
-
$:.push File.expand_path('../lib', __FILE__)
|
1
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
3
2
|
require 'kalc/version'
|
4
|
-
#
|
5
3
|
Gem::Specification.new do |s|
|
6
4
|
s.name = 'kalc'
|
7
5
|
s.version = Kalc::VERSION
|
8
6
|
s.authors = ['Chris Parker']
|
9
|
-
s.email = %w
|
7
|
+
s.email = %w[mrcsparker@gmail.com]
|
10
8
|
s.homepage = 'https://github.com/mrcsparker/kalc'
|
11
|
-
s.summary =
|
12
|
-
s.description =
|
9
|
+
s.summary = 'Small calculation language.'
|
10
|
+
s.description = "Calculation language slightly based on Excel's formula language."
|
13
11
|
|
14
12
|
s.rubyforge_project = 'kalc'
|
15
13
|
|
16
14
|
s.files = `git ls-files`.split("\n")
|
17
15
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
19
|
-
s.require_paths = %w
|
17
|
+
s.require_paths = %w[lib]
|
20
18
|
|
21
19
|
# specify any dependencies here; for example:
|
22
20
|
s.add_development_dependency 'rake'
|
23
21
|
s.add_development_dependency 'rspec'
|
24
|
-
s.add_runtime_dependency 'parslet', '~> 1.
|
22
|
+
s.add_runtime_dependency 'parslet', '~> 1.8.2'
|
25
23
|
end
|
data/lib/kalc/ast.rb
CHANGED
@@ -40,11 +40,9 @@ module Kalc
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def eval(context)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@value.eval(context)
|
47
|
-
end
|
43
|
+
-@value.eval(context)
|
44
|
+
rescue StandardError
|
45
|
+
@value.eval(context)
|
48
46
|
end
|
49
47
|
end
|
50
48
|
|
@@ -57,11 +55,9 @@ module Kalc
|
|
57
55
|
end
|
58
56
|
|
59
57
|
def eval(context)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
@value.eval(context)
|
64
|
-
end
|
58
|
+
+@value.eval(context)
|
59
|
+
rescue StandardError
|
60
|
+
@value.eval(context)
|
65
61
|
end
|
66
62
|
end
|
67
63
|
|
@@ -72,8 +68,8 @@ module Kalc
|
|
72
68
|
@value = value
|
73
69
|
end
|
74
70
|
|
75
|
-
def eval(
|
76
|
-
@value == 'TRUE'
|
71
|
+
def eval(_context)
|
72
|
+
@value == 'TRUE'
|
77
73
|
end
|
78
74
|
end
|
79
75
|
|
@@ -81,11 +77,11 @@ module Kalc
|
|
81
77
|
attr_reader :value
|
82
78
|
|
83
79
|
def initialize(value)
|
84
|
-
@value = BigDecimal
|
80
|
+
@value = BigDecimal(value.to_s)
|
85
81
|
end
|
86
82
|
|
87
|
-
def eval(
|
88
|
-
BigDecimal
|
83
|
+
def eval(_context)
|
84
|
+
BigDecimal(@value)
|
89
85
|
end
|
90
86
|
end
|
91
87
|
|
@@ -123,10 +119,10 @@ module Kalc
|
|
123
119
|
end
|
124
120
|
|
125
121
|
def eval(context)
|
126
|
-
@ops.inject(@left.eval(context))
|
122
|
+
@ops.inject(@left.eval(context)) do |x, op|
|
127
123
|
a = Arithmetic.new(x, op[:right].eval(context), op[:operator])
|
128
124
|
a.eval(context)
|
129
|
-
|
125
|
+
end
|
130
126
|
end
|
131
127
|
end
|
132
128
|
|
@@ -141,7 +137,7 @@ module Kalc
|
|
141
137
|
@operator = operator
|
142
138
|
end
|
143
139
|
|
144
|
-
def eval(
|
140
|
+
def eval(_context)
|
145
141
|
case @operator.to_s.strip
|
146
142
|
when '&&'
|
147
143
|
@left && @right
|
@@ -218,7 +214,8 @@ module Kalc
|
|
218
214
|
|
219
215
|
def eval(context)
|
220
216
|
var = context.get_variable(@variable)
|
221
|
-
|
217
|
+
raise "Invalid variable: #{@variable}" unless var
|
218
|
+
|
222
219
|
var.class == BigDecimal ? var : var.eval(context)
|
223
220
|
end
|
224
221
|
end
|
@@ -230,7 +227,7 @@ module Kalc
|
|
230
227
|
@value = value
|
231
228
|
end
|
232
229
|
|
233
|
-
def eval(
|
230
|
+
def eval(_context)
|
234
231
|
value.to_s
|
235
232
|
end
|
236
233
|
end
|
@@ -246,11 +243,12 @@ module Kalc
|
|
246
243
|
|
247
244
|
def eval(context)
|
248
245
|
to_call = context.get_function(@name)
|
249
|
-
|
246
|
+
raise "Unknown function #{@name}" unless to_call
|
247
|
+
|
250
248
|
to_call.call(context, *@variable_list)
|
251
249
|
rescue ArgumentError
|
252
|
-
|
253
|
-
|
250
|
+
raise "Argument Error. Function #{@name} was called with #{@variable_list.count} parameters. " \
|
251
|
+
"It needs at least #{to_call.parameters.select { |a| a.first == :req }.count - 1} parameters"
|
254
252
|
end
|
255
253
|
end
|
256
254
|
|
data/lib/kalc/environment.rb
CHANGED
@@ -11,31 +11,27 @@ module Kalc
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def add_function(name, value)
|
14
|
-
@functions.update(
|
14
|
+
@functions.update(name.to_s.strip => value)
|
15
15
|
end
|
16
16
|
|
17
17
|
def get_function(name)
|
18
|
-
if fun = @functions[name.to_s.strip]
|
18
|
+
if (fun = @functions[name.to_s.strip])
|
19
19
|
fun
|
20
20
|
elsif !@parent.nil?
|
21
21
|
@parent.get_function(name)
|
22
|
-
else
|
23
|
-
nil
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
25
|
def add_variable(name, value)
|
28
|
-
@variables.update(
|
26
|
+
@variables.update(name.to_s.strip => value)
|
29
27
|
value
|
30
28
|
end
|
31
29
|
|
32
30
|
def get_variable(name)
|
33
|
-
if var = @variables[name.to_s.strip]
|
31
|
+
if (var = @variables[name.to_s.strip])
|
34
32
|
var
|
35
33
|
elsif !@parent.nil?
|
36
34
|
@parent.get_variable(name)
|
37
|
-
else
|
38
|
-
nil
|
39
35
|
end
|
40
36
|
end
|
41
37
|
end
|
data/lib/kalc/grammar.rb
CHANGED
@@ -27,26 +27,24 @@ class Kalc::Grammar < Parslet::Parser
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
symbols :
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
30
|
+
symbols left_paren: '(',
|
31
|
+
right_paren: ')',
|
32
|
+
left_brace: '{',
|
33
|
+
right_brace: '}',
|
34
|
+
comma: ',',
|
35
|
+
colon: ':',
|
36
|
+
question_mark: '?'
|
37
37
|
|
38
38
|
def self.operators(operators = {})
|
39
39
|
trailing_chars = Hash.new { |hash, symbol| hash[symbol] = [] }
|
40
40
|
|
41
41
|
operators.each_value do |symbol|
|
42
42
|
operators.each_value do |op|
|
43
|
-
|
44
|
-
char = op[symbol.length, 1]
|
43
|
+
next unless op[0, symbol.length] == symbol
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
45
|
+
char = op[symbol.length, 1]
|
46
|
+
|
47
|
+
trailing_chars[symbol] << char unless char.nil? || char.empty?
|
50
48
|
end
|
51
49
|
end
|
52
50
|
|
@@ -54,238 +52,232 @@ class Kalc::Grammar < Parslet::Parser
|
|
54
52
|
trailing = trailing_chars[symbol]
|
55
53
|
|
56
54
|
if trailing.empty?
|
57
|
-
rule(name) {
|
55
|
+
rule(name) {str(symbol).as(:operator) >> spaces?}
|
58
56
|
else
|
59
57
|
pattern = "[#{Regexp.escape(trailing.join)}]"
|
60
58
|
|
61
|
-
rule(name)
|
59
|
+
rule(name) do
|
62
60
|
(str(symbol) >> match(pattern).absnt?).as(:operator) >> spaces?
|
63
|
-
|
61
|
+
end
|
64
62
|
end
|
65
63
|
end
|
66
64
|
end
|
67
65
|
|
68
|
-
operators :
|
69
|
-
:
|
70
|
-
:
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
75
|
-
:
|
76
|
-
|
77
|
-
:
|
78
|
-
:
|
79
|
-
|
80
|
-
:
|
81
|
-
:
|
82
|
-
:
|
83
|
-
:
|
84
|
-
:
|
85
|
-
:
|
86
|
-
|
87
|
-
:
|
88
|
-
:
|
89
|
-
|
90
|
-
rule(:true_keyword)
|
66
|
+
operators logical_and: '&&',
|
67
|
+
string_and: 'and',
|
68
|
+
logical_or: '||',
|
69
|
+
string_or: 'or',
|
70
|
+
less_equal: '<=',
|
71
|
+
greater_equal: '>=',
|
72
|
+
equal: '==',
|
73
|
+
not_equal: '!=',
|
74
|
+
|
75
|
+
assign: ':=',
|
76
|
+
excel_equal: '=',
|
77
|
+
|
78
|
+
subtract: '-',
|
79
|
+
add: '+',
|
80
|
+
multiply: '*',
|
81
|
+
divide: '/',
|
82
|
+
modulus: '%',
|
83
|
+
power_of: '^',
|
84
|
+
|
85
|
+
less: '<',
|
86
|
+
greater: '>'
|
87
|
+
|
88
|
+
rule(:true_keyword) do
|
91
89
|
str('TRUE') >> spaces?
|
92
|
-
|
90
|
+
end
|
93
91
|
|
94
|
-
rule(:false_keyword)
|
92
|
+
rule(:false_keyword) do
|
95
93
|
str('FALSE') >> spaces?
|
96
|
-
|
94
|
+
end
|
97
95
|
|
98
|
-
rule(:boolean)
|
96
|
+
rule(:boolean) do
|
99
97
|
(true_keyword | false_keyword).as(:boolean)
|
100
|
-
|
98
|
+
end
|
101
99
|
|
102
|
-
rule(:string)
|
100
|
+
rule(:string) do
|
103
101
|
str('"') >>
|
104
|
-
|
105
|
-
str('\\') >> any |
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
rule(:exponent) {
|
102
|
+
(
|
103
|
+
str('\\') >> any | str('"').absnt? >> any
|
104
|
+
).repeat.as(:string) >> str('"')
|
105
|
+
end
|
106
|
+
|
107
|
+
rule(:exponent) do
|
112
108
|
match('[eE]') >> match('[-+]').maybe >> digits
|
113
|
-
|
109
|
+
end
|
114
110
|
|
115
111
|
# We are using a really broad definition of what a number is.
|
116
112
|
# Numbers can be 1, 1.0, 0.1, 1.0e4, +1.0E10, etc
|
117
|
-
rule(:number)
|
113
|
+
rule(:number) do
|
118
114
|
(match('[+-]').maybe >>
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
}
|
115
|
+
(str('.') >> digits >> exponent.maybe).as(:number) >> spaces?) |
|
116
|
+
(match('[+-]').maybe >> digits >> (str('.') >> digits).maybe >> exponent.maybe).as(:number) >> spaces?
|
117
|
+
end
|
123
118
|
|
124
|
-
rule(:identifier)
|
119
|
+
rule(:identifier) do
|
125
120
|
(alpha >> (alpha | digit).repeat) >> spaces?
|
126
|
-
|
121
|
+
end
|
127
122
|
|
128
|
-
rule(:quoted_identifier)
|
129
|
-
str("'") >>
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
str("'") >> spaces?
|
134
|
-
}
|
123
|
+
rule(:quoted_identifier) do
|
124
|
+
str("'") >> (
|
125
|
+
str('\\') >> any | str("'").absnt? >> any
|
126
|
+
).repeat(1) >> str("'") >> spaces?
|
127
|
+
end
|
135
128
|
|
136
|
-
rule(:argument)
|
129
|
+
rule(:argument) do
|
137
130
|
identifier.as(:argument)
|
138
|
-
|
131
|
+
end
|
139
132
|
|
140
133
|
# Should look like 'Name'
|
141
|
-
rule(:variable)
|
142
|
-
#identifier | (str("'") >> spaces? >> identifier.repeat >> str("'")) >> spaces?
|
134
|
+
rule(:variable) do
|
143
135
|
identifier | quoted_identifier
|
144
|
-
|
136
|
+
end
|
145
137
|
|
146
138
|
# Does not self-evaluate
|
147
139
|
# Use to call function: FUNCTION_NAME(variable_list, ..)
|
148
|
-
rule(:variable_list)
|
140
|
+
rule(:variable_list) do
|
149
141
|
conditional_expression >> (comma >> conditional_expression).repeat
|
150
|
-
|
142
|
+
end
|
151
143
|
|
152
|
-
rule(:paren_variable_list)
|
144
|
+
rule(:paren_variable_list) do
|
153
145
|
(left_paren >> variable_list.repeat >> right_paren).as(:paren_list)
|
154
|
-
|
146
|
+
end
|
155
147
|
|
156
148
|
# Does not self-evaluate
|
157
149
|
# Used to create function: DEF FUNCTION_NAME(argument_list, ..)
|
158
|
-
rule(:argument_list)
|
150
|
+
rule(:argument_list) do
|
159
151
|
argument >> (comma >> argument).repeat
|
160
|
-
|
152
|
+
end
|
161
153
|
|
162
|
-
rule(:paren_argument_list)
|
154
|
+
rule(:paren_argument_list) do
|
163
155
|
(left_paren >> argument_list.repeat >> right_paren).as(:paren_list)
|
164
|
-
|
156
|
+
end
|
165
157
|
|
166
158
|
# Atoms can self-evaluate
|
167
159
|
# This where the grammar starts
|
168
|
-
rule(:atom)
|
160
|
+
rule(:atom) do
|
169
161
|
paren_expression.as(:paren_expression) | boolean | variable.as(:variable) | number | string
|
170
|
-
|
162
|
+
end
|
171
163
|
|
172
164
|
# (1 + 2)
|
173
|
-
rule(:paren_expression)
|
165
|
+
rule(:paren_expression) do
|
174
166
|
left_paren >> conditional_expression >> right_paren
|
175
|
-
|
167
|
+
end
|
176
168
|
|
177
|
-
rule(:non_ops_expression)
|
169
|
+
rule(:non_ops_expression) do
|
178
170
|
(atom.as(:left) >>
|
179
|
-
|
180
|
-
|
181
|
-
|
171
|
+
power_of >>
|
172
|
+
atom.as(:right)).as(:non_ops) | atom
|
173
|
+
end
|
182
174
|
|
183
175
|
# IF(1, 2, 3)
|
184
176
|
# AND(1, 2, ...)
|
185
|
-
rule(:function_call_expression)
|
177
|
+
rule(:function_call_expression) do
|
186
178
|
(identifier.as(:name) >> paren_variable_list.as(:variable_list)).as(:function_call) |
|
187
|
-
|
188
|
-
|
189
|
-
|
179
|
+
(str('+') >> non_ops_expression).as(:positive) |
|
180
|
+
(str('-') >> non_ops_expression).as(:negative) | non_ops_expression
|
181
|
+
end
|
190
182
|
|
191
183
|
# 1 + 2
|
192
|
-
rule(:additive_expression)
|
184
|
+
rule(:additive_expression) do
|
193
185
|
multiplicative_expression.as(:left) >>
|
194
|
-
|
195
|
-
|
196
|
-
|
186
|
+
((add | subtract) >>
|
187
|
+
multiplicative_expression.as(:right)).repeat.as(:ops)
|
188
|
+
end
|
197
189
|
|
198
190
|
# 1 * 2
|
199
|
-
rule(:multiplicative_expression)
|
191
|
+
rule(:multiplicative_expression) do
|
200
192
|
function_call_expression.as(:left) >>
|
201
|
-
|
202
|
-
|
203
|
-
|
193
|
+
((multiply | divide | modulus) >>
|
194
|
+
function_call_expression.as(:right)).repeat.as(:ops)
|
195
|
+
end
|
204
196
|
|
205
197
|
# 1 < 2
|
206
198
|
# 1 > 2
|
207
199
|
# 1 <= 2
|
208
200
|
# 1 >= 2
|
209
|
-
rule(:relational_expression)
|
201
|
+
rule(:relational_expression) do
|
210
202
|
additive_expression.as(:left) >>
|
211
|
-
|
212
|
-
|
213
|
-
|
203
|
+
((less | greater | less_equal | greater_equal) >>
|
204
|
+
relational_expression.as(:right)).repeat.as(:ops)
|
205
|
+
end
|
214
206
|
|
215
207
|
# 1 = 2
|
216
|
-
rule(:equality_expression)
|
208
|
+
rule(:equality_expression) do
|
217
209
|
relational_expression.as(:left) >>
|
218
|
-
|
219
|
-
|
220
|
-
|
210
|
+
((excel_equal | equal | not_equal) >>
|
211
|
+
equality_expression.as(:right)).repeat.as(:ops)
|
212
|
+
end
|
221
213
|
|
222
214
|
# 1 && 2
|
223
|
-
rule(:logical_and_expression)
|
215
|
+
rule(:logical_and_expression) do
|
224
216
|
equality_expression.as(:left) >>
|
225
|
-
|
226
|
-
|
227
|
-
|
217
|
+
((logical_and | string_and) >>
|
218
|
+
logical_and_expression.as(:right)).repeat.as(:ops)
|
219
|
+
end
|
228
220
|
|
229
221
|
# 1 || 2
|
230
|
-
rule(:logical_or_expression)
|
222
|
+
rule(:logical_or_expression) do
|
231
223
|
logical_and_expression.as(:left) >>
|
232
|
-
|
233
|
-
|
234
|
-
|
224
|
+
((logical_or | string_or) >>
|
225
|
+
logical_or_expression.as(:right)).repeat.as(:ops)
|
226
|
+
end
|
235
227
|
|
236
228
|
# 1 > 2 ? 3 : 4
|
237
|
-
rule(:conditional_expression)
|
229
|
+
rule(:conditional_expression) do
|
238
230
|
logical_or_expression.as(:condition) >>
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
231
|
+
(question_mark >>
|
232
|
+
conditional_expression.as(:true_cond) >>
|
233
|
+
colon >>
|
234
|
+
conditional_expression.as(:false_cond)).maybe
|
235
|
+
end
|
244
236
|
|
245
237
|
# 'a' = 1
|
246
238
|
# We don't allow for nested assignments:
|
247
239
|
# IF('a' = 1, 1, 2)
|
248
|
-
rule(:assignment_expression)
|
240
|
+
rule(:assignment_expression) do
|
249
241
|
(variable.as(:identifier) >>
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
242
|
+
assign >>
|
243
|
+
assignment_expression.as(:value)).as(:assign) |
|
244
|
+
conditional_expression
|
245
|
+
end
|
254
246
|
|
255
|
-
rule(:expression)
|
247
|
+
rule(:expression) do
|
256
248
|
assignment_expression
|
257
|
-
|
249
|
+
end
|
258
250
|
|
259
|
-
rule(:expressions)
|
251
|
+
rule(:expressions) do
|
260
252
|
expression >> (separator >> expressions).repeat
|
261
|
-
|
253
|
+
end
|
262
254
|
|
263
|
-
rule(:function_body)
|
255
|
+
rule(:function_body) do
|
264
256
|
expressions.as(:expressions)
|
265
|
-
|
257
|
+
end
|
266
258
|
|
267
|
-
rule(:function_definition_expression)
|
259
|
+
rule(:function_definition_expression) do
|
268
260
|
(str('DEFINE') >> spaces? >> identifier.as(:name) >>
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
261
|
+
paren_argument_list.as(:argument_list) >>
|
262
|
+
left_brace >> function_body.as(:body) >> right_brace).as(:function_definition) |
|
263
|
+
expressions.as(:expressions)
|
264
|
+
end
|
273
265
|
|
274
|
-
rule(:function_definition_expressions)
|
266
|
+
rule(:function_definition_expressions) do
|
275
267
|
function_definition_expression >> separator.maybe >> function_definition_expressions.repeat
|
276
|
-
|
268
|
+
end
|
277
269
|
|
278
|
-
rule(:commands)
|
270
|
+
rule(:commands) do
|
279
271
|
function_definition_expressions | expressions
|
280
|
-
|
272
|
+
end
|
281
273
|
|
282
|
-
rule(:line)
|
274
|
+
rule(:line) do
|
283
275
|
commands.as(:commands)
|
284
|
-
|
276
|
+
end
|
285
277
|
|
286
|
-
rule(:lines)
|
278
|
+
rule(:lines) do
|
287
279
|
line >> (new_line >> lines).repeat
|
288
|
-
|
280
|
+
end
|
289
281
|
|
290
282
|
root :lines
|
291
283
|
end
|
data/lib/kalc/interpreter.rb
CHANGED
@@ -8,7 +8,6 @@ module Kalc
|
|
8
8
|
|
9
9
|
def initialize
|
10
10
|
@env = Environment.new do |env|
|
11
|
-
|
12
11
|
env.add_function(:IF, lambda { |cxt, cond, if_true, if_false|
|
13
12
|
cond.eval(cxt) ? if_true.eval(cxt) : if_false.eval(cxt)
|
14
13
|
})
|
@@ -43,7 +42,7 @@ module Kalc
|
|
43
42
|
rand(val.eval(cxt))
|
44
43
|
})
|
45
44
|
|
46
|
-
env.add_function(:SYSTEM, lambda { |
|
45
|
+
env.add_function(:SYSTEM, lambda { |_cxt, _val|
|
47
46
|
throw "Nope. I don't think so!"
|
48
47
|
})
|
49
48
|
|
@@ -99,15 +98,21 @@ module Kalc
|
|
99
98
|
})
|
100
99
|
|
101
100
|
env.add_function(:MAX, lambda { |cxt, first, *args|
|
102
|
-
(args<<first).map { |a| a.eval(cxt) }
|
101
|
+
evaluated_args = (args << first).map { |a| a.eval(cxt) }
|
102
|
+
return BigDecimal('NaN') if evaluated_args.any? { |a| a.is_a?(BigDecimal) && a.nan? }
|
103
|
+
|
104
|
+
evaluated_args.max
|
103
105
|
})
|
104
106
|
|
105
107
|
env.add_function(:MIN, lambda { |cxt, first, *args|
|
106
|
-
(args<<first).map { |a| a.eval(cxt) }
|
108
|
+
evaluated_args = (args << first).map { |a| a.eval(cxt) }
|
109
|
+
return BigDecimal('NaN') if evaluated_args.any? { |a| a.is_a?(BigDecimal) && a.nan? }
|
110
|
+
|
111
|
+
evaluated_args.min
|
107
112
|
})
|
108
113
|
|
109
114
|
math_funs =
|
110
|
-
|
115
|
+
%w[acos acosh asin asinh atan atanh cbrt cos cosh erf erfc exp gamma lgamma log log2 log10 sin sinh sqrt tan tanh]
|
111
116
|
|
112
117
|
math_funs.each do |math_fun|
|
113
118
|
env.add_function(math_fun.upcase.to_sym, lambda { |cxt, val|
|
@@ -117,12 +122,12 @@ module Kalc
|
|
117
122
|
|
118
123
|
# Strings
|
119
124
|
string_funs =
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
125
|
+
%w[chomp chop chr clear count
|
126
|
+
downcase
|
127
|
+
hex
|
128
|
+
inspect intern
|
129
|
+
to_sym length size lstrip succ next oct ord reverse rstrip strip
|
130
|
+
swapcase to_c to_f to_i to_r upcase]
|
126
131
|
|
127
132
|
string_funs.each do |str_fun|
|
128
133
|
env.add_function(str_fun.upcase.to_sym, lambda { |cxt, val|
|
@@ -147,7 +152,7 @@ module Kalc
|
|
147
152
|
})
|
148
153
|
|
149
154
|
env.add_function(:DOLLAR, lambda { |cxt, val, decimal_places|
|
150
|
-
"%.#{Integer(decimal_places.eval(cxt))}f"
|
155
|
+
format("%.#{Integer(decimal_places.eval(cxt))}f", BigDecimal(val.eval(cxt)))
|
151
156
|
})
|
152
157
|
|
153
158
|
env.add_function(:EXACT, lambda { |cxt, string1, string2|
|
@@ -160,8 +165,8 @@ module Kalc
|
|
160
165
|
})
|
161
166
|
|
162
167
|
env.add_function(:FIXED, lambda { |cxt, val, decimal_places, no_commas|
|
163
|
-
output = "%.#{Integer(decimal_places.eval(cxt))}f"
|
164
|
-
output = output.to_s.reverse.scan(/(?:\d*\.)?\d{1,3}-?/).join(',').reverse
|
168
|
+
output = format("%.#{Integer(decimal_places.eval(cxt))}f", BigDecimal(val.eval(cxt)))
|
169
|
+
output = output.to_s.reverse.scan(/(?:\d*\.)?\d{1,3}-?/).join(',').reverse unless no_commas.eval(cxt)
|
165
170
|
output
|
166
171
|
})
|
167
172
|
|
@@ -179,7 +184,6 @@ module Kalc
|
|
179
184
|
})
|
180
185
|
|
181
186
|
env.add_function(:MID, lambda { |cxt, val, start_position, number_of_characters|
|
182
|
-
|
183
187
|
start = Integer(start_position.eval(cxt)) - 1
|
184
188
|
chars = Integer(number_of_characters.eval(cxt)) - 1
|
185
189
|
|
@@ -187,7 +191,7 @@ module Kalc
|
|
187
191
|
})
|
188
192
|
|
189
193
|
env.add_function(:PROPER, lambda { |cxt, val|
|
190
|
-
val.eval(cxt).split(' ').map
|
194
|
+
val.eval(cxt).split(' ').map(&:capitalize).join(' ')
|
191
195
|
})
|
192
196
|
|
193
197
|
env.add_function(:REPLACE, lambda { |cxt, val, start_position, number_of_chars, new_text|
|
@@ -242,11 +246,11 @@ module Kalc
|
|
242
246
|
})
|
243
247
|
|
244
248
|
# Debug
|
245
|
-
env.add_function(:P, lambda { |
|
249
|
+
env.add_function(:P, lambda { |_cxt, *output|
|
246
250
|
p output
|
247
251
|
})
|
248
252
|
|
249
|
-
env.add_function(:PP, lambda { |
|
253
|
+
env.add_function(:PP, lambda { |_cxt, *output|
|
250
254
|
pp output
|
251
255
|
})
|
252
256
|
|
@@ -255,13 +259,18 @@ module Kalc
|
|
255
259
|
})
|
256
260
|
|
257
261
|
env.add_function(:FLOOR, lambda { |cxt, val|
|
258
|
-
val.eval(cxt)
|
262
|
+
value = val.eval(cxt)
|
263
|
+
return value if value.is_a?(BigDecimal) && (value.nan? || value.infinite?)
|
264
|
+
|
265
|
+
value.floor
|
259
266
|
})
|
260
267
|
|
261
268
|
env.add_function(:CEILING, lambda { |cxt, val|
|
262
|
-
val.eval(cxt)
|
263
|
-
|
269
|
+
value = val.eval(cxt)
|
270
|
+
return value if value.is_a?(BigDecimal) && (value.nan? || value.infinite?)
|
264
271
|
|
272
|
+
value.ceil
|
273
|
+
})
|
265
274
|
end
|
266
275
|
end
|
267
276
|
|
data/lib/kalc/repl.rb
CHANGED
@@ -18,27 +18,26 @@ module Kalc
|
|
18
18
|
puts 'You are ready to go. Have fun!'
|
19
19
|
puts ''
|
20
20
|
|
21
|
-
function_list = %w
|
21
|
+
function_list = %w[quit exit functions variables ast] + @kalc.interpreter.env.functions.map(&:first)
|
22
22
|
|
23
23
|
begin
|
24
24
|
comp = proc { |s| function_list.grep(/^#{Regexp.escape(s)}/) }
|
25
25
|
Readline.completion_append_character = ''
|
26
26
|
Readline.completion_proc = comp
|
27
27
|
|
28
|
-
while input = Readline.readline("kalc-#{Kalc::VERSION} > ", true)
|
28
|
+
while (input = Readline.readline("kalc-#{Kalc::VERSION} > ", true))
|
29
29
|
begin
|
30
|
-
|
31
|
-
when (input == 'quit' || input == 'exit')
|
30
|
+
if input == 'quit' || input == 'exit'
|
32
31
|
break
|
33
|
-
|
34
|
-
puts @kalc.interpreter.env.functions.map
|
35
|
-
|
32
|
+
elsif input == 'functions'
|
33
|
+
puts @kalc.interpreter.env.functions.map(&:first).join(', ')
|
34
|
+
elsif input == 'variables'
|
36
35
|
puts @kalc.interpreter.env.variables.map { |v| "#{v[0]} = #{v[1]}" }.join("\n\r")
|
37
|
-
|
36
|
+
elsif input == 'reload'
|
38
37
|
load_env
|
39
|
-
|
38
|
+
elsif input == 'ast'
|
40
39
|
pp @kalc.ast
|
41
|
-
|
40
|
+
elsif input != ''
|
42
41
|
puts @kalc.run(input)
|
43
42
|
end
|
44
43
|
rescue Parslet::ParseFailed => e
|
@@ -55,12 +54,12 @@ module Kalc
|
|
55
54
|
end
|
56
55
|
|
57
56
|
def heading
|
58
|
-
|
57
|
+
'
|
59
58
|
This is Kalc, a small line-based language.
|
60
59
|
More information about Kalc can be found at https://github.com/mrcsparker/kalc.
|
61
60
|
|
62
61
|
Kalc is free software, provided as is, with no warranty.
|
63
|
-
|
62
|
+
'
|
64
63
|
end
|
65
64
|
end
|
66
65
|
end
|
data/lib/kalc/transform.rb
CHANGED
@@ -1,116 +1,115 @@
|
|
1
1
|
module Kalc
|
2
2
|
class Transform < Parslet::Transform
|
3
|
-
|
4
|
-
rule(:condition => subtree(:condition)) {
|
3
|
+
rule(condition: subtree(:condition)) do
|
5
4
|
condition
|
6
|
-
|
5
|
+
end
|
7
6
|
|
8
|
-
rule(:
|
7
|
+
rule(right: subtree(:right), ops: []) do
|
9
8
|
right
|
10
|
-
|
9
|
+
end
|
11
10
|
|
12
|
-
rule(:
|
11
|
+
rule(left: subtree(:left), non_ops: []) do
|
13
12
|
left
|
14
|
-
|
13
|
+
end
|
15
14
|
|
16
|
-
rule(:
|
15
|
+
rule(right: subtree(:right), non_ops: []) do
|
17
16
|
right
|
18
|
-
|
17
|
+
end
|
19
18
|
|
20
|
-
rule(:
|
19
|
+
rule(commands: sequence(:commands)) do
|
21
20
|
Ast::Commands.new(commands)
|
22
|
-
|
21
|
+
end
|
23
22
|
|
24
|
-
rule(:
|
23
|
+
rule(commands: simple(:commands)) do
|
25
24
|
Ast::Commands.new([commands])
|
26
|
-
|
25
|
+
end
|
27
26
|
|
28
|
-
rule(:
|
27
|
+
rule(negative: simple(:negative)) do
|
29
28
|
Ast::Negative.new(negative)
|
30
|
-
|
29
|
+
end
|
31
30
|
|
32
|
-
rule(:
|
31
|
+
rule(positive: simple(:positive)) do
|
33
32
|
Ast::Positive.new(positive)
|
34
|
-
|
33
|
+
end
|
35
34
|
|
36
|
-
rule(:
|
35
|
+
rule(expressions: sequence(:expressions)) do
|
37
36
|
Ast::Expressions.new(expressions)
|
38
|
-
|
37
|
+
end
|
39
38
|
|
40
|
-
rule(:
|
39
|
+
rule(expressions: simple(:expressions)) do
|
41
40
|
Ast::Expressions.new([expressions])
|
42
|
-
|
41
|
+
end
|
43
42
|
|
44
|
-
rule(:
|
43
|
+
rule(string: simple(:string)) do
|
45
44
|
Ast::StringValue.new(string)
|
46
|
-
|
45
|
+
end
|
47
46
|
|
48
|
-
rule(:
|
49
|
-
string = '' if string
|
47
|
+
rule(string: sequence(:string)) do
|
48
|
+
string = '' if string.nil?
|
50
49
|
Ast::StringValue.new(string)
|
51
|
-
|
50
|
+
end
|
52
51
|
|
53
|
-
rule(:
|
52
|
+
rule(boolean: simple(:boolean)) do
|
54
53
|
Ast::BooleanValue.new(boolean)
|
55
|
-
|
54
|
+
end
|
56
55
|
|
57
|
-
rule(:
|
56
|
+
rule(number: simple(:number)) do
|
58
57
|
Ast::BigDecimalNumber.new(number)
|
59
|
-
|
58
|
+
end
|
60
59
|
|
61
|
-
rule(:
|
60
|
+
rule(non_ops: subtree(:non_ops)) do
|
62
61
|
Ast::NonOps.new(non_ops)
|
63
|
-
|
62
|
+
end
|
64
63
|
|
65
|
-
rule(:
|
64
|
+
rule(left: simple(:left), ops: subtree(:ops)) do
|
66
65
|
ops.empty? ? left : Ast::Ops.new(left, ops)
|
67
|
-
|
66
|
+
end
|
68
67
|
|
69
|
-
rule(:
|
68
|
+
rule(left: simple(:left), right: simple(:right), operator: simple(:operator)) do
|
70
69
|
Ast::Arithmetic.new(left, right, operator)
|
71
|
-
|
70
|
+
end
|
72
71
|
|
73
|
-
rule(:
|
72
|
+
rule(condition: simple(:condition), true_cond: simple(:true_cond), false_cond: simple(:false_cond)) do
|
74
73
|
Ast::Conditional.new(condition, true_cond, false_cond)
|
75
|
-
|
74
|
+
end
|
76
75
|
|
77
|
-
rule(:
|
76
|
+
rule(variable: simple(:variable)) do
|
78
77
|
Ast::Variable.new(variable)
|
79
|
-
|
78
|
+
end
|
80
79
|
|
81
|
-
rule(:
|
80
|
+
rule(identifier: simple(:identifier)) do
|
82
81
|
identifier
|
83
|
-
|
82
|
+
end
|
84
83
|
|
85
|
-
rule(:
|
84
|
+
rule(assign: { value: simple(:value), identifier: simple(:identifier), operator: simple(:operator) }) do
|
86
85
|
Ast::Identifier.new(identifier, value)
|
87
|
-
|
86
|
+
end
|
88
87
|
|
89
|
-
rule(:
|
88
|
+
rule(paren_list: sequence(:paren_list)) do
|
90
89
|
paren_list
|
91
|
-
|
90
|
+
end
|
92
91
|
|
93
|
-
rule(:
|
92
|
+
rule(paren_list: '()') do
|
94
93
|
[]
|
95
|
-
|
94
|
+
end
|
96
95
|
|
97
|
-
rule(:
|
96
|
+
rule(paren_expression: simple(:paren_expression)) do
|
98
97
|
Ast::ParenExpression.new(paren_expression)
|
99
|
-
|
98
|
+
end
|
100
99
|
|
101
|
-
rule(:
|
102
|
-
|
100
|
+
rule(function_call: { name: simple(:name),
|
101
|
+
variable_list: sequence(:variable_list) }) do
|
103
102
|
Ast::FunctionCall.new(name, variable_list)
|
104
|
-
|
103
|
+
end
|
105
104
|
|
106
|
-
rule(:
|
105
|
+
rule(argument: simple(:argument)) do
|
107
106
|
Ast::Identifier.new(argument, argument)
|
108
|
-
|
107
|
+
end
|
109
108
|
|
110
|
-
rule(:
|
111
|
-
|
112
|
-
|
109
|
+
rule(function_definition: { name: simple(:name),
|
110
|
+
argument_list: sequence(:argument_list),
|
111
|
+
body: simple(:body) }) do
|
113
112
|
Ast::FunctionDefinition.new(name, argument_list, body)
|
114
|
-
|
113
|
+
end
|
115
114
|
end
|
116
115
|
end
|
data/lib/kalc/version.rb
CHANGED
data/spec/grammar_spec.rb
CHANGED
data/spec/interpreter_spec.rb
CHANGED
@@ -44,7 +44,7 @@ describe Kalc::Interpreter do
|
|
44
44
|
expect(evaluate("'a b' := 1; 'a b' + 1")).to eq(2)
|
45
45
|
end
|
46
46
|
|
47
|
-
it { expect(evaluate('((75.0)*(25.0))+((125.0)*(25.0))+((150.0)*(25.0))+((250.0)*(25.0))')).to eq(
|
47
|
+
it { expect(evaluate('((75.0)*(25.0))+((125.0)*(25.0))+((150.0)*(25.0))+((250.0)*(25.0))')).to eq(15_000) }
|
48
48
|
|
49
49
|
it { expect(evaluate('(((40.0)/1000*(4380.0)*(200.0))-((40.0)/1000*(4380.0)*((((75.0)*(25.0))+((125.0)*(25.0))+((150.0)*(25.0))+((250.0)*(25.0)))/(10.0)/(40.0)/(0.8))))*(0.05)')).to eq(1341.375) }
|
50
50
|
|
@@ -52,21 +52,21 @@ describe Kalc::Interpreter do
|
|
52
52
|
it { expect(evaluate('-2')).to eq(-2) }
|
53
53
|
it { expect(evaluate('-1000')).to eq(-1000) }
|
54
54
|
it { expect(evaluate('-1000.0001')).to eq(-1000.0001) }
|
55
|
-
it { expect(evaluate('1 + -1')).to eq(0
|
55
|
+
it { expect(evaluate('1 + -1')).to eq(0) }
|
56
56
|
it { expect(evaluate('1 + -10')).to eq(-9) }
|
57
57
|
end
|
58
58
|
|
59
59
|
context 'Positive numbers' do
|
60
60
|
it { expect(evaluate('1 + +1')).to eq(2) }
|
61
61
|
it { expect(evaluate('1 + +1 - 1')).to eq(1) }
|
62
|
-
it { expect(evaluate('+10000.0001')).to eq(
|
62
|
+
it { expect(evaluate('+10000.0001')).to eq(10_000.0001) }
|
63
63
|
end
|
64
64
|
|
65
65
|
context 'Boolean value' do
|
66
66
|
it { expect(evaluate('TRUE')).to eq(true) }
|
67
67
|
it { expect(evaluate('FALSE')).to eq(false) }
|
68
68
|
it { expect(evaluate('FALSE || TRUE')).to eq(true) }
|
69
|
-
it { expect(evaluate('FALSE && TRUE')).to
|
69
|
+
it { expect(evaluate('FALSE && TRUE')).to eq(false) }
|
70
70
|
end
|
71
71
|
|
72
72
|
context 'Decimal numbers' do
|
@@ -84,7 +84,7 @@ describe Kalc::Interpreter do
|
|
84
84
|
end
|
85
85
|
|
86
86
|
context 'Exponents' do
|
87
|
-
it { expect(evaluate('1.23e+10')).to eq(
|
87
|
+
it { expect(evaluate('1.23e+10')).to eq(12_300_000_000.0) }
|
88
88
|
it { expect(evaluate('1.23e-10')).to eq(1.23e-10) }
|
89
89
|
end
|
90
90
|
|
@@ -96,8 +96,12 @@ describe Kalc::Interpreter do
|
|
96
96
|
context 'Min and Max Functions' do
|
97
97
|
it { expect(evaluate('MAX(3, 1, 2)')).to eq(3) }
|
98
98
|
it { expect(evaluate('MAX(-1, 2*4, (3-1)*2, 5, 6)')).to eq(8) }
|
99
|
+
it { expect(evaluate('MAX(0/0, 2)').to_s).to eq('NaN') }
|
100
|
+
it { expect(evaluate('MAX(1/0, 3)').to_s).to eq('Infinity') }
|
99
101
|
it { expect(evaluate('MIN(3, 1, 2)')).to eq(1) }
|
100
102
|
it { expect(evaluate('MIN(-1, 2*4, (3-1)*2, 5, 6)')).to eq(-1) }
|
103
|
+
it { expect(evaluate('MIN(0/0, 2)').to_s).to eq('NaN') }
|
104
|
+
it { expect(evaluate('MIN(-1/0, 3)').to_s).to eq('-Infinity') }
|
101
105
|
context 'having variables' do
|
102
106
|
it { expect(evaluate('var := 15; MAX(1, var, 10)')).to eq(15) }
|
103
107
|
it { expect(evaluate('var := 15; MIN(1, var, -10)')).to eq(-10) }
|
@@ -109,10 +113,14 @@ describe Kalc::Interpreter do
|
|
109
113
|
it { expect(evaluate('FLOOR(3.8)')).to eq(3) }
|
110
114
|
it { expect(evaluate('FLOOR(-3.4)')).to eq(-4) }
|
111
115
|
it { expect(evaluate('FLOOR(3)')).to eq(3) }
|
116
|
+
it { expect(evaluate('FLOOR(0/0)').to_s).to eq('NaN') }
|
117
|
+
it { expect(evaluate('FLOOR(1/0)').to_s).to eq('Infinity') }
|
112
118
|
it { expect(evaluate('CEILING(3)')).to eq(3) }
|
113
119
|
it { expect(evaluate('CEILING(3.8)')).to eq(4) }
|
114
120
|
it { expect(evaluate('CEILING(3.8)')).to eq(4) }
|
115
121
|
it { expect(evaluate('CEILING(-3.2)')).to eq(-3) }
|
122
|
+
it { expect(evaluate('CEILING(0/0)').to_s).to eq('NaN') }
|
123
|
+
it { expect(evaluate('CEILING(1/0)').to_s).to eq('Infinity') }
|
116
124
|
|
117
125
|
context 'having variables' do
|
118
126
|
it { expect(evaluate('var := 2.456; FLOOR(var)')).to eq(2) }
|
@@ -135,6 +143,7 @@ describe Kalc::Interpreter do
|
|
135
143
|
end
|
136
144
|
|
137
145
|
private
|
146
|
+
|
138
147
|
def evaluate(expression)
|
139
148
|
g = @grammar.parse(expression)
|
140
149
|
ast = @transform.apply(g)
|
data/spec/stdlib_spec.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
class Kalc::Stdlib
|
4
|
-
|
5
4
|
end
|
6
5
|
|
7
6
|
describe Kalc::Stdlib do
|
8
|
-
|
9
7
|
before(:each) do
|
10
8
|
@grammar = Kalc::Grammar.new
|
11
9
|
@transform = Kalc::Transform.new
|
@@ -51,6 +49,7 @@ describe Kalc::Stdlib do
|
|
51
49
|
end
|
52
50
|
|
53
51
|
private
|
52
|
+
|
54
53
|
def evaluate(expression)
|
55
54
|
r = Kalc::Runner.new
|
56
55
|
r.run(expression)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kalc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Parker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 1.8.2
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 1.8.2
|
55
55
|
description: Calculation language slightly based on Excel's formula language.
|
56
56
|
email:
|
57
57
|
- mrcsparker@gmail.com
|
@@ -103,13 +103,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
103
|
version: '0'
|
104
104
|
requirements: []
|
105
105
|
rubyforge_project: kalc
|
106
|
-
rubygems_version: 2.
|
106
|
+
rubygems_version: 2.7.7
|
107
107
|
signing_key:
|
108
108
|
specification_version: 4
|
109
109
|
summary: Small calculation language.
|
110
|
-
test_files:
|
111
|
-
- spec/grammar_spec.rb
|
112
|
-
- spec/interpreter_spec.rb
|
113
|
-
- spec/spec_helper.rb
|
114
|
-
- spec/stdlib_spec.rb
|
115
|
-
has_rdoc:
|
110
|
+
test_files: []
|