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
eric (author)
Wed Jan 28 08:47:47 -0800 2009
bruce (committer)
Wed Jan 28 21:26:39 -0800 2009
keyword_search / lib / keyword_search.rl
100644 113 lines (79 sloc) 2.417 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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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 = word
        results[key] ||= []
      }
      
      action default {
        key = nil
      }
      
      action word {
        word = data[tokstart..p-1]
      }
      
      action value {
        (results[key || :default] ||= []) << [ word, positive_match ]
      }
      
      action negative_match {
        positive_match = false
      }
      
      action positive_match {
        positive_match = true
      }
      
      action quote { quotes += 1 }
      
      action unquote { quotes -= 1 }
 
      seperators = ' '+ | / *[,|] */ ;
 
      bareword = ( [^ '"(:] . [^ "):]* ) > start % word ; # allow apostrophes
      dquoted = '"' @ quote ( [^"]* > start % word ) :>> '"' @ unquote;
      squoted = '\'' @ quote ( [^']* > start % word ) :>> '\'' @ unquote;
 
      anyword = dquoted | squoted | bareword ;
 
      anyvalue = anyword % value ;
      multivalues = anyvalue ( seperators anyvalue )* ;
      groupedvalues = '(' @ quote multivalues :>> ')' @ unquote;
 
      value = groupedvalues | anyvalue ;
 
      pair = bareword % key ':' value ;
 
      value_only = value > default ;
      
      match_mode = ('-' % negative_match | '+'? % positive_match ) ;
 
      definition = match_mode? <: ( pair | value_only ) ;
      
      definitions = definition ( ' '+ definition )*;
 
      main := ' '* definitions? ' '* 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
      eof = nil
      word = nil
      pe = data.length
      key = nil
      positive_match = nil
      tokstart = nil
      results = {}
      quotes = 0
      %% write init;
      %% write exec;
      unless quotes.zero?
       raise ParseError, "Unclosed quotes"
      end
      results
    end
    
  end
  
end