dentaku 3.5.0 → 3.5.1
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/CHANGELOG.md +9 -0
- data/README.md +1 -1
- data/lib/dentaku/ast/arithmetic.rb +18 -17
- data/lib/dentaku/ast/bitwise.rb +23 -6
- data/lib/dentaku/ast/comparators.rb +12 -2
- data/lib/dentaku/bulk_expression_solver.rb +1 -5
- data/lib/dentaku/exceptions.rb +2 -2
- data/lib/dentaku/parser.rb +5 -0
- data/lib/dentaku/token_scanner.rb +2 -2
- data/lib/dentaku/version.rb +1 -1
- data/spec/ast/arithmetic_spec.rb +7 -0
- data/spec/ast/comparator_spec.rb +8 -0
- data/spec/ast/or_spec.rb +1 -1
- data/spec/bulk_expression_solver_spec.rb +9 -0
- data/spec/calculator_spec.rb +13 -0
- data/spec/tokenizer_spec.rb +12 -0
- data/spec/visitor_spec.rb +2 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d5592654ee45adeb24167b584374fb2d69bc67d1db4d5f4ac99050130f187f8f
|
|
4
|
+
data.tar.gz: 31d3952b08887ae934661f4c0ecc5c298b2f36f0f8365cc1503495eb044eb558
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8e0d003f897e06173c91b200e62d9fed12ec3bacfe0f2ecc3f3705a1cbf914d1705948b79962f5ac6a5397138ff89c2123aa5c3753963f0046a88280b29825b
|
|
7
|
+
data.tar.gz: 5f48d3b8fef4e56ed308e717da88937bef508e47dfca4c3e16610aff7523f11405e53e2b55d00c53b5eca8efbe7be64df0d0d12e7ddafbc61099cde08bf92a53
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## [v3.5.1]
|
|
4
|
+
- add bitwise shift left and shift right operators
|
|
5
|
+
- improve numeric conversions
|
|
6
|
+
- improve parse exceptions
|
|
7
|
+
- improve bitwise exceptions
|
|
8
|
+
- include variable name in bulk expression exceptions
|
|
9
|
+
|
|
3
10
|
## [v3.5.0]
|
|
4
11
|
- fix bug with function argument count
|
|
5
12
|
- add XOR operator
|
|
@@ -224,6 +231,8 @@
|
|
|
224
231
|
## [v0.1.0] 2012-01-20
|
|
225
232
|
- initial release
|
|
226
233
|
|
|
234
|
+
[Unreleased]: https://github.com/rubysolo/dentaku/compare/v3.5.1...HEAD
|
|
235
|
+
[v3.5.1]: https://github.com/rubysolo/dentaku/compare/v3.5.0...v3.5.1
|
|
227
236
|
[v3.5.0]: https://github.com/rubysolo/dentaku/compare/v3.4.2...v3.5.0
|
|
228
237
|
[v3.4.2]: https://github.com/rubysolo/dentaku/compare/v3.4.1...v3.4.2
|
|
229
238
|
[v3.4.1]: https://github.com/rubysolo/dentaku/compare/v3.4.0...v3.4.1
|
data/README.md
CHANGED
|
@@ -137,7 +137,7 @@ application, AST caching will consume more memory with each new formula.
|
|
|
137
137
|
BUILT-IN OPERATORS AND FUNCTIONS
|
|
138
138
|
---------------------------------
|
|
139
139
|
|
|
140
|
-
Math: `+`, `-`, `*`, `/`, `%`, `^`, `|`,
|
|
140
|
+
Math: `+`, `-`, `*`, `/`, `%`, `^`, `|`, `&`, `<<`, `>>`
|
|
141
141
|
|
|
142
142
|
Also, all functions from Ruby's Math module, including `SIN`, `COS`, `TAN`, etc.
|
|
143
143
|
|
|
@@ -31,29 +31,30 @@ module Dentaku
|
|
|
31
31
|
def value(context = {})
|
|
32
32
|
l = cast(left.value(context))
|
|
33
33
|
r = cast(right.value(context))
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
end
|
|
34
|
+
|
|
35
|
+
l.public_send(operator, r)
|
|
36
|
+
rescue ::TypeError => e
|
|
37
|
+
# Right cannot be converted to a suitable type for left. e.g. [] + 1
|
|
38
|
+
raise Dentaku::ArgumentError.for(:incompatible_type, value: r, for: l.class), e.message
|
|
40
39
|
end
|
|
41
40
|
|
|
42
41
|
private
|
|
43
42
|
|
|
44
|
-
def cast(val
|
|
43
|
+
def cast(val)
|
|
45
44
|
validate_value(val)
|
|
46
|
-
numeric(val
|
|
45
|
+
numeric(val)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def numeric(val)
|
|
49
|
+
case val.to_s
|
|
50
|
+
when /\A\d*\.\d+\z/ then decimal(val)
|
|
51
|
+
when /\A-?\d+\z/ then val.to_i
|
|
52
|
+
else val
|
|
53
|
+
end
|
|
47
54
|
end
|
|
48
55
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
v = v.to_i if prefer_integer && v.frac.zero?
|
|
52
|
-
v
|
|
53
|
-
rescue ::TypeError
|
|
54
|
-
# If we got a TypeError BigDecimal or to_i failed;
|
|
55
|
-
# let value through so ruby things like Time - integer work
|
|
56
|
-
val
|
|
56
|
+
def decimal(val)
|
|
57
|
+
BigDecimal(val.to_s, Float::DIG + 1)
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
def valid_node?(node)
|
|
@@ -143,7 +144,7 @@ module Dentaku
|
|
|
143
144
|
end
|
|
144
145
|
|
|
145
146
|
def value(context = {})
|
|
146
|
-
r = cast(right.value(context)
|
|
147
|
+
r = decimal(cast(right.value(context)))
|
|
147
148
|
raise Dentaku::ZeroDivisionError if r.zero?
|
|
148
149
|
|
|
149
150
|
cast(cast(left.value(context)) / r)
|
data/lib/dentaku/ast/bitwise.rb
CHANGED
|
@@ -2,23 +2,40 @@ require_relative './operation'
|
|
|
2
2
|
|
|
3
3
|
module Dentaku
|
|
4
4
|
module AST
|
|
5
|
-
class
|
|
5
|
+
class Bitwise < Operation
|
|
6
6
|
def value(context = {})
|
|
7
|
-
|
|
7
|
+
left_value = left.value(context)
|
|
8
|
+
right_value = right.value(context)
|
|
9
|
+
|
|
10
|
+
left_value.public_send(operator, right_value)
|
|
11
|
+
rescue NoMethodError => e
|
|
12
|
+
raise Dentaku::ArgumentError.for(:invalid_operator, value: left_value, for: left_value.class)
|
|
13
|
+
rescue TypeError => e
|
|
14
|
+
raise Dentaku::ArgumentError.for(:invalid_operator, value: right_value, for: right_value.class)
|
|
8
15
|
end
|
|
16
|
+
end
|
|
9
17
|
|
|
18
|
+
class BitwiseOr < Bitwise
|
|
10
19
|
def operator
|
|
11
20
|
:|
|
|
12
21
|
end
|
|
13
22
|
end
|
|
14
23
|
|
|
15
|
-
class BitwiseAnd <
|
|
16
|
-
def
|
|
17
|
-
|
|
24
|
+
class BitwiseAnd < Bitwise
|
|
25
|
+
def operator
|
|
26
|
+
:&
|
|
18
27
|
end
|
|
28
|
+
end
|
|
19
29
|
|
|
30
|
+
class BitwiseShiftLeft < Bitwise
|
|
20
31
|
def operator
|
|
21
|
-
|
|
32
|
+
:<<
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class BitwiseShiftRight < Bitwise
|
|
37
|
+
def operator
|
|
38
|
+
:>>
|
|
22
39
|
end
|
|
23
40
|
end
|
|
24
41
|
end
|
|
@@ -16,8 +16,8 @@ module Dentaku
|
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def value(context = {})
|
|
19
|
-
l = validate_value(left.value(context))
|
|
20
|
-
r = validate_value(right.value(context))
|
|
19
|
+
l = validate_value(cast(left.value(context)))
|
|
20
|
+
r = validate_value(cast(right.value(context)))
|
|
21
21
|
|
|
22
22
|
l.public_send(operator, r)
|
|
23
23
|
rescue ::ArgumentError => e
|
|
@@ -26,6 +26,16 @@ module Dentaku
|
|
|
26
26
|
|
|
27
27
|
private
|
|
28
28
|
|
|
29
|
+
def cast(val)
|
|
30
|
+
return val unless val.is_a?(::String)
|
|
31
|
+
return val if val.empty?
|
|
32
|
+
return val unless val.match?(/\A-?\d*(\.\d+)?\z/)
|
|
33
|
+
|
|
34
|
+
v = BigDecimal(val, Float::DIG + 1)
|
|
35
|
+
v = v.to_i if v.frac.zero?
|
|
36
|
+
v
|
|
37
|
+
end
|
|
38
|
+
|
|
29
39
|
def validate_value(value)
|
|
30
40
|
unless value.respond_to?(operator)
|
|
31
41
|
raise Dentaku::ArgumentError.for(:invalid_operator, operation: self.class, operator: operator),
|
|
@@ -89,13 +89,9 @@ module Dentaku
|
|
|
89
89
|
def with_rescues(var_name, results, block)
|
|
90
90
|
yield
|
|
91
91
|
|
|
92
|
-
rescue UnboundVariableError,
|
|
92
|
+
rescue Dentaku::UnboundVariableError, Dentaku::ZeroDivisionError, Dentaku::ArgumentError => ex
|
|
93
93
|
ex.recipient_variable = var_name
|
|
94
94
|
results[var_name] = block.call(ex)
|
|
95
|
-
|
|
96
|
-
rescue Dentaku::ArgumentError => ex
|
|
97
|
-
results[var_name] = block.call(ex)
|
|
98
|
-
|
|
99
95
|
ensure
|
|
100
96
|
if results[var_name] == :undefined && calculator.memory.has_key?(var_name.downcase)
|
|
101
97
|
results[var_name] = calculator.memory[var_name.downcase]
|
data/lib/dentaku/exceptions.rb
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
module Dentaku
|
|
2
2
|
class Error < StandardError
|
|
3
|
+
attr_accessor :recipient_variable
|
|
3
4
|
end
|
|
4
5
|
|
|
5
6
|
class UnboundVariableError < Error
|
|
6
|
-
attr_accessor :recipient_variable
|
|
7
|
-
|
|
8
7
|
attr_reader :unbound_variables
|
|
9
8
|
|
|
10
9
|
def initialize(unbound_variables)
|
|
@@ -74,6 +73,7 @@ module Dentaku
|
|
|
74
73
|
|
|
75
74
|
class ArgumentError < ::ArgumentError
|
|
76
75
|
attr_reader :reason, :meta
|
|
76
|
+
attr_accessor :recipient_variable
|
|
77
77
|
|
|
78
78
|
def initialize(reason, **meta)
|
|
79
79
|
@reason = reason
|
data/lib/dentaku/parser.rb
CHANGED
|
@@ -10,8 +10,11 @@ module Dentaku
|
|
|
10
10
|
pow: AST::Exponentiation,
|
|
11
11
|
negate: AST::Negation,
|
|
12
12
|
mod: AST::Modulo,
|
|
13
|
+
|
|
13
14
|
bitor: AST::BitwiseOr,
|
|
14
15
|
bitand: AST::BitwiseAnd,
|
|
16
|
+
bitshiftleft: AST::BitwiseShiftLeft,
|
|
17
|
+
bitshiftright: AST::BitwiseShiftRight,
|
|
15
18
|
|
|
16
19
|
lt: AST::LessThan,
|
|
17
20
|
gt: AST::GreaterThan,
|
|
@@ -38,6 +41,8 @@ module Dentaku
|
|
|
38
41
|
|
|
39
42
|
def consume(count = 2)
|
|
40
43
|
operator = operations.pop
|
|
44
|
+
fail! :invalid_statement if operator.nil?
|
|
45
|
+
|
|
41
46
|
operator.peek(output)
|
|
42
47
|
|
|
43
48
|
args_size = operator.arity || count
|
|
@@ -120,9 +120,9 @@ module Dentaku
|
|
|
120
120
|
|
|
121
121
|
def operator
|
|
122
122
|
names = {
|
|
123
|
-
pow: '^', add: '+', subtract: '-', multiply: '*', divide: '/', mod: '%', bitor: '|', bitand: '&'
|
|
123
|
+
pow: '^', add: '+', subtract: '-', multiply: '*', divide: '/', mod: '%', bitor: '|', bitand: '&', bitshiftleft: '<<', bitshiftright: '>>'
|
|
124
124
|
}.invert
|
|
125
|
-
new(:operator, '
|
|
125
|
+
new(:operator, '\^|\+|-|\*|\/|%|\||&|<<|>>', lambda { |raw| names[raw] })
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
def grouping
|
data/lib/dentaku/version.rb
CHANGED
data/spec/ast/arithmetic_spec.rb
CHANGED
|
@@ -45,6 +45,13 @@ describe Dentaku::AST::Arithmetic do
|
|
|
45
45
|
expect(add(x, one, 'x' => '.1')).to eq(1.1)
|
|
46
46
|
expect { add(x, one, 'x' => 'invalid') }.to raise_error(Dentaku::ArgumentError)
|
|
47
47
|
expect { add(x, one, 'x' => '') }.to raise_error(Dentaku::ArgumentError)
|
|
48
|
+
|
|
49
|
+
int_one = Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, "1")
|
|
50
|
+
decimal_one = Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, "1.0")
|
|
51
|
+
|
|
52
|
+
expect(add(int_one, int_one).class).to eq(Integer)
|
|
53
|
+
expect(add(int_one, decimal_one).class).to eq(BigDecimal)
|
|
54
|
+
expect(add(decimal_one, decimal_one).class).to eq(BigDecimal)
|
|
48
55
|
end
|
|
49
56
|
|
|
50
57
|
it 'performs arithmetic on arrays' do
|
data/spec/ast/comparator_spec.rb
CHANGED
|
@@ -5,7 +5,9 @@ require 'dentaku/token'
|
|
|
5
5
|
|
|
6
6
|
describe Dentaku::AST::Comparator do
|
|
7
7
|
let(:one) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 1) }
|
|
8
|
+
let(:one_str) { Dentaku::AST::String.new Dentaku::Token.new(:string, '1') }
|
|
8
9
|
let(:two) { Dentaku::AST::Numeric.new Dentaku::Token.new(:numeric, 2) }
|
|
10
|
+
let(:two_str) { Dentaku::AST::String.new Dentaku::Token.new(:string, '2') }
|
|
9
11
|
let(:x) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'x') }
|
|
10
12
|
let(:y) { Dentaku::AST::Identifier.new Dentaku::Token.new(:identifier, 'y') }
|
|
11
13
|
let(:nilly) do
|
|
@@ -21,6 +23,12 @@ describe Dentaku::AST::Comparator do
|
|
|
21
23
|
expect(equal(x, y).value(ctx)).to be_falsey
|
|
22
24
|
end
|
|
23
25
|
|
|
26
|
+
it 'performs conversion from string to numeric operands' do
|
|
27
|
+
expect(less_than(one, two_str).value(ctx)).to be_truthy
|
|
28
|
+
expect(less_than(one_str, two_str).value(ctx)).to be_truthy
|
|
29
|
+
expect(less_than(one_str, two).value(ctx)).to be_truthy
|
|
30
|
+
end
|
|
31
|
+
|
|
24
32
|
it 'raises a dentaku argument error when incorrect arguments are passed in' do
|
|
25
33
|
expect { less_than(one, nilly).value(ctx) }.to raise_error Dentaku::ArgumentError
|
|
26
34
|
expect { less_than_or_equal(one, nilly).value(ctx) }.to raise_error Dentaku::ArgumentError
|
data/spec/ast/or_spec.rb
CHANGED
|
@@ -143,6 +143,15 @@ RSpec.describe Dentaku::BulkExpressionSolver do
|
|
|
143
143
|
expect(exception.recipient_variable).to eq('more_apples')
|
|
144
144
|
end
|
|
145
145
|
|
|
146
|
+
it 'stores the recipient variable on the exception when there is an ArgumentError' do
|
|
147
|
+
expressions = {apples: "NULL", more_apples: "1 + apples"}
|
|
148
|
+
exception = nil
|
|
149
|
+
described_class.new(expressions, calculator).solve do |ex|
|
|
150
|
+
exception = ex
|
|
151
|
+
end
|
|
152
|
+
expect(exception.recipient_variable).to eq('more_apples')
|
|
153
|
+
end
|
|
154
|
+
|
|
146
155
|
it 'safely handles argument errors' do
|
|
147
156
|
expressions = {i: "a / 5 + d", a: "m * 12", d: "a + b"}
|
|
148
157
|
result = described_class.new(expressions, calculator.store(m: 3)).solve
|
data/spec/calculator_spec.rb
CHANGED
|
@@ -40,6 +40,8 @@ describe Dentaku::Calculator do
|
|
|
40
40
|
expect(calculator.evaluate("2 | 3 * 9")).to eq (27)
|
|
41
41
|
expect(calculator.evaluate("2 & 3 * 9")).to eq (2)
|
|
42
42
|
expect(calculator.evaluate("5%")).to eq (0.05)
|
|
43
|
+
expect(calculator.evaluate('1 << 3')).to eq (8)
|
|
44
|
+
expect(calculator.evaluate('0xFF >> 6')).to eq (3)
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
describe 'evaluate' do
|
|
@@ -67,6 +69,7 @@ describe Dentaku::Calculator do
|
|
|
67
69
|
expect(calculator.evaluate('ROUNDDOWN(a)', a: nil)).to be_nil
|
|
68
70
|
expect(calculator.evaluate('ROUNDUP(a)', a: nil)).to be_nil
|
|
69
71
|
expect(calculator.evaluate('SUM(a,b)', a: nil, b: nil)).to be_nil
|
|
72
|
+
expect(calculator.evaluate('1.0 & "bar"')).to be_nil
|
|
70
73
|
end
|
|
71
74
|
|
|
72
75
|
it 'treats explicit nil as logical false' do
|
|
@@ -82,6 +85,13 @@ describe Dentaku::Calculator do
|
|
|
82
85
|
end
|
|
83
86
|
end
|
|
84
87
|
|
|
88
|
+
describe 'ast' do
|
|
89
|
+
it 'raises parsing errors' do
|
|
90
|
+
expect { calculator.ast('()') }.to raise_error(Dentaku::ParseError)
|
|
91
|
+
expect { calculator.ast('(}') }.to raise_error(Dentaku::TokenizerError)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
85
95
|
describe 'evaluate!' do
|
|
86
96
|
it 'raises exception when formula has error' do
|
|
87
97
|
expect { calculator.evaluate!('1 + + 1') }.to raise_error(Dentaku::ParseError)
|
|
@@ -108,6 +118,9 @@ describe Dentaku::Calculator do
|
|
|
108
118
|
expect { calculator.evaluate!('ROUNDDOWN(a)', a: nil) }.to raise_error(Dentaku::ArgumentError)
|
|
109
119
|
expect { calculator.evaluate!('ROUNDUP(a)', a: nil) }.to raise_error(Dentaku::ArgumentError)
|
|
110
120
|
expect { calculator.evaluate!('SUM(a,b)', a: nil, b: nil) }.to raise_error(Dentaku::ArgumentError)
|
|
121
|
+
expect { calculator.evaluate!('"foo" & "bar"') }.to raise_error(Dentaku::ArgumentError)
|
|
122
|
+
expect { calculator.evaluate!('1.0 & "bar"') }.to raise_error(Dentaku::ArgumentError)
|
|
123
|
+
expect { calculator.evaluate!('1 & "bar"') }.to raise_error(Dentaku::ArgumentError)
|
|
111
124
|
end
|
|
112
125
|
|
|
113
126
|
it 'raises argument error if a function is called with incorrect arity' do
|
data/spec/tokenizer_spec.rb
CHANGED
|
@@ -89,6 +89,18 @@ describe Dentaku::Tokenizer do
|
|
|
89
89
|
expect(tokens.map(&:value)).to eq([2, :bitand, 3])
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
it 'tokenizes bitwise SHIFT LEFT' do
|
|
93
|
+
tokens = tokenizer.tokenize('2 << 3')
|
|
94
|
+
expect(tokens.map(&:category)).to eq([:numeric, :operator, :numeric])
|
|
95
|
+
expect(tokens.map(&:value)).to eq([2, :bitshiftleft, 3])
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'tokenizes bitwise SHIFT RIGHT' do
|
|
99
|
+
tokens = tokenizer.tokenize('2 >> 3')
|
|
100
|
+
expect(tokens.map(&:category)).to eq([:numeric, :operator, :numeric])
|
|
101
|
+
expect(tokens.map(&:value)).to eq([2, :bitshiftright, 3])
|
|
102
|
+
end
|
|
103
|
+
|
|
92
104
|
it 'ignores whitespace' do
|
|
93
105
|
tokens = tokenizer.tokenize('1 / 1 ')
|
|
94
106
|
expect(tokens.map(&:category)).to eq([:numeric, :operator, :numeric])
|
data/spec/visitor_spec.rb
CHANGED
|
@@ -90,6 +90,7 @@ describe TestVisitor do
|
|
|
90
90
|
def generic_subclasses
|
|
91
91
|
[
|
|
92
92
|
:Arithmetic,
|
|
93
|
+
:Bitwise,
|
|
93
94
|
:Combinator,
|
|
94
95
|
:Comparator,
|
|
95
96
|
:Function,
|
|
@@ -111,7 +112,7 @@ describe TestVisitor do
|
|
|
111
112
|
visit_nodes('1 < 2 and 3 <= 4 or 5 > 6 AND 7 >= 8 OR 9 != 10 and true')
|
|
112
113
|
visit_nodes('IF(a[0] = NULL, "five", \'seven\')')
|
|
113
114
|
visit_nodes('case (a % 5) when 0 then a else b end')
|
|
114
|
-
visit_nodes('0xCAFE & 0xDECAF | 0xBEEF')
|
|
115
|
+
visit_nodes('0xCAFE & (0xDECAF << 3) | (0xBEEF >> 5)')
|
|
115
116
|
visit_nodes('2017-12-24 23:59:59')
|
|
116
117
|
visit_nodes('ALL({1, 2, 3}, "val", val % 2 == 0)')
|
|
117
118
|
visit_nodes('ANY(vals, val, val > 1)')
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dentaku
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.5.
|
|
4
|
+
version: 3.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Solomon White
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-10-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|
|
@@ -283,7 +283,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
283
283
|
- !ruby/object:Gem::Version
|
|
284
284
|
version: '0'
|
|
285
285
|
requirements: []
|
|
286
|
-
rubygems_version: 3.3.
|
|
286
|
+
rubygems_version: 3.3.7
|
|
287
287
|
signing_key:
|
|
288
288
|
specification_version: 4
|
|
289
289
|
summary: A formula language parser and evaluator
|