public
Description: Generic support for extracting GMail-style search keywords/values from strings
Homepage: http://codefluency.rubyforge.org/keyword_search
Clone URL: git://github.com/bruce/keyword_search.git
Search Repo:
keyword_search / lib / keyword_search.rl
100644 89 lines (67 sloc) 2.016 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
require File.dirname(__FILE__) << '/keyword_search/definition.rb'
 
module KeywordSearch
 
  class ParseError < ::SyntaxError; end
      
  class << self
  
    %%{
      
      machine parser;
      
      action start {
        tokstart = p;
      }
      
      action key {
        key = data[tokstart...p-1]
        results[key] ||= []
      }
      
      action default {
        key = nil
      }
      
      action value {
        value = data[tokstart..p-1]
        if ["("].include?(value[0,1])
          value = parse(value[1..-2])[:default]
        elsif ["'", '"'].include?(value[0,1])
          value = value[1..-2]
        end
        (results[key || :default] ||= []) << value
      }
      
      action quote { quotes += 1 }
      
      action unquote { quotes -= 1 }
      
      bareword = [^ '"(:] [^ "):]*; # allow apostrophes
      grouped = '(' @ quote any* :>> ')' @ unquote;
      dquoted = '"' @ quote any* :>> '"' @ unquote;
      squoted = '\'' @ quote any* :>> '\'' @ unquote;
      
      
      value = ( grouped | dquoted | squoted | bareword );
      
      pair = (bareword > start ':') % key value > start % value ;
      
      definition = ( pair | value > start > default % value) ' ';
      main := definition** 0
              @!{ raise ParseError, "At offset #{p}, near: '#{data[p,10]}'" };
      
    }%%
    
    def search(input_string, definition=nil, &block)
      definition ||= Definition.new(&block)
      results = parse(input_string)
      results.each do |key, terms|
        definition.handle(key, terms)
      end
      results
    end
    
    #######
    private
    #######
    
    def parse(input) #:nodoc:
      data = input + ' '
      %% write data;
      p = 0
      pe = data.length
      key = nil
      tokstart = nil
      results = {}
      quotes = 0
      %% write init;
      %% write exec;
      %% write eof;
      unless quotes.zero?
       raise ParseError, "Unclosed quotes"
      end
      results
    end
    
  end
  
end