ddql 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82f78b8b46dd03069598a3945141114c781eef9da44a0f409d8a8ab3c6bae4b0
4
- data.tar.gz: ec23e584baad9e0359c9471cf3002371863f888cf5cf718ef7cdeb6eb8291686
3
+ metadata.gz: '038be470281a3216e90884f9b14e2f30c087494906a79089d7dff5ea32bdd29a'
4
+ data.tar.gz: 812b0f99b1b1f2c84bddf6ebbe0abfeecdbdbfc47d036c3d65d5a0234b961f8d
5
5
  SHA512:
6
- metadata.gz: 14f90543034d8fdd8813aad9db740efb95f11b9feb9ba17b0f23fd64b603cc7b804cde2fee516c95d11d593b0d57f219edee1d736246d5670e41afbd15c5ae1d
7
- data.tar.gz: 62e76c1c990a1168a4510b56636a6ca9105d1720794e4535a07bd53c5742cd5f5710fcd21ace66ff3e39623d800a07bcf8ee6ac71a87a38650ecf9ce4ac57441
6
+ metadata.gz: c313fd79cb88f08c98af30190e24d52a66e05365c02e29a6c1900d77bef056443f3db18bf3e204b81bdcaa0cefb4e00b778cad159c53c3cee6b9d4ddc775d76b
7
+ data.tar.gz: ced003fd8bcd3fa2b7a9e828fa76d01e101109b9eca210778b9283e226004cc4803a17f15b763ab8659a3d07506add93d28be0078fd11c15e5c04f290dc10345
data/.gitignore CHANGED
@@ -9,3 +9,5 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+ vendor
13
+ spec/examples.txt
@@ -2,6 +2,8 @@
2
2
  sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
+ env:
6
+ - JRUBY_OPTS="-J-Xss8m"
5
7
  rvm:
6
8
  - 2.5
7
9
  - 2.6
data/Gemfile CHANGED
@@ -2,3 +2,17 @@ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in ddql.gemspec
4
4
  gemspec
5
+
6
+ group :development do
7
+ platform :mri do
8
+ gem 'pry-byebug', '= 3.8.0'
9
+ # gem 'pry', '~> 0.11'
10
+ end
11
+
12
+ platform :jruby do
13
+ gem 'pry-debugger-jruby', '~> 1.2'
14
+ # gem 'pry', '0.11.3-java'
15
+ end
16
+
17
+ gem 'pry', '~> 0.10'
18
+ end
@@ -1,45 +1,57 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ddql (0.1.0)
5
- parslet (~> 2.0)
4
+ ddql (1.0.0)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
9
8
  specs:
10
9
  byebug (11.1.3)
11
10
  coderay (1.1.3)
12
- diff-lcs (1.3)
13
- method_source (1.0.0)
14
- parslet (2.0.0)
15
- pry (0.13.1)
16
- coderay (~> 1.1)
17
- method_source (~> 1.0)
18
- pry-byebug (3.9.0)
11
+ diff-lcs (1.4.4)
12
+ ffi (1.13.1-java)
13
+ method_source (0.9.2)
14
+ pry (0.11.3)
15
+ coderay (~> 1.1.0)
16
+ method_source (~> 0.9.0)
17
+ pry (0.11.3-java)
18
+ coderay (~> 1.1.0)
19
+ method_source (~> 0.9.0)
20
+ spoon (~> 0.0)
21
+ pry-byebug (3.8.0)
19
22
  byebug (~> 11.0)
20
- pry (~> 0.13.0)
23
+ pry (~> 0.10)
24
+ pry-debugger-jruby (1.2.1-java)
25
+ pry (>= 0.10, < 0.12)
26
+ ruby-debug-base (~> 0.10.4)
21
27
  rake (13.0.1)
22
- rspec (3.8.0)
23
- rspec-core (~> 3.8.0)
24
- rspec-expectations (~> 3.8.0)
25
- rspec-mocks (~> 3.8.0)
26
- rspec-core (3.8.0)
27
- rspec-support (~> 3.8.0)
28
- rspec-expectations (3.8.3)
28
+ rspec (3.9.0)
29
+ rspec-core (~> 3.9.0)
30
+ rspec-expectations (~> 3.9.0)
31
+ rspec-mocks (~> 3.9.0)
32
+ rspec-core (3.9.2)
33
+ rspec-support (~> 3.9.3)
34
+ rspec-expectations (3.9.2)
29
35
  diff-lcs (>= 1.2.0, < 2.0)
30
- rspec-support (~> 3.8.0)
31
- rspec-mocks (3.8.0)
36
+ rspec-support (~> 3.9.0)
37
+ rspec-mocks (3.9.1)
32
38
  diff-lcs (>= 1.2.0, < 2.0)
33
- rspec-support (~> 3.8.0)
34
- rspec-support (3.8.0)
39
+ rspec-support (~> 3.9.0)
40
+ rspec-support (3.9.3)
41
+ ruby-debug-base (0.10.6-java)
42
+ spoon (0.0.6)
43
+ ffi
35
44
 
36
45
  PLATFORMS
46
+ java
37
47
  ruby
38
48
 
39
49
  DEPENDENCIES
40
50
  bundler (~> 2.0)
41
51
  ddql!
42
- pry-byebug (~> 3.9)
52
+ pry (~> 0.10)
53
+ pry-byebug (= 3.8.0)
54
+ pry-debugger-jruby (~> 1.2)
43
55
  rake (~> 13.0)
44
56
  rspec (~> 3.0)
45
57
 
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # DDQL
2
2
 
3
+ [![Build Status](https://travis-ci.com/iss-lab/DDQL.svg?branch=master)](https://travis-ci.com/iss-lab/DDQL)
4
+
3
5
  A (Ruby) DataDesk query language parser written in Parslet.
4
6
 
5
7
  ## Installation
@@ -7,10 +7,15 @@ require 'ddql'
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
9
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- require 'pry'
10
+
11
11
  PARSER = DDQL::Parser.new
12
- puts 'Create a new instance of the parser or use the PARSER constant'
13
- Pry.start
14
12
 
15
- # require "irb"
16
- # IRB.start(__FILE__)
13
+ begin
14
+ require 'pry'
15
+ puts 'Create a new instance of the parser or use the PARSER constant'
16
+ Pry.start
17
+ rescue
18
+ require "irb"
19
+ puts 'Create a new instance of the parser or use the PARSER constant'
20
+ IRB.start(__FILE__)
21
+ end
@@ -27,10 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
29
 
30
- spec.add_dependency "parslet", "~> 2.0"
31
-
32
30
  spec.add_development_dependency "bundler", "~> 2.0"
33
31
  spec.add_development_dependency "rake", "~> 13.0"
34
32
  spec.add_development_dependency "rspec", "~> 3.0"
35
- spec.add_development_dependency "pry-byebug", "~> 3.9"
36
33
  end
@@ -1,6 +1,13 @@
1
- require 'parslet'
2
- require 'ddql/parser'
3
1
  require 'ddql/version'
2
+ require_relative 'ddql/linked_list'
3
+ require_relative 'ddql/query_expression_error'
4
+ require_relative 'ddql/lexer'
5
+ require_relative 'ddql/operators'
6
+ require_relative 'ddql/parser'
7
+ require_relative 'ddql/string_refinements'
8
+ require_relative 'ddql/token_type'
9
+ require_relative 'ddql/token'
10
+
4
11
 
5
12
  module DDQL
6
13
  class ParseError < StandardError; end
@@ -0,0 +1,79 @@
1
+ module DDQL
2
+ # +AggOperators+ return a Hash structure describing the type of aggregation, an optional
3
+ # expression (filter) to apply, an optional factor (field) against which the aggregation
4
+ # is applied, and the entity type for which the aggregation is applicable.
5
+ #
6
+ # ==== Examples
7
+ # +SUM {type: IssuerPerson, fields: [SomeFactor], expression: [OtherFactor] == 'Value'}+
8
+ #
9
+ # {
10
+ # agg: {op_sum: 'SUM'},
11
+ # sub_query_expression: "[OtherFactor] == 'Value'",
12
+ # sub_query_fields: 'SomeFactor',
13
+ # sub_query_type: 'IssuerPerson',
14
+ # }
15
+ #
16
+ # +CNT{type:Issuer, expression: [SomeFactor]} / CNT{type:Issuer, expression: [OtherFactor]}+
17
+ #
18
+ # {
19
+ # left: {
20
+ # agg: {op_cnt: 'CNT'},
21
+ # sub_query_fields: 'SomeFactor',
22
+ # sub_query_type: 'Issuer',
23
+ # },
24
+ # op: {op_divide: '/'},
25
+ # right: {
26
+ # agg: {op_cnt: 'CNT'},
27
+ # sub_query_fields: 'OtherFactor',
28
+ # sub_query_type: 'Issuer',
29
+ # },
30
+ # }
31
+ #
32
+ # +MAX{type:Case, expression: [SomeFactor]} > 5+
33
+ #
34
+ # {
35
+ # left: {
36
+ # agg: {op_max: 'MAX'},
37
+ # sub_query_fields: 'SomeFactor',
38
+ # sub_query_type: 'Case',
39
+ # },
40
+ # op: {op_gt: '>'},
41
+ # right: {int: 5},
42
+ # }
43
+ class AggOperator < Operator
44
+ def initialize(symbol, name, return_type:, type: :prefix, precedence: 8, right: false)
45
+ super(symbol, name, type, precedence, right, return_type)
46
+ end
47
+
48
+ def parse(parser, token, expression: nil)
49
+ new_expression = parser.parse(precedence: precedence)
50
+ if expression
51
+ expression = expression.merge(new_expression)
52
+ else
53
+ expression = new_expression
54
+ end
55
+ expression.merge!(agg: {:"op_#{symbol.downcase}" => symbol})
56
+
57
+ if expression.key?(:op) && expression.key?(:right)
58
+ if expression[:left]&.empty?
59
+ expression.delete :left
60
+ end
61
+ op = expression.delete :op
62
+ right = expression.delete :right
63
+ {
64
+ left: expression,
65
+ op: op,
66
+ right: right,
67
+ }
68
+ elsif expression.key?(:yes_no_op)
69
+ yes_no = expression.delete :yes_no_op
70
+ {
71
+ left: expression,
72
+ yes_no_op: yes_no,
73
+ }
74
+ else
75
+ expression
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,26 @@
1
+ module DDQL
2
+ class CoalesceOperator < AggOperator
3
+ def initialize
4
+ super("COALESCE", "Coalesce", return_type: :match)
5
+ end
6
+
7
+ def parse(parser, token, expression: nil)
8
+ new_expression = parser.parse(precedence: precedence)
9
+ if expression
10
+ expression = expression.merge(new_expression)
11
+ else
12
+ expression = new_expression
13
+ end
14
+
15
+ left_factor, right_factor = expression[:string].split('|', 2)
16
+ {
17
+ op_coalesce: {
18
+ factors: {
19
+ left_factor: {factor: left_factor},
20
+ right_factor: {factor: right_factor},
21
+ },
22
+ }
23
+ }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module DDQL
2
+ class InfixFloatMapOperator < Operator
3
+ attr_reader :comparison, :op_type, :op_symbol
4
+
5
+ def initialize(symbol, name, ordinal, op_type, comparison)
6
+ super(symbol, name, :infix, 4, false, :boolean, ordinal)
7
+ @op_type = op_type
8
+ @comparison = comparison
9
+ @op_symbol = :"op_float_map_#{op_type}_#{comparison}"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module DDQL
2
+ class InfixStringMapOperator < Operator
3
+ def initialize(symbol, name, ordinal)
4
+ super(symbol, name, :infix, 4, false, :boolean, ordinal)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'linked_list'
2
+
3
+ module DDQL
4
+ class Lexer
5
+ def self.lex(expression, pattern: TokenType.all_types_pattern, available_types: TokenType::ALL)
6
+ tokens = LinkedList.new.doubly_linked!
7
+ md = pattern.match expression
8
+ while md
9
+ token_type = available_types.detect { |tt| tt.match?(match_data: md) }
10
+ if token_type
11
+ tokens << Token.new(
12
+ data: token_type.interpreted_data_from(match_data: md),
13
+ location: expression.length - md.string.length,
14
+ type: token_type,
15
+ )
16
+ end
17
+ md = pattern.match md.post_match
18
+ end
19
+ tokens
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,171 @@
1
+ module DDQL
2
+ class LinkedList
3
+ include Enumerable
4
+
5
+ class Node
6
+ attr_accessor :next, :previous
7
+ attr_reader :value
8
+
9
+ def initialize(value)
10
+ @next = nil
11
+ @previous = nil
12
+ @value = value
13
+ end
14
+
15
+ def to_s
16
+ "Node[#{@value}]"
17
+ end
18
+ end
19
+
20
+ attr_reader :head, :size
21
+
22
+ def initialize(head=nil)
23
+ @doubly_linked = false
24
+ @head = head
25
+ @size = 0
26
+ end
27
+
28
+ def append(value)
29
+ if @head
30
+ tail = find_tail
31
+ tail.next = Node.new(value)
32
+ tail.next.previous = tail if @doubly_linked
33
+ else
34
+ @head = Node.new(value)
35
+ end
36
+ @size += 1
37
+ end
38
+ alias :<< :append
39
+
40
+ def append_after(target, value)
41
+ node = find(target)
42
+ return unless node
43
+ old_next = node.next
44
+ node.next = Node.new(value)
45
+ node.next.next = old_next
46
+ end
47
+
48
+ def doubly_linked!
49
+ current = nil
50
+ each_node do |node|
51
+ node.previous = current
52
+ current = node
53
+ end
54
+ @doubly_linked = true
55
+ self
56
+ end
57
+
58
+ def doubly_linked?
59
+ @doubly_linked
60
+ end
61
+
62
+ def delete(value)
63
+ if value.is_a?(Node) && @head == value
64
+ @head = @head.next
65
+ value.next = value.previous = nil
66
+ elsif @head.value == value
67
+ node = @head.next
68
+ @head.next = @head.previous = nil
69
+ value = @head
70
+ @head = node
71
+ else
72
+ node = find_before(value)
73
+ node.next = node.next.next if node && node.next
74
+ node.next = node.previous = nil if node
75
+ end
76
+ @size -= 1
77
+ value
78
+ end
79
+
80
+ def each
81
+ return to_enum unless block_given?
82
+ node = @head
83
+ while node
84
+ yield node.value
85
+ node = node.next
86
+ end
87
+ end
88
+
89
+ def find(value)
90
+ node = @head
91
+ is_node = value.is_a?(Node)
92
+ return false if !node.next
93
+ return node if (is_node && node == value) || node.value == value
94
+ while (node = node.next)
95
+ return node if (is_node && node == value) || node.value == value
96
+ end
97
+ end
98
+
99
+ def find_before(value)
100
+ node = @head
101
+ return false if !node.next
102
+
103
+ is_node = value.is_a?(Node)
104
+ if (is_node && node.next == value) || node.next.value == value
105
+ return node
106
+ end
107
+
108
+ while (node = node.next)
109
+ if (is_node && node.next == value) || node.next.value == value
110
+ return node
111
+ end
112
+ end
113
+ end
114
+
115
+ def find_tail
116
+ node = @head
117
+ return node if !node.next
118
+ return node if !node.next while (node = node.next)
119
+ end
120
+ alias :tail :find_tail
121
+
122
+ def peek
123
+ return nil unless @head
124
+ @head.value
125
+ end
126
+
127
+ def poll
128
+ return nil unless @head
129
+ previous_head = @head
130
+ @head = previous_head.next
131
+ @size -= 1
132
+ @head.previous = nil if @head
133
+ previous_head.next = nil
134
+ previous_head.value
135
+ end
136
+
137
+ def print(stream=STDOUT)
138
+ node = @head
139
+ prefix = ''
140
+ postfix = ''
141
+ stream.print "(size: #{size}) "
142
+ while node
143
+ postfix = "\n⨂" unless node.next
144
+ stream.print prefix
145
+ stream.print node
146
+ stream.print postfix
147
+ stream.print "\n"
148
+ node = node.next
149
+ prefix = ' ⤿ ' if node
150
+ end
151
+ end
152
+
153
+ def singly_linked!
154
+ each_node do |node|
155
+ node.previous = nil
156
+ end
157
+ @doubly_linked = false
158
+ self
159
+ end
160
+
161
+ private
162
+ def each_node
163
+ raise 'requires a block' unless block_given?
164
+ node = @head
165
+ while node
166
+ yield node
167
+ node = node.next
168
+ end
169
+ end
170
+ end
171
+ end