searchconditionparser.y
class SearchConditionParser
options no_result_var
rule
search_condition : boolean_primary { [val[0]] }
| search_condition AND boolean_primary { (val[0] << val[2]).flatten }
comparison_predicate : id COMP_OP value { Predicate.new(val[1].to_sym, val[0], val[2]) }
between_predicate : id BETWEEN value AND value { Predicate.new(:between, val[0], [val[2], val[3]]) }
in_predicate : id IN '(' value_list ')' { Predicate.new(:in, val[0], val[3]) }
like_predicate : id LIKE value { Predicate.new(:like, val[0], val[2]) }
null_predicate : id IS NULL { Predicate.new(:'=', val[0], nil) }
boolean_primary : predicate
| '(' search_condition ')' { val[1] }
predicate : comparison_predicate
| between_predicate
| in_predicate
| like_predicate
| null_predicate
id : IDENTIFIER
| IDENTIFIER '.' id
value : STRING
| NUMBER
| NULL
value_list : value { [val[0]] }
| value_list ',' value { val[0] << val[2] }
end
---- header
require 'strscan'
---- inner
Predicate = Struct.new("Predicate", :operator, :column, :value)
def initialize(obj)
src = obj.is_a?(IO) ? obj.read : obj.to_s
@ss = StringScanner.new(src)
end
def scan
piece = nil
until @ss.eos?
if (tok = @ss.scan /\s+/)
elsif (tok = @ss.scan /(?:>=|<=|<>|!=|^=|=|>|<)/)
yield :COMP_OP, tok
elsif (tok = @ss.scan /(?:IS|IN|BETWEEN|LIKE|NOT|AND|OR)/i)
yield tok.upcase.to_sym, tok
elsif (tok = @ss.scan /NULL/i)
yield :NULL, nil
elsif (tok = @ss.scan /'[^']*'/i)
yield :STRING, tok.slice(1...-1)
elsif (tok = @ss.scan /\d+/)
yield :NUMBER, tok.to_i
elsif (tok = @ss.scan /[,\(\).]/)
yield tok, tok
elsif (tok = @ss.scan /[a-z_]\w*/i)
yield :IDENTIFIER, tok
else
raise RuntimeError, 'must not happen'
end
end
yield false, '$'
end
def parse
yyparse self, :scan
end
実行結果
require 'searchconditionparser.tab'
require 'pp'
pp SearchConditionParser.new(%q{
a = 100 and b != 200 and c in ('ABC', 'DEF')
AND a like 'A%' AND a is null
}).parse
~/work$ racc searchconditionparser.y && ruby sp.rb
[#,
#,
#,
#,
#]