dentaku 3.2.0 → 3.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/dentaku.png)](http://badge.fury.io/rb/dentaku)
|
6
6
|
[![Build Status](https://travis-ci.org/rubysolo/dentaku.png?branch=master)](https://travis-ci.org/rubysolo/dentaku)
|
7
7
|
[![Code Climate](https://codeclimate.com/github/rubysolo/dentaku.png)](https://codeclimate.com/github/rubysolo/dentaku)
|
8
|
-
[![Hakiri](https://hakiri.io/github/rubysolo/dentaku/master.svg)](https://hakiri.io/github/rubysolo/dentaku)
|
9
8
|
[![Coverage](https://codecov.io/gh/rubysolo/dentaku/branch/master/graph/badge.svg)](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
|