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.
Files changed (100) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +5 -10
  3. data/.travis.yml +4 -6
  4. data/CHANGELOG.md +86 -2
  5. data/README.md +7 -6
  6. data/dentaku.gemspec +1 -1
  7. data/lib/dentaku/ast/access.rb +21 -1
  8. data/lib/dentaku/ast/arithmetic.rb +51 -15
  9. data/lib/dentaku/ast/array.rb +41 -0
  10. data/lib/dentaku/ast/bitwise.rb +30 -5
  11. data/lib/dentaku/ast/case/case_conditional.rb +17 -2
  12. data/lib/dentaku/ast/case/case_else.rb +17 -3
  13. data/lib/dentaku/ast/case/case_switch_variable.rb +14 -0
  14. data/lib/dentaku/ast/case/case_then.rb +17 -3
  15. data/lib/dentaku/ast/case/case_when.rb +21 -3
  16. data/lib/dentaku/ast/case.rb +19 -3
  17. data/lib/dentaku/ast/comparators.rb +38 -28
  18. data/lib/dentaku/ast/function.rb +11 -3
  19. data/lib/dentaku/ast/function_registry.rb +21 -0
  20. data/lib/dentaku/ast/functions/all.rb +23 -0
  21. data/lib/dentaku/ast/functions/and.rb +2 -2
  22. data/lib/dentaku/ast/functions/any.rb +23 -0
  23. data/lib/dentaku/ast/functions/avg.rb +2 -2
  24. data/lib/dentaku/ast/functions/count.rb +8 -0
  25. data/lib/dentaku/ast/functions/duration.rb +51 -0
  26. data/lib/dentaku/ast/functions/enum.rb +37 -0
  27. data/lib/dentaku/ast/functions/filter.rb +23 -0
  28. data/lib/dentaku/ast/functions/if.rb +19 -2
  29. data/lib/dentaku/ast/functions/map.rb +23 -0
  30. data/lib/dentaku/ast/functions/or.rb +4 -4
  31. data/lib/dentaku/ast/functions/pluck.rb +30 -0
  32. data/lib/dentaku/ast/functions/round.rb +1 -1
  33. data/lib/dentaku/ast/functions/rounddown.rb +1 -1
  34. data/lib/dentaku/ast/functions/roundup.rb +1 -1
  35. data/lib/dentaku/ast/functions/ruby_math.rb +50 -3
  36. data/lib/dentaku/ast/functions/string_functions.rb +105 -12
  37. data/lib/dentaku/ast/functions/xor.rb +44 -0
  38. data/lib/dentaku/ast/grouping.rb +3 -1
  39. data/lib/dentaku/ast/identifier.rb +16 -4
  40. data/lib/dentaku/ast/literal.rb +10 -0
  41. data/lib/dentaku/ast/negation.rb +7 -1
  42. data/lib/dentaku/ast/nil.rb +4 -0
  43. data/lib/dentaku/ast/node.rb +8 -0
  44. data/lib/dentaku/ast/operation.rb +17 -0
  45. data/lib/dentaku/ast/string.rb +7 -0
  46. data/lib/dentaku/ast.rb +8 -0
  47. data/lib/dentaku/bulk_expression_solver.rb +38 -27
  48. data/lib/dentaku/calculator.rb +21 -8
  49. data/lib/dentaku/date_arithmetic.rb +45 -0
  50. data/lib/dentaku/exceptions.rb +11 -8
  51. data/lib/dentaku/flat_hash.rb +9 -2
  52. data/lib/dentaku/parser.rb +57 -16
  53. data/lib/dentaku/print_visitor.rb +101 -0
  54. data/lib/dentaku/token_matcher.rb +1 -1
  55. data/lib/dentaku/token_scanner.rb +9 -3
  56. data/lib/dentaku/tokenizer.rb +7 -2
  57. data/lib/dentaku/version.rb +1 -1
  58. data/lib/dentaku/visitor/infix.rb +82 -0
  59. data/lib/dentaku.rb +20 -7
  60. data/spec/ast/addition_spec.rb +7 -1
  61. data/spec/ast/all_spec.rb +25 -0
  62. data/spec/ast/and_function_spec.rb +6 -6
  63. data/spec/ast/and_spec.rb +1 -1
  64. data/spec/ast/any_spec.rb +23 -0
  65. data/spec/ast/arithmetic_spec.rb +64 -29
  66. data/spec/ast/avg_spec.rb +9 -5
  67. data/spec/ast/comparator_spec.rb +31 -1
  68. data/spec/ast/count_spec.rb +7 -7
  69. data/spec/ast/division_spec.rb +7 -1
  70. data/spec/ast/filter_spec.rb +25 -0
  71. data/spec/ast/function_spec.rb +20 -15
  72. data/spec/ast/map_spec.rb +27 -0
  73. data/spec/ast/max_spec.rb +16 -3
  74. data/spec/ast/min_spec.rb +16 -3
  75. data/spec/ast/mul_spec.rb +11 -6
  76. data/spec/ast/negation_spec.rb +48 -0
  77. data/spec/ast/node_spec.rb +11 -8
  78. data/spec/ast/numeric_spec.rb +1 -1
  79. data/spec/ast/or_spec.rb +7 -7
  80. data/spec/ast/pluck_spec.rb +32 -0
  81. data/spec/ast/round_spec.rb +14 -4
  82. data/spec/ast/rounddown_spec.rb +14 -4
  83. data/spec/ast/roundup_spec.rb +14 -4
  84. data/spec/ast/string_functions_spec.rb +73 -0
  85. data/spec/ast/sum_spec.rb +11 -6
  86. data/spec/ast/switch_spec.rb +5 -5
  87. data/spec/ast/xor_spec.rb +35 -0
  88. data/spec/bulk_expression_solver_spec.rb +37 -1
  89. data/spec/calculator_spec.rb +341 -32
  90. data/spec/dentaku_spec.rb +19 -6
  91. data/spec/external_function_spec.rb +32 -6
  92. data/spec/parser_spec.rb +100 -123
  93. data/spec/print_visitor_spec.rb +66 -0
  94. data/spec/spec_helper.rb +6 -4
  95. data/spec/token_matcher_spec.rb +8 -8
  96. data/spec/token_scanner_spec.rb +4 -4
  97. data/spec/tokenizer_spec.rb +56 -13
  98. data/spec/visitor/infix_spec.rb +31 -0
  99. data/spec/visitor_spec.rb +138 -0
  100. metadata +52 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 402c5a79a4d28d7323c53de8852007fa1c49c087
4
- data.tar.gz: e5bd251b569708d740a7aeedde619e3bbfa34037
2
+ SHA256:
3
+ metadata.gz: d5592654ee45adeb24167b584374fb2d69bc67d1db4d5f4ac99050130f187f8f
4
+ data.tar.gz: 31d3952b08887ae934661f4c0ecc5c298b2f36f0f8365cc1503495eb044eb558
5
5
  SHA512:
6
- metadata.gz: a7dda0285c345400228dd7ec606958a95b9f57bd54f87a4333b4533d8f671b9ac66485ec8d29f66360b0dc026b00bb57ca842f4cf07e763c5840a2d84fa5a89e
7
- data.tar.gz: d58d8f1451b0092feb6a8038f0d1efe8b6957c5de0a67badf53538395963098912853ea848c5fab03581ffeeddf89e4bb8812a735ec8ba6be48a76db35760e97
6
+ metadata.gz: d8e0d003f897e06173c91b200e62d9fed12ec3bacfe0f2ecc3f3705a1cbf914d1705948b79962f5ac6a5397138ff89c2123aa5c3753963f0046a88280b29825b
7
+ data.tar.gz: 5f48d3b8fef4e56ed308e717da88937bef508e47dfca4c3e16610aff7523f11405e53e2b55d00c53b5eca8efbe7be64df0d0d12e7ddafbc61099cde08bf92a53
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.4
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/Tab:
91
+ Layout/IndentationStyle:
97
92
  Enabled: true
98
93
 
99
94
  # Blank lines should not have any spaces.
100
- Layout/TrailingBlankLines:
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/UnneededPercentQ:
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
- Lint/EndAlignment:
108
+ Layout/EndAlignment:
114
109
  Enabled: true
115
110
  EnforcedStyleAlignWith: variable
116
111
 
data/.travis.yml CHANGED
@@ -1,12 +1,10 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  rvm:
4
- - 2.0.0-p648
5
- - 2.1.10
6
- - 2.2.9
7
- - 2.3.6
8
- - 2.4.3
9
- - 2.5.0
4
+ - 2.5.9
5
+ - 2.6.7
6
+ - 2.7.3
7
+ - 3.0.1
10
8
  before_install:
11
9
  - gem update bundler
12
10
  - gem update --system
data/CHANGELOG.md CHANGED
@@ -1,6 +1,78 @@
1
1
  # Change Log
2
2
 
3
- ## [v3.2.0] Unreleased
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] 2017-01-10
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#L292))
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-2018 Solomon White
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.rubyforge_project = "dentaku"
17
+ s.add_dependency('concurrent-ruby')
18
18
 
19
19
  s.add_development_dependency('codecov')
20
20
  s.add_development_dependency('pry')
@@ -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, prefer_integer = true)
43
+ def cast(val)
38
44
  validate_value(val)
39
- numeric(val, prefer_integer)
45
+ numeric(val)
40
46
  end
41
47
 
42
- def numeric(val, prefer_integer)
43
- v = BigDecimal.new(val, Float::DIG + 1)
44
- v = v.to_i if prefer_integer && v.frac.zero?
45
- v
46
- rescue ::TypeError
47
- # If we got a TypeError BigDecimal or to_i failed;
48
- # let value through so ruby things like Time - integer work
49
- val
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.dependencies.any? || node.type == :numeric)
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), false)
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
@@ -2,15 +2,40 @@ require_relative './operation'
2
2
 
3
3
  module Dentaku
4
4
  module AST
5
- class BitwiseOr < Operation
5
+ class Bitwise < Operation
6
6
  def value(context = {})
7
- left.value(context) | right.value(context)
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 BitwiseAnd < Operation
12
- def value(context = {})
13
- left.value(context) & right.value(context)
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
- def self.arity
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
- def self.arity
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
- def self.arity
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