rgviz 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,403 @@
1
+ module Rgviz
2
+ class Parser < Lexer
3
+ def initialize(string)
4
+ super
5
+ @query = Query.new
6
+ next_token
7
+ end
8
+
9
+ def parse
10
+ parse_select
11
+ parse_where
12
+ parse_group_by
13
+ parse_pivot
14
+ parse_order_by
15
+ parse_limit
16
+ parse_offset
17
+ parse_label
18
+ parse_format
19
+ parse_options
20
+
21
+ check Token::EOF
22
+ @query
23
+ end
24
+
25
+ def parse_select
26
+ return if not token_is! Token::Select
27
+
28
+ @query.select = Select.new
29
+
30
+ return if token_is! Token::STAR
31
+
32
+ parse_columns @query.select.columns
33
+
34
+ raise ParseException.new "Expecting select columns" if @query.select.columns.empty?
35
+ end
36
+
37
+ def parse_where
38
+ return if not token_is! Token::Where
39
+
40
+ @query.where = Where.new parse_expression
41
+ end
42
+
43
+ def parse_group_by
44
+ return if not token_is! Token::Group
45
+ check! Token::By
46
+
47
+ @query.group_by = GroupBy.new
48
+ @query.group_by.columns = parse_columns
49
+
50
+ raise ParseException.new "Expecting group by columns" if @query.group_by.columns.empty?
51
+ end
52
+
53
+ def parse_pivot
54
+ return if not token_is! Token::Pivot
55
+
56
+ @query.pivot = Pivot.new
57
+ @query.pivot.columns = parse_columns
58
+
59
+ raise ParseException.new "Expecting pivot columns" if @query.pivot.columns.empty?
60
+ end
61
+
62
+ def parse_order_by
63
+ return if not token_is! Token::Order
64
+ check! Token::By
65
+
66
+ @query.order_by = OrderBy.new
67
+ @query.order_by.sorts = parse_sorts
68
+
69
+ raise ParseException.new "Expecting order by columns" if @query.order_by.sorts.empty?
70
+ end
71
+
72
+ def parse_limit
73
+ return if not token_is! Token::Limit
74
+
75
+ check Token::INTEGER
76
+
77
+ @query.limit = @token.number
78
+ next_token
79
+ end
80
+
81
+ def parse_offset
82
+ return if not token_is! Token::Offset
83
+
84
+ check Token::INTEGER
85
+
86
+ @query.offset = @token.number
87
+ next_token
88
+ end
89
+
90
+ def parse_label
91
+ return if not token_is! Token::Label
92
+
93
+ @query.labels = []
94
+
95
+ column = parse_column
96
+ raise ParseException.new "Expecting label" unless column
97
+
98
+ check Token::STRING
99
+ @query.labels << Label.new(column, @token.string)
100
+ next_token
101
+
102
+ while token_is! Token::COMMA
103
+ column = parse_column
104
+ break unless column
105
+
106
+ check Token::STRING
107
+ @query.labels << Label.new(column, @token.string)
108
+ next_token
109
+ end
110
+
111
+ raise ParseException.new "Expecting label" if @query.labels.empty?
112
+ end
113
+
114
+ def parse_format
115
+ return if not token_is! Token::Format
116
+
117
+ @query.formats = []
118
+
119
+ column = parse_column
120
+ raise ParseException.new "Expecting format" unless column
121
+
122
+ check Token::STRING
123
+ @query.formats << Format.new(column, @token.string)
124
+ next_token
125
+
126
+ while token_is! Token::COMMA
127
+ column = parse_column
128
+ break unless column
129
+
130
+ check Token::STRING
131
+ @query.formats << Format.new(column, @token.string)
132
+ next_token
133
+ end
134
+
135
+ raise ParseException.new "Expecting format" if @query.formats.empty?
136
+ end
137
+
138
+ def parse_options
139
+ return if not token_is! Token::Options
140
+
141
+ @query.options = []
142
+
143
+ while @token.value == Token::NoValues || @token.value == Token::NoFormat
144
+ @query.options << Option.new(@token.value)
145
+ next_token
146
+ end
147
+
148
+ raise ParseException.new "Expecting option" if @query.options.empty?
149
+ raise ParseException.new "Unknown option" if @token.value != Token::EOF
150
+ end
151
+
152
+ def parse_sorts
153
+ sorts = []
154
+
155
+ column = parse_column
156
+ return sorts unless column
157
+
158
+ order = parse_sort_order
159
+
160
+ sorts << Sort.new(column, order)
161
+ while token_is! Token::COMMA
162
+ column = parse_column
163
+ break unless column
164
+
165
+ order = parse_sort_order
166
+ sorts << Sort.new(column, order)
167
+ end
168
+ sorts
169
+ end
170
+
171
+ def parse_sort_order
172
+ if token_is! Token::Asc
173
+ Sort::Asc
174
+ elsif token_is! Token::Desc
175
+ Sort::Desc
176
+ else
177
+ Sort::Asc
178
+ end
179
+ end
180
+
181
+ def parse_expression
182
+ parse_or_expression
183
+ end
184
+
185
+ def parse_or_expression
186
+ left = parse_and_expression
187
+ if token_is! Token::Or
188
+ right = parse_and_expression
189
+ BinaryExpression.new left, BinaryExpression::Or, right
190
+ else
191
+ left
192
+ end
193
+ end
194
+
195
+ def parse_and_expression
196
+ left = parse_not_expression
197
+ if token_is! Token::And
198
+ right = parse_not_expression
199
+ BinaryExpression.new left, BinaryExpression::And, right
200
+ else
201
+ left
202
+ end
203
+ end
204
+
205
+ def parse_not_expression
206
+ if token_is! Token::Not
207
+ operand = parse_primary_expression
208
+ UnaryExpression.new UnaryExpression::Not, operand
209
+ else
210
+ parse_primary_expression
211
+ end
212
+ end
213
+
214
+ def parse_primary_expression
215
+ case @token.value
216
+ when Token::LPAREN
217
+ next_token
218
+ exp = parse_expression
219
+ check! Token::RPAREN
220
+ return exp
221
+ else
222
+ left = parse_column
223
+ raise ParseException.new "Expecting left exp" unless left
224
+
225
+ case @token.value
226
+ when Token::Is
227
+ next_token
228
+ if token_is! Token::Not
229
+ check! Token::Null
230
+ return UnaryExpression.new UnaryExpression::IsNotNull, left
231
+ elsif token_is! Token::Null
232
+ return UnaryExpression.new UnaryExpression::IsNull, left
233
+ end
234
+ when Token::EQ, Token::NEQ, Token::LT, Token::LTE, Token::GT, Token::GTE,
235
+ Token::Contains, Token::Matches, Token::Like
236
+ operator = @token.value
237
+ next_token
238
+ right = parse_column
239
+ raise ParseException.new "Expecting right exp" unless right
240
+ return BinaryExpression.new left, operator, right
241
+ when Token::Starts, Token::Ends
242
+ operator = "#{@token.value}_with".to_sym
243
+ next_token
244
+ check! Token::With
245
+ right = parse_column
246
+ raise ParseException.new "Expecting right exp" unless right
247
+ return BinaryExpression.new left, operator, right
248
+ else
249
+ raise ParseException.new "Expecting comparison"
250
+ end
251
+ end
252
+ end
253
+
254
+ def parse_columns(columns = [])
255
+ column = parse_column
256
+ return columns unless column
257
+
258
+ columns << column
259
+ while token_is! Token::COMMA
260
+ column = parse_column
261
+ break unless column
262
+ columns << column
263
+ end
264
+ columns
265
+ end
266
+
267
+ def parse_column
268
+ parse_arithmetic_expression
269
+ end
270
+
271
+ def parse_arithmetic_expression
272
+ parse_summation_or_substraction
273
+ end
274
+
275
+ def parse_summation_or_substraction
276
+ column1 = parse_multiplication_or_divition
277
+ case @token.value
278
+ when Token::PLUS
279
+ next_token
280
+ column2 = parse_multiplication_or_divition
281
+ ScalarFunctionColumn.new ScalarFunctionColumn::Sum, column1, column2
282
+ when Token::MINUS
283
+ next_token
284
+ column2 = parse_multiplication_or_divition
285
+ ScalarFunctionColumn.new ScalarFunctionColumn::Difference, column1, column2
286
+ else
287
+ column1
288
+ end
289
+ end
290
+
291
+ def parse_multiplication_or_divition
292
+ column1 = parse_atomic_column
293
+ case @token.value
294
+ when Token::STAR
295
+ next_token
296
+ column2 = parse_atomic_column
297
+ ScalarFunctionColumn.new ScalarFunctionColumn::Product, column1, column2
298
+ when Token::SLASH
299
+ next_token
300
+ column2 = parse_atomic_column
301
+ ScalarFunctionColumn.new ScalarFunctionColumn::Quotient, column1, column2
302
+ else
303
+ column1
304
+ end
305
+ end
306
+
307
+ def parse_atomic_column
308
+ case @token.value
309
+ when Token::ID
310
+ return value_column(IdColumn.new @token.string)
311
+ when Token::Contains, Token::Starts, Token::Ends, Token::With,
312
+ Token::Matches, Token::Like, Token::NoValues, Token::NoFormat,
313
+ Token::Is, Token::Null
314
+ return value_column(IdColumn.new @token.value.to_s)
315
+ when Token::PLUS
316
+ next_token
317
+ check Token::INTEGER, Token::DECIMAL
318
+ return value_column(NumberColumn.new @token.number)
319
+ when Token::MINUS
320
+ next_token
321
+ check Token::INTEGER, Token::DECIMAL
322
+ return value_column(NumberColumn.new -@token.number)
323
+ when Token::INTEGER, Token::DECIMAL
324
+ return value_column(NumberColumn.new @token.number)
325
+ when Token::LPAREN
326
+ next_token
327
+ column = parse_column
328
+ check! Token::RPAREN
329
+ return column
330
+ when Token::STRING
331
+ return value_column(StringColumn.new @token.string)
332
+ when Token::False
333
+ return value_column(BooleanColumn.new false)
334
+ when Token::True
335
+ return value_column(BooleanColumn.new true)
336
+ when Token::Date
337
+ next_token
338
+ check Token::STRING
339
+ return value_column(DateColumn.new Date.parse(@token.string))
340
+ when Token::DateTime
341
+ next_token
342
+ check Token::STRING
343
+ return value_column(DateTimeColumn.new Time.parse(@token.string))
344
+ when Token::TimeOfDay
345
+ next_token
346
+ check Token::STRING
347
+ return value_column(TimeOfDayColumn.new Time.parse(@token.string))
348
+ when Token::Avg, Token::Count, Token::Min, Token::Max, Token::Sum
349
+ function = @token.value
350
+ next_token
351
+ if token_is! Token::LPAREN
352
+ column = parse_column
353
+ check! Token::RPAREN
354
+ return AggregateColumn.new function, column
355
+ else
356
+ return IdColumn.new function.to_s
357
+ end
358
+ when Token::Year, Token::Month, Token::Day,
359
+ Token::Hour, Token::Minute, Token::Second, Token::Millisecond,
360
+ Token::Now, Token::DateDiff, Token::Lower, Token::Upper,
361
+ Token::Quarter, Token::DayOfWeek, Token::ToDate
362
+ function = @token.value
363
+ next_token
364
+ if token_is! Token::LPAREN
365
+ columns = parse_columns
366
+ check! Token::RPAREN
367
+ return ScalarFunctionColumn.new function, *columns
368
+ else
369
+ return IdColumn.new function.to_s
370
+ end
371
+ end
372
+ nil
373
+ end
374
+
375
+ def value_column(col)
376
+ column = col
377
+ next_token
378
+ return column
379
+ end
380
+
381
+ def token_is?(token_value)
382
+ @token.value == token_value
383
+ end
384
+
385
+ def token_is!(token_value)
386
+ if @token.value == token_value
387
+ next_token
388
+ true
389
+ else
390
+ false
391
+ end
392
+ end
393
+
394
+ def check(*token_values)
395
+ raise ParseException.new "Expecting token #{token_values}" unless token_values.any?{|value| @token.value == value}
396
+ end
397
+
398
+ def check!(*token_values)
399
+ check *token_values
400
+ next_token
401
+ end
402
+ end
403
+ end
@@ -0,0 +1,83 @@
1
+ module Rgviz
2
+ class Token
3
+
4
+ And = :and
5
+ Asc = :asc
6
+ Avg = :avg
7
+ By = :by
8
+ Contains = :contains
9
+ Count = :count
10
+ Desc = :desc
11
+ Date = :date
12
+ DateDiff = :datediff
13
+ DateTime = :datetime
14
+ Day = :day
15
+ DayOfWeek = :dayofweek
16
+ Ends = :ends
17
+ False = :false
18
+ Format = :format
19
+ Group = :group
20
+ Hour = :hour
21
+ Is = :is
22
+ Label = :label
23
+ Like = :like
24
+ Limit = :limit
25
+ Lower = :lower
26
+ Matches = :matches
27
+ Millisecond = :millisecond
28
+ Min = :min
29
+ Minute = :minute
30
+ Max = :max
31
+ Month = :month
32
+ Not = :not
33
+ Now = :now
34
+ NoFormat = :no_format
35
+ NoValues = :no_values
36
+ Null = :null
37
+ Offset = :offset
38
+ Options = :options
39
+ Or = :or
40
+ Order = :order
41
+ Pivot = :pivot
42
+ Quarter = :quarter
43
+ Second = :second
44
+ Select = :select
45
+ Starts = :starts
46
+ Sum = :sum
47
+ TimeOfDay = :timeofday
48
+ Timestamp = :timestamp
49
+ ToDate = :todate
50
+ True = :true
51
+ Upper = :upper
52
+ Where = :where
53
+ With = :with
54
+ Year = :year
55
+
56
+ ID = :ID
57
+ INTEGER = :INTEGER
58
+ DECIMAL = :DECIMAL
59
+ STRING = :STRING
60
+
61
+ PLUS = :PLUS
62
+ MINUS = :MINUS
63
+ STAR = :STAR
64
+ SLASH = :SLASH
65
+
66
+ COMMA = :COMMA
67
+ LPAREN = :LPAREN
68
+ RPAREN = :RPAREN
69
+ EQ = :EQ
70
+ LT = :LT
71
+ LTE = :LTE
72
+ GT = :GT
73
+ GTE = :GTE
74
+ NEQ = :NEQ
75
+
76
+ EOF = :EOF
77
+
78
+ attr_accessor :start
79
+ attr_accessor :value
80
+ attr_accessor :string
81
+ attr_accessor :number
82
+ end
83
+ end
@@ -0,0 +1,18 @@
1
+ module Rgviz
2
+ class Visitor
3
+ ['Query', 'Select', 'GroupBy', 'Pivot', 'OrderBy',
4
+ 'Sort', 'Where', 'Label', 'Format', 'Option',
5
+ 'BinaryExpression', 'UnaryExpression',
6
+ 'IdColumn', 'NumberColumn', 'StringColumn',
7
+ 'BooleanColumn', 'DateColumn', 'DateTimeColumn',
8
+ 'TimeOfDayColumn', 'ScalarFunctionColumn', 'AggregateColumn'].each do |name|
9
+ define_method "visit#{name}" do |node|
10
+ true
11
+ end
12
+
13
+ define_method "endVisit#{name}" do |node|
14
+ true
15
+ end
16
+ end
17
+ end
18
+ end
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rgviz
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 13
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- version: "0.2"
8
+ - 3
9
+ version: "0.3"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ary Borenszweig
@@ -28,6 +28,11 @@ extra_rdoc_files: []
28
28
 
29
29
  files:
30
30
  - lib/rgviz.rb
31
+ - lib/rgviz/lexer.rb
32
+ - lib/rgviz/nodes.rb
33
+ - lib/rgviz/parser.rb
34
+ - lib/rgviz/token.rb
35
+ - lib/rgviz/visitor.rb
31
36
  has_rdoc: true
32
37
  homepage: http://code.google.com/p/rgviz
33
38
  licenses: []