ddql 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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