class Sexp::Matcher
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.
Public Class Methods
Setter for match_subs?
.
# File lib/sexp_matcher.rb, line 259 def self.match_subs= o @@match_subs = o end
Should =~
match sub-trees?
# File lib/sexp_matcher.rb, line 252 def self.match_subs? @@match_subs end
Parse a lispy string representation of a matcher into a Matcher
. See Parser
.
# File lib/sexp_matcher.rb, line 390 def self.parse s Parser.new(s).parse end
Public Instance Methods
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)]
# File lib/sexp_matcher.rb, line 314 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
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
# File lib/sexp_matcher.rb, line 297 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
Returns a Matcher
that matches if this has a sibling o
Example:
s(:a) >> s(:b)
# File lib/sexp_matcher.rb, line 361 def >> other Sibling.new self, other end
Is this matcher greedy? Defaults to false.
# File lib/sexp_matcher.rb, line 368 def greedy? false end
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.
# File lib/sexp_matcher.rb, line 274 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