Skip to content

Commit

Permalink
sql parser
Browse files Browse the repository at this point in the history
  • Loading branch information
ajoo committed Oct 12, 2006
1 parent 15f913f commit 5be6ef6
Showing 1 changed file with 152 additions and 20 deletions.
172 changes: 152 additions & 20 deletions test/src/sql_parser.rb
Expand Up @@ -7,10 +7,18 @@ module SqlParser
MyKeywords = Keywords.case_insensitive(%w{
select from where group by having order desc asc
inner left right full outer inner join on cross
union all distinct as
union all distinct as exists in between
case when else end and or not true false
})
MyOperators = Operators.new(%w{+ - * / % == > < >= <= <> != : ( ) . ,})
MyOperators = Operators.new(%w{+ - * / % = > < >= <= <> != : ( ) . ,})
def self.operators(*ops)
result = []
ops.each do |op|
result << (MyOperators[op] >> op.to_sym)
end
Parsers.sum(*result)
end
Comparators = operators(*%w{= > < >= <= <> !=})
MyLexer = Parsers.integer.token(:int) | MyKeywords.lexer | MyOperators.lexer
MyLexeme = MyLexer.lexeme(Parsers.whitespaces | Parsers.comment_line('#')) << Parsers.eof
def keyword
Expand All @@ -19,6 +27,12 @@ def keyword
def operator
MyOperators
end
def comma
operator[',']
end
def list expr
paren(expr.delimited(comma))
end
def word(&block)
if block.nil?
token(:word, &Id)
Expand All @@ -32,27 +46,93 @@ def calculate_simple_cases(val, cases, default)
def calculate_full_cases(cases, default)
CaseExpr.new(cases, default)
end
def make_bool_expression expr
compare = operator['>'] >> Gt | operator['<'] >> Lt | operator['>='] >> Ge | operator['<='] >> Le |
operator['=='] >> Eq | operator['!='] >> Ne | operator['<>'] >> Ne
comparison = sequence(expr, compare, expr) {|e1,f,e2|f.call(e1,e2)}
def logical_operator op
proc{|a,b|CompoundPredicate.new(a,op,b)}
end
NotEqual = proc {|x,y|ComparePredicate.new(x,:'<>',y)}
def make_predicate expr, rel
expr_list = list expr
comparison = make_comparison_predicate expr, rel
group_comparison = sequence(expr_list, Comparators, expr_list) do |g1, op, g2|
GroupComparisonPredicate.new(g1, op, g2)
end
bool = nil
lazy_bool = lazy{bool}
bool_term = keyword[:true] >> true | keyword[:false] >> false |
comparison | operator['('] >> lazy_bool << operator[')']
comparison | group_comparison | paren(lazy_bool) |
make_exists(rel) | make_not_exists(rel)
bool_table = OperatorTable.new.
infixl(keyword[:or] >> Or, 20).
infixl(keyword[:and] >> And, 30).
infixl(keyword[:not] >> Not, 30)
infixl(keyword[:or] >> logical_operator(:or), 20).
infixl(keyword[:and] >> logical_operator(:and), 30).
prefix(keyword[:not] >> proc{|pred|NotPredicate.new(pred)}, 40)
bool = Expressions.build(bool_term, bool_table)
end
def make_expression bool
def make_exists rel
keyword[:exists] >> rel.map do |r|
ExistsPredicate.new r
end
end
def make_not_exists rel
keyword[:not] >> keyword[:exists] >> rel.map do |r|
NotExistsPredicate.new r
end
end
def make_in val, expr
keyword[:in] >> list(expr).map do |vals|
InPredicate.new(val, vals)
end
end
def make_not_in val, expr
keyword[:not] >> keyword[:in] >> list(expr).map do |vals|
NotInPredicate.new(val, vals)
end
end
def make_in_relation val, rel
keyword[:in] >> rel.map do |r|
InRelationPredicate.new(val, r)
end
end
def make_not_in_relation val, rel
keyword[:not] >> keyword[:in] >> rel.map do |r|
NotInRelationPredicate.new(val, r)
end
end
def make_between val, expr
make_between_clause(val, expr) {|a,b|BetweenPredicate.new val, a, b}
end
def make_not_between val, expr
keyword[:not] >> make_between_clause(val, expr) {|a,b|NotBetweenPredicate.new val, a, b}
end
def make_comparison_predicate expr, rel
compare = operator['>'] >> Gt | operator['<'] >> Lt | operator['>='] >> Ge | operator['<='] >> Le |
operator['='] >> Eq | operator['!='] >> NotEqual | operator['<>'] >> NotEqual
expr.bind do |val1|
comparison = sequence(compare, expr) {|f,e2|f.call(val1,e2)}
in_clause = make_in val1, expr
not_in_clause = make_not_in val1, expr
in_relation = make_in_relation val1, rel
not_in_relation = make_not_in_relation val1, rel
between = make_between val1, expr
not_between = make_not_between val1, expr
comparison | in_clause | not_in_clause |
in_relation | not_in_relation | between | not_between
end
end
def make_between_clause val, expr, &maker
variant1 = keyword[:between] >> paren(sequence(expr, comma >> expr, &maker))
variant2 = keyword[:between] >> sequence(expr, keyword[:and] >> expr, &maker)
variant1 | variant2
end
def paren parser
operator['('] >> parser << operator[')']
end
def make_expression predicate, rel
expr = nil
lazy_expr = lazy{expr}
simple_case = sequence(keyword[:when], lazy_expr, operator[':'], lazy_expr) do |w,cond,t,val|
[cond, val]
end
full_case = sequence(keyword[:when], bool, operator[':'], lazy_expr) do |w,cond,t,val|
full_case = sequence(keyword[:when], predicate, operator[':'], lazy_expr) do |w,cond,t,val|
[cond, val]
end
default_case = (keyword[:else] >> lazy_expr).optional
Expand All @@ -79,27 +159,79 @@ def make_expression bool
prefix(operator['-'] >> Neg, 50)
expr = Expressions.build(term, table)
end
def make_relation expr
exprs = expr.delimited1(operator[','])
def make_relation expr, pred
exprs = expr.delimited1(comma)
relation = nil
lazy_relation = lazy{relation}
term_relation = word {|w|TableRelation.new w} | operator['('] >> lazy_relation << operator[')']
sub_relation = sequence(term_relation, (keyword[:as].optional >> word).optional) do |rel, name|
case when name.nil?: rel else AliasRelation.new(rel, name) end
end
relation = sequence(keyword[:select], exprs, keyword[:from], sub_relation) do |_, projected, _, from|
SelectRelation.new(projected, from)
joined_relation = sub_relation.postfix(make_join_with(lazy{joined_relation}, pred))
where_clause = keyword[:where] >> pred
order_element = sequence(expr, (keyword[:asc] >> true | keyword[:desc] >> false).optional(true)) do |e,order|
OrderElement.new e, order
end
order_elements = order_element.separated1(comma)
exprs = expr.separated1(comma)
order_by_clause = keyword[:order] >> keyword[:by] >> order_elements
group_by = keyword[:group] >> keyword[:by] >> exprs
group_by_clause = sequence(group_by, (keyword[:having] >> pred).optional) do |by, having|
GroupByClause.new(by, having)
end
relation = sub_relation | sequence(keyword[:select], keyword[:distinct].optional(false), exprs,
keyword[:from], joined_relation,
where_clause.optional, group_by_clause.optional, order_by_clause.optional
) do |_, distinct, projected, _, from, where, groupby, orderby|
SelectRelation.new(projected, distinct, from, where, groupby, orderby)
end
relation = relation.infixl(union_maker)
end
def union_maker
keyword[:union] >> (keyword[:all]>>true|false).map do |all|
proc {|r1, r2|UnionRelation.new(r1, all, r2)}
end
end
def make_join_with rel, pred
crossjoin = keyword[:cross] >> keyword[:join] >> rel.map do |r|
proc {|r0| CrossJoinRelation.new(r0, r)}
end
leftjoin = outer_join :left
rightjoin = outer_join :right
innerjoin = keyword[:inner].optional >> keyword[:join] >> :inner
join_with_condition = sequence(sum(leftjoin, rightjoin, innerjoin), rel,
keyword[:on], pred) do |kind, r, _, on|
proc{|r0|JoinRelation.new(kind, r0, r, on)}
end
sum(crossjoin, join_with_condition)
end
def outer_join kind
keyword[kind] >> keyword[:outer].optional >> keyword[:join] >> kind
end

def expression
expr = nil
expr = make_expression(make_bool_expression(lazy{expr}))
assemble[0]
end

def relation
make_relation(expression)
assemble[2]
end

def predicate
assemble[1]
end

def assemble
pred = nil
rel = nil
lazy_predicate = lazy{pred}
lazy_rel = lazy{rel}
expr = make_expression lazy_predicate, lazy_rel
pred = make_predicate expr, lazy_rel
rel = make_relation expr, pred
return expr, pred, rel
end


def make parser
MyLexeme.nested(parser << eof)
end
Expand Down

0 comments on commit 5be6ef6

Please sign in to comment.