sexp_processor 4.12.0 → 4.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a33c4da3c4f6c19506d4aee6f94e76dca65aa94a3af108f751bd916cfbd0cb8f
4
- data.tar.gz: de4b25babc82dc51481153180ae65a5d9ab54079c3d2b77519666ff8b537f3fa
3
+ metadata.gz: dae436d9eece3ad19a5d390da622ee2b73db2db5e73a4a6b06fe351e8759d45f
4
+ data.tar.gz: 64c4cda4d2f25f759812de2dea4bd7d85e948a6d9cba8507a7d64318c358d116
5
5
  SHA512:
6
- metadata.gz: 34e1e1f066956df4c8b71e96a2183c62183c02628ef7e7193f0c7bd48897960e4c7a47a9e08e8e9dcd02b7d202f6f2a6072511cf8d231932bb3e12de9799feef
7
- data.tar.gz: 28d1ef553de797ccdb2f00fdd3b81b1c05d26dd7ce7b8e4fbb397e175fcb7d45d2647a684c1b3b6778f01df38ef6544e151ec3484f3349257ab7de3eb5f4a586
6
+ metadata.gz: 8647ce87dca77073d067bd2027b139499fa8db297ea524c453d709f5e52f057c9355b8b2f49e265aa09aef7c1a29dc6038409fb5ba19d33c9fb2eeb972b4c210
7
+ data.tar.gz: 38eb466cf27e2eeb30a14c9eabae202ab3b1d7f119b7b2a715c848fdcf98b8e2e130d656e05336781572d7ca9ff74ea8185683e065e6af557cac50f483b92436
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1,3 +1,50 @@
1
+ === 4.15.0 / 2020-06-09
2
+
3
+ * 1 minor enhancement:
4
+
5
+ * Added `child` and `include` to Sexp::Matcher.parse language.
6
+
7
+ === 4.14.1 / 2020-02-09
8
+
9
+ * 2 bug fixes:
10
+
11
+ * Declared support for ruby 2.2+ to gemspec.
12
+ * Fixed alias for `not?` to `-` for ruby <= 2.4. (nard-tech).
13
+
14
+ === 4.14.0 / 2020-02-06
15
+
16
+ * 4 minor enhancements:
17
+
18
+ * Added '-' as an alias for the 'not?' pattern matching command.
19
+ * Added Klass matcher to match on types.
20
+ * Added `k` shortcut for Klass & hooked into Sexp::Matcher.parse.
21
+ * Added any matcher to pattern parser.
22
+
23
+ === 4.13.0 / 2019-09-24
24
+
25
+ * 4 minor enhancements:
26
+
27
+ * Added Sexp.q (query) and deprecated Sexp.s to distinguish better and match inspect output.
28
+ * Extended Sexp::Matcher::Parser to allow `not?` patterns.
29
+ * Extended Sexp::Matcher::Parser to cover more method names.
30
+ * Split out all pattern-oriented code to sexp_matcher.rb.
31
+
32
+ * 1 bug fix:
33
+
34
+ * Fixed bug w/ ruby's Array#eql? and #hash not looking at ivars.
35
+
36
+ === 4.12.1 / 2019-06-03
37
+
38
+ * 1 minor enhancement:
39
+
40
+ * Sexp#line now raises if setting w/ non-integer (eg nil).
41
+
42
+ * 3 bug fixes:
43
+
44
+ * Fixed pt_testcase.rb for block args w/ trailing commas.
45
+ * Fixed pt_testcase.rb for stabby proc sexps.
46
+ * Simple fixes for STRICT_SEXP=1.
47
+
1
48
  === 4.12.0 / 2019-03-12
2
49
 
3
50
  * 3 bug fixes:
@@ -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
data/Rakefile CHANGED
@@ -11,6 +11,8 @@ Hoe.add_include_dirs("../../ruby_parser/dev/lib")
11
11
  Hoe.spec 'sexp_processor' do
12
12
  developer 'Ryan Davis', 'ryand-ruby@zenspider.com'
13
13
 
14
+ require_ruby_version "~> 2.2"
15
+
14
16
  license "MIT"
15
17
  end
16
18
 
@@ -363,26 +363,26 @@ class ParseTreeTestCase < Minitest::Test
363
363
  ###
364
364
  # 1.9 specific tests
365
365
 
366
- add_19edgecases("lambda { || (x + 1) }",
366
+ add_19edgecases("-> () { (x + 1) }",
367
367
  s(:iter,
368
- s(:call, nil, :lambda),
368
+ s(:lambda),
369
369
  s(:args),
370
370
  s(:call, s(:call, nil, :x), :+, s(:lit, 1))),
371
371
  "stabby_args" => "->() { (x + 1) }",
372
372
  "stabby_args_doend" => "->() do (x + 1) end")
373
373
 
374
- add_19edgecases("lambda { (x + 1) }",
374
+ add_19edgecases("-> { (x + 1) }",
375
375
  s(:iter,
376
- s(:call, nil, :lambda),
376
+ s(:lambda),
377
377
  0,
378
378
  s(:call, s(:call, nil, :x), :+, s(:lit, 1))),
379
379
  "stabby_args_0_no_parens" => "-> { (x + 1) }",
380
380
  "stabby_args_0_no_parens_doend" => "-> do (x + 1) end",
381
381
  "stabby_args_0_spacebar_broken" => "->{x+1}") # I hate you
382
382
 
383
- add_19edgecases("lambda { |x, y| (x + y) }",
383
+ add_19edgecases("-> (x, y) { (x + y) }",
384
384
  s(:iter,
385
- s(:call, nil, :lambda),
385
+ s(:lambda),
386
386
  s(:args, :x, :y),
387
387
  s(:call, s(:lvar, :x), :+, s(:lvar, :y))),
388
388
  "stabby_args_2" => "->(x, y) { (x + y) }",
@@ -390,9 +390,9 @@ class ParseTreeTestCase < Minitest::Test
390
390
  "stabby_args_2_no_parens" => "-> x, y { (x + y) }",
391
391
  "stabby_args_2_no_parens_doend" => "-> x, y do (x + y) end")
392
392
 
393
- add_19edgecases("lambda { |x| (x + 1) }",
393
+ add_19edgecases("-> (x) { (x + 1) }",
394
394
  s(:iter,
395
- s(:call, nil, :lambda),
395
+ s(:lambda),
396
396
  s(:args, :x),
397
397
  s(:call, s(:lvar, :x), :+, s(:lit, 1))),
398
398
  "stabby_args_1" => "->(x) { (x + 1) }",
@@ -2080,17 +2080,15 @@ class ParseTreeTestCase < Minitest::Test
2080
2080
  "Ruby" => "lambda { |a,| a }",
2081
2081
  "ParseTree" => s(:iter,
2082
2082
  s(:call, nil, :lambda),
2083
- s(:args, :a),
2084
- s(:lvar, :a)),
2085
- "Ruby2Ruby" => "lambda { |a| a }")
2083
+ s(:args, :a, nil),
2084
+ s(:lvar, :a)))
2086
2085
 
2087
2086
  add_tests("lambda_args_norm_comma2",
2088
- "Ruby" => "lambda { |a,b,| a }",
2087
+ "Ruby" => "lambda { |a, b,| a }",
2089
2088
  "ParseTree" => s(:iter,
2090
2089
  s(:call, nil, :lambda),
2091
- s(:args, :a, :b),
2092
- s(:lvar, :a)),
2093
- "Ruby2Ruby" => "lambda { |a, b| a }")
2090
+ s(:args, :a, :b, nil),
2091
+ s(:lvar, :a)))
2094
2092
 
2095
2093
  add_tests("lambda_args_norm_star",
2096
2094
  "Ruby" => "lambda { |a, *star| star }",
@@ -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
  #
@@ -202,13 +210,16 @@ class Sexp < Array # ZenTest FULL
202
210
  each_sexp.find_all { |sexp| sexp.sexp_type == name }
203
211
  end
204
212
 
213
+ UNASSIGNED = Object.new
214
+
205
215
  ##
206
216
  # If passed a line number, sets the line and returns self. Otherwise
207
217
  # returns the line number. This allows you to do message cascades
208
218
  # and still get the sexp back.
209
219
 
210
- def line n = nil
211
- if n then
220
+ def line n = UNASSIGNED
221
+ if n != UNASSIGNED then
222
+ raise ArgumentError, "setting %p.line %p" % [self, n] unless Integer === n
212
223
  @line = n
213
224
  self
214
225
  else
@@ -366,1055 +377,5 @@ def s *args, &blk
366
377
  Sexp.new(*args)
367
378
  end
368
379
 
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
-
380
+ require "sexp_matcher" unless defined? Sexp::Matcher
1420
381
  require "strict_sexp" if ENV["STRICT_SEXP"].to_i > 0