dentaku 3.2.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 +5 -5
- data/.rubocop.yml +5 -10
- data/.travis.yml +4 -6
- data/CHANGELOG.md +86 -2
- data/README.md +7 -6
- data/dentaku.gemspec +1 -1
- data/lib/dentaku/ast/access.rb +21 -1
- data/lib/dentaku/ast/arithmetic.rb +51 -15
- data/lib/dentaku/ast/array.rb +41 -0
- data/lib/dentaku/ast/bitwise.rb +30 -5
- data/lib/dentaku/ast/case/case_conditional.rb +17 -2
- data/lib/dentaku/ast/case/case_else.rb +17 -3
- data/lib/dentaku/ast/case/case_switch_variable.rb +14 -0
- data/lib/dentaku/ast/case/case_then.rb +17 -3
- data/lib/dentaku/ast/case/case_when.rb +21 -3
- data/lib/dentaku/ast/case.rb +19 -3
- data/lib/dentaku/ast/comparators.rb +38 -28
- data/lib/dentaku/ast/function.rb +11 -3
- data/lib/dentaku/ast/function_registry.rb +21 -0
- data/lib/dentaku/ast/functions/all.rb +23 -0
- data/lib/dentaku/ast/functions/and.rb +2 -2
- data/lib/dentaku/ast/functions/any.rb +23 -0
- data/lib/dentaku/ast/functions/avg.rb +2 -2
- data/lib/dentaku/ast/functions/count.rb +8 -0
- data/lib/dentaku/ast/functions/duration.rb +51 -0
- data/lib/dentaku/ast/functions/enum.rb +37 -0
- data/lib/dentaku/ast/functions/filter.rb +23 -0
- data/lib/dentaku/ast/functions/if.rb +19 -2
- data/lib/dentaku/ast/functions/map.rb +23 -0
- data/lib/dentaku/ast/functions/or.rb +4 -4
- data/lib/dentaku/ast/functions/pluck.rb +30 -0
- data/lib/dentaku/ast/functions/round.rb +1 -1
- data/lib/dentaku/ast/functions/rounddown.rb +1 -1
- data/lib/dentaku/ast/functions/roundup.rb +1 -1
- data/lib/dentaku/ast/functions/ruby_math.rb +50 -3
- data/lib/dentaku/ast/functions/string_functions.rb +105 -12
- data/lib/dentaku/ast/functions/xor.rb +44 -0
- data/lib/dentaku/ast/grouping.rb +3 -1
- data/lib/dentaku/ast/identifier.rb +16 -4
- data/lib/dentaku/ast/literal.rb +10 -0
- data/lib/dentaku/ast/negation.rb +7 -1
- data/lib/dentaku/ast/nil.rb +4 -0
- data/lib/dentaku/ast/node.rb +8 -0
- data/lib/dentaku/ast/operation.rb +17 -0
- data/lib/dentaku/ast/string.rb +7 -0
- data/lib/dentaku/ast.rb +8 -0
- data/lib/dentaku/bulk_expression_solver.rb +38 -27
- data/lib/dentaku/calculator.rb +21 -8
- data/lib/dentaku/date_arithmetic.rb +45 -0
- data/lib/dentaku/exceptions.rb +11 -8
- data/lib/dentaku/flat_hash.rb +9 -2
- data/lib/dentaku/parser.rb +57 -16
- data/lib/dentaku/print_visitor.rb +101 -0
- data/lib/dentaku/token_matcher.rb +1 -1
- data/lib/dentaku/token_scanner.rb +9 -3
- data/lib/dentaku/tokenizer.rb +7 -2
- data/lib/dentaku/version.rb +1 -1
- data/lib/dentaku/visitor/infix.rb +82 -0
- data/lib/dentaku.rb +20 -7
- data/spec/ast/addition_spec.rb +7 -1
- data/spec/ast/all_spec.rb +25 -0
- data/spec/ast/and_function_spec.rb +6 -6
- data/spec/ast/and_spec.rb +1 -1
- data/spec/ast/any_spec.rb +23 -0
- data/spec/ast/arithmetic_spec.rb +64 -29
- data/spec/ast/avg_spec.rb +9 -5
- data/spec/ast/comparator_spec.rb +31 -1
- data/spec/ast/count_spec.rb +7 -7
- data/spec/ast/division_spec.rb +7 -1
- data/spec/ast/filter_spec.rb +25 -0
- data/spec/ast/function_spec.rb +20 -15
- data/spec/ast/map_spec.rb +27 -0
- data/spec/ast/max_spec.rb +16 -3
- data/spec/ast/min_spec.rb +16 -3
- data/spec/ast/mul_spec.rb +11 -6
- data/spec/ast/negation_spec.rb +48 -0
- data/spec/ast/node_spec.rb +11 -8
- data/spec/ast/numeric_spec.rb +1 -1
- data/spec/ast/or_spec.rb +7 -7
- data/spec/ast/pluck_spec.rb +32 -0
- data/spec/ast/round_spec.rb +14 -4
- data/spec/ast/rounddown_spec.rb +14 -4
- data/spec/ast/roundup_spec.rb +14 -4
- data/spec/ast/string_functions_spec.rb +73 -0
- data/spec/ast/sum_spec.rb +11 -6
- data/spec/ast/switch_spec.rb +5 -5
- data/spec/ast/xor_spec.rb +35 -0
- data/spec/bulk_expression_solver_spec.rb +37 -1
- data/spec/calculator_spec.rb +341 -32
- data/spec/dentaku_spec.rb +19 -6
- data/spec/external_function_spec.rb +32 -6
- data/spec/parser_spec.rb +100 -123
- data/spec/print_visitor_spec.rb +66 -0
- data/spec/spec_helper.rb +6 -4
- data/spec/token_matcher_spec.rb +8 -8
- data/spec/token_scanner_spec.rb +4 -4
- data/spec/tokenizer_spec.rb +56 -13
- data/spec/visitor/infix_spec.rb +31 -0
- data/spec/visitor_spec.rb +138 -0
- metadata +52 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
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/.rubocop.yml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
AllCops:
|
|
2
|
-
TargetRubyVersion: 2.
|
|
2
|
+
TargetRubyVersion: 2.6
|
|
3
3
|
# RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
|
|
4
4
|
# to ignore them, so only the ones explicitly set in this file are enabled.
|
|
5
5
|
DisabledByDefault: true
|
|
@@ -8,11 +8,6 @@ AllCops:
|
|
|
8
8
|
Style/AndOr:
|
|
9
9
|
Enabled: true
|
|
10
10
|
|
|
11
|
-
# Do not use braces for hash literals when they are the last argument of a
|
|
12
|
-
# method call.
|
|
13
|
-
Style/BracesAroundHashParameters:
|
|
14
|
-
Enabled: true
|
|
15
|
-
|
|
16
11
|
# Align `when` with `case`.
|
|
17
12
|
Layout/CaseIndentation:
|
|
18
13
|
Enabled: true
|
|
@@ -93,11 +88,11 @@ Style/StringLiterals:
|
|
|
93
88
|
EnforcedStyle: double_quotes
|
|
94
89
|
|
|
95
90
|
# Detect hard tabs, no hard tabs.
|
|
96
|
-
Layout/
|
|
91
|
+
Layout/IndentationStyle:
|
|
97
92
|
Enabled: true
|
|
98
93
|
|
|
99
94
|
# Blank lines should not have any spaces.
|
|
100
|
-
Layout/
|
|
95
|
+
Layout/TrailingEmptyLines:
|
|
101
96
|
Enabled: true
|
|
102
97
|
|
|
103
98
|
# No trailing whitespace.
|
|
@@ -105,12 +100,12 @@ Layout/TrailingWhitespace:
|
|
|
105
100
|
Enabled: true
|
|
106
101
|
|
|
107
102
|
# Use quotes for string literals when they are enough.
|
|
108
|
-
Style/
|
|
103
|
+
Style/RedundantPercentQ:
|
|
109
104
|
Enabled: true
|
|
110
105
|
|
|
111
106
|
# Align `end` with the matching keyword or starting expression except for
|
|
112
107
|
# assignments, where it should be aligned with the LHS.
|
|
113
|
-
|
|
108
|
+
Layout/EndAlignment:
|
|
114
109
|
Enabled: true
|
|
115
110
|
EnforcedStyleAlignWith: variable
|
|
116
111
|
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,78 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
## [v3.
|
|
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
|
+
|
|
10
|
+
## [v3.5.0]
|
|
11
|
+
- fix bug with function argument count
|
|
12
|
+
- add XOR operator
|
|
13
|
+
- make function args publicly accessible
|
|
14
|
+
- better argument handling for collection functions
|
|
15
|
+
- better dependency reporting for collection functions
|
|
16
|
+
- allow ruby math-backed functions to be serialized
|
|
17
|
+
- improve scientific notation handling
|
|
18
|
+
- improve comparator argument errors
|
|
19
|
+
- respect case sensitivity in nested case statments
|
|
20
|
+
- add visitor pattern
|
|
21
|
+
|
|
22
|
+
## [v3.4.2]
|
|
23
|
+
- add FILTER function
|
|
24
|
+
- add concurrent-ruby dependency to make global calculator object thread safe
|
|
25
|
+
- add Ruby 3 support
|
|
26
|
+
- allow formulas to access intermediate context values
|
|
27
|
+
- fix incorrect Ruby Math function return type
|
|
28
|
+
- fix context mutation bug
|
|
29
|
+
- fix dependency resolution bug
|
|
30
|
+
|
|
31
|
+
## [v3.4.1] 2020-12-12
|
|
32
|
+
- prevent extra evaluations in bulk expression solver
|
|
33
|
+
|
|
34
|
+
## [v3.4.0] 2020-12-07
|
|
35
|
+
- allow access to intermediate values of flattened hashes
|
|
36
|
+
- catch invalid array syntax in the parse phase
|
|
37
|
+
- drop support for Ruby < 2.5, add support for Ruby 2.7
|
|
38
|
+
- add support for subtracting date literals
|
|
39
|
+
- improve error handling
|
|
40
|
+
- improve math function implementation
|
|
41
|
+
- add caching for calculated variable values
|
|
42
|
+
- allow custom unbound variable handling block at Dentaku module level
|
|
43
|
+
- add enum functions `ANY`, `ALL`, `MAP` and `PLUCK`
|
|
44
|
+
- allow self-referential formulas in bulk expression solver
|
|
45
|
+
- misc internal fixes and enhancements
|
|
46
|
+
|
|
47
|
+
## [v3.3.4] 2019-11-21
|
|
48
|
+
- bugfix release
|
|
49
|
+
|
|
50
|
+
## [v3.3.3] 2019-11-20
|
|
51
|
+
- date / duration addition and subtraction
|
|
52
|
+
- validate arity for custom functions with variable arity
|
|
53
|
+
- make AST serializable with Marshal.dump
|
|
54
|
+
- performance optimization for arithmetic node validation
|
|
55
|
+
- support lazy evaluation for expensive values
|
|
56
|
+
- short-circuit IF function
|
|
57
|
+
- better error when empty string is used in arithmetic operation
|
|
58
|
+
|
|
59
|
+
## [v3.3.2] 2019-06-10
|
|
60
|
+
- add ability to pre-load AST cache
|
|
61
|
+
- fix negation node bug
|
|
62
|
+
|
|
63
|
+
## [v3.3.1] 2019-03-26
|
|
64
|
+
- better errors for parse failures and exceptions in internal functions
|
|
65
|
+
- fix Ruby 2.6.0 deprecation warnings
|
|
66
|
+
- fix issue with functions in nested case statements
|
|
67
|
+
|
|
68
|
+
## [v3.3.0] 2018-12-04
|
|
69
|
+
- add array literal syntax
|
|
70
|
+
- return correct type from string function AST nodes
|
|
71
|
+
|
|
72
|
+
## [v3.2.1] 2018-10-24
|
|
73
|
+
- make `evaluate` rescue more exceptions
|
|
74
|
+
|
|
75
|
+
## [v3.2.0] 2018-03-14
|
|
4
76
|
- add `COUNT` and `AVG` functions
|
|
5
77
|
- add unicode support 😎
|
|
6
78
|
- fix CASE parsing bug
|
|
@@ -8,7 +80,7 @@
|
|
|
8
80
|
- add variadic MUL function
|
|
9
81
|
- performance optimization
|
|
10
82
|
|
|
11
|
-
## [v3.1.0]
|
|
83
|
+
## [v3.1.0] 2018-01-10
|
|
12
84
|
- allow decimals with no leading zero
|
|
13
85
|
- nested hash and array support in bulk expression solver
|
|
14
86
|
- add a variadic SUM function
|
|
@@ -159,6 +231,18 @@
|
|
|
159
231
|
## [v0.1.0] 2012-01-20
|
|
160
232
|
- initial release
|
|
161
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
|
|
236
|
+
[v3.5.0]: https://github.com/rubysolo/dentaku/compare/v3.4.2...v3.5.0
|
|
237
|
+
[v3.4.2]: https://github.com/rubysolo/dentaku/compare/v3.4.1...v3.4.2
|
|
238
|
+
[v3.4.1]: https://github.com/rubysolo/dentaku/compare/v3.4.0...v3.4.1
|
|
239
|
+
[v3.4.0]: https://github.com/rubysolo/dentaku/compare/v3.3.4...v3.4.0
|
|
240
|
+
[v3.3.4]: https://github.com/rubysolo/dentaku/compare/v3.3.3...v3.3.4
|
|
241
|
+
[v3.3.3]: https://github.com/rubysolo/dentaku/compare/v3.3.2...v3.3.3
|
|
242
|
+
[v3.3.2]: https://github.com/rubysolo/dentaku/compare/v3.3.1...v3.3.2
|
|
243
|
+
[v3.3.1]: https://github.com/rubysolo/dentaku/compare/v3.3.0...v3.3.1
|
|
244
|
+
[v3.3.0]: https://github.com/rubysolo/dentaku/compare/v3.2.1...v3.3.0
|
|
245
|
+
[v3.2.1]: https://github.com/rubysolo/dentaku/compare/v3.2.0...v3.2.1
|
|
162
246
|
[v3.2.0]: https://github.com/rubysolo/dentaku/compare/v3.1.0...v3.2.0
|
|
163
247
|
[v3.1.0]: https://github.com/rubysolo/dentaku/compare/v3.0.0...v3.1.0
|
|
164
248
|
[v3.0.0]: https://github.com/rubysolo/dentaku/compare/v2.0.11...v3.0.0
|
data/README.md
CHANGED
|
@@ -5,7 +5,6 @@ Dentaku
|
|
|
5
5
|
[](http://badge.fury.io/rb/dentaku)
|
|
6
6
|
[](https://travis-ci.org/rubysolo/dentaku)
|
|
7
7
|
[](https://codeclimate.com/github/rubysolo/dentaku)
|
|
8
|
-
[](https://hakiri.io/github/rubysolo/dentaku)
|
|
9
8
|
[](https://codecov.io/gh/rubysolo/dentaku)
|
|
10
9
|
|
|
11
10
|
|
|
@@ -138,20 +137,22 @@ application, AST caching will consume more memory with each new formula.
|
|
|
138
137
|
BUILT-IN OPERATORS AND FUNCTIONS
|
|
139
138
|
---------------------------------
|
|
140
139
|
|
|
141
|
-
Math: `+`, `-`, `*`, `/`, `%`, `^`, `|`,
|
|
140
|
+
Math: `+`, `-`, `*`, `/`, `%`, `^`, `|`, `&`, `<<`, `>>`
|
|
142
141
|
|
|
143
142
|
Also, all functions from Ruby's Math module, including `SIN`, `COS`, `TAN`, etc.
|
|
144
143
|
|
|
145
144
|
Comparison: `<`, `>`, `<=`, `>=`, `<>`, `!=`, `=`,
|
|
146
145
|
|
|
147
|
-
Logic: `IF`, `AND`, `OR`, `NOT`, `SWITCH`
|
|
146
|
+
Logic: `IF`, `AND`, `OR`, `XOR`, `NOT`, `SWITCH`
|
|
148
147
|
|
|
149
148
|
Numeric: `MIN`, `MAX`, `SUM`, `AVG`, `COUNT`, `ROUND`, `ROUNDDOWN`, `ROUNDUP`
|
|
150
149
|
|
|
151
|
-
Selections: `CASE` (syntax see [spec](https://github.com/rubysolo/dentaku/blob/master/spec/calculator_spec.rb#
|
|
150
|
+
Selections: `CASE` (syntax see [spec](https://github.com/rubysolo/dentaku/blob/master/spec/calculator_spec.rb#L593))
|
|
152
151
|
|
|
153
152
|
String: `LEFT`, `RIGHT`, `MID`, `LEN`, `FIND`, `SUBSTITUTE`, `CONCAT`, `CONTAINS`
|
|
154
153
|
|
|
154
|
+
Collection: `MAP`, `FILTER`, `ALL`, `ANY`, `PLUCK`
|
|
155
|
+
|
|
155
156
|
RESOLVING DEPENDENCIES
|
|
156
157
|
----------------------
|
|
157
158
|
|
|
@@ -284,7 +285,7 @@ using Calculator#add_functions.
|
|
|
284
285
|
FUNCTION ALIASES
|
|
285
286
|
----------------
|
|
286
287
|
|
|
287
|
-
Every function can be aliased by synonyms. For example, it can be useful if
|
|
288
|
+
Every function can be aliased by synonyms. For example, it can be useful if
|
|
288
289
|
your application is multilingual.
|
|
289
290
|
|
|
290
291
|
```ruby
|
|
@@ -321,7 +322,7 @@ LICENSE
|
|
|
321
322
|
|
|
322
323
|
(The MIT License)
|
|
323
324
|
|
|
324
|
-
Copyright © 2012-
|
|
325
|
+
Copyright © 2012-2022 Solomon White
|
|
325
326
|
|
|
326
327
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
327
328
|
this software and associated documentation files (the ‘Software’), to deal in
|
data/dentaku.gemspec
CHANGED
|
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|
|
14
14
|
Dentaku is a parser and evaluator for mathematical formulas
|
|
15
15
|
DESC
|
|
16
16
|
|
|
17
|
-
s.
|
|
17
|
+
s.add_dependency('concurrent-ruby')
|
|
18
18
|
|
|
19
19
|
s.add_development_dependency('codecov')
|
|
20
20
|
s.add_development_dependency('pry')
|
data/lib/dentaku/ast/access.rb
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
|
+
require_relative "./node"
|
|
2
|
+
|
|
1
3
|
module Dentaku
|
|
2
4
|
module AST
|
|
3
|
-
class Access
|
|
5
|
+
class Access < Node
|
|
6
|
+
attr_reader :structure, :index
|
|
7
|
+
|
|
4
8
|
def self.arity
|
|
5
9
|
2
|
|
6
10
|
end
|
|
7
11
|
|
|
12
|
+
def self.min_param_count
|
|
13
|
+
arity
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.max_param_count
|
|
17
|
+
arity
|
|
18
|
+
end
|
|
19
|
+
|
|
8
20
|
def self.peek(*)
|
|
9
21
|
end
|
|
10
22
|
|
|
@@ -22,6 +34,14 @@ module Dentaku
|
|
|
22
34
|
def dependencies(context = {})
|
|
23
35
|
@structure.dependencies(context) + @index.dependencies(context)
|
|
24
36
|
end
|
|
37
|
+
|
|
38
|
+
def type
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def accept(visitor)
|
|
43
|
+
visitor.visit_access(self)
|
|
44
|
+
end
|
|
25
45
|
end
|
|
26
46
|
end
|
|
27
47
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require_relative './operation'
|
|
2
|
+
require_relative '../date_arithmetic'
|
|
2
3
|
require 'bigdecimal'
|
|
3
4
|
require 'bigdecimal/util'
|
|
4
5
|
|
|
@@ -12,6 +13,7 @@ module Dentaku
|
|
|
12
13
|
raise NodeError.new(:numeric, left.type, :left),
|
|
13
14
|
"#{self.class} requires numeric operands"
|
|
14
15
|
end
|
|
16
|
+
|
|
15
17
|
unless valid_right?
|
|
16
18
|
raise NodeError.new(:numeric, right.type, :right),
|
|
17
19
|
"#{self.class} requires numeric operands"
|
|
@@ -29,36 +31,42 @@ module Dentaku
|
|
|
29
31
|
def value(context = {})
|
|
30
32
|
l = cast(left.value(context))
|
|
31
33
|
r = cast(right.value(context))
|
|
34
|
+
|
|
32
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
|
|
33
39
|
end
|
|
34
40
|
|
|
35
41
|
private
|
|
36
42
|
|
|
37
|
-
def cast(val
|
|
43
|
+
def cast(val)
|
|
38
44
|
validate_value(val)
|
|
39
|
-
numeric(val
|
|
45
|
+
numeric(val)
|
|
40
46
|
end
|
|
41
47
|
|
|
42
|
-
def numeric(val
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def decimal(val)
|
|
57
|
+
BigDecimal(val.to_s, Float::DIG + 1)
|
|
50
58
|
end
|
|
51
59
|
|
|
52
60
|
def valid_node?(node)
|
|
53
|
-
node && (node.
|
|
61
|
+
node && (node.type == :numeric || node.type == :integer || node.dependencies.any?)
|
|
54
62
|
end
|
|
55
63
|
|
|
56
64
|
def valid_left?
|
|
57
|
-
valid_node?(left)
|
|
65
|
+
valid_node?(left) || left.type == :datetime
|
|
58
66
|
end
|
|
59
67
|
|
|
60
68
|
def valid_right?
|
|
61
|
-
valid_node?(right)
|
|
69
|
+
valid_node?(right) || right.type == :duration || right.type == :datetime
|
|
62
70
|
end
|
|
63
71
|
|
|
64
72
|
def validate_value(val)
|
|
@@ -77,7 +85,7 @@ module Dentaku
|
|
|
77
85
|
end
|
|
78
86
|
|
|
79
87
|
def validate_format(string)
|
|
80
|
-
unless string =~ /\A-?\d*(\.\d+)?\z/
|
|
88
|
+
unless string =~ /\A-?\d*(\.\d+)?\z/ && !string.empty?
|
|
81
89
|
raise Dentaku::ArgumentError.for(:invalid_value, value: string, for: BigDecimal),
|
|
82
90
|
"String input '#{string}' is not coercible to numeric"
|
|
83
91
|
end
|
|
@@ -92,6 +100,14 @@ module Dentaku
|
|
|
92
100
|
def self.precedence
|
|
93
101
|
10
|
|
94
102
|
end
|
|
103
|
+
|
|
104
|
+
def value(context = {})
|
|
105
|
+
if left.type == :datetime
|
|
106
|
+
Dentaku::DateArithmetic.new(left.value(context)).add(right.value(context))
|
|
107
|
+
else
|
|
108
|
+
super
|
|
109
|
+
end
|
|
110
|
+
end
|
|
95
111
|
end
|
|
96
112
|
|
|
97
113
|
class Subtraction < Arithmetic
|
|
@@ -102,6 +118,14 @@ module Dentaku
|
|
|
102
118
|
def self.precedence
|
|
103
119
|
10
|
|
104
120
|
end
|
|
121
|
+
|
|
122
|
+
def value(context = {})
|
|
123
|
+
if left.type == :datetime
|
|
124
|
+
Dentaku::DateArithmetic.new(left.value(context)).sub(right.value(context))
|
|
125
|
+
else
|
|
126
|
+
super
|
|
127
|
+
end
|
|
128
|
+
end
|
|
105
129
|
end
|
|
106
130
|
|
|
107
131
|
class Multiplication < Arithmetic
|
|
@@ -120,7 +144,7 @@ module Dentaku
|
|
|
120
144
|
end
|
|
121
145
|
|
|
122
146
|
def value(context = {})
|
|
123
|
-
r = cast(right.value(context)
|
|
147
|
+
r = decimal(cast(right.value(context)))
|
|
124
148
|
raise Dentaku::ZeroDivisionError if r.zero?
|
|
125
149
|
|
|
126
150
|
cast(cast(left.value(context)) / r)
|
|
@@ -159,6 +183,14 @@ module Dentaku
|
|
|
159
183
|
end
|
|
160
184
|
end
|
|
161
185
|
|
|
186
|
+
def dependencies(context = {})
|
|
187
|
+
if percent?
|
|
188
|
+
@right.dependencies(context)
|
|
189
|
+
else
|
|
190
|
+
super
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
162
194
|
def percent?
|
|
163
195
|
left.nil?
|
|
164
196
|
end
|
|
@@ -189,6 +221,10 @@ module Dentaku
|
|
|
189
221
|
:**
|
|
190
222
|
end
|
|
191
223
|
|
|
224
|
+
def display_operator
|
|
225
|
+
"^"
|
|
226
|
+
end
|
|
227
|
+
|
|
192
228
|
def self.precedence
|
|
193
229
|
30
|
|
194
230
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require_relative "./node"
|
|
2
|
+
|
|
3
|
+
module Dentaku
|
|
4
|
+
module AST
|
|
5
|
+
class Array < Node
|
|
6
|
+
def self.arity
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.min_param_count
|
|
10
|
+
0
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.max_param_count
|
|
14
|
+
Float::INFINITY
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.peek(*)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(*elements)
|
|
21
|
+
@elements = *elements
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def value(context = {})
|
|
25
|
+
@elements.map { |el| el.value(context) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def dependencies(context = {})
|
|
29
|
+
@elements.flat_map { |el| el.dependencies(context) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def type
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def accept(visitor)
|
|
37
|
+
visitor.visit_array(self)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/dentaku/ast/bitwise.rb
CHANGED
|
@@ -2,15 +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
|
|
9
16
|
end
|
|
10
17
|
|
|
11
|
-
class
|
|
12
|
-
def
|
|
13
|
-
|
|
18
|
+
class BitwiseOr < Bitwise
|
|
19
|
+
def operator
|
|
20
|
+
:|
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class BitwiseAnd < Bitwise
|
|
25
|
+
def operator
|
|
26
|
+
:&
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class BitwiseShiftLeft < Bitwise
|
|
31
|
+
def operator
|
|
32
|
+
:<<
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class BitwiseShiftRight < Bitwise
|
|
37
|
+
def operator
|
|
38
|
+
:>>
|
|
14
39
|
end
|
|
15
40
|
end
|
|
16
41
|
end
|
|
@@ -1,23 +1,38 @@
|
|
|
1
|
+
require 'dentaku/exceptions'
|
|
2
|
+
|
|
1
3
|
module Dentaku
|
|
2
4
|
module AST
|
|
3
5
|
class CaseConditional < Node
|
|
4
6
|
attr_reader :when,
|
|
5
7
|
:then
|
|
6
8
|
|
|
9
|
+
def self.min_param_count
|
|
10
|
+
2
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.max_param_count
|
|
14
|
+
2
|
|
15
|
+
end
|
|
16
|
+
|
|
7
17
|
def initialize(when_statement, then_statement)
|
|
8
18
|
@when = when_statement
|
|
9
19
|
unless @when.is_a?(AST::CaseWhen)
|
|
10
|
-
raise 'Expected first argument to be a CaseWhen'
|
|
20
|
+
raise ParseError.for(:node_invalid), 'Expected first argument to be a CaseWhen'
|
|
11
21
|
end
|
|
22
|
+
|
|
12
23
|
@then = then_statement
|
|
13
24
|
unless @then.is_a?(AST::CaseThen)
|
|
14
|
-
raise 'Expected second argument to be a CaseThen'
|
|
25
|
+
raise ParseError.for(:node_invalid), 'Expected second argument to be a CaseThen'
|
|
15
26
|
end
|
|
16
27
|
end
|
|
17
28
|
|
|
18
29
|
def dependencies(context = {})
|
|
19
30
|
@when.dependencies(context) + @then.dependencies(context)
|
|
20
31
|
end
|
|
32
|
+
|
|
33
|
+
def accept(visitor)
|
|
34
|
+
visitor.visit_case_conditional(self)
|
|
35
|
+
end
|
|
21
36
|
end
|
|
22
37
|
end
|
|
23
38
|
end
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
module Dentaku
|
|
2
2
|
module AST
|
|
3
3
|
class CaseElse < Node
|
|
4
|
-
|
|
5
|
-
1
|
|
6
|
-
end
|
|
4
|
+
attr_reader :node
|
|
7
5
|
|
|
8
6
|
def initialize(node)
|
|
9
7
|
@node = node
|
|
@@ -16,6 +14,22 @@ module Dentaku
|
|
|
16
14
|
def dependencies(context = {})
|
|
17
15
|
@node.dependencies(context)
|
|
18
16
|
end
|
|
17
|
+
|
|
18
|
+
def self.arity
|
|
19
|
+
1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.min_param_count
|
|
23
|
+
1
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.max_param_count
|
|
27
|
+
1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def accept(visitor)
|
|
31
|
+
visitor.visit_else(self)
|
|
32
|
+
end
|
|
19
33
|
end
|
|
20
34
|
end
|
|
21
35
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
module Dentaku
|
|
2
2
|
module AST
|
|
3
3
|
class CaseSwitchVariable < Node
|
|
4
|
+
attr_reader :node
|
|
5
|
+
|
|
4
6
|
def initialize(node)
|
|
5
7
|
@node = node
|
|
6
8
|
end
|
|
@@ -16,6 +18,18 @@ module Dentaku
|
|
|
16
18
|
def self.arity
|
|
17
19
|
1
|
|
18
20
|
end
|
|
21
|
+
|
|
22
|
+
def self.min_param_count
|
|
23
|
+
1
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.max_param_count
|
|
27
|
+
1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def accept(visitor)
|
|
31
|
+
visitor.visit_switch(self)
|
|
32
|
+
end
|
|
19
33
|
end
|
|
20
34
|
end
|
|
21
35
|
end
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
module Dentaku
|
|
2
2
|
module AST
|
|
3
3
|
class CaseThen < Node
|
|
4
|
-
|
|
5
|
-
1
|
|
6
|
-
end
|
|
4
|
+
attr_reader :node
|
|
7
5
|
|
|
8
6
|
def initialize(node)
|
|
9
7
|
@node = node
|
|
@@ -16,6 +14,22 @@ module Dentaku
|
|
|
16
14
|
def dependencies(context = {})
|
|
17
15
|
@node.dependencies(context)
|
|
18
16
|
end
|
|
17
|
+
|
|
18
|
+
def self.arity
|
|
19
|
+
1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.min_param_count
|
|
23
|
+
1
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.max_param_count
|
|
27
|
+
1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def accept(visitor)
|
|
31
|
+
visitor.visit_then(self)
|
|
32
|
+
end
|
|
19
33
|
end
|
|
20
34
|
end
|
|
21
35
|
end
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
module Dentaku
|
|
2
2
|
module AST
|
|
3
3
|
class CaseWhen < Operation
|
|
4
|
-
|
|
5
|
-
1
|
|
6
|
-
end
|
|
4
|
+
attr_reader :node
|
|
7
5
|
|
|
8
6
|
def initialize(node)
|
|
9
7
|
@node = node
|
|
@@ -16,6 +14,26 @@ module Dentaku
|
|
|
16
14
|
def dependencies(context = {})
|
|
17
15
|
@node.dependencies(context)
|
|
18
16
|
end
|
|
17
|
+
|
|
18
|
+
def self.arity
|
|
19
|
+
1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.min_param_count
|
|
23
|
+
1
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.max_param_count
|
|
27
|
+
1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def accept(visitor)
|
|
31
|
+
visitor.visit_when(self)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_s
|
|
35
|
+
'WHEN'
|
|
36
|
+
end
|
|
19
37
|
end
|
|
20
38
|
end
|
|
21
39
|
end
|