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

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?.



608
609
610
# File 'lib/sexp.rb', line 608

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

.match_subs?Boolean

Should #=~ match sub-trees?

Returns:

  • (Boolean)


601
602
603
# File 'lib/sexp.rb', line 601

def self.match_subs?
  @@match_subs
end

.parse(s) ⇒ Object

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



739
740
741
# File 'lib/sexp.rb', line 739

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)


690
691
692
# File 'lib/sexp.rb', line 690

def & other
  All.new self, other
end

#-@Object

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

Example:

-s(:a)


700
701
702
# File 'lib/sexp.rb', line 700

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)


663
664
665
666
667
668
# File 'lib/sexp.rb', line 663

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)


646
647
648
649
650
651
# File 'lib/sexp.rb', line 646

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)


710
711
712
# File 'lib/sexp.rb', line 710

def >> other
  Sibling.new self, other
end

#greedy?Boolean

Is this matcher greedy? Defaults to false.

Returns:

  • (Boolean)


717
718
719
# File 'lib/sexp.rb', line 717

def greedy?
  false
end

#inspectObject

:nodoc:



721
722
723
724
725
# File 'lib/sexp.rb', line 721

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

#pretty_print(q) ⇒ Object

:nodoc:



727
728
729
730
731
732
733
# File 'lib/sexp.rb', line 727

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)


623
624
625
626
627
628
629
630
631
632
633
634
635
636
# File 'lib/sexp.rb', line 623

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)


678
679
680
# File 'lib/sexp.rb', line 678

def | other
  Any.new self, other
end