tsearch 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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