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.
@@ -1,7 +1,7 @@
1
1
  module DDQL
2
2
  class ListOperator < Operator
3
- def initialize(symbol, name, type, ordinal)
4
- super(symbol, name, type, 4, false, :boolean, ordinal)
3
+ def initialize(symbol, name, type)
4
+ super(symbol, name, type, 4, false, :boolean)
5
5
  end
6
6
  end
7
7
  end
@@ -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, token, expression: nil)
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
- op_expression = token.as_hash
14
-
13
+
14
+ foreign_value_factor = expression[:factor]
15
+ foreign_key_factor = (next_expression.delete(:left) || next_expression)[:factor]
15
16
  {
16
- left: expression,
17
- op: op_expression[:op],
18
- right: next_expression.delete(:left) || next_expression,
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, :ordinal
3
+ attr_reader :associativity, :symbol, :name, :type, :precedence, :return_type
4
4
 
5
- def initialize(symbol, name, type, precedence, right, return_type, ordinal=0)
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
- @ordinal = ordinal
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 -= 1 if right?
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
@@ -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("==", "Double Equals", :infix, 3, false, :boolean, 0).register(cache)
42
- Operator.new("=", "Single Equals", :infix, 3, false, :boolean, 0).register(cache)
43
- Operator.new("!=", "Not Equals", :infix, 3, false, :boolean, 1).register(cache)
44
- Operator.new("<=", "Less Than or Equals", :infix, 4, false, :boolean, 4).register(cache)
45
- Operator.new("<", "Less Than", :infix, 4, false, :boolean, 2).register(cache)
46
- Operator.new(">=", "Greater Than or Equals", :infix, 4, false, :boolean, 5).register(cache)
47
- Operator.new(">", "Greater Than", :infix, 4, false, :boolean, 3).register(cache)
48
- # Operator.new("GROUP BY", "Group By", :infix, 7, false, :double).register(cache)
49
- Operator.new("CTN", "Contains", :infix, 4, false, :boolean, 6).register(cache)
50
- Operator.new("STW", "Starts With", :infix, 4, false, :boolean, 7).register(cache)
51
- Operator.new("IN", "In Any", :infix, 4, false, :boolean, 8).register(cache)
52
- Operator.new("LCTN", "Contains", :infix, 4, false, :boolean, 6).register(cache)
53
- Operator.new("ON", "On Date", :infix, 4, false, :boolean, 0).register(cache)
54
- Operator.new("EPST", "On Or After Date", :infix, 4, false, :boolean, 1).register(cache)
55
- Operator.new("PST", "After Date", :infix, 4, false, :boolean, 1).register(cache)
56
- Operator.new("EPRE", "On Or Before Date", :infix, 4, false, :boolean, 1).register(cache)
57
- Operator.new("PRE", "Before Date", :infix, 4, false, :boolean, 0).register(cache)
58
- Operator.new("OR", "Or", :infix, 1, false, :boolean).register(cache)
59
- Operator.new("AND", "And", :infix, 2, false, :boolean).register(cache)
60
- Operator.new("IS NULL", "Is Null", :postfix, 9, false, :boolean, 0).register(cache)
61
- Operator.new("IS NOT NULL", "Is Not Null", :postfix, 9, false, :boolean, 1).register(cache)
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", 0).register(cache)
82
- InfixStringMapOperator.new("ALL_MAP", "Has All", 1).register(cache)
83
- InfixStringMapOperator.new("NONE_MAP", "Has None", 2).register(cache)
84
- InfixFloatMapOperator.new("ANY_GREATER_THAN_FLOAT_MAP", "Has Any Greater Than", 1, :any, :gt).register(cache)
85
- InfixFloatMapOperator.new("ALL_GREATER_THAN_FLOAT_MAP", "Has All Greater Than", 2, :all, :gt).register(cache)
86
- InfixFloatMapOperator.new("NONE_GREATER_THAN_FLOAT_MAP", "Has None Greater Than", 3, :none, :gt).register(cache)
87
- InfixFloatMapOperator.new("ANY_GREATER_THAN_OR_EQUAL_FLOAT_MAP", "Has Any Greater Than Or Equal To", 4, :any, :ge).register(cache)
88
- InfixFloatMapOperator.new("ALL_GREATER_THAN_OR_EQUAL_FLOAT_MAP", "Has All Greater Than Or Equal To", 5, :all, :ge).register(cache)
89
- InfixFloatMapOperator.new("NONE_GREATER_THAN_OR_EQUAL_FLOAT_MAP", "Has None Greater Than Or Equal To", 6, :none, :ge).register(cache)
90
- InfixFloatMapOperator.new("ANY_LESS_THAN_FLOAT_MAP", "Has Any Less Than", 7, :any, :lt).register(cache)
91
- InfixFloatMapOperator.new("ALL_LESS_THAN_FLOAT_MAP", "Has All Less Than", 8, :all, :lt).register(cache)
92
- InfixFloatMapOperator.new("NONE_LESS_THAN_FLOAT_MAP", "Has None Less Than", 9, :none, :lt).register(cache)
93
- InfixFloatMapOperator.new("ANY_LESS_THAN_OR_EQUAL_FLOAT_MAP", "Has Any Less Than Or Equal To", 10, :any, :le).register(cache)
94
- InfixFloatMapOperator.new("ALL_LESS_THAN_OR_EQUAL_FLOAT_MAP", "Has All Less Than Or Equal To", 11, :all, :le).register(cache)
95
- InfixFloatMapOperator.new("NONE_LESS_THAN_OR_EQUAL_FLOAT_MAP", "Has None Less Than Or Equal To", 12, :none, :le).register(cache)
96
- InfixFloatMapOperator.new("ANY_EQUAL_FLOAT_MAP", "Has Any Equal To", 13, :any, :eq).register(cache)
97
- InfixFloatMapOperator.new("ALL_EQUAL_FLOAT_MAP", "Has All Equal To", 14, :all, :eq).register(cache)
98
- InfixFloatMapOperator.new("NONE_EQUAL_FLOAT_MAP", "Has None Equal To", 15, :none, :eq).register(cache)
99
- ListOperator.new("ANY", "Has Any", :infix, 0).register(cache)
100
- ListOperator.new("ALL", "Has All", :infix, 1).register(cache)
101
- ListOperator.new("NONE", "Has None", :infix, 2).register(cache)
102
- ListOperator.new("EMPTY", "Is Empty", :postfix, 3).register(cache)
103
- PostfixNullTypeOperator.new("NO_INFORMATION", :NoInformation, 99).register(cache)
104
- PostfixNullTypeOperator.new("NOT_APPLICABLE", :NotApplicable, 101).register(cache)
105
- PostfixNullTypeOperator.new("NOT_COLLECTED", :NotCollected, 103).register(cache)
106
- PostfixNullTypeOperator.new("NOT_DISCLOSED", :NotDisclosed, 100).register(cache)
107
- PostfixNullTypeOperator.new("NOT_MEANINGFUL", :NotMeaningful, 102).register(cache)
108
- Operator.new("NOT", "Not", :prefix, 9, false, :boolean).register(cache)
109
- Operator.new("YES", "Is True", :postfix, 9, false, :boolean, 0).register(cache)
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
- def initialize(expression)
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, ordinal)
6
- super("IS #{symbol}", "Is #{symbol}", :postfix, 9, false, :boolean, ordinal)
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
@@ -1,6 +1,10 @@
1
1
  module DDQL
2
2
  module StringRefinements
3
3
  refine String do
4
+ def blank?
5
+ empty? || strip.empty?
6
+ end
7
+
4
8
  def squish
5
9
  self.dup.squish!
6
10
  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
@@ -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
- # case name
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+)(?>\.?)(?>\d*)|(?>\d*)(?>\.?)(?>\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][+-]?[^0]\d+)'/)
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, token, expression: nil)
239
+ def parse(parser, _token, expression: nil)
233
240
  new_expression = parser.parse
234
- parser.consume TokenType::RPAREN #if parser.peek&.data == ')'
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.prefix?
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, token, expression: nil)
447
+ def parse(parser, _token, expression: nil)
418
448
  new_expression = parser.parse
419
- if parser.peek&.type == TokenType::SUB_Q_GROUP
420
- token = parser.consume TokenType::SUB_Q_GROUP
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(token.data.strip).merge parser.parse
519
+ as_hash(data).merge parser.parse
453
520
  else
454
- expression.merge(as_hash(token.data.strip))
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
- # QUERY = Query.new
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