lunokhod 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ require 'lunokhod/verifier'
2
+ require 'stringio'
3
+
4
+ module Lunokhod
5
+ class ErrorReport
6
+ attr_reader :errors
7
+ attr_reader :surveys
8
+
9
+ def initialize(surveys)
10
+ @errors = []
11
+ @surveys = surveys
12
+ end
13
+
14
+ def errors?
15
+ !errors.empty?
16
+ end
17
+
18
+ def run
19
+ surveys.each do |s|
20
+ v = Verifier.new(s)
21
+ v.run { |error| errors << error }
22
+ end
23
+ end
24
+
25
+ def to_s
26
+ io = StringIO.new("")
27
+
28
+ errors.each do |e|
29
+ source = e.survey.source
30
+
31
+ if e.bad_tag?
32
+ io.puts "#{source}:#{e.at_fault.line}: #{e.node_type} #{e.key} is not defined"
33
+ elsif e.duplicate_question?
34
+ io.puts "#{source}: question tag #{e.key} is used multiple times"
35
+ e.at_fault.each do |n|
36
+ io.puts " at #{file}:#{n.line}"
37
+ end
38
+ end
39
+ end
40
+
41
+ io.string
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,208 @@
1
+ require 'lunokhod/ast'
2
+ require 'case'
3
+
4
+ module Lunokhod
5
+ class Parser < BasicObject
6
+ attr_reader :data
7
+ attr_reader :source
8
+ attr_reader :surveys
9
+
10
+ def initialize(data, source = '(input)')
11
+ @data = data
12
+ @source = source
13
+ @surveys = []
14
+ end
15
+
16
+ def parse
17
+ instance_eval(data, source)
18
+ end
19
+
20
+ def survey(name, options = {}, &block)
21
+ survey = Ast::Survey.new(sline, name, options)
22
+ survey.source = source
23
+ @surveys << survey
24
+
25
+ _with_unwind do
26
+ @current_node = survey
27
+ instance_eval(&block)
28
+ end
29
+ end
30
+
31
+ def translations(spec)
32
+ spec.each do |lang, path|
33
+ translation = Ast::Translation.new(sline, lang, path)
34
+ translation.parent = @current_node
35
+ @current_node.translations << translation
36
+ end
37
+ end
38
+
39
+ def dependency(options = {})
40
+ rule = options[:rule]
41
+ dependency = Ast::Dependency.new(sline, rule)
42
+
43
+ # Does this apply to a question? If not, we'll apply it to the current
44
+ # node.
45
+ if @current_question
46
+ dependency.parent = @current_question
47
+ @current_question.dependencies << dependency
48
+ else
49
+ dependency.parent = @current_node
50
+ @current_node.dependencies << dependency
51
+ end
52
+
53
+ dependency.parse_rule
54
+ @current_dependency = dependency
55
+ end
56
+
57
+ def validation(options = {})
58
+ rule = options[:rule]
59
+ validation = Ast::Validation.new(sline, rule)
60
+ validation.parent = @current_dependency
61
+ validation.parse_rule
62
+ @current_answer.validations << validation
63
+ @current_dependency = validation
64
+ end
65
+
66
+ def _grid(tag, text, &block)
67
+ grid = Ast::Grid.new(sline, tag.to_s, text)
68
+ grid.parent = @current_node
69
+ @current_node.questions << grid
70
+
71
+ _with_unwind do
72
+ @current_question = grid
73
+ @current_node = grid
74
+ instance_eval(&block)
75
+ end
76
+ end
77
+
78
+ def _repeater(tag, text, &block)
79
+ repeater = Ast::Repeater.new(sline, tag.to_s, text)
80
+ repeater.parent = @current_node
81
+ @current_node.questions << repeater
82
+
83
+ _with_unwind do
84
+ @current_question = nil
85
+ @current_node = repeater
86
+ instance_eval(&block)
87
+ end
88
+ end
89
+
90
+ def _label(tag, text, options = {})
91
+ question = Ast::Label.new(sline, text, tag.to_s, options)
92
+ question.parent = @current_node
93
+ @current_node.questions << question
94
+ @current_question = question
95
+ end
96
+
97
+ def _question(tag, text, options = {})
98
+ question = Ast::Question.new(sline, text, tag.to_s, options)
99
+ question.parent = @current_node
100
+ @current_node.questions << question
101
+ @current_question = question
102
+ end
103
+
104
+ def _answer(tag, t1, t2 = nil, options = {})
105
+ text, type, other, options = _disambiguate_answer(t1, t2, options)
106
+ answer = Ast::Answer.new(sline, text, type, other, tag.to_s, [], options)
107
+
108
+ answer.parent = @current_question
109
+ @current_question.answers << answer
110
+ @current_answer = answer
111
+ end
112
+
113
+ def _disambiguate_answer(t1, t2, options)
114
+ c = ::Case
115
+
116
+ case c[t1, t2]
117
+ when c[::String, ::NilClass]
118
+ [t1, nil, false, options] # text, type, other, options
119
+ when c[::String, ::Hash]
120
+ [t1, nil, false, t2]
121
+ when c[::String, ::Symbol]
122
+ [t1, t2, false, options]
123
+ when c[::Symbol, ::Hash]
124
+ [nil, t1, false, t2]
125
+ when c[::Symbol, ::NilClass]
126
+ [nil, t1, false, options]
127
+ when c[:other, ::Symbol]
128
+ [nil, t2, true, options]
129
+ else ::Kernel.raise "Unknown case #{[t1, t2].inspect}"
130
+ end
131
+ end
132
+
133
+ def _condition(label, *predicate)
134
+ condition = Ast::Condition.new(sline, label, predicate)
135
+ condition.parent = @current_dependency
136
+ condition.parse_condition
137
+ @current_dependency.conditions << condition
138
+ end
139
+
140
+ def _group(tag, name = nil, options = {}, &block)
141
+ group = Ast::Group.new(sline, tag.to_s, name, options)
142
+ group.parent = @current_node
143
+ @current_node.questions << group
144
+
145
+ _with_unwind do
146
+ @current_question = nil
147
+ @current_node = group
148
+ instance_eval(&block)
149
+ end
150
+ end
151
+
152
+ def _section(tag, name, options = {}, &block)
153
+ section = Ast::Section.new(sline, tag.to_s, name, options)
154
+ section.parent = @current_node
155
+ @current_node.sections << section
156
+
157
+ _with_unwind do
158
+ @current_node = section
159
+ instance_eval(&block)
160
+ end
161
+ end
162
+
163
+ def _with_unwind
164
+ old_dependency = @current_dependency
165
+ old_node = @current_node
166
+ old_question = @current_question
167
+ old_answer = @current_answer
168
+
169
+ yield
170
+
171
+ @current_dependency = old_dependency
172
+ @current_node = old_node
173
+ @current_question = old_question
174
+ @current_answer = old_answer
175
+ end
176
+
177
+ # Current line in the survey.
178
+ def sline
179
+ ::Kernel.caller(1).detect { |l| l.include?(source) }.split(':')[1]
180
+ end
181
+
182
+ # Intercept DSL keywords that may be suffixed with tags.
183
+ def method_missing(m, *args, &block)
184
+ case m
185
+ when /^q(?:uestion)?(?:_(.+))?$/
186
+ _question(*args.unshift($1), &block)
187
+ when /^a(?:nswer)?(?:_(.+))?$/
188
+ _answer(*args.unshift($1), &block)
189
+ when /^l(?:abel)?(?:_(.+))?$/
190
+ _label(*args.unshift($1), &block)
191
+ when /^g(?:roup)?(?:_(.+))?$/
192
+ _group(*args.unshift($1), &block)
193
+ when /^s(?:ection)?(?:_(.+))?$/
194
+ _section(*args.unshift($1), &block)
195
+ when /^grid(?:_(.+))?$/
196
+ _grid(*args.unshift($1), &block)
197
+ when /^repeater(?:_(.+))?$/
198
+ _repeater(*args.unshift($1), &block)
199
+ when /^dependency_.+$/
200
+ dependency(*args)
201
+ when /^condition(?:_(.+))$/
202
+ _condition(*args.unshift($1), &block)
203
+ else
204
+ super
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,19 @@
1
+ %% name = RuleParser
2
+ %% ctag_node = ast Tag(name)
3
+ %% conj_node = ast Conj(op)
4
+ %% phrase_node = ast Phrase(left, conj, right)
5
+
6
+ %%{
7
+ attr_reader :ast
8
+ }
9
+
10
+ lp = "("
11
+ rp = ")"
12
+ space = /\s/
13
+ term = < /\w+/ > ~ctag_node(text)
14
+ and = "and" ~conj_node("and")
15
+ or = "or" ~conj_node("or")
16
+ conj = and | or
17
+ phrase = (phrase:l space+ conj:c space+ phrase:r) ~phrase_node(l, c, r) | lp phrase rp | term
18
+
19
+ root = phrase:t { @ast = t }
@@ -0,0 +1,638 @@
1
+ class RuleParser
2
+ # :stopdoc:
3
+
4
+ # This is distinct from setup_parser so that a standalone parser
5
+ # can redefine #initialize and still have access to the proper
6
+ # parser setup code.
7
+ def initialize(str, debug=false)
8
+ setup_parser(str, debug)
9
+ end
10
+
11
+
12
+
13
+ # Prepares for parsing +str+. If you define a custom initialize you must
14
+ # call this method before #parse
15
+ def setup_parser(str, debug=false)
16
+ @string = str
17
+ @pos = 0
18
+ @memoizations = Hash.new { |h,k| h[k] = {} }
19
+ @result = nil
20
+ @failed_rule = nil
21
+ @failing_rule_offset = -1
22
+
23
+ setup_foreign_grammar
24
+ end
25
+
26
+ attr_reader :string
27
+ attr_reader :failing_rule_offset
28
+ attr_accessor :result, :pos
29
+
30
+
31
+ def current_column(target=pos)
32
+ if c = string.rindex("\n", target-1)
33
+ return target - c - 1
34
+ end
35
+
36
+ target + 1
37
+ end
38
+
39
+ def current_line(target=pos)
40
+ cur_offset = 0
41
+ cur_line = 0
42
+
43
+ string.each_line do |line|
44
+ cur_line += 1
45
+ cur_offset += line.size
46
+ return cur_line if cur_offset >= target
47
+ end
48
+
49
+ -1
50
+ end
51
+
52
+ def lines
53
+ lines = []
54
+ string.each_line { |l| lines << l }
55
+ lines
56
+ end
57
+
58
+
59
+
60
+ def get_text(start)
61
+ @string[start..@pos-1]
62
+ end
63
+
64
+ def show_pos
65
+ width = 10
66
+ if @pos < width
67
+ "#{@pos} (\"#{@string[0,@pos]}\" @ \"#{@string[@pos,width]}\")"
68
+ else
69
+ "#{@pos} (\"... #{@string[@pos - width, width]}\" @ \"#{@string[@pos,width]}\")"
70
+ end
71
+ end
72
+
73
+ def failure_info
74
+ l = current_line @failing_rule_offset
75
+ c = current_column @failing_rule_offset
76
+
77
+ if @failed_rule.kind_of? Symbol
78
+ info = self.class::Rules[@failed_rule]
79
+ "line #{l}, column #{c}: failed rule '#{info.name}' = '#{info.rendered}'"
80
+ else
81
+ "line #{l}, column #{c}: failed rule '#{@failed_rule}'"
82
+ end
83
+ end
84
+
85
+ def failure_caret
86
+ l = current_line @failing_rule_offset
87
+ c = current_column @failing_rule_offset
88
+
89
+ line = lines[l-1]
90
+ "#{line}\n#{' ' * (c - 1)}^"
91
+ end
92
+
93
+ def failure_character
94
+ l = current_line @failing_rule_offset
95
+ c = current_column @failing_rule_offset
96
+ lines[l-1][c-1, 1]
97
+ end
98
+
99
+ def failure_oneline
100
+ l = current_line @failing_rule_offset
101
+ c = current_column @failing_rule_offset
102
+
103
+ char = lines[l-1][c-1, 1]
104
+
105
+ if @failed_rule.kind_of? Symbol
106
+ info = self.class::Rules[@failed_rule]
107
+ "@#{l}:#{c} failed rule '#{info.name}', got '#{char}'"
108
+ else
109
+ "@#{l}:#{c} failed rule '#{@failed_rule}', got '#{char}'"
110
+ end
111
+ end
112
+
113
+ class ParseError < RuntimeError
114
+ end
115
+
116
+ def raise_error
117
+ raise ParseError, failure_oneline
118
+ end
119
+
120
+ def show_error(io=STDOUT)
121
+ error_pos = @failing_rule_offset
122
+ line_no = current_line(error_pos)
123
+ col_no = current_column(error_pos)
124
+
125
+ io.puts "On line #{line_no}, column #{col_no}:"
126
+
127
+ if @failed_rule.kind_of? Symbol
128
+ info = self.class::Rules[@failed_rule]
129
+ io.puts "Failed to match '#{info.rendered}' (rule '#{info.name}')"
130
+ else
131
+ io.puts "Failed to match rule '#{@failed_rule}'"
132
+ end
133
+
134
+ io.puts "Got: #{string[error_pos,1].inspect}"
135
+ line = lines[line_no-1]
136
+ io.puts "=> #{line}"
137
+ io.print(" " * (col_no + 3))
138
+ io.puts "^"
139
+ end
140
+
141
+ def set_failed_rule(name)
142
+ if @pos > @failing_rule_offset
143
+ @failed_rule = name
144
+ @failing_rule_offset = @pos
145
+ end
146
+ end
147
+
148
+ attr_reader :failed_rule
149
+
150
+ def match_string(str)
151
+ len = str.size
152
+ if @string[pos,len] == str
153
+ @pos += len
154
+ return str
155
+ end
156
+
157
+ return nil
158
+ end
159
+
160
+ def scan(reg)
161
+ if m = reg.match(@string[@pos..-1])
162
+ width = m.end(0)
163
+ @pos += width
164
+ return true
165
+ end
166
+
167
+ return nil
168
+ end
169
+
170
+ if "".respond_to? :getbyte
171
+ def get_byte
172
+ if @pos >= @string.size
173
+ return nil
174
+ end
175
+
176
+ s = @string.getbyte @pos
177
+ @pos += 1
178
+ s
179
+ end
180
+ else
181
+ def get_byte
182
+ if @pos >= @string.size
183
+ return nil
184
+ end
185
+
186
+ s = @string[@pos]
187
+ @pos += 1
188
+ s
189
+ end
190
+ end
191
+
192
+ def parse(rule=nil)
193
+ # We invoke the rules indirectly via apply
194
+ # instead of by just calling them as methods because
195
+ # if the rules use left recursion, apply needs to
196
+ # manage that.
197
+
198
+ if !rule
199
+ apply(:_root)
200
+ else
201
+ method = rule.gsub("-","_hyphen_")
202
+ apply :"_#{method}"
203
+ end
204
+ end
205
+
206
+ class MemoEntry
207
+ def initialize(ans, pos)
208
+ @ans = ans
209
+ @pos = pos
210
+ @result = nil
211
+ @set = false
212
+ @left_rec = false
213
+ end
214
+
215
+ attr_reader :ans, :pos, :result, :set
216
+ attr_accessor :left_rec
217
+
218
+ def move!(ans, pos, result)
219
+ @ans = ans
220
+ @pos = pos
221
+ @result = result
222
+ @set = true
223
+ @left_rec = false
224
+ end
225
+ end
226
+
227
+ def external_invoke(other, rule, *args)
228
+ old_pos = @pos
229
+ old_string = @string
230
+
231
+ @pos = other.pos
232
+ @string = other.string
233
+
234
+ begin
235
+ if val = __send__(rule, *args)
236
+ other.pos = @pos
237
+ other.result = @result
238
+ else
239
+ other.set_failed_rule "#{self.class}##{rule}"
240
+ end
241
+ val
242
+ ensure
243
+ @pos = old_pos
244
+ @string = old_string
245
+ end
246
+ end
247
+
248
+ def apply_with_args(rule, *args)
249
+ memo_key = [rule, args]
250
+ if m = @memoizations[memo_key][@pos]
251
+ @pos = m.pos
252
+ if !m.set
253
+ m.left_rec = true
254
+ return nil
255
+ end
256
+
257
+ @result = m.result
258
+
259
+ return m.ans
260
+ else
261
+ m = MemoEntry.new(nil, @pos)
262
+ @memoizations[memo_key][@pos] = m
263
+ start_pos = @pos
264
+
265
+ ans = __send__ rule, *args
266
+
267
+ lr = m.left_rec
268
+
269
+ m.move! ans, @pos, @result
270
+
271
+ # Don't bother trying to grow the left recursion
272
+ # if it's failing straight away (thus there is no seed)
273
+ if ans and lr
274
+ return grow_lr(rule, args, start_pos, m)
275
+ else
276
+ return ans
277
+ end
278
+
279
+ return ans
280
+ end
281
+ end
282
+
283
+ def apply(rule)
284
+ if m = @memoizations[rule][@pos]
285
+ @pos = m.pos
286
+ if !m.set
287
+ m.left_rec = true
288
+ return nil
289
+ end
290
+
291
+ @result = m.result
292
+
293
+ return m.ans
294
+ else
295
+ m = MemoEntry.new(nil, @pos)
296
+ @memoizations[rule][@pos] = m
297
+ start_pos = @pos
298
+
299
+ ans = __send__ rule
300
+
301
+ lr = m.left_rec
302
+
303
+ m.move! ans, @pos, @result
304
+
305
+ # Don't bother trying to grow the left recursion
306
+ # if it's failing straight away (thus there is no seed)
307
+ if ans and lr
308
+ return grow_lr(rule, nil, start_pos, m)
309
+ else
310
+ return ans
311
+ end
312
+
313
+ return ans
314
+ end
315
+ end
316
+
317
+ def grow_lr(rule, args, start_pos, m)
318
+ while true
319
+ @pos = start_pos
320
+ @result = m.result
321
+
322
+ if args
323
+ ans = __send__ rule, *args
324
+ else
325
+ ans = __send__ rule
326
+ end
327
+ return nil unless ans
328
+
329
+ break if @pos <= m.pos
330
+
331
+ m.move! ans, @pos, @result
332
+ end
333
+
334
+ @result = m.result
335
+ @pos = m.pos
336
+ return m.ans
337
+ end
338
+
339
+ class RuleInfo
340
+ def initialize(name, rendered)
341
+ @name = name
342
+ @rendered = rendered
343
+ end
344
+
345
+ attr_reader :name, :rendered
346
+ end
347
+
348
+ def self.rule_info(name, rendered)
349
+ RuleInfo.new(name, rendered)
350
+ end
351
+
352
+
353
+ # :startdoc:
354
+
355
+
356
+ attr_reader :ast
357
+
358
+
359
+ # :stopdoc:
360
+
361
+ module AST
362
+ class Node; end
363
+ class Conj < Node
364
+ def initialize(op)
365
+ @op = op
366
+ end
367
+ attr_reader :op
368
+ end
369
+ class Tag < Node
370
+ def initialize(name)
371
+ @name = name
372
+ end
373
+ attr_reader :name
374
+ end
375
+ class Phrase < Node
376
+ def initialize(left, conj, right)
377
+ @left = left
378
+ @conj = conj
379
+ @right = right
380
+ end
381
+ attr_reader :left
382
+ attr_reader :conj
383
+ attr_reader :right
384
+ end
385
+ end
386
+ def conj_node(op)
387
+ AST::Conj.new(op)
388
+ end
389
+ def ctag_node(name)
390
+ AST::Tag.new(name)
391
+ end
392
+ def phrase_node(left, conj, right)
393
+ AST::Phrase.new(left, conj, right)
394
+ end
395
+ def setup_foreign_grammar; end
396
+
397
+ # lp = "("
398
+ def _lp
399
+ _tmp = match_string("(")
400
+ set_failed_rule :_lp unless _tmp
401
+ return _tmp
402
+ end
403
+
404
+ # rp = ")"
405
+ def _rp
406
+ _tmp = match_string(")")
407
+ set_failed_rule :_rp unless _tmp
408
+ return _tmp
409
+ end
410
+
411
+ # space = /\s/
412
+ def _space
413
+ _tmp = scan(/\A(?-mix:\s)/)
414
+ set_failed_rule :_space unless _tmp
415
+ return _tmp
416
+ end
417
+
418
+ # term = < /\w+/ > {ctag_node(text)}
419
+ def _term
420
+
421
+ _save = self.pos
422
+ while true # sequence
423
+ _text_start = self.pos
424
+ _tmp = scan(/\A(?-mix:\w+)/)
425
+ if _tmp
426
+ text = get_text(_text_start)
427
+ end
428
+ unless _tmp
429
+ self.pos = _save
430
+ break
431
+ end
432
+ @result = begin; ctag_node(text); end
433
+ _tmp = true
434
+ unless _tmp
435
+ self.pos = _save
436
+ end
437
+ break
438
+ end # end sequence
439
+
440
+ set_failed_rule :_term unless _tmp
441
+ return _tmp
442
+ end
443
+
444
+ # and = "and" {conj_node("and")}
445
+ def _and
446
+
447
+ _save = self.pos
448
+ while true # sequence
449
+ _tmp = match_string("and")
450
+ unless _tmp
451
+ self.pos = _save
452
+ break
453
+ end
454
+ @result = begin; conj_node("and"); end
455
+ _tmp = true
456
+ unless _tmp
457
+ self.pos = _save
458
+ end
459
+ break
460
+ end # end sequence
461
+
462
+ set_failed_rule :_and unless _tmp
463
+ return _tmp
464
+ end
465
+
466
+ # or = "or" {conj_node("or")}
467
+ def _or
468
+
469
+ _save = self.pos
470
+ while true # sequence
471
+ _tmp = match_string("or")
472
+ unless _tmp
473
+ self.pos = _save
474
+ break
475
+ end
476
+ @result = begin; conj_node("or"); end
477
+ _tmp = true
478
+ unless _tmp
479
+ self.pos = _save
480
+ end
481
+ break
482
+ end # end sequence
483
+
484
+ set_failed_rule :_or unless _tmp
485
+ return _tmp
486
+ end
487
+
488
+ # conj = (and | or)
489
+ def _conj
490
+
491
+ _save = self.pos
492
+ while true # choice
493
+ _tmp = apply(:_and)
494
+ break if _tmp
495
+ self.pos = _save
496
+ _tmp = apply(:_or)
497
+ break if _tmp
498
+ self.pos = _save
499
+ break
500
+ end # end choice
501
+
502
+ set_failed_rule :_conj unless _tmp
503
+ return _tmp
504
+ end
505
+
506
+ # phrase = (phrase:l space+ conj:c space+ phrase:r {phrase_node(l, c, r)} | lp phrase rp | term)
507
+ def _phrase
508
+
509
+ _save = self.pos
510
+ while true # choice
511
+
512
+ _save1 = self.pos
513
+ while true # sequence
514
+ _tmp = apply(:_phrase)
515
+ l = @result
516
+ unless _tmp
517
+ self.pos = _save1
518
+ break
519
+ end
520
+ _save2 = self.pos
521
+ _tmp = apply(:_space)
522
+ if _tmp
523
+ while true
524
+ _tmp = apply(:_space)
525
+ break unless _tmp
526
+ end
527
+ _tmp = true
528
+ else
529
+ self.pos = _save2
530
+ end
531
+ unless _tmp
532
+ self.pos = _save1
533
+ break
534
+ end
535
+ _tmp = apply(:_conj)
536
+ c = @result
537
+ unless _tmp
538
+ self.pos = _save1
539
+ break
540
+ end
541
+ _save3 = self.pos
542
+ _tmp = apply(:_space)
543
+ if _tmp
544
+ while true
545
+ _tmp = apply(:_space)
546
+ break unless _tmp
547
+ end
548
+ _tmp = true
549
+ else
550
+ self.pos = _save3
551
+ end
552
+ unless _tmp
553
+ self.pos = _save1
554
+ break
555
+ end
556
+ _tmp = apply(:_phrase)
557
+ r = @result
558
+ unless _tmp
559
+ self.pos = _save1
560
+ break
561
+ end
562
+ @result = begin; phrase_node(l, c, r); end
563
+ _tmp = true
564
+ unless _tmp
565
+ self.pos = _save1
566
+ end
567
+ break
568
+ end # end sequence
569
+
570
+ break if _tmp
571
+ self.pos = _save
572
+
573
+ _save4 = self.pos
574
+ while true # sequence
575
+ _tmp = apply(:_lp)
576
+ unless _tmp
577
+ self.pos = _save4
578
+ break
579
+ end
580
+ _tmp = apply(:_phrase)
581
+ unless _tmp
582
+ self.pos = _save4
583
+ break
584
+ end
585
+ _tmp = apply(:_rp)
586
+ unless _tmp
587
+ self.pos = _save4
588
+ end
589
+ break
590
+ end # end sequence
591
+
592
+ break if _tmp
593
+ self.pos = _save
594
+ _tmp = apply(:_term)
595
+ break if _tmp
596
+ self.pos = _save
597
+ break
598
+ end # end choice
599
+
600
+ set_failed_rule :_phrase unless _tmp
601
+ return _tmp
602
+ end
603
+
604
+ # root = phrase:t { @ast = t }
605
+ def _root
606
+
607
+ _save = self.pos
608
+ while true # sequence
609
+ _tmp = apply(:_phrase)
610
+ t = @result
611
+ unless _tmp
612
+ self.pos = _save
613
+ break
614
+ end
615
+ @result = begin; @ast = t ; end
616
+ _tmp = true
617
+ unless _tmp
618
+ self.pos = _save
619
+ end
620
+ break
621
+ end # end sequence
622
+
623
+ set_failed_rule :_root unless _tmp
624
+ return _tmp
625
+ end
626
+
627
+ Rules = {}
628
+ Rules[:_lp] = rule_info("lp", "\"(\"")
629
+ Rules[:_rp] = rule_info("rp", "\")\"")
630
+ Rules[:_space] = rule_info("space", "/\\s/")
631
+ Rules[:_term] = rule_info("term", "< /\\w+/ > {ctag_node(text)}")
632
+ Rules[:_and] = rule_info("and", "\"and\" {conj_node(\"and\")}")
633
+ Rules[:_or] = rule_info("or", "\"or\" {conj_node(\"or\")}")
634
+ Rules[:_conj] = rule_info("conj", "(and | or)")
635
+ Rules[:_phrase] = rule_info("phrase", "(phrase:l space+ conj:c space+ phrase:r {phrase_node(l, c, r)} | lp phrase rp | term)")
636
+ Rules[:_root] = rule_info("root", "phrase:t { @ast = t }")
637
+ # :startdoc:
638
+ end