Class: Sexp::Matcher

Inherits:
Sexp show all
Defined in:
lib/sexp.rb

Overview

Defines a family of objects that can be used to match sexps to certain types of patterns, much like regexps can be used on strings. Generally you won’t use this class directly.

You would normally create a matcher using the top-level #s method, but with a block, calling into the Sexp factory methods. For example:

s{ s(:class, m(/^Test/), _, ___) }

This creates a matcher for classes whose names start with “Test”. It uses Sexp.m to create a Sexp::Matcher::Pattern matcher, Sexp._ to create a Sexp::Matcher::Wild matcher, and Sexp._ to create a Sexp::Matcher::Remaining matcher. It works like this:

s{              # start to create a pattern
  s(            # create a sexp matcher
    :class.     # for class nodes
    m(/^Test/), # matching name slots that start with "Test"
    _,          # any superclass value
    ___         # and whatever is in the class
   )
 }

Then you can use that with #=~, #/, Sexp#replace_sexp, and others.

For more examples, see the various Sexp class methods, the examples, and the tests supplied with Sexp.

  • For pattern creation, see factory methods: Sexp::_, Sexp::_, etc.

  • For matching returning truthy/falsey results, see Sexp#=~.

  • For case expressions, see Matcher#===.

  • For getting all subtree matches, see Sexp#/.

If rdoc didn’t suck, these would all be links.

Direct Known Subclasses

All, Any, Atom, Child, Include, Not, Pattern, Remaining, Sibling, Type, Wild

Defined Under Namespace

Classes: Parser

Constant Summary

Constants inherited from Sexp

UNASSIGNED

Instance Attribute Summary

Attributes inherited from Sexp

#comments, #file, #line

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Sexp

#==, _, ___, all, any, #array_type?, atom, child, #compact, #deep_each, #depth, #each_of_type, #each_sexp, #find_and_replace_all, #find_node, #find_nodes, from_array, #gsub, include, #initialize, #line_max, m, #map, #mass, #method_missing, #new, not?, #replace_sexp, #respond_to?, s, #search_each, #sexp_body, #sexp_body=, #sexp_type, #sexp_type=, #shift, #structure, #sub, t, #to_a

Constructor Details

This class inherits a constructor from Sexp

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Sexp

Class Method Details

.match_subs=(o) ⇒ Object

Setter for match_subs?.



611
612
613
# File 'lib/sexp.rb', line 611

def self.match_subs= o
  @@match_subs = o
end

.match_subs?Boolean

Should #=~ match sub-trees?

Returns:

  • (Boolean)


604
605
606
# File 'lib/sexp.rb', line 604

def self.match_subs?
  @@match_subs
end

.parse(s) ⇒ Object

Parse a lispy string representation of a matcher into a Matcher. See Parser.



742
743
744
# File 'lib/sexp.rb', line 742

def self.parse s
  Parser.new(s).parse
end

Instance Method Details

#&(other) ⇒ Object

Combines the Matcher with another Matcher, the resulting one will be satisfied only if both Matchers would be satisfied.

TODO: redirect Example:

t(:a) & include(:b)


693
694
695
# File 'lib/sexp.rb', line 693

def & other
  All.new self, other
end

#-@Object

Returns a Matcher that matches whenever this Matcher would not have matched

Example:

-s(:a)


703
704
705
# File 'lib/sexp.rb', line 703

def -@
  Not.new self
end

#/(sexp) ⇒ Object

Searches through sexp for all sub-trees that match this matcher and returns a MatchCollection for each match.

TODO: redirect? Example:

Q{ s(:b) } / s(:a, s(:b)) => [s(:b)]

Raises:

  • (ArgumentError)


666
667
668
669
670
671
# File 'lib/sexp.rb', line 666

def / sexp
  raise ArgumentError, "can't both be matchers" if Matcher === sexp

  # TODO: move search_each into matcher?
  MatchCollection.new sexp.search_each(self).to_a
end

#=~(sexp) ⇒ Object Also known as: ===

Tree equivalent to String#=~, returns true if self matches sexp as a whole or in a sub-tree (if match_subs?).

TODO: maybe this should NOT be aliased to === ?

TODO: example

Raises:

  • (ArgumentError)


649
650
651
652
653
654
# File 'lib/sexp.rb', line 649

def =~ sexp
  raise ArgumentError, "Can't both be matchers: %p" % [sexp] if Matcher === sexp

  self.satisfy?(sexp) ||
    (self.class.match_subs? && sexp.each_sexp.any? { |sub| self =~ sub })
end

#>>(other) ⇒ Object

Returns a Matcher that matches if this has a sibling o

Example:

s(:a) >> s(:b)


713
714
715
# File 'lib/sexp.rb', line 713

def >> other
  Sibling.new self, other
end

#greedy?Boolean

Is this matcher greedy? Defaults to false.

Returns:

  • (Boolean)


720
721
722
# File 'lib/sexp.rb', line 720

def greedy?
  false
end

#inspectObject

:nodoc:



724
725
726
727
728
# File 'lib/sexp.rb', line 724

def inspect # :nodoc:
  s = super
  s[0] = "q"
  s
end

#pretty_print(q) ⇒ Object

:nodoc:



730
731
732
733
734
735
736
# File 'lib/sexp.rb', line 730

def pretty_print q # :nodoc:
  q.group 1, "q(", ")" do
    q.seplist self do |v|
      q.pp v
    end
  end
end

#satisfy?(o) ⇒ Boolean

Does this matcher actually match o? Returns falsey if o is not a Sexp or if any sub-tree of o is not satisfied by or equal to its corresponding sub-matcher.

– TODO: push this up to Sexp and make this the workhorse TODO: do the same with ===/satisfy?

Returns:

  • (Boolean)


626
627
628
629
630
631
632
633
634
635
636
637
638
639
# File 'lib/sexp.rb', line 626

def satisfy? o
  return unless o.kind_of?(Sexp) &&
    (length == o.length || Matcher === last && last.greedy?)

  each_with_index.all? { |child, i|
    sexp = o.at i
    if Sexp === child then # TODO: when will this NOT be a matcher?
      sexp = o.sexp_body i if child.respond_to?(:greedy?) && child.greedy?
      child.satisfy? sexp
    else
      child == sexp
    end
  }
end

#|(other) ⇒ Object

Combines the Matcher with another Matcher, the resulting one will be satisfied if either Matcher would be satisfied.

TODO: redirect Example:

s(:a) | s(:b)


681
682
683
# File 'lib/sexp.rb', line 681

def | other
  Any.new self, other
end