class Ferret::Expression_Parser
Attributes
expr[R]
Public Class Methods
new(raw_expr, schema)
click to toggle source
Calls superclass method
# File lib/sql-ferret.rb, line 1061 def initialize raw_expr, schema super() @raw_expr = raw_expr @schema = schema @expr = Ferret::Expression.new @scanner = Ferret::Scanner.new @raw_expr @first_star_offset = nil first_table_name = @scanner.get_id 'table-name' @expr.stages[0].table = @schema[first_table_name] or ugh 'unknown-table', table: first_table_name, offset: @scanner.last_token_offset, expr: @raw_expr @scanner.pass :':' parenthesised = @scanner.pass? :'(' loop do exemplar_escaped, exemplar_column_name = @scanner.get_optional_escaped_id 'column-expected' if exemplar_column_name then exemplar_column = @expr.stages[0].table[exemplar_column_name] or ugh 'unknown-field', field: exemplar_column_name, table: @expr.stages[0].table.name, role: 'key-field', offset: @scanner.last_token_offset, expr: @raw_expr # the key column must be a column, not a ghost field unless exemplar_column.column? then ugh 'not-a-column', field: exemplar_column.name, table: @expr.stages[0].table.name, offset: @scanner.last_token_offset, expr: @raw_expr end exemplar_interpretation = exemplar_escaped ? nil : exemplar_column.interpretation key_output_name = parse_optional_output_name_override || exemplar_column_name @expr.exemplars.push Ferret::Exemplar.new( exemplar_column, exemplar_interpretation) @expr.selectees.push Ferret::Selectee.new( @expr.stages[0], exemplar_column, key_output_name, exemplar_interpretation) end break unless parenthesised and @scanner.pass? :',' end @scanner.pass :')' if parenthesised if @scanner.pass? :':' then # Colon without dereference: we should expect a fetch # verb. @expr.type = parse_fetch_verb else @scanner.pass :'->' if @scanner.pass? :':' then # Colon past dereference: we should expect an update # verb. @expr.type = parse_update_verb else # Note that [[parse_stage]] can change [[@expr.type]] # if it meets the [[-> :]]. parse_stage @expr.stages.last, parens: false end end if @expr.modification? then if @first_star_offset then ugh 'star-in-modification', offset: @first_star_offset, expr: @raw_expr end end @scanner.expected_eof! if @expr.modification? then ugh 'multiple-columns-selected-in-modification' \ if @expr.multicolumn end unless @expr.multicolumn then # In single-column expressions, only the very last # selectee is actually selected. @expr.selectees[0 ... -1] = [] end @expr.assign_stage_qualifiers @schema.alias_generator return end
Public Instance Methods
parse_fetch_verb()
click to toggle source
# File lib/sql-ferret.rb, line 1314 def parse_fetch_verb verb = @scanner.get_id 'fetch-verb' case verb when 'select' then return @scanner.pass?('distinct') ? :select_distinct : :select when 'distinct' then return :select_distinct else ugh 'unknown-fetch-verb', got: verb, input: @expr, offset: @scanner.last_token_offset end end
parse_optional_join_arrow()
click to toggle source
# File lib/sql-ferret.rb, line 1308 def parse_optional_join_arrow return :left if @scanner.pass? :'->' return :inner if @scanner.pass? :'<->' return nil end
parse_optional_output_name_override()
click to toggle source
# File lib/sql-ferret.rb, line 1298 def parse_optional_output_name_override if @scanner.pass? :"'" then override = @scanner.get_id 'output-name-override' @scanner.pass :"'" return override else return nil end end
parse_stage(stage, parens: false)
click to toggle source
# File lib/sql-ferret.rb, line 1178 def parse_stage stage, parens: false starred = false stage_empty = true loop do field_escaped, field_name = @scanner.get_optional_escaped_id 'field-expected' if field_name then field_offset = @scanner.last_token_offset raise 'assertion failed' unless stage.table field = stage.table[field_name] or ugh 'unknown-field', field: field_name, expr: @raw_expr, offset: field_offset field_output_name = parse_optional_output_name_override || field_name # Has this column, or its name, been used already? (0 ... @expr.selectees.length).reverse_each do |i| selectee = @expr.selectees[i] if (selectee.stage == stage and selectee.field == field) or selectee.output_name == field_output_name then # Possible conflict detected. if selectee.star? then # The previous selectee was implicit, added due # to star expansion. We'll just discard it, for # explicit fields take precedence. @expr.selectees.delete_at i else ugh 'duplicate-field-in-stage', field: field.name, output_name: field_output_name, expr: @raw_expr, offset: field_offset end end end @expr.selectees.push Ferret::Selectee.new( stage, field, field_output_name, field_escaped ? nil : field.interpretation) stage_empty = false if @scanner.pass? :'(' then join_type = parse_optional_join_arrow or expected!('join-arrow', candidates: '-> <->') # If something goes wrong trying to start a new # stage, it must be the last field's fault. # ([[start_subsequent_stage]] won't attach the # offset to the ugh on its own just because it # doesn't _have_ the offset.) ugh? offset: field_offset do start_subsequent_stage stage, field, join_type end parse_stage @expr.stages.last, parens: true @scanner.pass :')' else if !parens and @scanner.pass? :':' then @expr.type = parse_fetch_verb break end if join_type = parse_optional_join_arrow then ugh? offset: field_offset do start_subsequent_stage stage, field, join_type end if !parens and @scanner.pass? :':' then @expr.type = parse_update_verb else parse_stage @expr.stages.last, parens: parens end break end end elsif @scanner.pass? :'*' then @first_star_offset ||= @scanner.last_token_offset # only one star per stage @scanner.expected! 'field-name' if starred starred = true @expr.multicolumn = true stage.table.columns.each do |column| # We'll skip columns that have been selected (at # this stage) already, or columns whose names have # already been used. next if @expr.selectees.any? do |selectee| (selectee.stage == stage and selectee.column == column) or selectee.output_name == column.name end @expr.selectees.push Ferret::Selectee.new( stage, column, column.name, column.interpretation, true) end # Note that [[->]] can not appear immediately # following a [[*]]. break else if stage_empty then @scanner.expected! 'field-name' end break end if @scanner.pass? :',' then @expr.multicolumn = true else break end end return end
parse_update_verb()
click to toggle source
# File lib/sql-ferret.rb, line 1330 def parse_update_verb verb = @scanner.get_id 'update-verb' case verb when 'update', 'set' then return :update when 'delete' then return :delete else ugh 'unknown-update-verb', got: verb, input: @expr, offset: @scanner.last_token_offset end end
start_subsequent_stage(parent, stalk, join_type)
click to toggle source
# File lib/sql-ferret.rb, line 1159 def start_subsequent_stage parent, stalk, join_type raise 'type mismatch' \ unless parent.is_a? Ferret::Stage raise 'type mismatch' \ unless stalk.is_a? Ferret::Field raise 'assertion failed' \ unless [:left, :inner].include? join_type # Note that we don't have the field's offset. But the # caller might. unless stalk.ref then ugh 'unable-to-dereference', field: field.name end @expr.stages.push Ferret::Stage.new( parent, stalk, join_type) return end