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
data/lib/ddql/list_operator.rb
CHANGED
data/lib/ddql/lookup_operator.rb
CHANGED
@@ -6,16 +6,18 @@ module DDQL
|
|
6
6
|
super("LOOKUP BY", "Lookup", type: :infix, return_type: :string)
|
7
7
|
end
|
8
8
|
|
9
|
-
def parse(parser,
|
9
|
+
def parse(parser, _token, expression: nil)
|
10
10
|
precedence = self.precedence
|
11
11
|
precedence -= 1 if right?
|
12
12
|
next_expression = parser.parse(precedence: precedence)
|
13
|
-
|
14
|
-
|
13
|
+
|
14
|
+
foreign_value_factor = expression[:factor]
|
15
|
+
foreign_key_factor = (next_expression.delete(:left) || next_expression)[:factor]
|
15
16
|
{
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
op_lookup_by: {
|
18
|
+
foreign_key: {factor: foreign_key_factor},
|
19
|
+
foreign_value: {factor: foreign_value_factor},
|
20
|
+
},
|
19
21
|
}
|
20
22
|
end
|
21
23
|
end
|
data/lib/ddql/operator.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
module DDQL
|
2
2
|
class Operator
|
3
|
-
attr_reader :associativity, :symbol, :name, :type, :precedence, :return_type
|
3
|
+
attr_reader :associativity, :symbol, :name, :type, :precedence, :return_type
|
4
4
|
|
5
|
-
def initialize(symbol, name, type, precedence, right, return_type
|
5
|
+
def initialize(symbol, name, type, precedence, right, return_type)
|
6
6
|
@symbol = symbol
|
7
7
|
@name = name
|
8
8
|
@type = type
|
9
9
|
@precedence = precedence
|
10
10
|
@associativity = right ? :right : :left
|
11
11
|
@return_type = return_type
|
12
|
-
|
12
|
+
end
|
13
|
+
|
14
|
+
def any_type?
|
15
|
+
return_type == :any
|
13
16
|
end
|
14
17
|
|
15
18
|
def boolean?
|
@@ -118,10 +121,14 @@ module DDQL
|
|
118
121
|
|
119
122
|
def parse_infix(parser, token, expression)
|
120
123
|
precedence = self.precedence
|
121
|
-
precedence -=
|
124
|
+
precedence -= 0.05 if right?
|
122
125
|
next_expression = parser.parse(precedence: precedence)
|
123
126
|
op_expression = token.as_hash
|
124
127
|
|
128
|
+
if math? && next_expression&.dig(:left, :factor) && next_expression.size == 1
|
129
|
+
return op_expression.merge(left: expression, right: next_expression[:left])
|
130
|
+
end
|
131
|
+
|
125
132
|
if token.and? || token.or?
|
126
133
|
return boolean_stmt(expression, op_expression, next_expression)
|
127
134
|
end
|
@@ -162,6 +169,9 @@ module DDQL
|
|
162
169
|
next_expression = next_expression[:lstatement]
|
163
170
|
elsif next_expression.key?(:rstatement) && !next_expression.key?(:lstatement)
|
164
171
|
next_expression = next_expression[:rstatement]
|
172
|
+
elsif op_expression[:op].key?(:op_not) && next_expression.key?(:op_not)
|
173
|
+
next_expression.delete(:op_not)
|
174
|
+
return next_expression
|
165
175
|
end
|
166
176
|
op_expression[:op].merge(next_expression)
|
167
177
|
end
|
data/lib/ddql/operators.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'singleton'
|
2
|
-
|
3
2
|
require_relative 'operator'
|
4
3
|
require_relative 'agg_operator'
|
5
4
|
require_relative 'coalesce_operator'
|
@@ -38,33 +37,36 @@ module DDQL
|
|
38
37
|
private
|
39
38
|
def build_cache
|
40
39
|
cache = {}
|
41
|
-
Operator.new(
|
42
|
-
Operator.new(
|
43
|
-
Operator.new("
|
44
|
-
Operator.new("
|
45
|
-
Operator.new("
|
46
|
-
Operator.new("
|
47
|
-
Operator.new("
|
48
|
-
|
49
|
-
Operator.new("
|
50
|
-
Operator.new("
|
51
|
-
Operator.new("
|
52
|
-
Operator.new("
|
53
|
-
Operator.new("
|
54
|
-
Operator.new("
|
55
|
-
Operator.new("
|
56
|
-
Operator.new("
|
57
|
-
Operator.new("
|
58
|
-
Operator.new("
|
59
|
-
Operator.new("
|
60
|
-
Operator.new("
|
61
|
-
Operator.new("
|
40
|
+
Operator.new(TokenType::NESTED_OPEN_PATTERN, "Nested Query Opener", :prefix, 3, false, :none).register(cache)
|
41
|
+
Operator.new(TokenType::NESTED_CLOSE_PATTERN, "Nested Query Closer", :postfix, 3, false, :none).register(cache)
|
42
|
+
Operator.new("==", "Double Equals", :infix, 3, false, :boolean).register(cache)
|
43
|
+
Operator.new("=", "Single Equals", :infix, 3, false, :boolean).register(cache)
|
44
|
+
Operator.new("!=", "Not Equals", :infix, 3, false, :boolean).register(cache)
|
45
|
+
Operator.new("<=", "Less Than or Equals", :infix, 4, false, :boolean).register(cache)
|
46
|
+
Operator.new("<", "Less Than", :infix, 4, false, :boolean).register(cache)
|
47
|
+
Operator.new(">=", "Greater Than or Equals", :infix, 4, false, :boolean).register(cache)
|
48
|
+
Operator.new(">", "Greater Than", :infix, 4, false, :boolean).register(cache)
|
49
|
+
Operator.new("CTN", "Contains", :infix, 4, false, :boolean).register(cache)
|
50
|
+
Operator.new("STW", "Starts With", :infix, 4, false, :boolean).register(cache)
|
51
|
+
Operator.new("IN", "In Any", :infix, 4, false, :boolean).register(cache)
|
52
|
+
Operator.new("LCTN", "Contains", :infix, 4, false, :boolean).register(cache)
|
53
|
+
Operator.new("ON", "On Date", :infix, 4, false, :boolean).register(cache)
|
54
|
+
Operator.new("EPST", "On Or After Date", :infix, 4, false, :boolean).register(cache)
|
55
|
+
Operator.new("PST", "After Date", :infix, 4, false, :boolean).register(cache)
|
56
|
+
Operator.new("EPRE", "On Or Before Date", :infix, 4, false, :boolean).register(cache)
|
57
|
+
Operator.new("PRE", "Before Date", :infix, 4, false, :boolean).register(cache)
|
58
|
+
Operator.new("OR", "Or", :infix, 0.1, false, :boolean).register(cache)
|
59
|
+
Operator.new("AND", "And", :infix, 0.2, false, :boolean).register(cache)
|
60
|
+
Operator.new("NOT", "Not", :prefix, 0.3, false, :boolean).register(cache)
|
61
|
+
Operator.new("IS NULL", "Is Null", :postfix, 9, false, :boolean).register(cache)
|
62
|
+
Operator.new("IS NOT NULL", "Is Not Null", :postfix, 9, false, :boolean).register(cache)
|
62
63
|
Operator.new("+", "Plus", :infix, 5, false, :double).register(cache)
|
63
64
|
Operator.new("-", "Minus", :infix, 5, false, :double).register(cache)
|
64
65
|
Operator.new("*", "Multiplied By", :infix, 6, false, :double).register(cache)
|
65
66
|
Operator.new("/", "Divided By", :infix, 6, false, :double).register(cache)
|
66
67
|
Operator.new("%", "Modulus", :infix, 6, false, :integer).register(cache)
|
67
68
|
Operator.new("^", "To the Power of", :infix, 7, true, :double).register(cache)
|
69
|
+
AggOperator.new("ALIAS", "Alias", return_type: :any).register(cache)
|
68
70
|
AggOperator.new("EXISTS", "Exists", return_type: :boolean).register(cache)
|
69
71
|
AggOperator.new("CNT", "Count", return_type: :integer).register(cache)
|
70
72
|
AggOperator.new("AVG", "Mean", return_type: :double).register(cache)
|
@@ -78,36 +80,35 @@ module DDQL
|
|
78
80
|
# PNT & RATIO don't work and aren't used in DD PROD as of 2020-07-24
|
79
81
|
# AggOperator.new("PNT", "Percent", return_type: :double).register(cache)
|
80
82
|
# AggOperator.new("RATIO", "Ratio", return_type: :double).register(cache)
|
81
|
-
InfixStringMapOperator.new("ANY_MAP", "Has Any"
|
82
|
-
InfixStringMapOperator.new("ALL_MAP", "Has All"
|
83
|
-
InfixStringMapOperator.new("NONE_MAP", "Has None"
|
84
|
-
InfixFloatMapOperator.new("ANY_GREATER_THAN_FLOAT_MAP", "Has Any Greater Than",
|
85
|
-
InfixFloatMapOperator.new("ALL_GREATER_THAN_FLOAT_MAP", "Has All Greater Than",
|
86
|
-
InfixFloatMapOperator.new("NONE_GREATER_THAN_FLOAT_MAP", "Has None Greater Than",
|
87
|
-
InfixFloatMapOperator.new("ANY_GREATER_THAN_OR_EQUAL_FLOAT_MAP", "Has Any Greater Than Or Equal To",
|
88
|
-
InfixFloatMapOperator.new("ALL_GREATER_THAN_OR_EQUAL_FLOAT_MAP", "Has All Greater Than Or Equal To",
|
89
|
-
InfixFloatMapOperator.new("NONE_GREATER_THAN_OR_EQUAL_FLOAT_MAP", "Has None Greater Than Or Equal To",
|
90
|
-
InfixFloatMapOperator.new("ANY_LESS_THAN_FLOAT_MAP", "Has Any Less Than",
|
91
|
-
InfixFloatMapOperator.new("ALL_LESS_THAN_FLOAT_MAP", "Has All Less Than",
|
92
|
-
InfixFloatMapOperator.new("NONE_LESS_THAN_FLOAT_MAP", "Has None Less Than",
|
93
|
-
InfixFloatMapOperator.new("ANY_LESS_THAN_OR_EQUAL_FLOAT_MAP", "Has Any Less Than Or Equal To",
|
94
|
-
InfixFloatMapOperator.new("ALL_LESS_THAN_OR_EQUAL_FLOAT_MAP", "Has All Less Than Or Equal To",
|
95
|
-
InfixFloatMapOperator.new("NONE_LESS_THAN_OR_EQUAL_FLOAT_MAP", "Has None Less Than Or Equal To",
|
96
|
-
InfixFloatMapOperator.new("ANY_EQUAL_FLOAT_MAP", "Has Any Equal To",
|
97
|
-
InfixFloatMapOperator.new("ALL_EQUAL_FLOAT_MAP", "Has All Equal To",
|
98
|
-
InfixFloatMapOperator.new("NONE_EQUAL_FLOAT_MAP", "Has None Equal To",
|
99
|
-
ListOperator.new("ANY", "Has Any", :infix
|
100
|
-
ListOperator.new("ALL", "Has All", :infix
|
101
|
-
ListOperator.new("NONE", "Has None", :infix
|
102
|
-
ListOperator.new("EMPTY", "Is Empty", :postfix
|
103
|
-
PostfixNullTypeOperator.new("NO_INFORMATION", :NoInformation
|
104
|
-
PostfixNullTypeOperator.new("NOT_APPLICABLE", :NotApplicable
|
105
|
-
PostfixNullTypeOperator.new("NOT_COLLECTED", :NotCollected
|
106
|
-
PostfixNullTypeOperator.new("NOT_DISCLOSED", :NotDisclosed
|
107
|
-
PostfixNullTypeOperator.new("NOT_MEANINGFUL", :NotMeaningful
|
108
|
-
Operator.new("
|
109
|
-
Operator.new("
|
110
|
-
Operator.new("NO", "Is False", :postfix, 9, false, :boolean, 1).register(cache)
|
83
|
+
InfixStringMapOperator.new("ANY_MAP", "Has Any").register(cache)
|
84
|
+
InfixStringMapOperator.new("ALL_MAP", "Has All").register(cache)
|
85
|
+
InfixStringMapOperator.new("NONE_MAP", "Has None").register(cache)
|
86
|
+
InfixFloatMapOperator.new("ANY_GREATER_THAN_FLOAT_MAP", "Has Any Greater Than", :any, :gt).register(cache)
|
87
|
+
InfixFloatMapOperator.new("ALL_GREATER_THAN_FLOAT_MAP", "Has All Greater Than", :all, :gt).register(cache)
|
88
|
+
InfixFloatMapOperator.new("NONE_GREATER_THAN_FLOAT_MAP", "Has None Greater Than", :none, :gt).register(cache)
|
89
|
+
InfixFloatMapOperator.new("ANY_GREATER_THAN_OR_EQUAL_FLOAT_MAP", "Has Any Greater Than Or Equal To", :any, :ge).register(cache)
|
90
|
+
InfixFloatMapOperator.new("ALL_GREATER_THAN_OR_EQUAL_FLOAT_MAP", "Has All Greater Than Or Equal To", :all, :ge).register(cache)
|
91
|
+
InfixFloatMapOperator.new("NONE_GREATER_THAN_OR_EQUAL_FLOAT_MAP", "Has None Greater Than Or Equal To", :none, :ge).register(cache)
|
92
|
+
InfixFloatMapOperator.new("ANY_LESS_THAN_FLOAT_MAP", "Has Any Less Than", :any, :lt).register(cache)
|
93
|
+
InfixFloatMapOperator.new("ALL_LESS_THAN_FLOAT_MAP", "Has All Less Than", :all, :lt).register(cache)
|
94
|
+
InfixFloatMapOperator.new("NONE_LESS_THAN_FLOAT_MAP", "Has None Less Than", :none, :lt).register(cache)
|
95
|
+
InfixFloatMapOperator.new("ANY_LESS_THAN_OR_EQUAL_FLOAT_MAP", "Has Any Less Than Or Equal To", :any, :le).register(cache)
|
96
|
+
InfixFloatMapOperator.new("ALL_LESS_THAN_OR_EQUAL_FLOAT_MAP", "Has All Less Than Or Equal To", :all, :le).register(cache)
|
97
|
+
InfixFloatMapOperator.new("NONE_LESS_THAN_OR_EQUAL_FLOAT_MAP", "Has None Less Than Or Equal To", :none, :le).register(cache)
|
98
|
+
InfixFloatMapOperator.new("ANY_EQUAL_FLOAT_MAP", "Has Any Equal To", :any, :eq).register(cache)
|
99
|
+
InfixFloatMapOperator.new("ALL_EQUAL_FLOAT_MAP", "Has All Equal To", :all, :eq).register(cache)
|
100
|
+
InfixFloatMapOperator.new("NONE_EQUAL_FLOAT_MAP", "Has None Equal To", :none, :eq).register(cache)
|
101
|
+
ListOperator.new("ANY", "Has Any", :infix).register(cache)
|
102
|
+
ListOperator.new("ALL", "Has All", :infix).register(cache)
|
103
|
+
ListOperator.new("NONE", "Has None", :infix).register(cache)
|
104
|
+
ListOperator.new("EMPTY", "Is Empty", :postfix).register(cache)
|
105
|
+
PostfixNullTypeOperator.new("NO_INFORMATION", :NoInformation).register(cache)
|
106
|
+
PostfixNullTypeOperator.new("NOT_APPLICABLE", :NotApplicable).register(cache)
|
107
|
+
PostfixNullTypeOperator.new("NOT_COLLECTED", :NotCollected).register(cache)
|
108
|
+
PostfixNullTypeOperator.new("NOT_DISCLOSED", :NotDisclosed).register(cache)
|
109
|
+
PostfixNullTypeOperator.new("NOT_MEANINGFUL", :NotMeaningful).register(cache)
|
110
|
+
Operator.new("YES", "Is True", :postfix, 9, false, :boolean).register(cache)
|
111
|
+
Operator.new("NO", "Is False", :postfix, 9, false, :boolean).register(cache)
|
111
112
|
end
|
112
113
|
end
|
113
114
|
end
|
data/lib/ddql/parser.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module DDQL
|
2
2
|
class Parser
|
3
|
+
attr_reader :tokens
|
4
|
+
|
3
5
|
class ParseException < StandardError
|
4
6
|
end
|
5
7
|
|
@@ -9,10 +11,51 @@ module DDQL
|
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
|
-
|
14
|
+
class ResolvedToken
|
15
|
+
attr_reader :value
|
16
|
+
|
17
|
+
def initialize(v)
|
18
|
+
@value = v
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse(_)
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.from_tokens(tokens)
|
27
|
+
opener = tokens.doubly_linked!.find { |node| node.value.type == TokenType::NESTED_OPENER }
|
28
|
+
if opener
|
29
|
+
closer = tokens.find_from_tail { |node| node.value.type == TokenType::NESTED_CLOSER }
|
30
|
+
new_tokens = DDQL::LinkedList.new
|
31
|
+
current = opener
|
32
|
+
while (current = current.next) && !(current === closer)
|
33
|
+
new_tokens << current.dup
|
34
|
+
end
|
35
|
+
new_tokens.tail.next = nil
|
36
|
+
new(tokens: tokens.replace!(
|
37
|
+
from: opener,
|
38
|
+
to: closer,
|
39
|
+
with: ResolvedToken.new(sub_query: from_tokens(new_tokens).parse)),
|
40
|
+
)
|
41
|
+
else
|
42
|
+
new(tokens: tokens)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.parse(expr)
|
47
|
+
from_tokens(Lexer.lex(expr)).record_expression(expr).parse
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize(expression: nil, tokens: nil)
|
13
51
|
@expression = expression
|
14
|
-
@tokens = Lexer.lex(expression)
|
15
52
|
@depth = 0
|
53
|
+
if expression
|
54
|
+
@tokens = Lexer.lex(expression)
|
55
|
+
else
|
56
|
+
@tokens = tokens
|
57
|
+
end
|
58
|
+
raise "tokens cannot be determined" if @tokens.nil? || @tokens.empty?
|
16
59
|
end
|
17
60
|
|
18
61
|
def consume(token_type)
|
@@ -31,6 +74,7 @@ module DDQL
|
|
31
74
|
def parse(precedence: 0)
|
32
75
|
@depth += 1
|
33
76
|
token = @tokens.poll
|
77
|
+
|
34
78
|
raise NoTokenException if token.nil?
|
35
79
|
|
36
80
|
expression = token.parse(self)
|
@@ -51,6 +95,28 @@ module DDQL
|
|
51
95
|
@tokens.peek
|
52
96
|
end
|
53
97
|
|
98
|
+
def record_expression(expr)
|
99
|
+
@expression = expr
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
# supports reading until the next +token_type+, does not support
|
104
|
+
# nested reads of +token_type+
|
105
|
+
#
|
106
|
+
# @raise [RuntimeError] if +token_type+ is not found
|
107
|
+
# @return [LinkedList<Token>] tokens exclusive of the final token_type
|
108
|
+
def until(token_type)
|
109
|
+
new_list = LinkedList.new.doubly_linked!
|
110
|
+
while token = @tokens.poll
|
111
|
+
if token.type?(token_type)
|
112
|
+
return new_list
|
113
|
+
else
|
114
|
+
new_list << token
|
115
|
+
end
|
116
|
+
end
|
117
|
+
raise "expected to consume tokens up to type[#{token_type.name}]"
|
118
|
+
end
|
119
|
+
|
54
120
|
private
|
55
121
|
def next_precedence
|
56
122
|
token = peek
|
@@ -2,8 +2,8 @@ module DDQL
|
|
2
2
|
class PostfixNullTypeOperator < Operator
|
3
3
|
attr_reader :pattern
|
4
4
|
|
5
|
-
def initialize(symbol, null_type
|
6
|
-
super("IS #{symbol}", "Is #{symbol}", :postfix, 9, false, :boolean
|
5
|
+
def initialize(symbol, null_type)
|
6
|
+
super("IS #{symbol}", "Is #{symbol}", :postfix, 9, false, :boolean)
|
7
7
|
@null_type = null_type
|
8
8
|
@pattern = /IS\s+#{symbol}/
|
9
9
|
end
|
data/lib/ddql/token.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
|
3
3
|
module DDQL
|
4
|
-
|
5
4
|
class Token
|
6
|
-
using StringRefinements
|
7
|
-
|
8
5
|
attr_reader :data, :type
|
9
6
|
attr_accessor :location
|
10
7
|
|
8
|
+
using ::DDQL::StringRefinements
|
9
|
+
|
11
10
|
def initialize(data:, location: nil, type:)
|
12
11
|
@data = data
|
13
12
|
@location = location
|
@@ -50,6 +49,11 @@ module DDQL
|
|
50
49
|
type.parse(parser, self, expression: expression)
|
51
50
|
end
|
52
51
|
|
52
|
+
def post_process(parser:, expression:)
|
53
|
+
raise "#{type} doesn't support post-processing" unless supports_post_processing?
|
54
|
+
type.post_process(parser: parser, expression: expression)
|
55
|
+
end
|
56
|
+
|
53
57
|
def postfix?
|
54
58
|
type.postfix?
|
55
59
|
end
|
@@ -62,6 +66,10 @@ module DDQL
|
|
62
66
|
type.simple_comparison?(data)
|
63
67
|
end
|
64
68
|
|
69
|
+
def supports_post_processing?
|
70
|
+
type.supports_post_processing?
|
71
|
+
end
|
72
|
+
|
65
73
|
def to_h
|
66
74
|
type.as_hash(data)
|
67
75
|
end
|
@@ -69,5 +77,9 @@ module DDQL
|
|
69
77
|
def to_s
|
70
78
|
"#{type.name} : #{data}"
|
71
79
|
end
|
80
|
+
|
81
|
+
def type?(token_type)
|
82
|
+
token_type == type
|
83
|
+
end
|
72
84
|
end
|
73
85
|
end
|
data/lib/ddql/token_type.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
module DDQL
|
2
2
|
class TokenType
|
3
|
-
using StringRefinements
|
4
|
-
|
5
3
|
attr_reader :label, :name, :pattern
|
6
4
|
|
5
|
+
using ::DDQL::StringRefinements
|
6
|
+
|
7
7
|
FACTOR_PATTERN = /\[[^\]]+\]/
|
8
|
+
NESTED_OPEN_PATTERN = '‹'
|
9
|
+
NESTED_CLOSE_PATTERN = '›'
|
8
10
|
|
9
11
|
def self.all_types_pattern
|
10
|
-
Regexp.compile(ALL.map { |tt| "(?<#{tt.name}>#{tt.pattern})" }.join('|'))
|
12
|
+
@pattern ||= Regexp.compile(ALL.map { |tt| "(?<#{tt.name}>#{tt.pattern})" }.join('|'))
|
11
13
|
end
|
12
14
|
|
13
15
|
def initialize(name:, pattern:, &block)
|
@@ -19,16 +21,12 @@ module DDQL
|
|
19
21
|
@value_transformer = block
|
20
22
|
end
|
21
23
|
|
24
|
+
def ==(other)
|
25
|
+
name == other.name
|
26
|
+
end
|
27
|
+
|
22
28
|
def as_hash(data)
|
23
|
-
|
24
|
-
# when :string_literal; {right: {string: data}}
|
25
|
-
# when :integer_literal; {right: {int: data}}
|
26
|
-
# when :numeric_literal, :sci_num_literal; {right: {float: data}}
|
27
|
-
# # when :lparen; {lstatement: data}
|
28
|
-
# when :rparen; Hash.new
|
29
|
-
# else
|
30
|
-
raise "subclass responsibility name[#{name}] data[#{data}]"
|
31
|
-
# end
|
29
|
+
raise "subclass responsibility name[#{name}] data[#{data}]"
|
32
30
|
end
|
33
31
|
|
34
32
|
def comparison?(data)
|
@@ -96,6 +94,10 @@ module DDQL
|
|
96
94
|
self
|
97
95
|
end
|
98
96
|
|
97
|
+
def supports_post_processing?
|
98
|
+
false
|
99
|
+
end
|
100
|
+
|
99
101
|
def trimming!(range=(1..-2))
|
100
102
|
@data_range = range
|
101
103
|
self
|
@@ -110,7 +112,6 @@ module DDQL
|
|
110
112
|
end
|
111
113
|
|
112
114
|
def as_hash(data)
|
113
|
-
# {right: {data_type => data}}
|
114
115
|
{data_type => data}
|
115
116
|
end
|
116
117
|
|
@@ -125,7 +126,7 @@ module DDQL
|
|
125
126
|
|
126
127
|
class Currency < Literal
|
127
128
|
def initialize
|
128
|
-
super(name: :currency, pattern: /'(?!')(?<code>[A-Z]{3}):(\d+\.?\d
|
129
|
+
super(name: :currency, pattern: /'(?!')(?<code>[A-Z]{3}):(\d+\.?\d*)'/)
|
129
130
|
@value_transformer = lambda do |s|
|
130
131
|
s = s.split(':', 2)
|
131
132
|
{currency_code: s.first, currency_value: {float: s.last.to_f}}
|
@@ -133,14 +134,13 @@ module DDQL
|
|
133
134
|
end
|
134
135
|
|
135
136
|
def as_hash(data)
|
136
|
-
# {right: data}
|
137
137
|
data
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
141
|
class Integer < Literal
|
142
142
|
def initialize
|
143
|
-
super(name: :integer, pattern: /'(?!')(?>[+-]?)(\d+)'/)
|
143
|
+
super(name: :integer, pattern: /'(?!['0])(?>[+-]?)(\d+)'/)
|
144
144
|
@value_transformer = -> (s) { s.to_i }
|
145
145
|
end
|
146
146
|
|
@@ -151,7 +151,7 @@ module DDQL
|
|
151
151
|
|
152
152
|
class Numeric < Literal
|
153
153
|
def initialize
|
154
|
-
super(name: :numeric, pattern: /'(?!')((?>[+-]?)(?>(?>\d
|
154
|
+
super(name: :numeric, pattern: /'((?!('|0\d)))((?>[+-]?)(?>(?>\d*)(?>\.?)(?>\d+)))'/)
|
155
155
|
@value_transformer = -> (s) { s.to_f }
|
156
156
|
end
|
157
157
|
|
@@ -162,7 +162,7 @@ module DDQL
|
|
162
162
|
|
163
163
|
class ScientificNumeric < Literal
|
164
164
|
def initialize
|
165
|
-
super(name: :sci_num, pattern: /'(?!')([+-]?\d(\.\d+)?[Ee][+-]
|
165
|
+
super(name: :sci_num, pattern: /'(?!('|0\d))([+-]?\d(\.\d+)?[Ee][+-]?\d+)'/)
|
166
166
|
@value_transformer = -> (s) { s.to_f }
|
167
167
|
end
|
168
168
|
|
@@ -192,6 +192,14 @@ module DDQL
|
|
192
192
|
@value_transformer = -> (s) { s.gsub('\\', '') }
|
193
193
|
end
|
194
194
|
|
195
|
+
def as_hash(data)
|
196
|
+
if data&.strip.each_byte.all? { |e| e == 0x30 }
|
197
|
+
Integer.new.as_hash(data.to_i)
|
198
|
+
else
|
199
|
+
super
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
195
203
|
def data_type
|
196
204
|
name
|
197
205
|
end
|
@@ -206,7 +214,6 @@ module DDQL
|
|
206
214
|
end
|
207
215
|
|
208
216
|
def as_hash(data)
|
209
|
-
# {left: {name => data}}
|
210
217
|
{name => data}
|
211
218
|
end
|
212
219
|
|
@@ -229,9 +236,9 @@ module DDQL
|
|
229
236
|
true
|
230
237
|
end
|
231
238
|
|
232
|
-
def parse(parser,
|
239
|
+
def parse(parser, _token, expression: nil)
|
233
240
|
new_expression = parser.parse
|
234
|
-
parser.consume TokenType::RPAREN
|
241
|
+
parser.consume TokenType::RPAREN
|
235
242
|
|
236
243
|
if expression.nil?
|
237
244
|
next_token = parser.peek
|
@@ -280,7 +287,7 @@ module DDQL
|
|
280
287
|
|
281
288
|
def parse(parser, token, expression: nil)
|
282
289
|
operator = Operators.instance.cache[token.op_data]
|
283
|
-
if expression.nil? && !operator
|
290
|
+
if expression.nil? && !operator&.prefix?
|
284
291
|
raise "expected op[#{operator&.name}] to be part of an expression"
|
285
292
|
end
|
286
293
|
operator.parse(parser, token, expression: expression)
|
@@ -291,6 +298,7 @@ module DDQL
|
|
291
298
|
end
|
292
299
|
|
293
300
|
protected
|
301
|
+
|
294
302
|
def op_symbol(data)
|
295
303
|
float_map_ops = Operators.float_map_ops
|
296
304
|
|
@@ -405,6 +413,28 @@ module DDQL
|
|
405
413
|
end
|
406
414
|
end
|
407
415
|
|
416
|
+
class NestedQuery < TokenType
|
417
|
+
def initialize
|
418
|
+
super(name: :nested_opener, pattern: Regexp.compile(Regexp.escape TokenType::NESTED_OPEN_PATTERN))
|
419
|
+
trimming!
|
420
|
+
end
|
421
|
+
|
422
|
+
def as_hash(data)
|
423
|
+
require 'pry'; binding.pry
|
424
|
+
{screen: data.split('#').last.to_i}
|
425
|
+
end
|
426
|
+
|
427
|
+
def expression?
|
428
|
+
true
|
429
|
+
end
|
430
|
+
|
431
|
+
def parse(parser, token, expression: nil)
|
432
|
+
require 'pry'; binding.pry
|
433
|
+
nested_parser = parser.class.from_tokens(parser.until(TokenType::NESTED_CLOSER))
|
434
|
+
nested_parser.parse
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
408
438
|
class SubQuery < TokenType
|
409
439
|
def initialize
|
410
440
|
super(name: :lbrace, pattern: /\{/)
|
@@ -414,15 +444,12 @@ module DDQL
|
|
414
444
|
true
|
415
445
|
end
|
416
446
|
|
417
|
-
def parse(parser,
|
447
|
+
def parse(parser, _token, expression: nil)
|
418
448
|
new_expression = parser.parse
|
419
|
-
if parser.peek&.
|
420
|
-
|
421
|
-
new_expression.merge!(token.parse(parser, expression: new_expression))
|
449
|
+
if parser.peek&.supports_post_processing?
|
450
|
+
_token, new_expression = parser.peek.post_process(parser: parser, expression: new_expression)
|
422
451
|
end
|
423
452
|
|
424
|
-
parser.consume TokenType::RBRACE if parser.peek&.type == TokenType::RBRACE
|
425
|
-
|
426
453
|
if expression.nil?
|
427
454
|
next_token = parser.peek
|
428
455
|
if next_token && (next_token.and? || next_token.or?)
|
@@ -438,9 +465,43 @@ module DDQL
|
|
438
465
|
end
|
439
466
|
end
|
440
467
|
|
468
|
+
class SubQueryAlias < TokenType
|
469
|
+
def initialize
|
470
|
+
super(name: :sub_query_alias, pattern: /AS\s+(?<sub_query_alias>#{FACTOR_PATTERN})/)
|
471
|
+
trimming!
|
472
|
+
end
|
473
|
+
|
474
|
+
def as_hash(data)
|
475
|
+
factor, desc = data.split(':', 2)
|
476
|
+
desc = factor unless desc
|
477
|
+
{name => {factor: factor, desc: desc}}
|
478
|
+
end
|
479
|
+
|
480
|
+
def parse(parser, token, expression: nil)
|
481
|
+
if expression.nil?
|
482
|
+
raise 'expected AS to be part of an ALIAS expression'
|
483
|
+
end
|
484
|
+
if expression && expression.key?(:sub_query_expression)
|
485
|
+
expression[:sub_query] = parser.class.parse(expression[:sub_query_expression])
|
486
|
+
expression.delete :sub_query_expression
|
487
|
+
end
|
488
|
+
expression.merge(as_hash(token.data))
|
489
|
+
end
|
490
|
+
|
491
|
+
def post_process(parser:, expression:)
|
492
|
+
token = parser.consume TokenType::SUB_Q_ALIAS
|
493
|
+
new_expression = expression.merge!(token.parse(parser, expression: expression))
|
494
|
+
[token, new_expression]
|
495
|
+
end
|
496
|
+
|
497
|
+
def supports_post_processing?
|
498
|
+
true
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
441
502
|
class SubQueryExpression < TokenType
|
442
503
|
def initialize
|
443
|
-
super(name: :sub_query_expression, pattern: /expression:\s*(?<sub_query_expression>[^\{\}]{5,})\s*,?\s*/)
|
504
|
+
super(name: :sub_query_expression, pattern: /expression:\s*(?<sub_query_expression>[^\{\}#{Regexp.escape(TokenType::NESTED_OPEN_PATTERN)}#{Regexp.escape(TokenType::NESTED_CLOSE_PATTERN)}]{5,})\s*,?\s*/)
|
444
505
|
end
|
445
506
|
|
446
507
|
def as_hash(data)
|
@@ -448,10 +509,16 @@ module DDQL
|
|
448
509
|
end
|
449
510
|
|
450
511
|
def parse(parser, token, expression: nil)
|
512
|
+
data = token.data.strip
|
513
|
+
if data.start_with? TokenType::NESTED_OPEN_PATTERN
|
514
|
+
sub_data = data[1..(data.index(TokenType::NESTED_CLOSE_PATTERN))]
|
515
|
+
data = parser.class.parse sub_data
|
516
|
+
end
|
517
|
+
|
451
518
|
if expression.nil? || expression.keys != %i[agg sub_query_fields sub_query_type]
|
452
|
-
as_hash(
|
519
|
+
as_hash(data).merge parser.parse
|
453
520
|
else
|
454
|
-
expression.merge(as_hash(
|
521
|
+
expression.merge(as_hash(data))
|
455
522
|
end
|
456
523
|
end
|
457
524
|
end
|
@@ -491,6 +558,16 @@ module DDQL
|
|
491
558
|
end
|
492
559
|
expression.merge(as_hash(token.data))
|
493
560
|
end
|
561
|
+
|
562
|
+
def post_process(parser:, expression:)
|
563
|
+
token = parser.consume TokenType::SUB_Q_GROUP
|
564
|
+
new_expression = expression.merge!(token.parse(parser, expression: expression))
|
565
|
+
[token, new_expression]
|
566
|
+
end
|
567
|
+
|
568
|
+
def supports_post_processing?
|
569
|
+
true
|
570
|
+
end
|
494
571
|
end
|
495
572
|
|
496
573
|
class SubQueryType < TokenType
|
@@ -519,8 +596,19 @@ module DDQL
|
|
519
596
|
def as_hash(_data)
|
520
597
|
Hash.new
|
521
598
|
end
|
599
|
+
|
600
|
+
def post_process(parser:, expression:)
|
601
|
+
token = parser.consume TokenType::RBRACE
|
602
|
+
[token, expression]
|
603
|
+
end
|
604
|
+
|
605
|
+
def supports_post_processing?
|
606
|
+
true
|
607
|
+
end
|
522
608
|
end
|
523
609
|
|
610
|
+
NESTED_OPENER = NestedQuery.new
|
611
|
+
NESTED_CLOSER = new(name: :nested_closer, pattern: Regexp.compile(Regexp.escape TokenType::NESTED_CLOSE_PATTERN))
|
524
612
|
LPAREN = Group.new
|
525
613
|
RPAREN = new(name: :rparen, pattern: /(?=[^%])\)/)
|
526
614
|
LBRACE = SubQuery.new #new(name: :lbrace, pattern: /\{/)
|
@@ -539,7 +627,7 @@ module DDQL
|
|
539
627
|
PREFIXOPERATOR = PrefixOperator.new
|
540
628
|
INFIXOPERATOR = InfixOperator.new
|
541
629
|
POSTFIXOPERATOR = PostfixOperator.new
|
542
|
-
|
630
|
+
SUB_Q_ALIAS = SubQueryAlias.new
|
543
631
|
SUB_Q_EXPR = SubQueryExpression.new
|
544
632
|
SUB_Q_FIELDS = SubQueryFields.new
|
545
633
|
SUB_Q_GROUP = SubQueryGrouping.new
|
@@ -547,15 +635,20 @@ module DDQL
|
|
547
635
|
WHITESPACE = new(name: :whitespace, pattern: /[\s]/).skipping!
|
548
636
|
|
549
637
|
ALL = [
|
638
|
+
NESTED_OPENER,
|
639
|
+
NESTED_CLOSER,
|
550
640
|
LPAREN,
|
551
641
|
RPAREN,
|
552
642
|
# LCAPTURE,
|
553
643
|
# RCAPTURE,
|
644
|
+
# NESTED_OPENER,
|
645
|
+
# NESTED_CLOSER,
|
554
646
|
LBRACE,
|
555
647
|
RBRACE,
|
556
648
|
SUB_Q_EXPR,
|
557
649
|
SUB_Q_FIELDS,
|
558
650
|
SUB_Q_TYPE,
|
651
|
+
SUB_Q_ALIAS,
|
559
652
|
SUB_Q_GROUP,
|
560
653
|
CURRENCY_LITERAL,
|
561
654
|
SCI_NUM_LITERAL,
|
@@ -568,7 +661,6 @@ module DDQL
|
|
568
661
|
PREFIXOPERATOR,
|
569
662
|
INFIXOPERATOR,
|
570
663
|
POSTFIXOPERATOR,
|
571
|
-
# QUERY, # TODO: do we need this?
|
572
664
|
WHITESPACE,
|
573
665
|
]
|
574
666
|
end
|