rgviz 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rgviz/lexer.rb +605 -0
- data/lib/rgviz/nodes.rb +437 -0
- data/lib/rgviz/parser.rb +403 -0
- data/lib/rgviz/token.rb +83 -0
- data/lib/rgviz/visitor.rb +18 -0
- metadata +8 -3
data/lib/rgviz/parser.rb
ADDED
@@ -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
|
data/lib/rgviz/token.rb
ADDED
@@ -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:
|
4
|
+
hash: 13
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
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: []
|