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