Class: Rgviz::Parser

Inherits:
Lexer
  • Object
show all
Defined in:
lib/rgviz/parser.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Lexer

#next_token

Constructor Details

#initialize(string) ⇒ Parser

Returns a new instance of Parser.



6
7
8
9
10
# File 'lib/rgviz/parser.rb', line 6

def initialize(string)
  super
  @query = Query.new
  next_token
end

Class Method Details

.parse(string) ⇒ Object



12
13
14
# File 'lib/rgviz/parser.rb', line 12

def self.parse(string)
  Parser.new(string).parse
end

Instance Method Details

#check(*token_values) ⇒ Object

Raises:



424
425
426
# File 'lib/rgviz/parser.rb', line 424

def check(*token_values)
  raise ParseException.new("Expecting token #{token_values}") unless token_values.any?{|value| @token.value == value}
end

#check!(*token_values) ⇒ Object



428
429
430
431
# File 'lib/rgviz/parser.rb', line 428

def check!(*token_values)
  check *token_values
  next_token
end

#parseObject

Raises:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/rgviz/parser.rb', line 16

def parse
  parse_select
  parse_where
  parse_group_by
  parse_pivot
  parse_order_by
  parse_limit
  parse_offset
  parse_label
  parse_format
  parse_options

  raise ParseException.new("Expecting end of query, got: #{@token.string}") if @token.value != Token::EOF
  @query
end

#parse_and_expressionObject



214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/rgviz/parser.rb', line 214

def parse_and_expression
  left = parse_not_expression
  return left unless token_is? Token::And

  operands = [left]
  while true
    if token_is! Token::And
      operands << parse_not_expression
    else
      return LogicalExpression.new(LogicalExpression::And, operands)
    end
  end
end

#parse_arithmetic_expressionObject



294
295
296
# File 'lib/rgviz/parser.rb', line 294

def parse_arithmetic_expression
  parse_summation_or_substraction
end

#parse_atomic_columnObject



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/rgviz/parser.rb', line 334

def parse_atomic_column
  case @token.value
  when Token::ID
    return value_column(IdColumn.new(@token.string))
  when Token::Contains, Token::Starts, Token::Ends, Token::With,
       Token::Matches, Token::Like, Token::NoValues, Token::NoFormat,
       Token::Is, Token::Null
    return value_column(IdColumn.new(@token.string))
  when Token::PLUS
    next_token
    check Token::INTEGER, Token::DECIMAL
    return value_column(NumberColumn.new(@token.number))
  when Token::MINUS
    next_token
    check Token::INTEGER, Token::DECIMAL
    return value_column(NumberColumn.new(-@token.number))
  when Token::INTEGER, Token::DECIMAL
    return value_column(NumberColumn.new(@token.number))
  when Token::LPAREN
    next_token
    column = parse_column
    check! Token::RPAREN
    return column
  when Token::STRING
    return value_column(StringColumn.new(@token.string))
  when Token::False
    return value_column(BooleanColumn.new(false))
  when Token::True
    return value_column(BooleanColumn.new(true))
  when Token::Date
    next_token
    check Token::STRING
    return value_column(DateColumn.new(parse_date(@token.string)))
  when Token::DateTime
    next_token
    check Token::STRING
    return value_column(DateTimeColumn.new(parse_time(@token.string)))
  when Token::TimeOfDay
    next_token
    check Token::STRING
    return value_column(TimeOfDayColumn.new(parse_time(@token.string)))
  when Token::Avg, Token::Count, Token::Min, Token::Max, Token::Sum
    function = @token.value
    string = @token.string
    next_token
    if token_is! Token::LPAREN
      column = parse_column
      check! Token::RPAREN
      return AggregateColumn.new(function, column)
    else
      return IdColumn.new(string)
    end
  when Token::Year, Token::Month, Token::Day,
       Token::Hour, Token::Minute, Token::Second, Token::Millisecond,
       Token::Now, Token::DateDiff, Token::Lower, Token::Upper,
       Token::Quarter, Token::DayOfWeek, Token::ToDate, Token::Concat,
       Token::Round,Token::Floor
    function = @token.value
    string = @token.string
    next_token
    if token_is! Token::LPAREN
      columns = parse_columns
      check! Token::RPAREN
      return ScalarFunctionColumn.new(function, *columns)
    else
      return IdColumn.new(string)
    end
  end
  nil
end

#parse_columnObject



290
291
292
# File 'lib/rgviz/parser.rb', line 290

def parse_column
  parse_arithmetic_expression
end

#parse_columns(columns = []) ⇒ Object



277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/rgviz/parser.rb', line 277

def parse_columns(columns = [])
  column = parse_column
  return columns unless column

  columns << column
  while token_is! Token::COMMA
    column = parse_column
    break unless column
    columns << column
  end
  columns
end

#parse_expressionObject



196
197
198
# File 'lib/rgviz/parser.rb', line 196

def parse_expression
  parse_or_expression
end

#parse_formatObject

Raises:



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/rgviz/parser.rb', line 121

def parse_format
  return if not token_is! Token::Format

  @query.formats = []

  column = parse_column
  raise ParseException.new("Expecting format") unless column

  check Token::STRING
  @query.formats << Format.new(column, @token.string)
  next_token

  while token_is! Token::COMMA
    column = parse_column
    break unless column

    check Token::STRING
    @query.formats << Format.new(column, @token.string)
    next_token
  end

  raise ParseException.new("Expecting format") if @query.formats.empty?
end

#parse_group_byObject

Raises:



50
51
52
53
54
55
56
57
58
# File 'lib/rgviz/parser.rb', line 50

def parse_group_by
  return if not token_is! Token::Group
  check! Token::By

  @query.group_by = GroupBy.new
  @query.group_by.columns = parse_columns

  raise ParseException.new("Expecting group by columns") if @query.group_by.columns.empty?
end

#parse_labelObject

Raises:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rgviz/parser.rb', line 97

def parse_label
  return if not token_is! Token::Label

  @query.labels = []

  column = parse_column
  raise ParseException.new("Expecting label") unless column

  check Token::STRING
  @query.labels << Label.new(column, @token.string)
  next_token

  while token_is! Token::COMMA
    column = parse_column
    break unless column

    check Token::STRING
    @query.labels << Label.new(column, @token.string)
    next_token
  end

  raise ParseException.new("Expecting label") if @query.labels.empty?
end

#parse_limitObject



79
80
81
82
83
84
85
86
# File 'lib/rgviz/parser.rb', line 79

def parse_limit
  return if not token_is! Token::Limit

  check Token::INTEGER

  @query.limit = @token.number
  next_token
end

#parse_multiplication_or_divitionObject



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
# File 'lib/rgviz/parser.rb', line 316

def parse_multiplication_or_divition
  left = parse_atomic_column
  while true
    case @token.value
    when Token::STAR
      next_token
      right = parse_atomic_column
      left = ScalarFunctionColumn.new ScalarFunctionColumn::Product, left, right
    when Token::SLASH
      next_token
      right = parse_atomic_column
      left = ScalarFunctionColumn.new ScalarFunctionColumn::Quotient, left, right
    else
      return left
    end
  end
end

#parse_not_expressionObject



228
229
230
231
232
233
234
235
# File 'lib/rgviz/parser.rb', line 228

def parse_not_expression
  if token_is! Token::Not
    operand = parse_primary_expression
    UnaryExpression.new UnaryExpression::Not, operand
  else
    parse_primary_expression
  end
end

#parse_offsetObject



88
89
90
91
92
93
94
95
# File 'lib/rgviz/parser.rb', line 88

def parse_offset
  return if not token_is! Token::Offset

  check Token::INTEGER

  @query.offset = @token.number
  next_token
end

#parse_optionsObject

Raises:



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/rgviz/parser.rb', line 145

def parse_options
  return if not token_is! Token::Options

  @query.options = Options.new

  while true
    case @token.value
    when Token::NoFormat
      @query.options.no_format = true
      next_token
    when Token::NoValues
      @query.options.no_values = true
      next_token
    else
      break
    end
  end

  raise ParseException.new("Expecting option") if !@query.options.no_format && !@query.options.no_values
  raise ParseException.new("Unknown option #{@token.string}") if @token.value != Token::EOF
end

#parse_or_expressionObject



200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/rgviz/parser.rb', line 200

def parse_or_expression
  left = parse_and_expression
  return left unless token_is? Token::Or

  operands = [left]
  while true
    if token_is! Token::Or
      operands << parse_and_expression
    else
      return LogicalExpression.new(LogicalExpression::Or, operands)
    end
  end
end

#parse_order_byObject

Raises:



69
70
71
72
73
74
75
76
77
# File 'lib/rgviz/parser.rb', line 69

def parse_order_by
  return if not token_is! Token::Order
  check! Token::By

  @query.order_by = OrderBy.new
  @query.order_by.sorts = parse_sorts

  raise ParseException.new("Expecting order by columns") if @query.order_by.sorts.empty?
end

#parse_pivotObject

Raises:



60
61
62
63
64
65
66
67
# File 'lib/rgviz/parser.rb', line 60

def parse_pivot
  return if not token_is! Token::Pivot

  @query.pivot = Pivot.new
  @query.pivot.columns = parse_columns

  raise ParseException.new("Expecting pivot columns") if @query.pivot.columns.empty?
end

#parse_primary_expressionObject



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/rgviz/parser.rb', line 237

def parse_primary_expression
  case @token.value
  when Token::LPAREN
    next_token
    exp = parse_expression
    check! Token::RPAREN
    return exp
  else
    left = parse_column
    raise ParseException.new("Expecting left exp") unless left

    case @token.value
    when Token::Is
      next_token
      if token_is! Token::Not
        check! Token::Null
        return UnaryExpression.new(UnaryExpression::IsNotNull, left)
      elsif token_is! Token::Null
        return UnaryExpression.new(UnaryExpression::IsNull, left)
      end
    when Token::EQ, Token::NEQ, Token::LT, Token::LTE, Token::GT, Token::GTE,
         Token::Contains, Token::Matches, Token::Like
      operator = @token.value
      next_token
      right = parse_column
      raise ParseException.new("Expecting right exp") unless right
      return BinaryExpression.new(left, operator, right)
    when Token::Starts, Token::Ends
      operator = "#{@token.value} with".to_sym
      next_token
      check! Token::With
      right = parse_column
      raise ParseException.new("Expecting right exp") unless right
      return BinaryExpression.new(left, operator, right)
    else
      raise ParseException.new("Expecting comparison")
    end
  end
end

#parse_selectObject

Raises:



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/rgviz/parser.rb', line 32

def parse_select
  return if not token_is! Token::Select

  @query.select = Select.new

  return if token_is! Token::STAR

  parse_columns @query.select.columns

  raise ParseException.new("Expecting select columns") if @query.select.columns.empty?
end

#parse_sort_orderObject



186
187
188
189
190
191
192
193
194
# File 'lib/rgviz/parser.rb', line 186

def parse_sort_order
  if token_is! Token::Asc
    Sort::Asc
  elsif token_is! Token::Desc
    Sort::Desc
  else
    Sort::Asc
  end
end

#parse_sortsObject



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/rgviz/parser.rb', line 167

def parse_sorts
  sorts = []

  column = parse_column
  return sorts unless column

  order = parse_sort_order

  sorts << Sort.new(column, order)
  while token_is! Token::COMMA
    column = parse_column
    break unless column

    order = parse_sort_order
    sorts << Sort.new(column, order)
  end
  sorts
end

#parse_summation_or_substractionObject



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/rgviz/parser.rb', line 298

def parse_summation_or_substraction
  left = parse_multiplication_or_divition
  while true
    case @token.value
    when Token::PLUS
      next_token
      right = parse_multiplication_or_divition
      left = ScalarFunctionColumn.new ScalarFunctionColumn::Sum, left, right
    when Token::MINUS
      next_token
      right = parse_multiplication_or_divition
      left = ScalarFunctionColumn.new ScalarFunctionColumn::Difference, left, right
    else
      return left
    end
  end
end

#parse_whereObject



44
45
46
47
48
# File 'lib/rgviz/parser.rb', line 44

def parse_where
  return if not token_is! Token::Where

  @query.where = Where.new parse_expression
end

#token_is!(token_value) ⇒ Object



415
416
417
418
419
420
421
422
# File 'lib/rgviz/parser.rb', line 415

def token_is!(token_value)
  if @token.value == token_value
    next_token
    true
  else
    false
  end
end

#token_is?(token_value) ⇒ Boolean

Returns:

  • (Boolean)


411
412
413
# File 'lib/rgviz/parser.rb', line 411

def token_is?(token_value)
  @token.value == token_value
end

#value_column(col) ⇒ Object



405
406
407
408
409
# File 'lib/rgviz/parser.rb', line 405

def value_column(col)
  column = col
  next_token
  return column
end