# sq - structured query # christian neukirchen, mit-licensed. # 06apr2008 # # usage: # # sq query datafiles... # # entries, seperated by two newlines: # # tag1 tag2 "tag3" # field=value "field2"=value field3="value" # field: value on rest of line # field: "multi line string # without any quotes in it" # # implemented query language: # # x has x # x=y has x with value y # x>y x=y x<=y comparison # x~rx x~~rx matches regexp case-insensitive, case-sensitive # q q conjunction # # x reverse sort by x # ~rx ~~rx entry matches rx case-insensitive, case-sensitive anywhere query = ARGV.shift or abort File.read($0)[/\A.*?\n\n/m].chop set = [] while hunk = gets("") hunk.rstrip! data = {"" => hunk} hunk = hunk.gsub(/".*?"/m) { $&.tr("\n", ' ') } hunk.each { |line| if line =~ /^([\w-]+):\s+("(.*)"|(.*))/ data[$1.downcase] = $2 || $3 else line.scan(/([\w-]+|"(.*?)")(\s*=\s*([\w+-]+|"(.*?)"))?/) { data[$2 || $1] = $5 || $4 || true } end } set << data end def dont_leak Array.send(:include, Comparable) String.send(:define_method, "~~") { |s| self =~ Regexp.new(s) } String.send(:define_method, "~") { |s| self =~ Regexp.new(s, "i") } String.send(:define_method, "=") { |s| self == s } String.send(:define_method, "sm") { split(/(\d+)/).map { |e| [e.to_i, e] } } end; dont_leak query.scan(/([\w-]+|"(.*?)"|)((=|<|>|<=|>=|~~|~)([\w+-]+|"(.*?)"))?/) { if $1 == "" && ($4 == "<" || $4 == ">") set = set.sort_by { |f| f[$6 || $5].sm } rescue set set.reverse! if $4 == ">" else set = set.find_all { |f| f.include?($2 || $1) && case $4 when "<", ">", "<=", ">="; f[$2 || $1].sm.send($4, ($6 || $5).sm) when "=", "~", "~~"; f[$2 || $1]. send($4, $6 || $5) when nil; true else false end } end } puts set.map { |f| f[""] }.join("\n\n") unless set.empty?