kalc 1.2.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|