class SQLPP::Parser
Public Class Methods
new(string)
click to toggle source
# File lib/sqlpp/parser.rb, line 110 def initialize(string) @tokenizer = SQLPP::Tokenizer.new(string) end
parse(string)
click to toggle source
# File lib/sqlpp/parser.rb, line 105 def self.parse(string) parser = new(string) parser.parse end
Public Instance Methods
_eat(type_or_types, pattern=nil)
click to toggle source
# File lib/sqlpp/parser.rb, line 517 def _eat(type_or_types, pattern=nil) _next if _peek(type_or_types, pattern) end
_ensure_stream_empty!()
click to toggle source
# File lib/sqlpp/parser.rb, line 552 def _ensure_stream_empty! unless _peek(:eof) raise TrailingTokens, _next.inspect end end
_expect(type_or_types, pattern=nil)
click to toggle source
# File lib/sqlpp/parser.rb, line 538 def _expect(type_or_types, pattern=nil) token = _next if !_match(token, type_or_types, pattern) raise UnexpectedToken, "expected #{type_or_types.inspect}(#{pattern.inspect}), got #{token.inspect}" end token end
_match(token, type_or_types, pattern=nil)
click to toggle source
# File lib/sqlpp/parser.rb, line 528 def _match(token, type_or_types, pattern=nil) types = type_or_types.is_a?(Array) ? type_or_types : [ type_or_types ] if types.include?(token.type) && (pattern.nil? || pattern === token.text) token else nil end end
_next()
click to toggle source
# File lib/sqlpp/parser.rb, line 548 def _next @tokenizer.next end
_parse_atom()
click to toggle source
# File lib/sqlpp/parser.rb, line 425 def _parse_atom if (lit = _eat(:lit)) AST::Atom.new(:lit, lit.text) elsif _eat(:key, :case) _parse_case elsif _eat(:punct, "(") expr = _parse_expr1 _eat :space _expect(:punct, ")") AST::Parens.new(expr) elsif _eat(:key, :null) AST::Atom.new(:lit, "NULL") elsif _eat(:punct, "*") AST::Atom.new(:lit, "*") else id = _expect(:id) if _eat(:punct, "(") args = _parse_list _expect(:punct, ")") AST::Atom.new(:func, id.text, args) elsif _eat(:punct, '.') id2 = _eat(:id) || _eat(:punct, '*') if !id2 raise UnexpectedToken, "expected id or *, got #{_peek.inspect}" end AST::Atom.new(:attr, id.text, id2.text) else AST::Atom.new(:attr, id.text) end end end
_parse_case()
click to toggle source
# File lib/sqlpp/parser.rb, line 465 def _parse_case _expect :space kase = AST::Atom.new(:case) unless _peek(:key, :when) kase.left = _parse_expr1 _eat :space end cases = [] while _eat(:key, :when) condition = _parse_expr1 _eat :space _expect :key, :then result = _parse_expr1 cases << [condition, result] _eat :space end if _eat(:key, :else) cases << _parse_expr1 _eat :space end _expect :key, :end kase.right = cases kase end
_parse_entity()
click to toggle source
# File lib/sqlpp/parser.rb, line 289 def _parse_entity _eat :space entity = if _eat(:punct, '(') from = _parse_from _eat :space _expect :punct, ')' AST::Parens.new(from) elsif _peek(:key, :select) _parse_select else id = _expect(:id) AST::Atom.new(:attr, id.text) end _eat :space if _eat(:key, :as) _eat :space id = _expect(:id) AST::As.new(id.text, entity) elsif (id = _eat(:id)) AST::Alias.new(id.text, entity) else entity end end
_parse_expr1()
click to toggle source
# File lib/sqlpp/parser.rb, line 318 def _parse_expr1 _eat :space left = _parse_expr2 _eat :space if (op = _eat(:key, /^(and|or|is)$/i)) op = op.text if op == :is _eat :space op2 = _eat(:key, :not) op = "#{op} #{op2.text}" if op2 end right = _parse_expr1 AST::Expr.new(left, op, right) else left end end
_parse_expr2()
click to toggle source
# File lib/sqlpp/parser.rb, line 341 def _parse_expr2 _eat :space left = _parse_expr3 _eat :space not_kw = _eat(:key, :not) _eat :space if not_kw if (op = _eat(:key, :between)) op = op.text _eat :space lo = _parse_expr3 _eat :space _expect :key, :and _eat :space hi = _parse_expr3 right = AST::Atom.new(:range, lo, hi) elsif (op = _eat(:key, :in)) op = op.text _eat :space _expect :punct, "(" right = AST::Atom.new(:list, _parse_list) _eat :space _expect :punct, ")" elsif (op = _eat(:punct, /<=|<>|>=|=|<|>/) || _eat(:key, /^i?like$/)) op = op.text right = _parse_expr3 end if right AST::Expr.new(left, op, right, not_kw != nil) elsif not_kw raise UnexpectedToken, "got #{not_kw.inspect}" else left end end
_parse_expr3()
click to toggle source
# File lib/sqlpp/parser.rb, line 388 def _parse_expr3 _eat :space if (op = (_eat(:punct, /[-+]/) || _eat(:key, /^(not|distinct)$/))) _eat :space AST::Unary.new(op.text, _parse_expr3) else atom = _parse_atom _eat :space if _eat(:punct, "[") subscript = _parse_expr1 _eat :space _expect(:punct, "]") _eat :space atom = AST::Subscript.new(atom, subscript) end if _eat(:punct, "::") _eat :space type = _parse_atom _eat :space atom = AST::TypeCast.new(atom, type) end if (op = _eat(:punct, /[-+*\/]/)) _eat :space AST::Expr.new(atom, op.text, _parse_expr3) else atom end end end
_parse_from()
click to toggle source
# File lib/sqlpp/parser.rb, line 255 def _parse_from entity = _parse_entity loop do _eat :space if (which = _eat(:key, /^(inner|cross|left|right|full|outer)$/)) type = which.text.to_s if type == "full" || type == "left" || type == "right" _eat :space _expect :key, :outer type << " outer" end _eat :space _expect :key, :join entity = AST::Join.new(type.downcase, entity, _parse_from) _eat :space if _eat(:key, :on) _eat :space entity.on = _parse_expr1 end else break end end entity end
_parse_list()
click to toggle source
list := ''
| expr | expr ',' args
# File lib/sqlpp/parser.rb, line 498 def _parse_list _eat :space args = [] return args if _peek(:punct, ')') loop do args << _parse_expr1 _eat :space if _eat(:punct, ",") _eat :space else break end end args end
_parse_select()
click to toggle source
— internal use —
# File lib/sqlpp/parser.rb, line 148 def _parse_select _expect :key, :select select = AST::Select.new _eat :space if _eat(:key, :distinct) select.distinct = true _eat :space end if !_peek(:key, /^(from|where)$/) && !_peek(:eof) list = [] loop do expr = _parse_expr1 _eat :space if _peek(:key, :as) _next _eat :space name = _expect(:id) expr = AST::As.new(name.text, expr) end list.push expr break unless _eat(:punct, ",") end _eat :space select.projections = list end if _eat(:key, :from) list = [] loop do _eat :space list << _parse_from _eat :space break unless _eat(:punct, ',') end _eat :space select.froms = list end if _eat(:key, :where) select.wheres = _parse_expr1 _eat :space end if _eat(:key, :group) _eat :space _expect :key, :by _eat :space select.groups = _parse_list end if _eat(:key, :order) _eat :space _expect :key, :by _eat :space list = [] loop do key = AST::SortKey.new(_parse_expr1, []) list << key _eat :space if (dir = _eat(:key, /^(asc|desc)$/)) _eat :space key.options << dir.text end if (opt = _eat(:key, :nulls)) opt = opt.text.to_s _eat :space sort = _eat(:key, /^(first|last)$/) opt << " " << sort.text.to_s if sort key.options << opt end _eat :space break unless _eat(:punct, ",") end select.orders = list end if _eat(:key, :limit) _eat :space atom = _parse_atom _eat :space select.limit = AST::Limit.new(atom) end if _eat(:key, :offset) _eat :space atom = _parse_atom _eat :space select.offset = AST::Offset.new(atom) end select end
_peek(type_or_types, pattern=nil)
click to toggle source
# File lib/sqlpp/parser.rb, line 521 def _peek(type_or_types, pattern=nil) token = _next _match(token, type_or_types, pattern) ensure @tokenizer.push(token) end
parse()
click to toggle source
# File lib/sqlpp/parser.rb, line 114 def parse _eat :space token = _peek(:key) raise UnexpectedToken, token.inspect unless token case token.text when :select then parse_select else raise UnexpectedToken, token.inspect end end
parse_expression()
click to toggle source
— exposed for testing purposes —
# File lib/sqlpp/parser.rb, line 128 def parse_expression _parse_expr1 ensure _ensure_stream_empty! end
parse_from()
click to toggle source
# File lib/sqlpp/parser.rb, line 134 def parse_from _parse_from ensure _ensure_stream_empty! end
parse_select()
click to toggle source
# File lib/sqlpp/parser.rb, line 140 def parse_select _parse_select ensure _ensure_stream_empty! end