ddql 1.0.0 → 1.0.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/.ruby-version +1 -1
- data/Gemfile.lock +20 -20
- data/lib/ddql.rb +2 -1
- data/lib/ddql/agg_operator.rb +69 -11
- data/lib/ddql/blank_refinements.rb +21 -0
- data/lib/ddql/coalesce_operator.rb +5 -6
- data/lib/ddql/infix_float_map_operator.rb +2 -2
- data/lib/ddql/infix_string_map_operator.rb +2 -2
- data/lib/ddql/linked_list.rb +134 -30
- data/lib/ddql/list_operator.rb +2 -2
- data/lib/ddql/lookup_operator.rb +8 -6
- data/lib/ddql/operator.rb +14 -4
- data/lib/ddql/operators.rb +53 -52
- data/lib/ddql/parser.rb +68 -2
- data/lib/ddql/postfix_null_type_operator.rb +2 -2
- data/lib/ddql/string_refinements.rb +4 -0
- data/lib/ddql/token.rb +15 -3
- data/lib/ddql/token_type.rb +125 -33
- data/lib/ddql/version.rb +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a257ea91453ff55380b71ef1a98e0a4b32d9688ee87b9f6d74e5141d58e73424
|
4
|
+
data.tar.gz: e6eae8733865abfb13efc7d2e252cb0a1c7b3ee1afbe2a7445248a1adef66c98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d539c78fd753b66f5303d5d1941b75e52e8a3a77c3c506823c73aa15b8a79804d23e5e54472c28888038d5073e3a4f0dc493b934b0f6dba6e9b660077eab5474
|
7
|
+
data.tar.gz: b94754405933f0607ca4412b02219df659c2068b52cac3f4d2c2db18fc7b5d246d84131bc0af665fd8b81eb2ac74c022ef0474bc5b1f9cd717e9bdda5d64d41c
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
ruby-2.
|
1
|
+
ruby-2.7.2
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ddql (1.0.
|
4
|
+
ddql (1.0.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -9,36 +9,36 @@ GEM
|
|
9
9
|
byebug (11.1.3)
|
10
10
|
coderay (1.1.3)
|
11
11
|
diff-lcs (1.4.4)
|
12
|
-
ffi (1.
|
12
|
+
ffi (1.15.3-java)
|
13
13
|
method_source (0.9.2)
|
14
|
-
pry (0.
|
14
|
+
pry (0.12.2)
|
15
15
|
coderay (~> 1.1.0)
|
16
16
|
method_source (~> 0.9.0)
|
17
|
-
pry (0.
|
17
|
+
pry (0.12.2-java)
|
18
18
|
coderay (~> 1.1.0)
|
19
19
|
method_source (~> 0.9.0)
|
20
20
|
spoon (~> 0.0)
|
21
21
|
pry-byebug (3.8.0)
|
22
22
|
byebug (~> 11.0)
|
23
23
|
pry (~> 0.10)
|
24
|
-
pry-debugger-jruby (1.2.
|
25
|
-
pry (>= 0.10, < 0.
|
26
|
-
ruby-debug-base (
|
27
|
-
rake (13.0.
|
28
|
-
rspec (3.
|
29
|
-
rspec-core (~> 3.
|
30
|
-
rspec-expectations (~> 3.
|
31
|
-
rspec-mocks (~> 3.
|
32
|
-
rspec-core (3.
|
33
|
-
rspec-support (~> 3.
|
34
|
-
rspec-expectations (3.
|
24
|
+
pry-debugger-jruby (1.2.2-java)
|
25
|
+
pry (>= 0.10, < 0.13)
|
26
|
+
ruby-debug-base (>= 0.10.4, < 0.12)
|
27
|
+
rake (13.0.3)
|
28
|
+
rspec (3.10.0)
|
29
|
+
rspec-core (~> 3.10.0)
|
30
|
+
rspec-expectations (~> 3.10.0)
|
31
|
+
rspec-mocks (~> 3.10.0)
|
32
|
+
rspec-core (3.10.1)
|
33
|
+
rspec-support (~> 3.10.0)
|
34
|
+
rspec-expectations (3.10.1)
|
35
35
|
diff-lcs (>= 1.2.0, < 2.0)
|
36
|
-
rspec-support (~> 3.
|
37
|
-
rspec-mocks (3.
|
36
|
+
rspec-support (~> 3.10.0)
|
37
|
+
rspec-mocks (3.10.2)
|
38
38
|
diff-lcs (>= 1.2.0, < 2.0)
|
39
|
-
rspec-support (~> 3.
|
40
|
-
rspec-support (3.
|
41
|
-
ruby-debug-base (0.
|
39
|
+
rspec-support (~> 3.10.0)
|
40
|
+
rspec-support (3.10.2)
|
41
|
+
ruby-debug-base (0.11.0-java)
|
42
42
|
spoon (0.0.6)
|
43
43
|
ffi
|
44
44
|
|
data/lib/ddql.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'ddql/version'
|
2
|
+
require_relative 'ddql/blank_refinements'
|
3
|
+
require_relative 'ddql/string_refinements'
|
2
4
|
require_relative 'ddql/linked_list'
|
3
5
|
require_relative 'ddql/query_expression_error'
|
4
6
|
require_relative 'ddql/lexer'
|
5
7
|
require_relative 'ddql/operators'
|
6
8
|
require_relative 'ddql/parser'
|
7
|
-
require_relative 'ddql/string_refinements'
|
8
9
|
require_relative 'ddql/token_type'
|
9
10
|
require_relative 'ddql/token'
|
10
11
|
|
data/lib/ddql/agg_operator.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module DDQL
|
2
|
+
using StringRefinements
|
3
|
+
|
2
4
|
# +AggOperators+ return a Hash structure describing the type of aggregation, an optional
|
3
5
|
# expression (filter) to apply, an optional factor (field) against which the aggregation
|
4
6
|
# is applied, and the entity type for which the aggregation is applicable.
|
@@ -13,7 +15,7 @@ module DDQL
|
|
13
15
|
# sub_query_type: 'IssuerPerson',
|
14
16
|
# }
|
15
17
|
#
|
16
|
-
# +CNT{type:Issuer,
|
18
|
+
# +CNT{type:Issuer, fields: [SomeFactor]} / CNT{type:Issuer, fields: [OtherFactor]}+
|
17
19
|
#
|
18
20
|
# {
|
19
21
|
# left: {
|
@@ -29,7 +31,7 @@ module DDQL
|
|
29
31
|
# },
|
30
32
|
# }
|
31
33
|
#
|
32
|
-
# +MAX{type:Case,
|
34
|
+
# +MAX{type:Case, fields: [SomeFactor]} > 5+
|
33
35
|
#
|
34
36
|
# {
|
35
37
|
# left: {
|
@@ -40,7 +42,18 @@ module DDQL
|
|
40
42
|
# op: {op_gt: '>'},
|
41
43
|
# right: {int: 5},
|
42
44
|
# }
|
45
|
+
#
|
46
|
+
# +ALIAS{type:Issuer, expression: [SomeFactor] > 5} AS [ThatFactor:That Factor]+
|
47
|
+
#
|
48
|
+
# {
|
49
|
+
# agg: {op_max: 'ALIAS'},
|
50
|
+
# sub_query_type: 'Issuer',
|
51
|
+
# sub_query_expression: '[SomeFactor] > 5',
|
52
|
+
# sub_query_alias: {factor: 'ThatFactor', desc: 'That Factor'},
|
53
|
+
# }
|
43
54
|
class AggOperator < Operator
|
55
|
+
using BlankRefinements
|
56
|
+
|
44
57
|
def initialize(symbol, name, return_type:, type: :prefix, precedence: 8, right: false)
|
45
58
|
super(symbol, name, type, precedence, right, return_type)
|
46
59
|
end
|
@@ -52,24 +65,69 @@ module DDQL
|
|
52
65
|
else
|
53
66
|
expression = new_expression
|
54
67
|
end
|
68
|
+
|
55
69
|
expression.merge!(agg: {:"op_#{symbol.downcase}" => symbol})
|
70
|
+
if parser.peek&.supports_post_processing?
|
71
|
+
token, expression = parser.peek.post_process(parser: parser, expression: expression)
|
72
|
+
end
|
73
|
+
as_agg(expression)
|
74
|
+
end
|
75
|
+
|
76
|
+
def as_left_op_right(expression)
|
77
|
+
new_expression = {
|
78
|
+
agg: expression.delete(:agg),
|
79
|
+
sub_query_type: expression.delete(:sub_query_type),
|
80
|
+
sub_query_fields: expression.delete(:sub_query_fields),
|
81
|
+
sub_query_expression: expression.delete(:sub_query_expression),
|
82
|
+
}.compact
|
56
83
|
|
57
|
-
if expression.key?(:op) && expression.
|
58
|
-
|
59
|
-
|
84
|
+
if expression[:lstatement].key?(:op) && expression[:lstatement][:left].blank?
|
85
|
+
{
|
86
|
+
left: new_expression,
|
87
|
+
op: expression[:lstatement][:op],
|
88
|
+
right: expression[:lstatement][:right],
|
89
|
+
}
|
90
|
+
elsif expression[:lstatement].key?(:lstatement)
|
91
|
+
if expression[:lstatement][:lstatement].empty?
|
92
|
+
expression[:lstatement][:lstatement] = new_expression
|
93
|
+
expression[:lstatement]
|
94
|
+
else
|
95
|
+
final_expression = expression[:lstatement]
|
96
|
+
final_expression[:lstatement][:left] = new_expression
|
97
|
+
final_expression
|
60
98
|
end
|
61
|
-
|
62
|
-
|
99
|
+
else
|
100
|
+
new_expression
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def as_agg(expr)
|
105
|
+
expression = expr.dup
|
106
|
+
if expression.key?(:yes_no_op)
|
107
|
+
# AGG Y/N
|
108
|
+
yes_no_op = expression.delete(:yes_no_op)
|
109
|
+
{
|
110
|
+
left: expression,
|
111
|
+
yes_no_op: yes_no_op,
|
112
|
+
}
|
113
|
+
elsif expression.key?(:op)
|
114
|
+
# AGG COMP LIT
|
115
|
+
expression.delete(:left)
|
116
|
+
op = expression.delete(:op)
|
117
|
+
right = expression.delete(:right)
|
63
118
|
{
|
64
119
|
left: expression,
|
65
120
|
op: op,
|
66
121
|
right: right,
|
67
122
|
}
|
68
|
-
elsif expression.key?(:
|
69
|
-
|
123
|
+
elsif expression.key?(:boolean_operator)
|
124
|
+
# AGG BOOL STMT
|
125
|
+
bool_op = expression.delete(:boolean_operator)
|
126
|
+
rstatement = expression.delete(:rstatement)
|
70
127
|
{
|
71
|
-
|
72
|
-
|
128
|
+
lstatement: as_left_op_right(expression),
|
129
|
+
boolean_operator: bool_op,
|
130
|
+
rstatement: rstatement,
|
73
131
|
}
|
74
132
|
else
|
75
133
|
expression
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module DDQL
|
2
|
+
module BlankRefinements
|
3
|
+
refine Enumerable do
|
4
|
+
def blank?
|
5
|
+
empty? || compact.empty?
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
refine Hash do
|
10
|
+
def blank?
|
11
|
+
empty? || compact.empty?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
refine NilClass do
|
16
|
+
def blank?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -13,13 +13,12 @@ module DDQL
|
|
13
13
|
end
|
14
14
|
|
15
15
|
left_factor, right_factor = expression[:string].split('|', 2)
|
16
|
+
|
16
17
|
{
|
17
|
-
op_coalesce:
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
},
|
22
|
-
}
|
18
|
+
op_coalesce: [
|
19
|
+
{factor: left_factor},
|
20
|
+
{factor: right_factor},
|
21
|
+
]
|
23
22
|
}
|
24
23
|
end
|
25
24
|
end
|
@@ -2,8 +2,8 @@ module DDQL
|
|
2
2
|
class InfixFloatMapOperator < Operator
|
3
3
|
attr_reader :comparison, :op_type, :op_symbol
|
4
4
|
|
5
|
-
def initialize(symbol, name,
|
6
|
-
super(symbol, name, :infix, 4, false, :boolean
|
5
|
+
def initialize(symbol, name, op_type, comparison)
|
6
|
+
super(symbol, name, :infix, 4, false, :boolean)
|
7
7
|
@op_type = op_type
|
8
8
|
@comparison = comparison
|
9
9
|
@op_symbol = :"op_float_map_#{op_type}_#{comparison}"
|
data/lib/ddql/linked_list.rb
CHANGED
@@ -2,13 +2,16 @@ module DDQL
|
|
2
2
|
class LinkedList
|
3
3
|
include Enumerable
|
4
4
|
|
5
|
+
class NavigationError < StandardError
|
6
|
+
end
|
7
|
+
|
5
8
|
class Node
|
6
9
|
attr_accessor :next, :previous
|
7
10
|
attr_reader :value
|
8
11
|
|
9
|
-
def initialize(value)
|
10
|
-
@next =
|
11
|
-
@previous =
|
12
|
+
def initialize(value, next_node: nil, previous_node: nil)
|
13
|
+
@next = next_node
|
14
|
+
@previous = previous_node
|
12
15
|
@value = value
|
13
16
|
end
|
14
17
|
|
@@ -22,61 +25,110 @@ module DDQL
|
|
22
25
|
def initialize(head=nil)
|
23
26
|
@doubly_linked = false
|
24
27
|
@head = head
|
25
|
-
@size = 0
|
28
|
+
@size = head.nil? ? 0 : 1
|
29
|
+
@tail = @head
|
26
30
|
end
|
27
31
|
|
28
32
|
def append(value)
|
29
33
|
if @head
|
30
34
|
tail = find_tail
|
31
|
-
|
32
|
-
|
35
|
+
if Node === value
|
36
|
+
value.previous = tail if @doubly_linked
|
37
|
+
tail.next = value
|
38
|
+
else
|
39
|
+
tail.next = Node.new(value)
|
40
|
+
tail.next.previous = tail if @doubly_linked
|
41
|
+
end
|
42
|
+
@tail = tail.next
|
33
43
|
else
|
34
|
-
@head = Node.new(value)
|
44
|
+
@head = @tail = Node === value ? value : Node.new(value)
|
35
45
|
end
|
36
46
|
@size += 1
|
37
47
|
end
|
38
48
|
alias :<< :append
|
39
49
|
|
50
|
+
# Insert the +value+ after the +target+
|
51
|
+
#
|
52
|
+
# @return [Node|NilClass] nil if +target+ is not found, otherwise the inserted node for +value+
|
40
53
|
def append_after(target, value)
|
41
|
-
node
|
54
|
+
node = find(target)
|
42
55
|
return unless node
|
43
|
-
old_next
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
56
|
+
old_next = node.next
|
57
|
+
if @doubly_linked
|
58
|
+
if Node === value
|
59
|
+
value.previous = node
|
60
|
+
value.next = old_next
|
61
|
+
node.next = value
|
62
|
+
else
|
63
|
+
node.next = Node.new(value, previous_node: node, next_node: old_next)
|
64
|
+
end
|
65
|
+
elsif Node === value
|
66
|
+
value.next = old_next
|
67
|
+
node.next = value
|
68
|
+
else
|
69
|
+
node.next = Node.new(value, next_node: old_next)
|
53
70
|
end
|
54
|
-
@
|
55
|
-
|
71
|
+
@tail = node.next if old_next.nil?
|
72
|
+
node.next
|
56
73
|
end
|
74
|
+
alias :insert :append_after
|
57
75
|
|
58
|
-
def
|
59
|
-
|
76
|
+
def at(index)
|
77
|
+
return nil if index < 0 || index >= size
|
78
|
+
return @head if index == 0
|
79
|
+
return @tail if index == size - 1
|
80
|
+
|
81
|
+
current_index = 0
|
82
|
+
current_node = head
|
83
|
+
while current_node = current_node.next
|
84
|
+
current_index += 1
|
85
|
+
return current_node if current_index == index
|
86
|
+
end
|
60
87
|
end
|
88
|
+
alias :[] :at
|
61
89
|
|
62
90
|
def delete(value)
|
63
91
|
if value.is_a?(Node) && @head == value
|
64
92
|
@head = @head.next
|
65
93
|
value.next = value.previous = nil
|
94
|
+
@tail = @head if @head.nil? || @head.next.nil?
|
66
95
|
elsif @head.value == value
|
67
96
|
node = @head.next
|
68
97
|
@head.next = @head.previous = nil
|
69
98
|
value = @head
|
70
99
|
@head = node
|
100
|
+
@tail = @head if @head.next.nil?
|
71
101
|
else
|
72
102
|
node = find_before(value)
|
103
|
+
|
104
|
+
if node && node.next == tail
|
105
|
+
@tail = node
|
106
|
+
node.next = nil
|
107
|
+
end
|
108
|
+
|
73
109
|
node.next = node.next.next if node && node.next
|
74
|
-
node.next
|
110
|
+
node.next.previous = node if doubly_linked! && node.next
|
75
111
|
end
|
76
112
|
@size -= 1
|
77
113
|
value
|
78
114
|
end
|
79
115
|
|
116
|
+
def doubly_linked!
|
117
|
+
return self if doubly_linked?
|
118
|
+
|
119
|
+
current = nil
|
120
|
+
each_node do |node|
|
121
|
+
node.previous = current
|
122
|
+
current = node
|
123
|
+
end
|
124
|
+
@doubly_linked = true
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
def doubly_linked?
|
129
|
+
@doubly_linked
|
130
|
+
end
|
131
|
+
|
80
132
|
def each
|
81
133
|
return to_enum unless block_given?
|
82
134
|
node = @head
|
@@ -86,13 +138,28 @@ module DDQL
|
|
86
138
|
end
|
87
139
|
end
|
88
140
|
|
89
|
-
def
|
141
|
+
def empty?
|
142
|
+
@size == 0
|
143
|
+
end
|
144
|
+
|
145
|
+
def find(value=nil, &blk)
|
90
146
|
node = @head
|
91
147
|
is_node = value.is_a?(Node)
|
92
|
-
return false if !node.next
|
93
|
-
return node if (is_node && node == value) || node.value == value
|
148
|
+
return false if !node.next && (!blk || (blk && !yield(node)))
|
149
|
+
return node if (is_node && node == value) || node.value == value || (blk && yield(node))
|
94
150
|
while (node = node.next)
|
95
|
-
return node if (is_node && node == value) || node.value == value
|
151
|
+
return node if (is_node && node == value) || node.value == value || (blk && yield(node))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def find_from_tail(value=nil, &blk)
|
156
|
+
raise NavigationError, "singly-linked lists don't support finding nodes from the tail" unless doubly_linked?
|
157
|
+
node = @tail
|
158
|
+
is_node = value.is_a?(Node)
|
159
|
+
return false if !node.previous && (!blk || (blk && !yield(node)))
|
160
|
+
return node if (is_node && node == value) || node.value == value || (blk && yield(node))
|
161
|
+
while (node = node.previous)
|
162
|
+
return node if (is_node && node == value) || node.value == value || (blk && yield(node))
|
96
163
|
end
|
97
164
|
end
|
98
165
|
|
@@ -113,9 +180,12 @@ module DDQL
|
|
113
180
|
end
|
114
181
|
|
115
182
|
def find_tail
|
116
|
-
|
117
|
-
|
118
|
-
|
183
|
+
if @tail.nil?
|
184
|
+
node = @head
|
185
|
+
return @tail = node if !node.next
|
186
|
+
return @tail = node if !node.next while (node = node.next)
|
187
|
+
end
|
188
|
+
@tail
|
119
189
|
end
|
120
190
|
alias :tail :find_tail
|
121
191
|
|
@@ -150,6 +220,40 @@ module DDQL
|
|
150
220
|
end
|
151
221
|
end
|
152
222
|
|
223
|
+
# Replace a range of nodes from +from+ to +to+, inclusive,
|
224
|
+
# with the given +with+. +from+, +to+, and +with+ can all
|
225
|
+
# be values or nodes.
|
226
|
+
#
|
227
|
+
# @return self
|
228
|
+
def replace!(from:, to:, with:)
|
229
|
+
first_node = find(from)
|
230
|
+
last_node = find(to)
|
231
|
+
tail = find_tail
|
232
|
+
raise 'cannot find appropriate range for replacement' if first_node.nil? || last_node.nil?
|
233
|
+
|
234
|
+
replacement = Node === with ? with : Node.new(with)
|
235
|
+
if first_node == head
|
236
|
+
@head = replacement
|
237
|
+
unless last_node == tail
|
238
|
+
new_tail = last_node.next
|
239
|
+
replacement.next = new_tail
|
240
|
+
new_tail.previous = replacement if doubly_linked?
|
241
|
+
end
|
242
|
+
return self
|
243
|
+
end
|
244
|
+
|
245
|
+
if doubly_linked?
|
246
|
+
replacement.previous = first_node.previous
|
247
|
+
replacement.previous.next = replacement
|
248
|
+
last_node.next.previous = replacement
|
249
|
+
elsif first_node != head
|
250
|
+
previous = find_before(first_node)
|
251
|
+
previous.next = replacement
|
252
|
+
end
|
253
|
+
replacement.next = last_node.next
|
254
|
+
self
|
255
|
+
end
|
256
|
+
|
153
257
|
def singly_linked!
|
154
258
|
each_node do |node|
|
155
259
|
node.previous = nil
|