rgviz 0.14 → 0.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,171 @@
1
+ require 'rgviz'
2
+
3
+ include Rgviz
4
+
5
+ describe Lexer do
6
+ def self.it_lexes_keyword(str, token_value)
7
+ it "lexes #{str}" do
8
+ lex = Lexer.new str
9
+ tok = lex.next_token
10
+ tok.value.should == token_value
11
+ end
12
+
13
+ it "lexes #{str} upcase" do
14
+ lex = Lexer.new str.upcase
15
+ tok = lex.next_token
16
+ tok.value.should == token_value
17
+ end
18
+ end
19
+
20
+ def self.it_lexes_id(str, id = str)
21
+ it "lexes identifier #{str}" do
22
+ lex = Lexer.new str
23
+ tok = lex.next_token
24
+ tok.value.should == Token::ID
25
+ tok.string.should == id
26
+ end
27
+ end
28
+
29
+ def self.it_lexes_string(str, id = str)
30
+ it "lexes string #{str}" do
31
+ lex = Lexer.new str
32
+ tok = lex.next_token
33
+ tok.value.should == Token::STRING
34
+ tok.string.should == id
35
+ end
36
+ end
37
+
38
+ def self.it_lexes_token(str, token_value)
39
+ it "lexes #{str}" do
40
+ lex = Lexer.new str
41
+ tok = lex.next_token
42
+ tok.value.should == token_value
43
+ end
44
+ end
45
+
46
+ def self.it_lexes_integer(str, number)
47
+ it "lexes #{str}" do
48
+ lex = Lexer.new str
49
+ tok = lex.next_token
50
+ tok.value.should == Token::INTEGER
51
+ tok.number.should == number
52
+ end
53
+ end
54
+
55
+ def self.it_lexes_decimal(str, number)
56
+ it "lexes #{str}" do
57
+ lex = Lexer.new str
58
+ tok = lex.next_token
59
+ tok.value.should == Token::DECIMAL
60
+ tok.number.should == number
61
+ end
62
+ end
63
+
64
+ def self.it_lexes_eof(str)
65
+ it "lexes eof #{str}" do
66
+ lex = Lexer.new str
67
+ tok = lex.next_token
68
+ tok.value.should == Token::EOF
69
+ end
70
+ end
71
+
72
+ def self.it_lexes_error(str)
73
+ it "lexes error #{str}" do
74
+ lex = Lexer.new "x#{str}"
75
+ tok = lex.next_token
76
+ tok.value.should == Token::ID
77
+ tok.string.should == 'x'
78
+ lambda { lex.next_token }.should raise_error(ParseException)
79
+ end
80
+ end
81
+
82
+ it_lexes_keyword 'and', Token::And
83
+ it_lexes_keyword 'asc', Token::Asc
84
+ it_lexes_keyword 'avg', Token::Avg
85
+ it_lexes_keyword 'by', Token::By
86
+ it_lexes_keyword 'contains', Token::Contains
87
+ it_lexes_keyword 'count', Token::Count
88
+ it_lexes_keyword 'date', Token::Date
89
+ it_lexes_keyword 'datediff', Token::DateDiff
90
+ it_lexes_keyword 'datetime', Token::DateTime
91
+ it_lexes_keyword 'day', Token::Day
92
+ it_lexes_keyword 'dayofweek', Token::DayOfWeek
93
+ it_lexes_keyword 'desc', Token::Desc
94
+ it_lexes_keyword 'ends', Token::Ends
95
+ it_lexes_keyword 'false', Token::False
96
+ it_lexes_keyword 'format', Token::Format
97
+ it_lexes_keyword 'group', Token::Group
98
+ it_lexes_keyword 'hour', Token::Hour
99
+ it_lexes_keyword 'is', Token::Is
100
+ it_lexes_keyword 'label', Token::Label
101
+ it_lexes_keyword 'like', Token::Like
102
+ it_lexes_keyword 'limit', Token::Limit
103
+ it_lexes_keyword 'lower', Token::Lower
104
+ it_lexes_keyword 'matches', Token::Matches
105
+ it_lexes_keyword 'millisecond', Token::Millisecond
106
+ it_lexes_keyword 'min', Token::Min
107
+ it_lexes_keyword 'minute', Token::Minute
108
+ it_lexes_keyword 'max', Token::Max
109
+ it_lexes_keyword 'month', Token::Month
110
+ it_lexes_keyword 'not', Token::Not
111
+ it_lexes_keyword 'now', Token::Now
112
+ it_lexes_keyword 'no_format', Token::NoFormat
113
+ it_lexes_keyword 'no_values', Token::NoValues
114
+ it_lexes_keyword 'null', Token::Null
115
+ it_lexes_keyword 'offset', Token::Offset
116
+ it_lexes_keyword 'options', Token::Options
117
+ it_lexes_keyword 'or', Token::Or
118
+ it_lexes_keyword 'order', Token::Order
119
+ it_lexes_keyword 'pivot', Token::Pivot
120
+ it_lexes_keyword 'quarter', Token::Quarter
121
+ it_lexes_keyword 'second', Token::Second
122
+ it_lexes_keyword 'select', Token::Select
123
+ it_lexes_keyword 'starts', Token::Starts
124
+ it_lexes_keyword 'sum', Token::Sum
125
+ it_lexes_keyword 'timeofday', Token::TimeOfDay
126
+ it_lexes_keyword 'timestamp', Token::Timestamp
127
+ it_lexes_keyword 'todate', Token::ToDate
128
+ it_lexes_keyword 'true', Token::True
129
+ it_lexes_keyword 'upper', Token::Upper
130
+ it_lexes_keyword 'where', Token::Where
131
+ it_lexes_keyword 'with', Token::With
132
+ it_lexes_keyword 'year', Token::Year
133
+
134
+ it_lexes_id 'selected'
135
+ it_lexes_id ' selected ', 'selected'
136
+ it_lexes_id '`selected`', 'selected'
137
+ it_lexes_id '`some string`', 'some string'
138
+ it_lexes_id 'hello123bye'
139
+ it_lexes_id 'hello_123_bye'
140
+ it_lexes_string "'hello world'", "hello world"
141
+ it_lexes_string '"hello world"', "hello world"
142
+
143
+ it_lexes_token '+', Token::PLUS
144
+ it_lexes_token '-', Token::MINUS
145
+ it_lexes_token '*', Token::STAR
146
+ it_lexes_token '/', Token::SLASH
147
+
148
+ it_lexes_token ',', Token::COMMA
149
+ it_lexes_token '(', Token::LPAREN
150
+ it_lexes_token ')', Token::RPAREN
151
+ it_lexes_token '=', Token::EQ
152
+ it_lexes_token '<', Token::LT
153
+ it_lexes_token '<=', Token::LTE
154
+ it_lexes_token '>', Token::GT
155
+ it_lexes_token '>=', Token::GTE
156
+ it_lexes_token '!=', Token::NEQ
157
+ it_lexes_token '<>', Token::NEQ
158
+
159
+ it_lexes_integer '1', 1
160
+ it_lexes_integer '0123', 123
161
+ it_lexes_integer '45678791230', 45678791230
162
+ it_lexes_decimal '123.456', 123.456
163
+ it_lexes_decimal '.456', 0.456
164
+
165
+ it_lexes_eof ''
166
+ it_lexes_eof " \t\n\r"
167
+
168
+ it_lexes_error '!'
169
+ it_lexes_error '?'
170
+ it_lexes_error ':'
171
+ end
@@ -0,0 +1,360 @@
1
+ require 'rgviz'
2
+
3
+ include Rgviz
4
+
5
+ describe Parser do
6
+ def parse(string)
7
+ Parser.new(string).parse
8
+ end
9
+
10
+ def parse_select_single_column(string)
11
+ query = parse "select #{string}"
12
+ select = query.select
13
+ select.columns.length.should == 1
14
+
15
+ select.columns[0]
16
+ end
17
+
18
+ def parse_where(string)
19
+ query = parse "where #{string}"
20
+ query.where.expression
21
+ end
22
+
23
+ def self.it_parses_as_identifier(str)
24
+ it "parses #{str} as identifier" do
25
+ col = parse_select_single_column "#{str}"
26
+ col.should be_a_kind_of IdColumn
27
+ col.name.should == "#{str}"
28
+ end
29
+ end
30
+
31
+ def self.it_parses_columns_container(str, method)
32
+ it "parses #{str}" do
33
+ query = parse "#{str} foo"
34
+ cols = query.send method
35
+ cols.columns.length == 1
36
+ cols.columns[0].should be_a_kind_of IdColumn
37
+ cols.columns[0].name.should == 'foo'
38
+ end
39
+ end
40
+
41
+ def self.it_raises_on(str)
42
+ it "raises on #{str}" do
43
+ lambda { parse "#{str}" }.should raise_error(ParseException)
44
+ end
45
+ end
46
+
47
+ it "parses empty" do
48
+ query = parse ''
49
+ query.select.should be_nil
50
+ end
51
+
52
+ it "parses select all" do
53
+ query = parse 'select *'
54
+ select = query.select
55
+ select.columns.should be_empty
56
+ end
57
+
58
+ it "parses select simple columns" do
59
+ query = parse 'select one, two, three'
60
+ select = query.select
61
+
62
+ select.columns.length.should == 3
63
+
64
+ ['one', 'two', 'three'].each_with_index do |name, i|
65
+ select.columns[i].should be_a_kind_of IdColumn
66
+ select.columns[i].name.should == name
67
+ end
68
+ end
69
+
70
+ it "parses select number" do
71
+ col = parse_select_single_column '1'
72
+ col.should be_a_kind_of NumberColumn
73
+ col.value.should == 1
74
+ end
75
+
76
+ it "parses select number with minus" do
77
+ col = parse_select_single_column '-1'
78
+ col.should be_a_kind_of NumberColumn
79
+ col.value.should == -1
80
+ end
81
+
82
+ it "parses select number with plus" do
83
+ col = parse_select_single_column '+1'
84
+ col.should be_a_kind_of NumberColumn
85
+ col.value.should == 1
86
+ end
87
+
88
+ it "parses select parenthesized number" do
89
+ col = parse_select_single_column '(1)'
90
+ col.should be_a_kind_of NumberColumn
91
+ col.value.should == 1
92
+ end
93
+
94
+ it "parses select string" do
95
+ col = parse_select_single_column '"hello"'
96
+ col.should be_a_kind_of StringColumn
97
+ col.value.should == 'hello'
98
+ end
99
+
100
+ it "parses select false" do
101
+ col = parse_select_single_column 'false'
102
+ col.should be_a_kind_of BooleanColumn
103
+ col.value.should == false
104
+ end
105
+
106
+ it "parses select true" do
107
+ col = parse_select_single_column 'true'
108
+ col.should be_a_kind_of BooleanColumn
109
+ col.value.should == true
110
+ end
111
+
112
+ it "parses select date" do
113
+ col = parse_select_single_column 'date "2010-01-02"'
114
+ col.should be_a_kind_of DateColumn
115
+ col.value.should == Date.parse('2010-01-02')
116
+ end
117
+
118
+ it "parses select timeofday" do
119
+ col = parse_select_single_column 'timeofday "12:01:02"'
120
+ col.should be_a_kind_of TimeOfDayColumn
121
+ col.value.hour.should == 12
122
+ col.value.min.should == 1
123
+ col.value.sec.should == 2
124
+ end
125
+
126
+ it "parses select datetime" do
127
+ col = parse_select_single_column 'datetime "2010-01-02 12:02:03"'
128
+ col.should be_a_kind_of DateTimeColumn
129
+ col.value.should == Time.parse('2010-01-02 12:02:03')
130
+ end
131
+
132
+ [
133
+ ['*', ScalarFunctionColumn::Product],
134
+ ['/', ScalarFunctionColumn::Quotient],
135
+ ['+', ScalarFunctionColumn::Sum],
136
+ ['-', ScalarFunctionColumn::Difference]
137
+ ].each do |str, func|
138
+ it "parses select #{func}" do
139
+ col = parse_select_single_column "1 #{str} one"
140
+ col.should be_a_kind_of ScalarFunctionColumn
141
+ col.function.should == func
142
+
143
+ args = col.arguments
144
+ args.length.should == 2
145
+
146
+ args[0].should be_a_kind_of NumberColumn
147
+ args[0].value.should == 1
148
+
149
+ args[1].should be_a_kind_of IdColumn
150
+ args[1].name.should == "one"
151
+ end
152
+ end
153
+
154
+ it "parses compound arithmetic" do
155
+ col = parse_select_single_column "1 * 2 + 3 * 4"
156
+ col.should be_kind_of ScalarFunctionColumn
157
+ col.function.should == ScalarFunctionColumn::Sum
158
+
159
+ col.arguments.length.should == 2
160
+
161
+ [0, 1].each do |i|
162
+ sub = col.arguments[i]
163
+ sub.should be_kind_of ScalarFunctionColumn
164
+ sub.function.should == ScalarFunctionColumn::Product
165
+ sub.arguments.length.should == 2
166
+ end
167
+ end
168
+
169
+ [
170
+ ['min', AggregateColumn::Min],
171
+ ['max', AggregateColumn::Max],
172
+ ['count', AggregateColumn::Count],
173
+ ['avg', AggregateColumn::Avg],
174
+ ['sum', AggregateColumn::Sum]
175
+ ].each do |str, function|
176
+ it "parses #{str} aggregation" do
177
+ col = parse_select_single_column "#{str}(col)"
178
+ col.should be_a_kind_of AggregateColumn
179
+ col.function.should == function
180
+
181
+ col.argument.should be_a_kind_of IdColumn
182
+ col.argument.name.should == 'col'
183
+ end
184
+
185
+ it_parses_as_identifier str
186
+ end
187
+
188
+ [
189
+ ['year', ScalarFunctionColumn::Year],
190
+ ['month', ScalarFunctionColumn::Month],
191
+ ['day', ScalarFunctionColumn::Day],
192
+ ['hour', ScalarFunctionColumn::Hour],
193
+ ['minute', ScalarFunctionColumn::Minute],
194
+ ['second', ScalarFunctionColumn::Second],
195
+ ['millisecond', ScalarFunctionColumn::Millisecond],
196
+ ['now', ScalarFunctionColumn::Now],
197
+ ['datediff', ScalarFunctionColumn::DateDiff],
198
+ ['lower', ScalarFunctionColumn::Lower],
199
+ ['upper', ScalarFunctionColumn::Upper],
200
+ ['quarter', ScalarFunctionColumn::Quarter],
201
+ ['dayofweek', ScalarFunctionColumn::DayOfWeek],
202
+ ['todate', ScalarFunctionColumn::ToDate]
203
+ ].each do |str, function|
204
+ it "parses #{str} function" do
205
+ col = parse_select_single_column "#{str}(col)"
206
+ col.should be_a_kind_of ScalarFunctionColumn
207
+ col.function.should == function
208
+
209
+ col.arguments.length.should == 1
210
+ col.arguments[0].should be_a_kind_of IdColumn
211
+ col.arguments[0].name.should == 'col'
212
+ end
213
+
214
+ it_parses_as_identifier str
215
+ end
216
+
217
+ it "parses datediff function with many arguments" do
218
+ col = parse_select_single_column "datediff(1, 2)"
219
+ col.should be_a_kind_of ScalarFunctionColumn
220
+ col.function.should == ScalarFunctionColumn::DateDiff
221
+
222
+ col.arguments.length.should == 2
223
+ col.arguments[0].value == 1
224
+ col.arguments[1].value == 2
225
+ end
226
+
227
+ [
228
+ ['=', BinaryExpression::Eq],
229
+ ['!=', BinaryExpression::Neq],
230
+ ['<>', BinaryExpression::Neq],
231
+ ['<', BinaryExpression::Lt],
232
+ ['<=', BinaryExpression::Lte],
233
+ ['>', BinaryExpression::Gt],
234
+ ['>=', BinaryExpression::Gte],
235
+ ['contains', BinaryExpression::Contains],
236
+ ['starts with', BinaryExpression::StartsWith],
237
+ ['ends with', BinaryExpression::EndsWith],
238
+ ['matches', BinaryExpression::Matches],
239
+ ['like', BinaryExpression::Like]
240
+ ].each do |str, operator|
241
+ it "parses where #{str} 1" do
242
+ exp = parse_where "col #{str} 1"
243
+ exp.should be_a_kind_of BinaryExpression
244
+ exp.operator.should == operator
245
+ exp.left.should be_a_kind_of IdColumn
246
+ exp.left.name.should == 'col'
247
+ exp.right.should be_a_kind_of NumberColumn
248
+ exp.right.value.should == 1
249
+ end
250
+ end
251
+
252
+ it_parses_as_identifier 'contains'
253
+ it_parses_as_identifier 'starts'
254
+ it_parses_as_identifier 'ends'
255
+ it_parses_as_identifier 'with'
256
+ it_parses_as_identifier 'matches'
257
+ it_parses_as_identifier 'like'
258
+ it_parses_as_identifier 'no_values'
259
+ it_parses_as_identifier 'no_format'
260
+ it_parses_as_identifier 'is'
261
+ it_parses_as_identifier 'null'
262
+
263
+ it_parses_columns_container 'group by', :group_by
264
+ it_parses_columns_container 'pivot', :pivot
265
+
266
+ [
267
+ ['', Sort::Asc],
268
+ [' asc', Sort::Asc],
269
+ [' desc', Sort::Desc]
270
+ ].each do |str, order|
271
+ it "parses order by" do
272
+ query = parse "order by foo #{order}, bar #{order}"
273
+ order_by = query.order_by
274
+
275
+ sorts = order_by.sorts
276
+
277
+ sorts.length.should == 2
278
+
279
+ sorts[0].column.should be_a_kind_of IdColumn
280
+ sorts[0].column.name.should == 'foo'
281
+ sorts[0].order.should == order
282
+
283
+ sorts[1].column.should be_a_kind_of IdColumn
284
+ sorts[1].column.name.should == 'bar'
285
+ sorts[1].order.should == order
286
+ end
287
+ end
288
+
289
+ it "parses limit" do
290
+ query = parse "limit 10"
291
+ query.limit.should == 10
292
+ end
293
+
294
+ it "parses offset" do
295
+ query = parse "offset 10"
296
+ query.offset.should == 10
297
+ end
298
+
299
+ it "parses label" do
300
+ query = parse "label one 'unu', two 'du'"
301
+ labels = query.labels
302
+ labels.length.should == 2
303
+
304
+ labels[0].column.should be_a_kind_of IdColumn
305
+ labels[0].column.name.should == 'one'
306
+ labels[0].label.should == 'unu'
307
+
308
+ labels[1].column.should be_a_kind_of IdColumn
309
+ labels[1].column.name.should == 'two'
310
+ labels[1].label.should == 'du'
311
+ end
312
+
313
+ it "parses format" do
314
+ query = parse "format one 'unu', two 'du'"
315
+ formats = query.formats
316
+ formats.length.should == 2
317
+
318
+ formats[0].column.should be_a_kind_of IdColumn
319
+ formats[0].column.name.should == 'one'
320
+ formats[0].pattern.should == 'unu'
321
+
322
+ formats[1].column.should be_a_kind_of IdColumn
323
+ formats[1].column.name.should == 'two'
324
+ formats[1].pattern.should == 'du'
325
+ end
326
+
327
+ it "parses options" do
328
+ query = parse "options no_values no_format"
329
+ options = query.options
330
+ options.length.should == 2
331
+
332
+ options[0].should be_a_kind_of Option
333
+ options[0].option.should == Option::NoValues
334
+
335
+ options[1].should be_a_kind_of Option
336
+ options[1].option.should == Option::NoFormat
337
+ end
338
+
339
+ it_raises_on 'select'
340
+ it_raises_on 'where'
341
+ it_raises_on 'where 1'
342
+ it_raises_on 'where 1 <'
343
+ it_raises_on 'group by'
344
+ it_raises_on 'group by foo bar'
345
+ it_raises_on 'pivot'
346
+ it_raises_on 'order by'
347
+ it_raises_on 'order by foo bar'
348
+ it_raises_on 'limit'
349
+ it_raises_on 'limit foo'
350
+ it_raises_on 'offset'
351
+ it_raises_on 'offset foo'
352
+ it_raises_on 'label'
353
+ it_raises_on 'label foo'
354
+ it_raises_on 'label `foo`'
355
+ it_raises_on 'format'
356
+ it_raises_on 'format foo'
357
+ it_raises_on 'format `foo`'
358
+ it_raises_on 'options'
359
+ it_raises_on 'options foobar'
360
+ 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: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 14
9
- version: "0.14"
8
+ - 15
9
+ version: "0.15"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ary Borenszweig
@@ -34,6 +34,8 @@ files:
34
34
  - lib/rgviz/token.rb
35
35
  - lib/rgviz/visitor.rb
36
36
  - lib/rgviz/table.rb
37
+ - spec/rgviz/lexer_spec.rb
38
+ - spec/rgviz/parser_spec.rb
37
39
  has_rdoc: true
38
40
  homepage: http://code.google.com/p/rgviz
39
41
  licenses: []