Class: Sexp::Matcher

Inherits:
Sexp show all
Defined in:
lib/sexp_matcher.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, #line_max

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, #eql?, #find_and_replace_all, #find_node, #find_nodes, from_array, #gsub, #hash, include, #initialize, k, m, #map, #mass, #method_missing, #new, not?, q, #replace_sexp, #respond_to?, s, #search_each, #sexp_body, #sexp_body=, #sexp_type, #sexp_type=, #shift, #structure, #sub, t, #to_a, #value

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



259
260
261
# File 'lib/sexp_matcher.rb', line 259

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

.match_subs?Boolean

Should #=~ match sub-trees?

Returns:

  • (Boolean)


252
253
254
# File 'lib/sexp_matcher.rb', line 252

def self.match_subs?
  @@match_subs
end

.parse(s) ⇒ Object

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



390
391
392
# File 'lib/sexp_matcher.rb', line 390

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)


341
342
343
# File 'lib/sexp_matcher.rb', line 341

def & other
  All.new self, other
end

#-@Object

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

Example:

-s(:a)


351
352
353
# File 'lib/sexp_matcher.rb', line 351

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)


314
315
316
317
318
319
# 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

#=~(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)


297
298
299
300
301
302
# 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

#>>(other) ⇒ Object

Returns a Matcher that matches if this has a sibling o

Example:

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


361
362
363
# File 'lib/sexp_matcher.rb', line 361

def >> other
  Sibling.new self, other
end

#greedy?Boolean

Is this matcher greedy? Defaults to false.

Returns:

  • (Boolean)


368
369
370
# File 'lib/sexp_matcher.rb', line 368

def greedy?
  false
end

#inspectObject

:nodoc:



372
373
374
375
376
# File 'lib/sexp_matcher.rb', line 372

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

#pretty_print(q) ⇒ Object

:nodoc:



378
379
380
381
382
383
384
# File 'lib/sexp_matcher.rb', line 378

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)


274
275
276
277
278
279
280
281
282
283
284
285
286
287
# 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

#|(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)


329
330
331
# File 'lib/sexp_matcher.rb', line 329

def | other
  Any.new self, other
end