sexp_processor 4.12.0 → 4.15.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: 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