sexp_processor 4.11.0 → 4.16.0

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.
data/lib/sexp.rb CHANGED
@@ -31,13 +31,15 @@ class Sexp < Array # ZenTest FULL
31
31
  super(args)
32
32
  end
33
33
 
34
+ alias _concat concat
35
+
34
36
  ##
35
37
  # Creates a new Sexp from Array +a+.
36
38
 
37
39
  def self.from_array a
38
40
  ary = Array === a ? a : [a]
39
41
 
40
- self.new(*ary.map { |x|
42
+ self.new._concat(ary.map { |x|
41
43
  case x
42
44
  when Sexp
43
45
  x
@@ -54,7 +56,7 @@ class Sexp < Array # ZenTest FULL
54
56
  # same +file+, +line+, and +comment+ as self.
55
57
 
56
58
  def new(*body)
57
- r = self.class.new(*body) # ensures a sexp from map
59
+ r = self.class.new._concat(body) # ensures a sexp from map
58
60
  r.file = self.file if self.file
59
61
  r.line = self.line if self.line
60
62
  r.comments = self.comments if self.comments
@@ -62,13 +64,21 @@ class Sexp < Array # ZenTest FULL
62
64
  end
63
65
 
64
66
  def map &blk # :nodoc:
65
- self.new(*super(&blk)) # ensures a sexp from map
67
+ self.new._concat(super(&blk)) # ensures a sexp from map
66
68
  end
67
69
 
68
70
  def == obj # :nodoc:
69
71
  obj.class == self.class and super # only because of a bug in ruby
70
72
  end
71
73
 
74
+ def eql? o
75
+ self.class == o.class && super
76
+ end
77
+
78
+ def hash
79
+ @hash ||= [self.class, *self].hash
80
+ end
81
+
72
82
  ##
73
83
  # Returns true if the node_type is +array+ or +args+.
74
84
  #
@@ -85,7 +95,7 @@ class Sexp < Array # ZenTest FULL
85
95
  end
86
96
 
87
97
  ##
88
- # Recursively enumerates the sexp yielding to +block+ for every element.
98
+ # Recursively enumerates the sexp yielding to +block+ for every sub-Sexp.
89
99
  #
90
100
  # Returning :skip will stop traversing that subtree:
91
101
  #
@@ -114,7 +124,7 @@ class Sexp < Array # ZenTest FULL
114
124
  # Enumeratates the sexp yielding to +b+ when the node_type == +t+.
115
125
 
116
126
  def each_of_type t, &b
117
- return enum_for(:each_of_type) unless block_given?
127
+ return enum_for(:each_of_type, t) unless block_given?
118
128
 
119
129
  each_sexp do | sexp |
120
130
  sexp.each_of_type(t, &b)
@@ -123,7 +133,7 @@ class Sexp < Array # ZenTest FULL
123
133
  end
124
134
 
125
135
  ##
126
- # Recursively enumerates all sub-sexps skipping non-Sexp elements.
136
+ # Enumerates all sub-sexps skipping non-Sexp elements.
127
137
 
128
138
  def each_sexp
129
139
  return enum_for(:each_sexp) unless block_given?
@@ -202,13 +212,16 @@ class Sexp < Array # ZenTest FULL
202
212
  each_sexp.find_all { |sexp| sexp.sexp_type == name }
203
213
  end
204
214
 
215
+ UNASSIGNED = Object.new
216
+
205
217
  ##
206
218
  # If passed a line number, sets the line and returns self. Otherwise
207
219
  # returns the line number. This allows you to do message cascades
208
220
  # and still get the sexp back.
209
221
 
210
- def line n = nil
211
- if n then
222
+ def line n = UNASSIGNED
223
+ if n != UNASSIGNED then
224
+ raise ArgumentError, "setting %p.line %p" % [self, n] unless Integer === n
212
225
  @line = n
213
226
  self
214
227
  else
@@ -278,11 +291,11 @@ class Sexp < Array # ZenTest FULL
278
291
  # the values without the node type.
279
292
 
280
293
  def sexp_body from = 1
281
- self.new(*self[from..-1])
294
+ self.new._concat(self[from..-1] || [])
282
295
  end
283
296
 
284
297
  ##
285
- # Returns the Sexp body, ie the values without the node type.
298
+ # Sets the Sexp body to new content.
286
299
 
287
300
  def sexp_body= v
288
301
  self[1..-1] = v
@@ -351,6 +364,14 @@ class Sexp < Array # ZenTest FULL
351
364
  end
352
365
 
353
366
  alias to_s inspect # :nodoc:
367
+
368
+ ##
369
+ # Return the value (last item) of a single element sexp (eg `s(:lit, 42)`).
370
+
371
+ def value
372
+ raise "multi item sexp" if size > 2
373
+ last
374
+ end
354
375
  end
355
376
 
356
377
  ##
@@ -366,1055 +387,5 @@ def s *args, &blk
366
387
  Sexp.new(*args)
367
388
  end
368
389
 
369
- class Sexp #:nodoc:
370
- ##
371
- # Verifies that +pattern+ is a Matcher and then dispatches to its
372
- # #=~ method.
373
- #
374
- # See Matcher.=~
375
-
376
- def =~ pattern
377
- raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern
378
- pattern =~ self
379
- end
380
-
381
- ##
382
- # Verifies that +pattern+ is a Matcher and then dispatches to its
383
- # #satisfy? method.
384
- #
385
- # TODO: rename match?
386
-
387
- def satisfy? pattern
388
- raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern
389
- pattern.satisfy? self
390
- end
391
-
392
- ##
393
- # Verifies that +pattern+ is a Matcher and then dispatches to its #/
394
- # method.
395
- #
396
- # TODO: rename grep? match_all ? find_all ?
397
-
398
- def / pattern
399
- raise ArgumentError, "Not a pattern: %p" % [pattern] unless Matcher === pattern
400
- pattern / self
401
- end
402
-
403
- ##
404
- # Recursively searches for the +pattern+ yielding the matches.
405
-
406
- def search_each pattern, &block # TODO: rename to grep?
407
- raise ArgumentError, "Needs a pattern" unless pattern.kind_of? Matcher
408
-
409
- return enum_for(:search_each, pattern) unless block_given?
410
-
411
- if pattern.satisfy? self then
412
- yield self
413
- end
414
-
415
- self.each_sexp do |subset|
416
- subset.search_each pattern, &block
417
- end
418
- end
419
-
420
- ##
421
- # Recursively searches for the +pattern+ yielding each match, and
422
- # replacing it with the result of the block.
423
- #
424
-
425
- def replace_sexp pattern, &block # TODO: rename to gsub?
426
- raise ArgumentError, "Needs a pattern" unless pattern.kind_of? Matcher
427
-
428
- return yield self if pattern.satisfy? self
429
-
430
- # TODO: Needs #new_from(*new_body) to copy file/line/comment
431
- self.class.new(*self.map { |subset|
432
- case subset
433
- when Sexp then
434
- subset.replace_sexp pattern, &block
435
- else
436
- subset
437
- end
438
- })
439
- end
440
-
441
- ##
442
- # Matches an S-Expression.
443
- #
444
- # See Matcher for examples.
445
-
446
- def self.s *args
447
- Matcher.new(*args)
448
- end
449
-
450
- ##
451
- # Matches any single item.
452
- #
453
- # See Wild for examples.
454
-
455
- def self._
456
- Wild.new
457
- end
458
-
459
- # TODO: reorder factory methods and classes to match
460
-
461
- ##
462
- # Matches all remaining input.
463
- #
464
- # See Remaining for examples.
465
-
466
- def self.___
467
- Remaining.new
468
- end
469
-
470
- ##
471
- # Matches an expression or any expression that includes the child.
472
- #
473
- # See Include for examples.
474
-
475
- def self.include child # TODO: rename, name is generic ruby
476
- Include.new(child)
477
- end
478
-
479
- ##
480
- # Matches any atom.
481
- #
482
- # See Atom for examples.
483
-
484
- def self.atom
485
- Atom.new
486
- end
487
-
488
- ##
489
- # Matches when any of the sub-expressions match.
490
- #
491
- # This is also available via Matcher#|.
492
- #
493
- # See Any for examples.
494
-
495
- def self.any *args
496
- Any.new(*args)
497
- end
498
-
499
- ##
500
- # Matches only when all sub-expressions match.
501
- #
502
- # This is also available via Matcher#&.
503
- #
504
- # See All for examples.
505
-
506
- def self.all *args
507
- All.new(*args)
508
- end
509
-
510
- ##
511
- # Matches when sub-expression does not match.
512
- #
513
- # This is also available via Matcher#-@.
514
- #
515
- # See Not for examples.
516
-
517
- def self.not? arg
518
- Not.new arg
519
- end
520
-
521
- # TODO: add Sibling factory method?
522
-
523
- ##
524
- # Matches anything that has a child matching the sub-expression.
525
- #
526
- # See Child for examples.
527
-
528
- def self.child child
529
- Child.new child
530
- end
531
-
532
- ##
533
- # Matches anything having the same sexp_type, which is the first
534
- # value in a Sexp.
535
- #
536
- # See Type for examples.
537
-
538
- def self.t name
539
- Type.new name
540
- end
541
-
542
- ##
543
- # Matches any atom who's string representation matches the patterns
544
- # passed in.
545
- #
546
- # See Pattern for examples.
547
-
548
- def self.m *values
549
- res = values.map { |value|
550
- case value
551
- when Regexp then
552
- value
553
- else
554
- re = Regexp.escape value.to_s
555
- Regexp.new "\\A%s\\Z" % re
556
- end
557
- }
558
- Pattern.new Regexp.union(*res)
559
- end
560
-
561
- ##
562
- # Defines a family of objects that can be used to match sexps to
563
- # certain types of patterns, much like regexps can be used on
564
- # strings. Generally you won't use this class directly.
565
- #
566
- # You would normally create a matcher using the top-level #s method,
567
- # but with a block, calling into the Sexp factory methods. For example:
568
- #
569
- # s{ s(:class, m(/^Test/), _, ___) }
570
- #
571
- # This creates a matcher for classes whose names start with "Test".
572
- # It uses Sexp.m to create a Sexp::Matcher::Pattern matcher, Sexp._
573
- # to create a Sexp::Matcher::Wild matcher, and Sexp.___ to create a
574
- # Sexp::Matcher::Remaining matcher. It works like this:
575
- #
576
- # s{ # start to create a pattern
577
- # s( # create a sexp matcher
578
- # :class. # for class nodes
579
- # m(/^Test/), # matching name slots that start with "Test"
580
- # _, # any superclass value
581
- # ___ # and whatever is in the class
582
- # )
583
- # }
584
- #
585
- # Then you can use that with #=~, #/, Sexp#replace_sexp, and others.
586
- #
587
- # For more examples, see the various Sexp class methods, the examples,
588
- # and the tests supplied with Sexp.
589
- #
590
- # * For pattern creation, see factory methods: Sexp::_, Sexp::___, etc.
591
- # * For matching returning truthy/falsey results, see Sexp#=~.
592
- # * For case expressions, see Matcher#===.
593
- # * For getting all subtree matches, see Sexp#/.
594
- #
595
- # If rdoc didn't suck, these would all be links.
596
-
597
- class Matcher < Sexp
598
- ##
599
- # Should #=~ match sub-trees?
600
-
601
- def self.match_subs?
602
- @@match_subs
603
- end
604
-
605
- ##
606
- # Setter for +match_subs?+.
607
-
608
- def self.match_subs= o
609
- @@match_subs = o
610
- end
611
-
612
- self.match_subs = true
613
-
614
- ##
615
- # Does this matcher actually match +o+? Returns falsey if +o+ is
616
- # not a Sexp or if any sub-tree of +o+ is not satisfied by or
617
- # equal to its corresponding sub-matcher.
618
- #
619
- #--
620
- # TODO: push this up to Sexp and make this the workhorse
621
- # TODO: do the same with ===/satisfy?
622
-
623
- def satisfy? o
624
- return unless o.kind_of?(Sexp) &&
625
- (length == o.length || Matcher === last && last.greedy?)
626
-
627
- each_with_index.all? { |child, i|
628
- sexp = o.at i
629
- if Sexp === child then # TODO: when will this NOT be a matcher?
630
- sexp = o.sexp_body i if child.respond_to?(:greedy?) && child.greedy?
631
- child.satisfy? sexp
632
- else
633
- child == sexp
634
- end
635
- }
636
- end
637
-
638
- ##
639
- # Tree equivalent to String#=~, returns true if +self+ matches
640
- # +sexp+ as a whole or in a sub-tree (if +match_subs?+).
641
- #
642
- # TODO: maybe this should NOT be aliased to === ?
643
- #
644
- # TODO: example
645
-
646
- def =~ sexp
647
- raise ArgumentError, "Can't both be matchers: %p" % [sexp] if Matcher === sexp
648
-
649
- self.satisfy?(sexp) ||
650
- (self.class.match_subs? && sexp.each_sexp.any? { |sub| self =~ sub })
651
- end
652
-
653
- alias === =~ # TODO?: alias === satisfy?
654
-
655
- ##
656
- # Searches through +sexp+ for all sub-trees that match this
657
- # matcher and returns a MatchCollection for each match.
658
- #
659
- # TODO: redirect?
660
- # Example:
661
- # Q{ s(:b) } / s(:a, s(:b)) => [s(:b)]
662
-
663
- def / sexp
664
- raise ArgumentError, "can't both be matchers" if Matcher === sexp
665
-
666
- # TODO: move search_each into matcher?
667
- MatchCollection.new sexp.search_each(self).to_a
668
- end
669
-
670
- ##
671
- # Combines the Matcher with another Matcher, the resulting one will
672
- # be satisfied if either Matcher would be satisfied.
673
- #
674
- # TODO: redirect
675
- # Example:
676
- # s(:a) | s(:b)
677
-
678
- def | other
679
- Any.new self, other
680
- end
681
-
682
- ##
683
- # Combines the Matcher with another Matcher, the resulting one will
684
- # be satisfied only if both Matchers would be satisfied.
685
- #
686
- # TODO: redirect
687
- # Example:
688
- # t(:a) & include(:b)
689
-
690
- def & other
691
- All.new self, other
692
- end
693
-
694
- ##
695
- # Returns a Matcher that matches whenever this Matcher would not have matched
696
- #
697
- # Example:
698
- # -s(:a)
699
-
700
- def -@
701
- Not.new self
702
- end
703
-
704
- ##
705
- # Returns a Matcher that matches if this has a sibling +o+
706
- #
707
- # Example:
708
- # s(:a) >> s(:b)
709
-
710
- def >> other
711
- Sibling.new self, other
712
- end
713
-
714
- ##
715
- # Is this matcher greedy? Defaults to false.
716
-
717
- def greedy?
718
- false
719
- end
720
-
721
- def inspect # :nodoc:
722
- s = super
723
- s[0] = "q"
724
- s
725
- end
726
-
727
- def pretty_print q # :nodoc:
728
- q.group 1, "q(", ")" do
729
- q.seplist self do |v|
730
- q.pp v
731
- end
732
- end
733
- end
734
-
735
- ##
736
- # Parse a lispy string representation of a matcher into a Matcher.
737
- # See +Parser+.
738
-
739
- def self.parse s
740
- Parser.new(s).parse
741
- end
742
-
743
- ##
744
- # Converts from a lispy string to Sexp matchers in a safe manner.
745
- #
746
- # "(a 42 _ (c) [t x] ___)" => s{ s(:a, 42, _, s(:c), t(:x), ___) }
747
-
748
- class Parser
749
-
750
- ##
751
- # The stream of tokens to parse. See #lex.
752
-
753
- attr_accessor :tokens
754
-
755
- ##
756
- # Create a new Parser instance on +s+
757
-
758
- def initialize s
759
- self.tokens = []
760
- lex s
761
- end
762
-
763
- ##
764
- # Converts +s+ into a stream of tokens and adds them to +tokens+.
765
-
766
- def lex s
767
- tokens.concat s.scan(%r%[()\[\]]|\"[^"]*\"|/[^/]*/|[\w-]+%) # "
768
- end
769
-
770
- ##
771
- # Returns the next token and removes it from the stream or raises if empty.
772
-
773
- def next_token
774
- raise SyntaxError, "unbalanced input" if tokens.empty?
775
- tokens.shift
776
- end
777
-
778
- ##
779
- # Returns the next token without removing it from the stream.
780
-
781
- def peek_token
782
- tokens.first
783
- end
784
-
785
- ##
786
- # Parses tokens and returns a +Matcher+ instance.
787
-
788
- def parse
789
- result = parse_sexp until tokens.empty?
790
- result
791
- end
792
-
793
- ##
794
- # Parses a string into a sexp matcher:
795
- #
796
- # SEXP : "(" SEXP:args* ")" => Sexp.q(*args)
797
- # | "[" CMD:cmd sexp:args* "]" => Sexp.cmd(*args)
798
- # | "nil" => nil
799
- # | /\d+/:n => n.to_i
800
- # | "___" => Sexp.___
801
- # | "_" => Sexp._
802
- # | /^\/(.*)\/$/:re => Regexp.new re[0]
803
- # | /^"(.*)"$/:s => String.new s[0]
804
- # | NAME:name => name.to_sym
805
- # NAME : /\w+/
806
- # CMD : "t" | "m" | "atom"
807
-
808
- def parse_sexp
809
- token = next_token
810
-
811
- case token
812
- when "(" then
813
- parse_list
814
- when "[" then
815
- parse_cmd
816
- when "nil" then
817
- nil
818
- when /^\d+$/ then
819
- token.to_i
820
- when "___" then
821
- Sexp.___
822
- when "_" then
823
- Sexp._
824
- when %r%^/(.*)/$% then
825
- re = $1
826
- raise SyntaxError, "Not allowed: /%p/" % [re] unless
827
- re =~ /\A([\w()|.*+^$]+)\z/
828
- Regexp.new re
829
- when /^"(.*)"$/ then
830
- $1
831
- when /^\w+$/ then
832
- token.to_sym
833
- else
834
- raise SyntaxError, "unhandled token: %p" % [token]
835
- end
836
- end
837
-
838
- ##
839
- # Parses a balanced list of expressions and returns the
840
- # equivalent matcher.
841
-
842
- def parse_list
843
- result = []
844
-
845
- result << parse_sexp while peek_token && peek_token != ")"
846
- next_token # pop off ")"
847
-
848
- Sexp.s(*result)
849
- end
850
-
851
- ##
852
- # A collection of allowed commands to convert into matchers.
853
-
854
- ALLOWED = [:t, :m, :atom].freeze
855
-
856
- ##
857
- # Parses a balanced command. A command is denoted by square
858
- # brackets and must conform to a whitelisted set of allowed
859
- # commands (see +ALLOWED+).
860
-
861
- def parse_cmd
862
- args = []
863
- args << parse_sexp while peek_token && peek_token != "]"
864
- next_token # pop off "]"
865
-
866
- cmd = args.shift
867
- args = Sexp.s(*args)
868
-
869
- raise SyntaxError, "bad cmd: %p" % [cmd] unless ALLOWED.include? cmd
870
-
871
- result = Sexp.send cmd, *args
872
-
873
- result
874
- end
875
- end # class Parser
876
- end # class Matcher
877
-
878
- ##
879
- # Matches any single item.
880
- #
881
- # examples:
882
- #
883
- # s(:a) / s{ _ } #=> [s(:a)]
884
- # s(:a, s(s(:b))) / s{ s(_) } #=> [s(s(:b))]
885
-
886
- class Wild < Matcher
887
- ##
888
- # Matches any single element.
889
-
890
- def satisfy? o
891
- true
892
- end
893
-
894
- def inspect # :nodoc:
895
- "_"
896
- end
897
-
898
- def pretty_print q # :nodoc:
899
- q.text "_"
900
- end
901
- end
902
-
903
- ##
904
- # Matches all remaining input. If remaining comes before any other
905
- # matchers, they will be ignored.
906
- #
907
- # examples:
908
- #
909
- # s(:a) / s{ s(:a, ___ ) } #=> [s(:a)]
910
- # s(:a, :b, :c) / s{ s(:a, ___ ) } #=> [s(:a, :b, :c)]
911
-
912
- class Remaining < Matcher
913
- ##
914
- # Always satisfied once this is reached. Think of it as a var arg.
915
-
916
- def satisfy? o
917
- true
918
- end
919
-
920
- def greedy?
921
- true
922
- end
923
-
924
- def inspect # :nodoc:
925
- "___"
926
- end
927
-
928
- def pretty_print q # :nodoc:
929
- q.text "___"
930
- end
931
- end
932
-
933
- ##
934
- # Matches when any of the sub-expressions match.
935
- #
936
- # This is also available via Matcher#|.
937
- #
938
- # examples:
939
- #
940
- # s(:a) / s{ any(s(:a), s(:b)) } #=> [s(:a)]
941
- # s(:a) / s{ s(:a) | s(:b) } #=> [s(:a)] # same thing via |
942
- # s(:a) / s{ any(s(:b), s(:c)) } #=> []
943
-
944
- class Any < Matcher
945
- ##
946
- # The collection of sub-matchers to match against.
947
-
948
- attr_reader :options
949
-
950
- ##
951
- # Create an Any matcher which will match any of the +options+.
952
-
953
- def initialize *options
954
- @options = options
955
- end
956
-
957
- ##
958
- # Satisfied when any sub expressions match +o+
959
-
960
- def satisfy? o
961
- options.any? { |exp|
962
- Sexp === exp && exp.satisfy?(o) || exp == o
963
- }
964
- end
965
-
966
- def == o # :nodoc:
967
- super && self.options == o.options
968
- end
969
-
970
- def inspect # :nodoc:
971
- options.map(&:inspect).join(" | ")
972
- end
973
-
974
- def pretty_print q # :nodoc:
975
- q.group 1, "any(", ")" do
976
- q.seplist options do |v|
977
- q.pp v
978
- end
979
- end
980
- end
981
- end
982
-
983
- ##
984
- # Matches only when all sub-expressions match.
985
- #
986
- # This is also available via Matcher#&.
987
- #
988
- # examples:
989
- #
990
- # s(:a) / s{ all(s(:a), s(:b)) } #=> []
991
- # s(:a, :b) / s{ t(:a) & include(:b)) } #=> [s(:a, :b)]
992
-
993
- class All < Matcher
994
- ##
995
- # The collection of sub-matchers to match against.
996
-
997
- attr_reader :options
998
-
999
- ##
1000
- # Create an All matcher which will match all of the +options+.
1001
-
1002
- def initialize *options
1003
- @options = options
1004
- end
1005
-
1006
- ##
1007
- # Satisfied when all sub expressions match +o+
1008
-
1009
- def satisfy? o
1010
- options.all? { |exp|
1011
- exp.kind_of?(Sexp) ? exp.satisfy?(o) : exp == o
1012
- }
1013
- end
1014
-
1015
- def == o # :nodoc:
1016
- super && self.options == o.options
1017
- end
1018
-
1019
- def inspect # :nodoc:
1020
- options.map(&:inspect).join(" & ")
1021
- end
1022
-
1023
- def pretty_print q # :nodoc:
1024
- q.group 1, "all(", ")" do
1025
- q.seplist options do |v|
1026
- q.pp v
1027
- end
1028
- end
1029
- end
1030
- end
1031
-
1032
- ##
1033
- # Matches when sub-expression does not match.
1034
- #
1035
- # This is also available via Matcher#-@.
1036
- #
1037
- # examples:
1038
- #
1039
- # s(:a) / s{ not?(s(:b)) } #=> [s(:a)]
1040
- # s(:a) / s{ -s(:b) } #=> [s(:a)]
1041
- # s(:a) / s{ s(not? :a) } #=> []
1042
-
1043
- class Not < Matcher
1044
-
1045
- ##
1046
- # The value to negate in the match.
1047
-
1048
- attr_reader :value
1049
-
1050
- ##
1051
- # Creates a Matcher which will match any Sexp that does not match the +value+
1052
-
1053
- def initialize value
1054
- @value = value
1055
- end
1056
-
1057
- def == o # :nodoc:
1058
- super && self.value == o.value
1059
- end
1060
-
1061
- ##
1062
- # Satisfied if a +o+ does not match the +value+
1063
-
1064
- def satisfy? o
1065
- !(value.kind_of?(Sexp) ? value.satisfy?(o) : value == o)
1066
- end
1067
-
1068
- def inspect # :nodoc:
1069
- "not?(%p)" % [value]
1070
- end
1071
-
1072
- def pretty_print q # :nodoc:
1073
- q.group 1, "not?(", ")" do
1074
- q.pp value
1075
- end
1076
- end
1077
- end
1078
-
1079
- ##
1080
- # Matches anything that has a child matching the sub-expression
1081
- #
1082
- # example:
1083
- #
1084
- # s(s(s(s(s(:a))))) / s{ child(s(:a)) } #=> [s(s(s(s(s(:a))))),
1085
- # s(s(s(s(:a)))),
1086
- # s(s(s(:a))),
1087
- # s(s(:a)),
1088
- # s(:a)]
1089
-
1090
- class Child < Matcher
1091
- ##
1092
- # The child to match.
1093
-
1094
- attr_reader :child
1095
-
1096
- ##
1097
- # Create a Child matcher which will match anything having a
1098
- # descendant matching +child+.
1099
-
1100
- def initialize child
1101
- @child = child
1102
- end
1103
-
1104
- ##
1105
- # Satisfied if matches +child+ or +o+ has a descendant matching
1106
- # +child+.
1107
-
1108
- def satisfy? o
1109
- if child.satisfy? o
1110
- true
1111
- elsif o.kind_of? Sexp
1112
- o.search_each(child).any?
1113
- end
1114
- end
1115
-
1116
- def == o # :nodoc:
1117
- super && self.child == o.child
1118
- end
1119
-
1120
- def inspect # :nodoc:
1121
- "child(%p)" % [child]
1122
- end
1123
-
1124
- def pretty_print q # :nodoc:
1125
- q.group 1, "child(", ")" do
1126
- q.pp child
1127
- end
1128
- end
1129
- end
1130
-
1131
- ##
1132
- # Matches any atom (non-Sexp).
1133
- #
1134
- # examples:
1135
- #
1136
- # s(:a) / s{ s(atom) } #=> [s(:a)]
1137
- # s(:a, s(:b)) / s{ s(atom) } #=> [s(:b)]
1138
-
1139
- class Atom < Matcher
1140
- ##
1141
- # Satisfied when +o+ is an atom.
1142
-
1143
- def satisfy? o
1144
- !(o.kind_of? Sexp)
1145
- end
1146
-
1147
- def inspect #:nodoc:
1148
- "atom"
1149
- end
1150
-
1151
- def pretty_print q # :nodoc:
1152
- q.text "atom"
1153
- end
1154
- end
1155
-
1156
- ##
1157
- # Matches any atom who's string representation matches the patterns
1158
- # passed in.
1159
- #
1160
- # examples:
1161
- #
1162
- # s(:a) / s{ m('a') } #=> [s(:a)]
1163
- # s(:a) / s{ m(/\w/,/\d/) } #=> [s(:a)]
1164
- # s(:tests, s(s(:test_a), s(:test_b))) / s{ m(/test_\w/) } #=> [s(:test_a),
1165
- #
1166
- # TODO: maybe don't require non-sexps? This does respond to =~ now.
1167
-
1168
- class Pattern < Matcher
1169
-
1170
- ##
1171
- # The regexp to match for the pattern.
1172
-
1173
- attr_reader :pattern
1174
-
1175
- def == o # :nodoc:
1176
- super && self.pattern == o.pattern
1177
- end
1178
-
1179
- ##
1180
- # Create a Patten matcher which will match any atom that either
1181
- # matches the input +pattern+.
1182
-
1183
- def initialize pattern
1184
- @pattern = pattern
1185
- end
1186
-
1187
- ##
1188
- # Satisfied if +o+ is an atom, and +o+ matches +pattern+
1189
-
1190
- def satisfy? o
1191
- !o.kind_of?(Sexp) && o.to_s =~ pattern # TODO: question to_s
1192
- end
1193
-
1194
- def inspect # :nodoc:
1195
- "m(%p)" % pattern
1196
- end
1197
-
1198
- def pretty_print q # :nodoc:
1199
- q.group 1, "m(", ")" do
1200
- q.pp pattern
1201
- end
1202
- end
1203
- end
1204
-
1205
- ##
1206
- # Matches anything having the same sexp_type, which is the first
1207
- # value in a Sexp.
1208
- #
1209
- # examples:
1210
- #
1211
- # s(:a, :b) / s{ t(:a) } #=> [s(:a, :b)]
1212
- # s(:a, :b) / s{ t(:b) } #=> []
1213
- # s(:a, s(:b, :c)) / s{ t(:b) } #=> [s(:b, :c)]
1214
-
1215
- class Type < Matcher
1216
- attr_reader :sexp_type
1217
-
1218
- ##
1219
- # Creates a Matcher which will match any Sexp who's type is +type+, where a type is
1220
- # the first element in the Sexp.
1221
-
1222
- def initialize type
1223
- @sexp_type = type
1224
- end
1225
-
1226
- def == o # :nodoc:
1227
- super && self.sexp_type == o.sexp_type
1228
- end
1229
-
1230
- ##
1231
- # Satisfied if the sexp_type of +o+ is +type+.
1232
-
1233
- def satisfy? o
1234
- o.kind_of?(Sexp) && o.sexp_type == sexp_type
1235
- end
1236
-
1237
- def inspect # :nodoc:
1238
- "t(%p)" % sexp_type
1239
- end
1240
-
1241
- def pretty_print q # :nodoc:
1242
- q.group 1, "t(", ")" do
1243
- q.pp sexp_type
1244
- end
1245
- end
1246
- end
1247
-
1248
- ##
1249
- # Matches an expression or any expression that includes the child.
1250
- #
1251
- # examples:
1252
- #
1253
- # s(:a, :b) / s{ include(:b) } #=> [s(:a, :b)]
1254
- # s(s(s(:a))) / s{ include(:a) } #=> [s(:a)]
1255
-
1256
- class Include < Matcher
1257
- ##
1258
- # The value that should be included in the match.
1259
-
1260
- attr_reader :value
1261
-
1262
- ##
1263
- # Creates a Matcher which will match any Sexp that contains the
1264
- # +value+.
1265
-
1266
- def initialize value
1267
- @value = value
1268
- end
1269
-
1270
- ##
1271
- # Satisfied if a +o+ is a Sexp and one of +o+'s elements matches
1272
- # value
1273
-
1274
- def satisfy? o
1275
- Sexp === o && o.any? { |c|
1276
- # TODO: switch to respond_to??
1277
- Sexp === value ? value.satisfy?(c) : value == c
1278
- }
1279
- end
1280
-
1281
- def == o # :nodoc:
1282
- super && self.value == o.value
1283
- end
1284
-
1285
- def inspect # :nodoc:
1286
- "include(%p)" % [value]
1287
- end
1288
-
1289
- def pretty_print q # :nodoc:
1290
- q.group 1, "include(", ")" do
1291
- q.pp value
1292
- end
1293
- end
1294
- end
1295
-
1296
- ##
1297
- # See Matcher for sibling relations: <,<<,>>,>
1298
-
1299
- class Sibling < Matcher
1300
-
1301
- ##
1302
- # The LHS of the matcher.
1303
-
1304
- attr_reader :subject
1305
-
1306
- ##
1307
- # The RHS of the matcher.
1308
-
1309
- attr_reader :sibling
1310
-
1311
- ##
1312
- # An optional distance requirement for the matcher.
1313
-
1314
- attr_reader :distance
1315
-
1316
- ##
1317
- # Creates a Matcher which will match any pair of Sexps that are siblings.
1318
- # Defaults to matching the immediate following sibling.
1319
-
1320
- def initialize subject, sibling, distance = nil
1321
- @subject = subject
1322
- @sibling = sibling
1323
- @distance = distance
1324
- end
1325
-
1326
- ##
1327
- # Satisfied if o contains +subject+ followed by +sibling+
1328
-
1329
- def satisfy? o
1330
- # Future optimizations:
1331
- # * Shortcut matching sibling
1332
- subject_matches = index_matches(subject, o)
1333
- return nil if subject_matches.empty?
1334
-
1335
- sibling_matches = index_matches(sibling, o)
1336
- return nil if sibling_matches.empty?
1337
-
1338
- subject_matches.any? { |i1, _data_1|
1339
- sibling_matches.any? { |i2, _data_2|
1340
- distance ? (i2-i1 == distance) : i2 > i1
1341
- }
1342
- }
1343
- end
1344
-
1345
- def == o # :nodoc:
1346
- super &&
1347
- self.subject == o.subject &&
1348
- self.sibling == o.sibling &&
1349
- self.distance == o.distance
1350
- end
1351
-
1352
- def inspect # :nodoc:
1353
- "%p >> %p" % [subject, sibling]
1354
- end
1355
-
1356
- def pretty_print q # :nodoc:
1357
- if distance then
1358
- q.group 1, "sibling(", ")" do
1359
- q.seplist [subject, sibling, distance] do |v|
1360
- q.pp v
1361
- end
1362
- end
1363
- else
1364
- q.group 1 do
1365
- q.pp subject
1366
- q.text " >> "
1367
- q.pp sibling
1368
- end
1369
- end
1370
- end
1371
-
1372
- private
1373
-
1374
- def index_matches pattern, o
1375
- indexes = []
1376
- return indexes unless o.kind_of? Sexp
1377
-
1378
- o.each_with_index do |e, i|
1379
- data = {}
1380
- if pattern.kind_of?(Sexp) ? pattern.satisfy?(e) : pattern == o[i]
1381
- indexes << [i, data]
1382
- end
1383
- end
1384
-
1385
- indexes
1386
- end
1387
- end # class Sibling
1388
-
1389
- ##
1390
- # Wraps the results of a Sexp query. MatchCollection defines
1391
- # MatchCollection#/ so that you can chain queries.
1392
- #
1393
- # For instance:
1394
- # res = s(:a, s(:b)) / s{ s(:a,_) } / s{ s(:b) }
1395
-
1396
- class MatchCollection < Array
1397
- ##
1398
- # See Traverse#search
1399
-
1400
- def / pattern
1401
- inject(self.class.new) { |result, match|
1402
- result.concat match / pattern
1403
- }
1404
- end
1405
-
1406
- def inspect # :nodoc:
1407
- "MatchCollection.new(%s)" % self.to_a.inspect[1..-2]
1408
- end
1409
-
1410
- alias :to_s :inspect # :nodoc:
1411
-
1412
- def pretty_print q # :nodoc:
1413
- q.group 1, "MatchCollection.new(", ")" do
1414
- q.seplist(self) {|v| q.pp v }
1415
- end
1416
- end
1417
- end # class MatchCollection
1418
- end
1419
-
390
+ require "sexp_matcher" unless defined? Sexp::Matcher
1420
391
  require "strict_sexp" if ENV["STRICT_SEXP"].to_i > 0