tsearch 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ require 'texticle/full_text_index'
2
+
3
+ ####
4
+ # Texticle exposes full text search capabilities from PostgreSQL, and allows
5
+ # you to declare full text indexes. Texticle will extend ActiveRecord with
6
+ # named_scope methods making searching easy and fun!
7
+ #
8
+ # Texticle.index is automatically added to ActiveRecord::Base.
9
+ #
10
+ # To declare an index on a model, just use the index method:
11
+ #
12
+ # class Product < ActiveRecord::Base
13
+ # index do
14
+ # name
15
+ # description
16
+ # end
17
+ # end
18
+ #
19
+ # This will allow you to do full text search on the name and description
20
+ # columns for the Product model. It defines a named_scope method called
21
+ # "search", so you can take advantage of the search like this:
22
+ #
23
+ # Product.search('foo bar')
24
+ #
25
+ # Indexes may also be named. For example:
26
+ #
27
+ # class Product < ActiveRecord::Base
28
+ # index 'author' do
29
+ # name
30
+ # author
31
+ # end
32
+ # end
33
+ #
34
+ # A named index will add a named_scope with the index name prefixed by
35
+ # "search". In order to take advantage of the "author" index, just call:
36
+ #
37
+ # Product.search_author('foo bar')
38
+ #
39
+ # Finally, column names can be ranked. The ranks are A, B, C, and D. This
40
+ # lets us declare that matches in the "name" column are more important
41
+ # than matches in the "description" column:
42
+ #
43
+ # class Product < ActiveRecord::Base
44
+ # index do
45
+ # name 'A'
46
+ # description 'B'
47
+ # end
48
+ # end
49
+ module Texticle
50
+ # The version of Texticle you are using.
51
+ VERSION = '1.0.2'
52
+
53
+ # A list of full text indexes
54
+ attr_accessor :full_text_indexes
55
+
56
+ ###
57
+ # Create an index with +name+ using +dictionary+
58
+ def index name = nil, dictionary = 'english', &block
59
+ search_name = ['search', name].compact.join('_')
60
+
61
+ class_eval do
62
+ named_scope search_name.to_sym, lambda { |term|
63
+ p = Texticle::Parser.new
64
+ term = p.parse(term)
65
+ {
66
+ :select => "#{table_name}.*, ts_rank_cd((#{full_text_indexes.first.to_s}),
67
+ to_tsquery(#{connection.quote(term)})) as rank",
68
+ :conditions =>
69
+ ["#{full_text_indexes.first.to_s} @@ to_tsquery(?)", term],
70
+ :order => 'rank DESC'
71
+ }
72
+ }
73
+ end
74
+ index_name = [table_name, name, 'fts_idx'].compact.join('_')
75
+ (self.full_text_indexes ||= []) <<
76
+ FullTextIndex.new(index_name, dictionary, self, &block)
77
+ end
78
+ end
@@ -0,0 +1,54 @@
1
+ module Texticle
2
+ class FullTextIndex # :nodoc:
3
+ attr_accessor :index_columns
4
+
5
+ def initialize name, dictionary, model_class, &block
6
+ @name = name
7
+ @dictionary = dictionary
8
+ @model_class = model_class
9
+ @index_columns = {}
10
+ @string = nil
11
+ instance_eval(&block)
12
+ end
13
+
14
+ def create
15
+ @model_class.connection.execute create_sql
16
+ end
17
+
18
+ def destroy
19
+ @model_class.connection.execute destroy_sql
20
+ end
21
+
22
+ def create_sql
23
+ <<-eosql
24
+ CREATE index #{@name}
25
+ ON #{@model_class.table_name}
26
+ USING gin((#{to_s}))
27
+ eosql
28
+ end
29
+
30
+ def destroy_sql
31
+ "DROP index IF EXISTS #{@name}"
32
+ end
33
+
34
+ def to_s
35
+ return @string if @string
36
+ vectors = []
37
+ @index_columns.sort_by { |k,v| k }.each do |weight, columns|
38
+ c = columns.map { |x| "coalesce(#{@model_class.table_name}.#{x}, '')" }
39
+ if weight == 'none'
40
+ vectors << "to_tsvector('#{@dictionary}', #{c.join(" || ' ' || ")})"
41
+ else
42
+ vectors <<
43
+ "setweight(to_tsvector('#{@dictionary}', #{c.join(" || ' ' || ")}), '#{weight}')"
44
+ end
45
+ end
46
+ @string = vectors.join(" || ' ' || ")
47
+ end
48
+
49
+ def method_missing name, *args
50
+ weight = args.shift || 'none'
51
+ (index_columns[weight] ||= []) << name.to_s
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,5 @@
1
+ class AndTermNode < Treetop::Runtime::SyntaxNode
2
+ def to_tquery
3
+ " & #{term.to_tquery}"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ class BooleanTermNode < Treetop::Runtime::SyntaxNode
2
+ end
@@ -0,0 +1,17 @@
1
+ class ExpressionNode < Treetop::Runtime::SyntaxNode
2
+ def to_tquery
3
+ buffer = []
4
+ elements[0].elements.each do |elem|
5
+ if elem.respond_to?(:to_tquery)
6
+ t = elem.to_tquery
7
+ if t =~ /^\s+(\||&).*$/ || buffer.empty?
8
+ buffer << t
9
+ else
10
+ buffer << " | " + t
11
+ end
12
+ end
13
+ end
14
+
15
+ buffer.join(' ')
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ class GroupedTermNode < Treetop::Runtime::SyntaxNode
2
+ def to_tquery
3
+ buffer = []
4
+ elements[1].elements.each do |elem|
5
+ t = elem.to_tquery
6
+ if t =~ /^\s+(\||&).*$/ || buffer.empty?
7
+ buffer << t
8
+ else
9
+ buffer << " | " + t
10
+ end
11
+ end
12
+
13
+ '(' + buffer.join(' ') + ')'
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ class NotTermNode < Treetop::Runtime::SyntaxNode
2
+ def to_tquery
3
+ " & !#{term.to_tquery}"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class OrTermNode < Treetop::Runtime::SyntaxNode
2
+ def to_tquery
3
+ " | #{term.to_tquery}"
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class QueryNode < Treetop::Runtime::SyntaxNode
2
+ def to_tquery
3
+ buffer = []
4
+ elements.each do |elem|
5
+ buffer << elem.to_tquery if elem.respond_to?(:to_tquery)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ class QuotedTermNode < Treetop::Runtime::SyntaxNode
2
+ def to_tquery
3
+ "'" + elements[1].text_value + "'"
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ class TermNode < Treetop::Runtime::SyntaxNode
2
+ # module TermNode
3
+ def to_tquery
4
+ terms = []
5
+
6
+ return '' if elements.empty?
7
+
8
+ elements.each do |child_elem|
9
+ next if child_elem.elements.empty?
10
+ if child_elem.respond_to?(:to_tquery)
11
+ terms << child_elem.to_tquery
12
+ else
13
+ child_elem.elements.each do |grand_child_elem|
14
+ if grand_child_elem.respond_to?(:to_tquery)
15
+ terms << grand_child_elem.to_tquery
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ terms.join(' ')
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ class WordNode < Treetop::Runtime::SyntaxNode
2
+ def to_tquery
3
+ word
4
+ end
5
+
6
+ def word
7
+ text_value
8
+ end
9
+ end
@@ -0,0 +1,53 @@
1
+ require 'treetop'
2
+ require File.dirname(__FILE__) + '/tquery'
3
+
4
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'nodes','**','*.rb'))].each {|f| require f}
5
+
6
+ module Texticle
7
+ class Parser
8
+ attr_accessor :parse_tree, :tquery
9
+
10
+ def parse(str)
11
+ s = str.strip
12
+ p = TQueryParser.new
13
+ @parse_tree = nil
14
+ @tquery = nil
15
+ return @tquery if s.blank?
16
+ failed = false
17
+ while @parse_tree.nil?
18
+ begin
19
+ @parse_tree = p.parse(s)
20
+ raise p.failure_reason if @parse_tree.nil?
21
+ result = parser_tree_to_tquery(@parse_tree)
22
+ break if effectively_empty?(result)
23
+ @tquery = result
24
+ rescue
25
+ raise $! if failed
26
+ failed = true
27
+ s = s.gsub(/[^A-Za-z0-9\s]/, '').strip
28
+ break if effectively_empty?(s)
29
+ end
30
+ end
31
+ @tquery
32
+ end
33
+
34
+ private
35
+ # the search string is effectively empty (meaning tsearch will see it as blank)
36
+ def effectively_empty?(str)
37
+ s = str.gsub(/[^A-Za-z0-9\s]/, '').strip
38
+ unique_terms = s.downcase.split(/\s+/).uniq
39
+ s.blank? || unique_terms.reject {|e| e == 'and' || e == 'or'}.empty?
40
+ end
41
+
42
+ def parser_tree_to_tquery(parse_tree)
43
+ results = if parse_tree.expression.nil?
44
+ ''
45
+ else
46
+ parse_tree.expression.to_tquery
47
+ end
48
+ unless results.nil?
49
+ results.gsub(/^\s+(\||\&)/, '')
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,55 @@
1
+ require 'rake'
2
+ require 'texticle'
3
+
4
+ namespace :texticle do
5
+ desc "Create full text index migration"
6
+ task :migration => :environment do
7
+ now = Time.now.utc
8
+ filename = "#{now.strftime('%Y%m%d%H%m%S')}_full_text_search_#{now.to_i}.rb"
9
+ File.open(File.join(RAILS_ROOT, 'db', 'migrate', filename), 'wb') { |fh|
10
+ fh.puts "class FullTextSearch#{now.to_i} < ActiveRecord::Migration"
11
+ fh.puts " def self.up"
12
+ Dir[File.join(RAILS_ROOT, 'app', 'models', '*.rb')].each do |f|
13
+ klass = File.basename(f, '.rb').classify.constantize
14
+ if klass.respond_to?(:full_text_indexes)
15
+ (klass.full_text_indexes || []).each do |fti|
16
+ fh.puts <<-eostmt
17
+ ActiveRecord::Base.connection.execute(<<-'eosql')
18
+ #{fti.destroy_sql}
19
+ eosql
20
+ ActiveRecord::Base.connection.execute(<<-'eosql')
21
+ #{fti.create_sql}
22
+ eosql
23
+ eostmt
24
+ end
25
+ end
26
+ end
27
+ fh.puts " end"
28
+ fh.puts "end"
29
+ }
30
+ end
31
+
32
+ desc "Create full text indexes"
33
+ task :create_indexes => ['texticle:destroy_indexes'] do
34
+ Dir[File.join(RAILS_ROOT, 'app', 'models', '*.rb')].each do |f|
35
+ klass = File.basename(f, '.rb').classify.constantize
36
+ if klass.respond_to?(:full_text_indexes)
37
+ (klass.full_text_indexes || []).each do |fti|
38
+ fti.create
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ desc "Destroy full text indexes"
45
+ task :destroy_indexes => [:environment] do
46
+ Dir[File.join(RAILS_ROOT, 'app', 'models', '*.rb')].each do |f|
47
+ klass = File.basename(f, '.rb').classify.constantize
48
+ if klass.respond_to?(:full_text_indexes)
49
+ (klass.full_text_indexes || []).each do |fti|
50
+ fti.destroy
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,762 @@
1
+ # Autogenerated from a Treetop grammar. Edits may be lost.
2
+
3
+
4
+ module TQuery
5
+ include Treetop::Runtime
6
+
7
+ def root
8
+ @root || :query
9
+ end
10
+
11
+ module Query0
12
+ def space1
13
+ elements[0]
14
+ end
15
+
16
+ def expression
17
+ elements[1]
18
+ end
19
+
20
+ def space2
21
+ elements[3]
22
+ end
23
+ end
24
+
25
+ def _nt_query
26
+ start_index = index
27
+ if node_cache[:query].has_key?(index)
28
+ cached = node_cache[:query][index]
29
+ if cached
30
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
31
+ @index = cached.interval.end
32
+ end
33
+ return cached
34
+ end
35
+
36
+ i0, s0 = index, []
37
+ r1 = _nt_space
38
+ s0 << r1
39
+ if r1
40
+ r2 = _nt_expression
41
+ s0 << r2
42
+ if r2
43
+ r4 = _nt_more_query
44
+ if r4
45
+ r3 = r4
46
+ else
47
+ r3 = instantiate_node(SyntaxNode,input, index...index)
48
+ end
49
+ s0 << r3
50
+ if r3
51
+ r5 = _nt_space
52
+ s0 << r5
53
+ end
54
+ end
55
+ end
56
+ if s0.last
57
+ r0 = instantiate_node(QueryNode,input, i0...index, s0)
58
+ r0.extend(Query0)
59
+ else
60
+ @index = i0
61
+ r0 = nil
62
+ end
63
+
64
+ node_cache[:query][start_index] = r0
65
+
66
+ r0
67
+ end
68
+
69
+ module MoreQuery0
70
+ def space1
71
+ elements[0]
72
+ end
73
+
74
+ def query
75
+ elements[1]
76
+ end
77
+
78
+ def space2
79
+ elements[2]
80
+ end
81
+ end
82
+
83
+ def _nt_more_query
84
+ start_index = index
85
+ if node_cache[:more_query].has_key?(index)
86
+ cached = node_cache[:more_query][index]
87
+ if cached
88
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
89
+ @index = cached.interval.end
90
+ end
91
+ return cached
92
+ end
93
+
94
+ i0, s0 = index, []
95
+ r1 = _nt_space
96
+ s0 << r1
97
+ if r1
98
+ r2 = _nt_query
99
+ s0 << r2
100
+ if r2
101
+ r3 = _nt_space
102
+ s0 << r3
103
+ end
104
+ end
105
+ if s0.last
106
+ r0 = instantiate_node(QueryNode,input, i0...index, s0)
107
+ r0.extend(MoreQuery0)
108
+ else
109
+ @index = i0
110
+ r0 = nil
111
+ end
112
+
113
+ node_cache[:more_query][start_index] = r0
114
+
115
+ r0
116
+ end
117
+
118
+ module Expression0
119
+ def space
120
+ elements[1]
121
+ end
122
+
123
+ def more
124
+ elements[2]
125
+ end
126
+ end
127
+
128
+ def _nt_expression
129
+ start_index = index
130
+ if node_cache[:expression].has_key?(index)
131
+ cached = node_cache[:expression][index]
132
+ if cached
133
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
134
+ @index = cached.interval.end
135
+ end
136
+ return cached
137
+ end
138
+
139
+ i0, s0 = index, []
140
+ s1, i1 = [], index
141
+ loop do
142
+ r2 = _nt_term
143
+ if r2
144
+ s1 << r2
145
+ else
146
+ break
147
+ end
148
+ end
149
+ if s1.empty?
150
+ @index = i1
151
+ r1 = nil
152
+ else
153
+ r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
154
+ end
155
+ s0 << r1
156
+ if r1
157
+ r3 = _nt_space
158
+ s0 << r3
159
+ if r3
160
+ r5 = _nt_more_expression
161
+ if r5
162
+ r4 = r5
163
+ else
164
+ r4 = instantiate_node(SyntaxNode,input, index...index)
165
+ end
166
+ s0 << r4
167
+ end
168
+ end
169
+ if s0.last
170
+ r0 = instantiate_node(ExpressionNode,input, i0...index, s0)
171
+ r0.extend(Expression0)
172
+ else
173
+ @index = i0
174
+ r0 = nil
175
+ end
176
+
177
+ node_cache[:expression][start_index] = r0
178
+
179
+ r0
180
+ end
181
+
182
+ module MoreExpression0
183
+ def space
184
+ elements[0]
185
+ end
186
+
187
+ def expression
188
+ elements[1]
189
+ end
190
+ end
191
+
192
+ def _nt_more_expression
193
+ start_index = index
194
+ if node_cache[:more_expression].has_key?(index)
195
+ cached = node_cache[:more_expression][index]
196
+ if cached
197
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
198
+ @index = cached.interval.end
199
+ end
200
+ return cached
201
+ end
202
+
203
+ i0, s0 = index, []
204
+ r1 = _nt_space
205
+ s0 << r1
206
+ if r1
207
+ r2 = _nt_expression
208
+ s0 << r2
209
+ end
210
+ if s0.last
211
+ r0 = instantiate_node(ExpressionNode,input, i0...index, s0)
212
+ r0.extend(MoreExpression0)
213
+ else
214
+ @index = i0
215
+ r0 = nil
216
+ end
217
+
218
+ node_cache[:more_expression][start_index] = r0
219
+
220
+ r0
221
+ end
222
+
223
+ module GroupedTerm0
224
+ end
225
+
226
+ def _nt_grouped_term
227
+ start_index = index
228
+ if node_cache[:grouped_term].has_key?(index)
229
+ cached = node_cache[:grouped_term][index]
230
+ if cached
231
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
232
+ @index = cached.interval.end
233
+ end
234
+ return cached
235
+ end
236
+
237
+ i0, s0 = index, []
238
+ if has_terminal?('(', false, index)
239
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
240
+ @index += 1
241
+ else
242
+ terminal_parse_failure('(')
243
+ r1 = nil
244
+ end
245
+ s0 << r1
246
+ if r1
247
+ s2, i2 = [], index
248
+ loop do
249
+ r3 = _nt_term
250
+ if r3
251
+ s2 << r3
252
+ else
253
+ break
254
+ end
255
+ end
256
+ if s2.empty?
257
+ @index = i2
258
+ r2 = nil
259
+ else
260
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
261
+ end
262
+ s0 << r2
263
+ if r2
264
+ if has_terminal?(')', false, index)
265
+ r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
266
+ @index += 1
267
+ else
268
+ terminal_parse_failure(')')
269
+ r4 = nil
270
+ end
271
+ s0 << r4
272
+ end
273
+ end
274
+ if s0.last
275
+ r0 = instantiate_node(GroupedTermNode,input, i0...index, s0)
276
+ r0.extend(GroupedTerm0)
277
+ else
278
+ @index = i0
279
+ r0 = nil
280
+ end
281
+
282
+ node_cache[:grouped_term][start_index] = r0
283
+
284
+ r0
285
+ end
286
+
287
+ module QuotedTerm0
288
+ def quoted_stuff
289
+ elements[1]
290
+ end
291
+
292
+ end
293
+
294
+ def _nt_quoted_term
295
+ start_index = index
296
+ if node_cache[:quoted_term].has_key?(index)
297
+ cached = node_cache[:quoted_term][index]
298
+ if cached
299
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
300
+ @index = cached.interval.end
301
+ end
302
+ return cached
303
+ end
304
+
305
+ i0, s0 = index, []
306
+ if has_terminal?('"', false, index)
307
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
308
+ @index += 1
309
+ else
310
+ terminal_parse_failure('"')
311
+ r1 = nil
312
+ end
313
+ s0 << r1
314
+ if r1
315
+ s2, i2 = [], index
316
+ loop do
317
+ if has_terminal?('\G[^\\"]', true, index)
318
+ r3 = true
319
+ @index += 1
320
+ else
321
+ r3 = nil
322
+ end
323
+ if r3
324
+ s2 << r3
325
+ else
326
+ break
327
+ end
328
+ end
329
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
330
+ s0 << r2
331
+ if r2
332
+ if has_terminal?('"', false, index)
333
+ r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
334
+ @index += 1
335
+ else
336
+ terminal_parse_failure('"')
337
+ r4 = nil
338
+ end
339
+ s0 << r4
340
+ end
341
+ end
342
+ if s0.last
343
+ r0 = instantiate_node(QuotedTermNode,input, i0...index, s0)
344
+ r0.extend(QuotedTerm0)
345
+ else
346
+ @index = i0
347
+ r0 = nil
348
+ end
349
+
350
+ node_cache[:quoted_term][start_index] = r0
351
+
352
+ r0
353
+ end
354
+
355
+ module Term0
356
+ def space1
357
+ elements[0]
358
+ end
359
+
360
+ def space2
361
+ elements[2]
362
+ end
363
+ end
364
+
365
+ def _nt_term
366
+ start_index = index
367
+ if node_cache[:term].has_key?(index)
368
+ cached = node_cache[:term][index]
369
+ if cached
370
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
371
+ @index = cached.interval.end
372
+ end
373
+ return cached
374
+ end
375
+
376
+ i0, s0 = index, []
377
+ r1 = _nt_space
378
+ s0 << r1
379
+ if r1
380
+ i2 = index
381
+ r3 = _nt_boolean_term
382
+ if r3
383
+ r2 = r3
384
+ else
385
+ r4 = _nt_quoted_term
386
+ if r4
387
+ r2 = r4
388
+ else
389
+ r5 = _nt_word
390
+ if r5
391
+ r2 = r5
392
+ else
393
+ r6 = _nt_grouped_term
394
+ if r6
395
+ r2 = r6
396
+ else
397
+ @index = i2
398
+ r2 = nil
399
+ end
400
+ end
401
+ end
402
+ end
403
+ s0 << r2
404
+ if r2
405
+ r7 = _nt_space
406
+ s0 << r7
407
+ end
408
+ end
409
+ if s0.last
410
+ r0 = instantiate_node(TermNode,input, i0...index, s0)
411
+ r0.extend(Term0)
412
+ else
413
+ @index = i0
414
+ r0 = nil
415
+ end
416
+
417
+ node_cache[:term][start_index] = r0
418
+
419
+ r0
420
+ end
421
+
422
+ def _nt_word
423
+ start_index = index
424
+ if node_cache[:word].has_key?(index)
425
+ cached = node_cache[:word][index]
426
+ if cached
427
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
428
+ @index = cached.interval.end
429
+ end
430
+ return cached
431
+ end
432
+
433
+ s0, i0 = [], index
434
+ loop do
435
+ r1 = _nt_word_char
436
+ if r1
437
+ s0 << r1
438
+ else
439
+ break
440
+ end
441
+ end
442
+ if s0.empty?
443
+ @index = i0
444
+ r0 = nil
445
+ else
446
+ r0 = instantiate_node(WordNode,input, i0...index, s0)
447
+ end
448
+
449
+ node_cache[:word][start_index] = r0
450
+
451
+ r0
452
+ end
453
+
454
+ def _nt_word_char
455
+ start_index = index
456
+ if node_cache[:word_char].has_key?(index)
457
+ cached = node_cache[:word_char][index]
458
+ if cached
459
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
460
+ @index = cached.interval.end
461
+ end
462
+ return cached
463
+ end
464
+
465
+ i0 = index
466
+ r1 = _nt_letter
467
+ if r1
468
+ r0 = r1
469
+ else
470
+ r2 = _nt_digit
471
+ if r2
472
+ r0 = r2
473
+ else
474
+ if has_terminal?('_', false, index)
475
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 1))
476
+ @index += 1
477
+ else
478
+ terminal_parse_failure('_')
479
+ r3 = nil
480
+ end
481
+ if r3
482
+ r0 = r3
483
+ else
484
+ if has_terminal?('&', false, index)
485
+ r4 = instantiate_node(SyntaxNode,input, index...(index + 1))
486
+ @index += 1
487
+ else
488
+ terminal_parse_failure('&')
489
+ r4 = nil
490
+ end
491
+ if r4
492
+ r0 = r4
493
+ else
494
+ if has_terminal?('.', false, index)
495
+ r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
496
+ @index += 1
497
+ else
498
+ terminal_parse_failure('.')
499
+ r5 = nil
500
+ end
501
+ if r5
502
+ r0 = r5
503
+ else
504
+ if has_terminal?('!', false, index)
505
+ r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
506
+ @index += 1
507
+ else
508
+ terminal_parse_failure('!')
509
+ r6 = nil
510
+ end
511
+ if r6
512
+ r0 = r6
513
+ else
514
+ @index = i0
515
+ r0 = nil
516
+ end
517
+ end
518
+ end
519
+ end
520
+ end
521
+ end
522
+
523
+ node_cache[:word_char][start_index] = r0
524
+
525
+ r0
526
+ end
527
+
528
+ def _nt_letter
529
+ start_index = index
530
+ if node_cache[:letter].has_key?(index)
531
+ cached = node_cache[:letter][index]
532
+ if cached
533
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
534
+ @index = cached.interval.end
535
+ end
536
+ return cached
537
+ end
538
+
539
+ if has_terminal?('\G[A-Za-z]', true, index)
540
+ r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
541
+ @index += 1
542
+ else
543
+ r0 = nil
544
+ end
545
+
546
+ node_cache[:letter][start_index] = r0
547
+
548
+ r0
549
+ end
550
+
551
+ def _nt_digit
552
+ start_index = index
553
+ if node_cache[:digit].has_key?(index)
554
+ cached = node_cache[:digit][index]
555
+ if cached
556
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
557
+ @index = cached.interval.end
558
+ end
559
+ return cached
560
+ end
561
+
562
+ if has_terminal?('\G[0-9]', true, index)
563
+ r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
564
+ @index += 1
565
+ else
566
+ r0 = nil
567
+ end
568
+
569
+ node_cache[:digit][start_index] = r0
570
+
571
+ r0
572
+ end
573
+
574
+ def _nt_space
575
+ start_index = index
576
+ if node_cache[:space].has_key?(index)
577
+ cached = node_cache[:space][index]
578
+ if cached
579
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
580
+ @index = cached.interval.end
581
+ end
582
+ return cached
583
+ end
584
+
585
+ s0, i0 = [], index
586
+ loop do
587
+ if has_terminal?('\G[\\s]', true, index)
588
+ r1 = true
589
+ @index += 1
590
+ else
591
+ r1 = nil
592
+ end
593
+ if r1
594
+ s0 << r1
595
+ else
596
+ break
597
+ end
598
+ end
599
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
600
+
601
+ node_cache[:space][start_index] = r0
602
+
603
+ r0
604
+ end
605
+
606
+ def _nt_boolean_term
607
+ start_index = index
608
+ if node_cache[:boolean_term].has_key?(index)
609
+ cached = node_cache[:boolean_term][index]
610
+ if cached
611
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
612
+ @index = cached.interval.end
613
+ end
614
+ return cached
615
+ end
616
+
617
+ i0 = index
618
+ r1 = _nt_and_term
619
+ if r1
620
+ r0 = r1
621
+ else
622
+ r2 = _nt_or_term
623
+ if r2
624
+ r0 = r2
625
+ else
626
+ @index = i0
627
+ r0 = nil
628
+ end
629
+ end
630
+
631
+ node_cache[:boolean_term][start_index] = r0
632
+
633
+ r0
634
+ end
635
+
636
+ module AndTerm0
637
+ def term
638
+ elements[1]
639
+ end
640
+ end
641
+
642
+ def _nt_and_term
643
+ start_index = index
644
+ if node_cache[:and_term].has_key?(index)
645
+ cached = node_cache[:and_term][index]
646
+ if cached
647
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
648
+ @index = cached.interval.end
649
+ end
650
+ return cached
651
+ end
652
+
653
+ i0, s0 = index, []
654
+ i1 = index
655
+ if has_terminal?('and', false, index)
656
+ r2 = instantiate_node(SyntaxNode,input, index...(index + 3))
657
+ @index += 3
658
+ else
659
+ terminal_parse_failure('and')
660
+ r2 = nil
661
+ end
662
+ if r2
663
+ r1 = r2
664
+ else
665
+ if has_terminal?('&&', false, index)
666
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
667
+ @index += 2
668
+ else
669
+ terminal_parse_failure('&&')
670
+ r3 = nil
671
+ end
672
+ if r3
673
+ r1 = r3
674
+ else
675
+ @index = i1
676
+ r1 = nil
677
+ end
678
+ end
679
+ s0 << r1
680
+ if r1
681
+ r4 = _nt_term
682
+ s0 << r4
683
+ end
684
+ if s0.last
685
+ r0 = instantiate_node(AndTermNode,input, i0...index, s0)
686
+ r0.extend(AndTerm0)
687
+ else
688
+ @index = i0
689
+ r0 = nil
690
+ end
691
+
692
+ node_cache[:and_term][start_index] = r0
693
+
694
+ r0
695
+ end
696
+
697
+ module OrTerm0
698
+ def term
699
+ elements[1]
700
+ end
701
+ end
702
+
703
+ def _nt_or_term
704
+ start_index = index
705
+ if node_cache[:or_term].has_key?(index)
706
+ cached = node_cache[:or_term][index]
707
+ if cached
708
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
709
+ @index = cached.interval.end
710
+ end
711
+ return cached
712
+ end
713
+
714
+ i0, s0 = index, []
715
+ i1 = index
716
+ if has_terminal?('or', false, index)
717
+ r2 = instantiate_node(SyntaxNode,input, index...(index + 2))
718
+ @index += 2
719
+ else
720
+ terminal_parse_failure('or')
721
+ r2 = nil
722
+ end
723
+ if r2
724
+ r1 = r2
725
+ else
726
+ if has_terminal?('||', false, index)
727
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
728
+ @index += 2
729
+ else
730
+ terminal_parse_failure('||')
731
+ r3 = nil
732
+ end
733
+ if r3
734
+ r1 = r3
735
+ else
736
+ @index = i1
737
+ r1 = nil
738
+ end
739
+ end
740
+ s0 << r1
741
+ if r1
742
+ r4 = _nt_term
743
+ s0 << r4
744
+ end
745
+ if s0.last
746
+ r0 = instantiate_node(OrTermNode,input, i0...index, s0)
747
+ r0.extend(OrTerm0)
748
+ else
749
+ @index = i0
750
+ r0 = nil
751
+ end
752
+
753
+ node_cache[:or_term][start_index] = r0
754
+
755
+ r0
756
+ end
757
+
758
+ end
759
+
760
+ class TQueryParser < Treetop::Runtime::CompiledParser
761
+ include TQuery
762
+ end