sexp_processor 4.11.0 → 4.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1100 @@
1
+ class Sexp #:nodoc:
2
+ ##
3
+ # Verifies that +pattern+ is a Matcher and then dispatches to its
4
+ # #=~ method.
5
+ #
6
+ # See Matcher.=~
7
+
8
+ def =~ pattern
9
+ raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern
10
+ pattern =~ self
11
+ end
12
+
13
+ ##
14
+ # Verifies that +pattern+ is a Matcher and then dispatches to its
15
+ # #satisfy? method.
16
+ #
17
+ # TODO: rename match?
18
+
19
+ def satisfy? pattern
20
+ raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern
21
+ pattern.satisfy? self
22
+ end
23
+
24
+ ##
25
+ # Verifies that +pattern+ is a Matcher and then dispatches to its #/
26
+ # method.
27
+ #
28
+ # TODO: rename grep? match_all ? find_all ?
29
+
30
+ def / pattern
31
+ raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern
32
+ pattern / self
33
+ end
34
+
35
+ ##
36
+ # Recursively searches for the +pattern+ yielding the matches.
37
+
38
+ def search_each pattern, &block # TODO: rename to grep?
39
+ raise ArgumentError, "Needs a pattern" unless pattern.kind_of? Matcher
40
+
41
+ return enum_for(:search_each, pattern) unless block_given?
42
+
43
+ if pattern.satisfy? self then
44
+ yield self
45
+ end
46
+
47
+ self.each_sexp do |subset|
48
+ subset.search_each pattern, &block
49
+ end
50
+ end
51
+
52
+ ##
53
+ # Recursively searches for the +pattern+ yielding each match, and
54
+ # replacing it with the result of the block.
55
+ #
56
+
57
+ def replace_sexp pattern, &block # TODO: rename to gsub?
58
+ raise ArgumentError, "Needs a pattern" unless pattern.kind_of? Matcher
59
+
60
+ return yield self if pattern.satisfy? self
61
+
62
+ # TODO: Needs #new_from(*new_body) to copy file/line/comment
63
+ self.class.new(*self.map { |subset|
64
+ case subset
65
+ when Sexp then
66
+ subset.replace_sexp pattern, &block
67
+ else
68
+ subset
69
+ end
70
+ })
71
+ end
72
+
73
+ ##
74
+ # Matches an S-Expression.
75
+ #
76
+ # See Matcher for examples.
77
+
78
+ def self.q *args
79
+ Matcher.new(*args)
80
+ end
81
+
82
+ def self.s *args
83
+ where = caller.first.split(/:/, 3).first(2).join ":"
84
+ warn "DEPRECATED: use Sexp.q(...) instead. From %s" % [where]
85
+ q(*args)
86
+ end
87
+
88
+ ##
89
+ # Matches any single item.
90
+ #
91
+ # See Wild for examples.
92
+
93
+ def self._
94
+ Wild.new
95
+ end
96
+
97
+ # TODO: reorder factory methods and classes to match
98
+
99
+ ##
100
+ # Matches all remaining input.
101
+ #
102
+ # See Remaining for examples.
103
+
104
+ def self.___
105
+ Remaining.new
106
+ end
107
+
108
+ ##
109
+ # Matches an expression or any expression that includes the child.
110
+ #
111
+ # See Include for examples.
112
+
113
+ def self.include child # TODO: rename, name is generic ruby
114
+ Include.new(child)
115
+ end
116
+
117
+ ##
118
+ # Matches any atom.
119
+ #
120
+ # See Atom for examples.
121
+
122
+ def self.atom
123
+ Atom.new
124
+ end
125
+
126
+ ##
127
+ # Matches when any of the sub-expressions match.
128
+ #
129
+ # This is also available via Matcher#|.
130
+ #
131
+ # See Any for examples.
132
+
133
+ def self.any *args
134
+ Any.new(*args)
135
+ end
136
+
137
+ ##
138
+ # Matches only when all sub-expressions match.
139
+ #
140
+ # This is also available via Matcher#&.
141
+ #
142
+ # See All for examples.
143
+
144
+ def self.all *args
145
+ All.new(*args)
146
+ end
147
+
148
+ ##
149
+ # Matches when sub-expression does not match.
150
+ #
151
+ # This is also available via Matcher#-@.
152
+ #
153
+ # See Not for examples.
154
+
155
+ def self.not? arg
156
+ Not.new arg
157
+ end
158
+
159
+ class << self
160
+ alias - not?
161
+ end
162
+
163
+ # TODO: add Sibling factory method?
164
+
165
+ ##
166
+ # Matches anything that has a child matching the sub-expression.
167
+ #
168
+ # See Child for examples.
169
+
170
+ def self.child child
171
+ Child.new child
172
+ end
173
+
174
+ ##
175
+ # Matches anything having the same sexp_type, which is the first
176
+ # value in a Sexp.
177
+ #
178
+ # See Type for examples.
179
+
180
+ def self.t name
181
+ Type.new name
182
+ end
183
+
184
+ ##
185
+ # Matches any atom who's string representation matches the patterns
186
+ # passed in.
187
+ #
188
+ # See Pattern for examples.
189
+
190
+ def self.m *values
191
+ res = values.map { |value|
192
+ case value
193
+ when Regexp then
194
+ value
195
+ else
196
+ re = Regexp.escape value.to_s
197
+ Regexp.new "\\A%s\\Z" % re
198
+ end
199
+ }
200
+ Pattern.new Regexp.union(*res)
201
+ end
202
+
203
+ ##
204
+ # Matches an atom of the specified +klass+ (or module).
205
+ #
206
+ # See Pattern for examples.
207
+
208
+ def self.k klass
209
+ Klass.new klass
210
+ end
211
+
212
+ ##
213
+ # Defines a family of objects that can be used to match sexps to
214
+ # certain types of patterns, much like regexps can be used on
215
+ # strings. Generally you won't use this class directly.
216
+ #
217
+ # You would normally create a matcher using the top-level #s method,
218
+ # but with a block, calling into the Sexp factory methods. For example:
219
+ #
220
+ # s{ s(:class, m(/^Test/), _, ___) }
221
+ #
222
+ # This creates a matcher for classes whose names start with "Test".
223
+ # It uses Sexp.m to create a Sexp::Matcher::Pattern matcher, Sexp._
224
+ # to create a Sexp::Matcher::Wild matcher, and Sexp.___ to create a
225
+ # Sexp::Matcher::Remaining matcher. It works like this:
226
+ #
227
+ # s{ # start to create a pattern
228
+ # s( # create a sexp matcher
229
+ # :class. # for class nodes
230
+ # m(/^Test/), # matching name slots that start with "Test"
231
+ # _, # any superclass value
232
+ # ___ # and whatever is in the class
233
+ # )
234
+ # }
235
+ #
236
+ # Then you can use that with #=~, #/, Sexp#replace_sexp, and others.
237
+ #
238
+ # For more examples, see the various Sexp class methods, the examples,
239
+ # and the tests supplied with Sexp.
240
+ #
241
+ # * For pattern creation, see factory methods: Sexp::_, Sexp::___, etc.
242
+ # * For matching returning truthy/falsey results, see Sexp#=~.
243
+ # * For case expressions, see Matcher#===.
244
+ # * For getting all subtree matches, see Sexp#/.
245
+ #
246
+ # If rdoc didn't suck, these would all be links.
247
+
248
+ class Matcher < Sexp
249
+ ##
250
+ # Should #=~ match sub-trees?
251
+
252
+ def self.match_subs?
253
+ @@match_subs
254
+ end
255
+
256
+ ##
257
+ # Setter for +match_subs?+.
258
+
259
+ def self.match_subs= o
260
+ @@match_subs = o
261
+ end
262
+
263
+ self.match_subs = true
264
+
265
+ ##
266
+ # Does this matcher actually match +o+? Returns falsey if +o+ is
267
+ # not a Sexp or if any sub-tree of +o+ is not satisfied by or
268
+ # equal to its corresponding sub-matcher.
269
+ #
270
+ #--
271
+ # TODO: push this up to Sexp and make this the workhorse
272
+ # TODO: do the same with ===/satisfy?
273
+
274
+ def satisfy? o
275
+ return unless o.kind_of?(Sexp) &&
276
+ (length == o.length || Matcher === last && last.greedy?)
277
+
278
+ each_with_index.all? { |child, i|
279
+ sexp = o.at i
280
+ if Sexp === child then # TODO: when will this NOT be a matcher?
281
+ sexp = o.sexp_body i if child.respond_to?(:greedy?) && child.greedy?
282
+ child.satisfy? sexp
283
+ else
284
+ child == sexp
285
+ end
286
+ }
287
+ end
288
+
289
+ ##
290
+ # Tree equivalent to String#=~, returns true if +self+ matches
291
+ # +sexp+ as a whole or in a sub-tree (if +match_subs?+).
292
+ #
293
+ # TODO: maybe this should NOT be aliased to === ?
294
+ #
295
+ # TODO: example
296
+
297
+ def =~ sexp
298
+ raise ArgumentError, "Can't both be matchers: %p" % [sexp] if Matcher === sexp
299
+
300
+ self.satisfy?(sexp) ||
301
+ (self.class.match_subs? && sexp.each_sexp.any? { |sub| self =~ sub })
302
+ end
303
+
304
+ alias === =~ # TODO?: alias === satisfy?
305
+
306
+ ##
307
+ # Searches through +sexp+ for all sub-trees that match this
308
+ # matcher and returns a MatchCollection for each match.
309
+ #
310
+ # TODO: redirect?
311
+ # Example:
312
+ # Q{ s(:b) } / s(:a, s(:b)) => [s(:b)]
313
+
314
+ def / sexp
315
+ raise ArgumentError, "can't both be matchers" if Matcher === sexp
316
+
317
+ # TODO: move search_each into matcher?
318
+ MatchCollection.new sexp.search_each(self).to_a
319
+ end
320
+
321
+ ##
322
+ # Combines the Matcher with another Matcher, the resulting one will
323
+ # be satisfied if either Matcher would be satisfied.
324
+ #
325
+ # TODO: redirect
326
+ # Example:
327
+ # s(:a) | s(:b)
328
+
329
+ def | other
330
+ Any.new self, other
331
+ end
332
+
333
+ ##
334
+ # Combines the Matcher with another Matcher, the resulting one will
335
+ # be satisfied only if both Matchers would be satisfied.
336
+ #
337
+ # TODO: redirect
338
+ # Example:
339
+ # t(:a) & include(:b)
340
+
341
+ def & other
342
+ All.new self, other
343
+ end
344
+
345
+ ##
346
+ # Returns a Matcher that matches whenever this Matcher would not have matched
347
+ #
348
+ # Example:
349
+ # -s(:a)
350
+
351
+ def -@
352
+ Not.new self
353
+ end
354
+
355
+ ##
356
+ # Returns a Matcher that matches if this has a sibling +o+
357
+ #
358
+ # Example:
359
+ # s(:a) >> s(:b)
360
+
361
+ def >> other
362
+ Sibling.new self, other
363
+ end
364
+
365
+ ##
366
+ # Is this matcher greedy? Defaults to false.
367
+
368
+ def greedy?
369
+ false
370
+ end
371
+
372
+ def inspect # :nodoc:
373
+ s = super
374
+ s[0] = "q"
375
+ s
376
+ end
377
+
378
+ def pretty_print q # :nodoc:
379
+ q.group 1, "q(", ")" do
380
+ q.seplist self do |v|
381
+ q.pp v
382
+ end
383
+ end
384
+ end
385
+
386
+ ##
387
+ # Parse a lispy string representation of a matcher into a Matcher.
388
+ # See +Parser+.
389
+
390
+ def self.parse s
391
+ Parser.new(s).parse
392
+ end
393
+
394
+ ##
395
+ # Converts from a lispy string to Sexp matchers in a safe manner.
396
+ #
397
+ # "(a 42 _ (c) [t x] ___)" => s{ s(:a, 42, _, s(:c), t(:x), ___) }
398
+
399
+ class Parser
400
+
401
+ ##
402
+ # The stream of tokens to parse. See #lex.
403
+
404
+ attr_accessor :tokens
405
+
406
+ ##
407
+ # Create a new Parser instance on +s+
408
+
409
+ def initialize s
410
+ self.tokens = lex s
411
+ end
412
+
413
+ ##
414
+ # Converts +s+ into a stream of tokens and adds them to +tokens+.
415
+
416
+ def lex s
417
+ s.scan %r%[()\[\]]|\"[^"]*\"|/[^/]*/|:?[\w?!=~-]+%
418
+ end
419
+
420
+ ##
421
+ # Returns the next token and removes it from the stream or raises if empty.
422
+
423
+ def next_token
424
+ raise SyntaxError, "unbalanced input" if tokens.empty?
425
+ tokens.shift
426
+ end
427
+
428
+ ##
429
+ # Returns the next token without removing it from the stream.
430
+
431
+ def peek_token
432
+ tokens.first
433
+ end
434
+
435
+ ##
436
+ # Parses tokens and returns a +Matcher+ instance.
437
+
438
+ def parse
439
+ result = parse_sexp until tokens.empty?
440
+ result
441
+ end
442
+
443
+ ##
444
+ # Parses a string into a sexp matcher:
445
+ #
446
+ # SEXP : "(" SEXP:args* ")" => Sexp.q(*args)
447
+ # | "[" CMD:cmd sexp:args* "]" => Sexp.cmd(*args)
448
+ # | "nil" => nil
449
+ # | /\d+/:n => n.to_i
450
+ # | "___" => Sexp.___
451
+ # | "_" => Sexp._
452
+ # | /^\/(.*)\/$/:re => Regexp.new re[0]
453
+ # | /^"(.*)"$/:s => String.new s[0]
454
+ # | UP_NAME:name => Object.const_get name
455
+ # | NAME:name => name.to_sym
456
+ # UP_NAME: /[A-Z]\w*/
457
+ # NAME : /:?[\w?!=~-]+/
458
+ # CMD : t | k | m | atom | not? | - | any | child | include
459
+
460
+ def parse_sexp
461
+ token = next_token
462
+
463
+ case token
464
+ when "(" then
465
+ parse_list
466
+ when "[" then
467
+ parse_cmd
468
+ when "nil" then
469
+ nil
470
+ when /^\d+$/ then
471
+ token.to_i
472
+ when "___" then
473
+ Sexp.___
474
+ when "_" then
475
+ Sexp._
476
+ when %r%^/(.*)/$% then
477
+ re = $1
478
+ raise SyntaxError, "Not allowed: /%p/" % [re] unless
479
+ re =~ /\A([\w()|.*+^$]+)\z/
480
+ Regexp.new re
481
+ when /^"(.*)"$/ then
482
+ $1
483
+ when /^([A-Z]\w*)$/ then
484
+ Object.const_get $1
485
+ when /^:?([\w?!=~-]+)$/ then
486
+ $1.to_sym
487
+ else
488
+ raise SyntaxError, "unhandled token: %p" % [token]
489
+ end
490
+ end
491
+
492
+ ##
493
+ # Parses a balanced list of expressions and returns the
494
+ # equivalent matcher.
495
+
496
+ def parse_list
497
+ result = []
498
+
499
+ result << parse_sexp while peek_token && peek_token != ")"
500
+ next_token # pop off ")"
501
+
502
+ Sexp.q(*result)
503
+ end
504
+
505
+ ##
506
+ # A collection of allowed commands to convert into matchers.
507
+
508
+ ALLOWED = [:t, :m, :k, :atom, :not?, :-, :any, :child, :include].freeze
509
+
510
+ ##
511
+ # Parses a balanced command. A command is denoted by square
512
+ # brackets and must conform to a whitelisted set of allowed
513
+ # commands (see +ALLOWED+).
514
+
515
+ def parse_cmd
516
+ args = []
517
+ args << parse_sexp while peek_token && peek_token != "]"
518
+ next_token # pop off "]"
519
+
520
+ cmd = args.shift
521
+ args = Sexp.q(*args)
522
+
523
+ raise SyntaxError, "bad cmd: %p" % [cmd] unless ALLOWED.include? cmd
524
+
525
+ result = Sexp.send cmd, *args
526
+
527
+ result
528
+ end
529
+ end # class Parser
530
+ end # class Matcher
531
+
532
+ ##
533
+ # Matches any single item.
534
+ #
535
+ # examples:
536
+ #
537
+ # s(:a) / s{ _ } #=> [s(:a)]
538
+ # s(:a, s(s(:b))) / s{ s(_) } #=> [s(s(:b))]
539
+
540
+ class Wild < Matcher
541
+ ##
542
+ # Matches any single element.
543
+
544
+ def satisfy? o
545
+ true
546
+ end
547
+
548
+ def inspect # :nodoc:
549
+ "_"
550
+ end
551
+
552
+ def pretty_print q # :nodoc:
553
+ q.text "_"
554
+ end
555
+ end
556
+
557
+ ##
558
+ # Matches all remaining input. If remaining comes before any other
559
+ # matchers, they will be ignored.
560
+ #
561
+ # examples:
562
+ #
563
+ # s(:a) / s{ s(:a, ___ ) } #=> [s(:a)]
564
+ # s(:a, :b, :c) / s{ s(:a, ___ ) } #=> [s(:a, :b, :c)]
565
+
566
+ class Remaining < Matcher
567
+ ##
568
+ # Always satisfied once this is reached. Think of it as a var arg.
569
+
570
+ def satisfy? o
571
+ true
572
+ end
573
+
574
+ def greedy?
575
+ true
576
+ end
577
+
578
+ def inspect # :nodoc:
579
+ "___"
580
+ end
581
+
582
+ def pretty_print q # :nodoc:
583
+ q.text "___"
584
+ end
585
+ end
586
+
587
+ ##
588
+ # Matches when any of the sub-expressions match.
589
+ #
590
+ # This is also available via Matcher#|.
591
+ #
592
+ # examples:
593
+ #
594
+ # s(:a) / s{ any(s(:a), s(:b)) } #=> [s(:a)]
595
+ # s(:a) / s{ s(:a) | s(:b) } #=> [s(:a)] # same thing via |
596
+ # s(:a) / s{ any(s(:b), s(:c)) } #=> []
597
+
598
+ class Any < Matcher
599
+ ##
600
+ # The collection of sub-matchers to match against.
601
+
602
+ attr_reader :options
603
+
604
+ ##
605
+ # Create an Any matcher which will match any of the +options+.
606
+
607
+ def initialize *options
608
+ @options = options
609
+ end
610
+
611
+ ##
612
+ # Satisfied when any sub expressions match +o+
613
+
614
+ def satisfy? o
615
+ options.any? { |exp|
616
+ Sexp === exp && exp.satisfy?(o) || exp == o
617
+ }
618
+ end
619
+
620
+ def == o # :nodoc:
621
+ super && self.options == o.options
622
+ end
623
+
624
+ def inspect # :nodoc:
625
+ options.map(&:inspect).join(" | ")
626
+ end
627
+
628
+ def pretty_print q # :nodoc:
629
+ q.group 1, "any(", ")" do
630
+ q.seplist options do |v|
631
+ q.pp v
632
+ end
633
+ end
634
+ end
635
+ end
636
+
637
+ ##
638
+ # Matches only when all sub-expressions match.
639
+ #
640
+ # This is also available via Matcher#&.
641
+ #
642
+ # examples:
643
+ #
644
+ # s(:a) / s{ all(s(:a), s(:b)) } #=> []
645
+ # s(:a, :b) / s{ t(:a) & include(:b)) } #=> [s(:a, :b)]
646
+
647
+ class All < Matcher
648
+ ##
649
+ # The collection of sub-matchers to match against.
650
+
651
+ attr_reader :options
652
+
653
+ ##
654
+ # Create an All matcher which will match all of the +options+.
655
+
656
+ def initialize *options
657
+ @options = options
658
+ end
659
+
660
+ ##
661
+ # Satisfied when all sub expressions match +o+
662
+
663
+ def satisfy? o
664
+ options.all? { |exp|
665
+ exp.kind_of?(Sexp) ? exp.satisfy?(o) : exp == o
666
+ }
667
+ end
668
+
669
+ def == o # :nodoc:
670
+ super && self.options == o.options
671
+ end
672
+
673
+ def inspect # :nodoc:
674
+ options.map(&:inspect).join(" & ")
675
+ end
676
+
677
+ def pretty_print q # :nodoc:
678
+ q.group 1, "all(", ")" do
679
+ q.seplist options do |v|
680
+ q.pp v
681
+ end
682
+ end
683
+ end
684
+ end
685
+
686
+ ##
687
+ # Matches when sub-expression does not match.
688
+ #
689
+ # This is also available via Matcher#-@.
690
+ #
691
+ # examples:
692
+ #
693
+ # s(:a) / s{ not?(s(:b)) } #=> [s(:a)]
694
+ # s(:a) / s{ -s(:b) } #=> [s(:a)]
695
+ # s(:a) / s{ s(not? :a) } #=> []
696
+
697
+ class Not < Matcher
698
+
699
+ ##
700
+ # The value to negate in the match.
701
+
702
+ attr_reader :value
703
+
704
+ ##
705
+ # Creates a Matcher which will match any Sexp that does not match the +value+
706
+
707
+ def initialize value
708
+ @value = value
709
+ end
710
+
711
+ def == o # :nodoc:
712
+ super && self.value == o.value
713
+ end
714
+
715
+ ##
716
+ # Satisfied if a +o+ does not match the +value+
717
+
718
+ def satisfy? o
719
+ !(value.kind_of?(Sexp) ? value.satisfy?(o) : value == o)
720
+ end
721
+
722
+ def inspect # :nodoc:
723
+ "not?(%p)" % [value]
724
+ end
725
+
726
+ def pretty_print q # :nodoc:
727
+ q.group 1, "not?(", ")" do
728
+ q.pp value
729
+ end
730
+ end
731
+ end
732
+
733
+ ##
734
+ # Matches anything that has a child matching the sub-expression
735
+ #
736
+ # example:
737
+ #
738
+ # s(s(s(s(s(:a))))) / s{ child(s(:a)) } #=> [s(s(s(s(s(:a))))),
739
+ # s(s(s(s(:a)))),
740
+ # s(s(s(:a))),
741
+ # s(s(:a)),
742
+ # s(:a)]
743
+
744
+ class Child < Matcher
745
+ ##
746
+ # The child to match.
747
+
748
+ attr_reader :child
749
+
750
+ ##
751
+ # Create a Child matcher which will match anything having a
752
+ # descendant matching +child+.
753
+
754
+ def initialize child
755
+ @child = child
756
+ end
757
+
758
+ ##
759
+ # Satisfied if matches +child+ or +o+ has a descendant matching
760
+ # +child+.
761
+
762
+ def satisfy? o
763
+ child.satisfy?(o) ||
764
+ (o.kind_of?(Sexp) && o.search_each(child).any?)
765
+ end
766
+
767
+ def == o # :nodoc:
768
+ super && self.child == o.child
769
+ end
770
+
771
+ def inspect # :nodoc:
772
+ "child(%p)" % [child]
773
+ end
774
+
775
+ def pretty_print q # :nodoc:
776
+ q.group 1, "child(", ")" do
777
+ q.pp child
778
+ end
779
+ end
780
+ end
781
+
782
+ ##
783
+ # Matches any atom (non-Sexp).
784
+ #
785
+ # examples:
786
+ #
787
+ # s(:a) / s{ s(atom) } #=> [s(:a)]
788
+ # s(:a, s(:b)) / s{ s(atom) } #=> [s(:b)]
789
+
790
+ class Atom < Matcher
791
+ ##
792
+ # Satisfied when +o+ is an atom.
793
+
794
+ def satisfy? o
795
+ !(o.kind_of? Sexp)
796
+ end
797
+
798
+ def inspect #:nodoc:
799
+ "atom"
800
+ end
801
+
802
+ def pretty_print q # :nodoc:
803
+ q.text "atom"
804
+ end
805
+ end
806
+
807
+ ##
808
+ # Matches any atom who's string representation matches the patterns
809
+ # passed in.
810
+ #
811
+ # examples:
812
+ #
813
+ # s(:a) / s{ m('a') } #=> [s(:a)]
814
+ # s(:a) / s{ m(/\w/,/\d/) } #=> [s(:a)]
815
+ # s(:tests, s(s(:test_a), s(:test_b))) / s{ m(/test_\w/) } #=> [s(:test_a),
816
+ #
817
+ # TODO: maybe don't require non-sexps? This does respond to =~ now.
818
+
819
+ class Pattern < Matcher
820
+
821
+ ##
822
+ # The regexp to match for the pattern.
823
+
824
+ attr_reader :pattern
825
+
826
+ def == o # :nodoc:
827
+ super && self.pattern == o.pattern
828
+ end
829
+
830
+ ##
831
+ # Create a Patten matcher which will match any atom that either
832
+ # matches the input +pattern+.
833
+
834
+ def initialize pattern
835
+ @pattern = pattern
836
+ end
837
+
838
+ ##
839
+ # Satisfied if +o+ is an atom, and +o+ matches +pattern+
840
+
841
+ def satisfy? o
842
+ !o.kind_of?(Sexp) && o.to_s =~ pattern # TODO: question to_s
843
+ end
844
+
845
+ def inspect # :nodoc:
846
+ "m(%p)" % pattern
847
+ end
848
+
849
+ def pretty_print q # :nodoc:
850
+ q.group 1, "m(", ")" do
851
+ q.pp pattern
852
+ end
853
+ end
854
+
855
+ def eql? o
856
+ super and self.pattern.eql? o.pattern
857
+ end
858
+
859
+ def hash
860
+ [super, pattern].hash
861
+ end
862
+ end
863
+
864
+ ##
865
+ # Matches any atom that is an instance of the specified class or module.
866
+ #
867
+ # examples:
868
+ #
869
+ # s(:lit, 6.28) / s{ q(:lit, k(Float)) } #=> [s(:lit, 6.28)]
870
+
871
+ class Klass < Pattern
872
+ def satisfy? o
873
+ o.kind_of? self.pattern
874
+ end
875
+
876
+ def inspect # :nodoc:
877
+ "k(%p)" % pattern
878
+ end
879
+
880
+ def pretty_print q # :nodoc:
881
+ q.group 1, "k(", ")" do
882
+ q.pp pattern
883
+ end
884
+ end
885
+ end
886
+
887
+ ##
888
+ # Matches anything having the same sexp_type, which is the first
889
+ # value in a Sexp.
890
+ #
891
+ # examples:
892
+ #
893
+ # s(:a, :b) / s{ t(:a) } #=> [s(:a, :b)]
894
+ # s(:a, :b) / s{ t(:b) } #=> []
895
+ # s(:a, s(:b, :c)) / s{ t(:b) } #=> [s(:b, :c)]
896
+
897
+ class Type < Matcher
898
+ attr_reader :sexp_type
899
+
900
+ ##
901
+ # Creates a Matcher which will match any Sexp who's type is +type+, where a type is
902
+ # the first element in the Sexp.
903
+
904
+ def initialize type
905
+ @sexp_type = type
906
+ end
907
+
908
+ def == o # :nodoc:
909
+ super && self.sexp_type == o.sexp_type
910
+ end
911
+
912
+ ##
913
+ # Satisfied if the sexp_type of +o+ is +type+.
914
+
915
+ def satisfy? o
916
+ o.kind_of?(Sexp) && o.sexp_type == sexp_type
917
+ end
918
+
919
+ def inspect # :nodoc:
920
+ "t(%p)" % sexp_type
921
+ end
922
+
923
+ def pretty_print q # :nodoc:
924
+ q.group 1, "t(", ")" do
925
+ q.pp sexp_type
926
+ end
927
+ end
928
+ end
929
+
930
+ ##
931
+ # Matches an expression or any expression that includes the child.
932
+ #
933
+ # examples:
934
+ #
935
+ # s(:a, :b) / s{ include(:b) } #=> [s(:a, :b)]
936
+ # s(s(s(:a))) / s{ include(:a) } #=> [s(:a)]
937
+
938
+ class Include < Matcher
939
+ ##
940
+ # The value that should be included in the match.
941
+
942
+ attr_reader :value
943
+
944
+ ##
945
+ # Creates a Matcher which will match any Sexp that contains the
946
+ # +value+.
947
+
948
+ def initialize value
949
+ @value = value
950
+ end
951
+
952
+ ##
953
+ # Satisfied if a +o+ is a Sexp and one of +o+'s elements matches
954
+ # value
955
+
956
+ def satisfy? o
957
+ Sexp === o && o.any? { |c|
958
+ # TODO: switch to respond_to??
959
+ Sexp === value ? value.satisfy?(c) : value == c
960
+ }
961
+ end
962
+
963
+ def == o # :nodoc:
964
+ super && self.value == o.value
965
+ end
966
+
967
+ def inspect # :nodoc:
968
+ "include(%p)" % [value]
969
+ end
970
+
971
+ def pretty_print q # :nodoc:
972
+ q.group 1, "include(", ")" do
973
+ q.pp value
974
+ end
975
+ end
976
+ end
977
+
978
+ ##
979
+ # See Matcher for sibling relations: <,<<,>>,>
980
+
981
+ class Sibling < Matcher
982
+
983
+ ##
984
+ # The LHS of the matcher.
985
+
986
+ attr_reader :subject
987
+
988
+ ##
989
+ # The RHS of the matcher.
990
+
991
+ attr_reader :sibling
992
+
993
+ ##
994
+ # An optional distance requirement for the matcher.
995
+
996
+ attr_reader :distance
997
+
998
+ ##
999
+ # Creates a Matcher which will match any pair of Sexps that are siblings.
1000
+ # Defaults to matching the immediate following sibling.
1001
+
1002
+ def initialize subject, sibling, distance = nil
1003
+ @subject = subject
1004
+ @sibling = sibling
1005
+ @distance = distance
1006
+ end
1007
+
1008
+ ##
1009
+ # Satisfied if o contains +subject+ followed by +sibling+
1010
+
1011
+ def satisfy? o
1012
+ # Future optimizations:
1013
+ # * Shortcut matching sibling
1014
+ subject_matches = index_matches(subject, o)
1015
+ return nil if subject_matches.empty?
1016
+
1017
+ sibling_matches = index_matches(sibling, o)
1018
+ return nil if sibling_matches.empty?
1019
+
1020
+ subject_matches.any? { |i1, _data_1|
1021
+ sibling_matches.any? { |i2, _data_2|
1022
+ distance ? (i2-i1 == distance) : i2 > i1
1023
+ }
1024
+ }
1025
+ end
1026
+
1027
+ def == o # :nodoc:
1028
+ super &&
1029
+ self.subject == o.subject &&
1030
+ self.sibling == o.sibling &&
1031
+ self.distance == o.distance
1032
+ end
1033
+
1034
+ def inspect # :nodoc:
1035
+ "%p >> %p" % [subject, sibling]
1036
+ end
1037
+
1038
+ def pretty_print q # :nodoc:
1039
+ if distance then
1040
+ q.group 1, "sibling(", ")" do
1041
+ q.seplist [subject, sibling, distance] do |v|
1042
+ q.pp v
1043
+ end
1044
+ end
1045
+ else
1046
+ q.group 1 do
1047
+ q.pp subject
1048
+ q.text " >> "
1049
+ q.pp sibling
1050
+ end
1051
+ end
1052
+ end
1053
+
1054
+ private
1055
+
1056
+ def index_matches pattern, o
1057
+ indexes = []
1058
+ return indexes unless o.kind_of? Sexp
1059
+
1060
+ o.each_with_index do |e, i|
1061
+ data = {}
1062
+ if pattern.kind_of?(Sexp) ? pattern.satisfy?(e) : pattern == o[i]
1063
+ indexes << [i, data]
1064
+ end
1065
+ end
1066
+
1067
+ indexes
1068
+ end
1069
+ end # class Sibling
1070
+
1071
+ ##
1072
+ # Wraps the results of a Sexp query. MatchCollection defines
1073
+ # MatchCollection#/ so that you can chain queries.
1074
+ #
1075
+ # For instance:
1076
+ # res = s(:a, s(:b)) / s{ s(:a,_) } / s{ s(:b) }
1077
+
1078
+ class MatchCollection < Array
1079
+ ##
1080
+ # See Traverse#search
1081
+
1082
+ def / pattern
1083
+ inject(self.class.new) { |result, match|
1084
+ result.concat match / pattern
1085
+ }
1086
+ end
1087
+
1088
+ def inspect # :nodoc:
1089
+ "MatchCollection.new(%s)" % self.to_a.inspect[1..-2]
1090
+ end
1091
+
1092
+ alias :to_s :inspect # :nodoc:
1093
+
1094
+ def pretty_print q # :nodoc:
1095
+ q.group 1, "MatchCollection.new(", ")" do
1096
+ q.seplist(self) {|v| q.pp v }
1097
+ end
1098
+ end
1099
+ end # class MatchCollection
1100
+ end # class Sexp