class Fast::Matcher

Joins the AST and the search expression to create a complete matcher that recusively check if the node pattern expression matches with the given AST.

Using captures

One of the most important features of the matcher is find captures and also bind them on demand in case the expression is using previous captures.

@example simple match

ast = Fast.ast("a = 1")
expression = Fast.expression("(lvasgn _ (int _))")
Matcher.new(expression, ast).match? # true

@example simple capture

ast = Fast.ast("a = 1")
expression = Fast.expression("(lvasgn _ (int $_))")
Matcher.new(expression, ast).match? # => [1]

Public Class Methods

new(pattern, ast, *args) click to toggle source
# File lib/fast.rb, line 692
def initialize(pattern, ast, *args)
  @ast = ast
  @expression = if pattern.is_a?(String)
                  Fast.expression(pattern)
                else
                  [*pattern].map(&Find.method(:new))
                end
  @captures = []
  prepare_arguments(@expression, args) if args.any?
end

Public Instance Methods

captures?(expression = @expression) click to toggle source

Look recursively into @param expression to check if the expression is have captures. @return [true] if any sub expression have captures.

# File lib/fast.rb, line 724
def captures?(expression = @expression)
  case expression
  when Capture then true
  when Array then expression.any?(&method(:captures?))
  when Find then captures?(expression.token)
  end
end
find_captures(expression = @expression) click to toggle source

Find search captures recursively.

@return [Array<Object>] of captures from the expression @return [true] in case of no captures in the expression @see Fast::Capture @see Fast::FindFromArgument

# File lib/fast.rb, line 738
def find_captures(expression = @expression)
  return true if expression == @expression && !captures?(expression)

  case expression
  when Capture then expression.captures
  when Array then expression.flat_map(&method(:find_captures)).compact
  when Find then find_captures(expression.token)
  end
end
match?(expression = @expression, ast = @ast) click to toggle source

@return [true] if the @param ast recursively matches with expression. @return find_captures case matches

# File lib/fast.rb, line 705
def match?(expression = @expression, ast = @ast)
  head, *tail_expression = expression
  return false unless head.match?(ast)
  return find_captures if tail_expression.empty?

  match_tail?(tail_expression, ast.children)
end
match_tail?(tail, child) click to toggle source

@return [true] if all children matches with tail

# File lib/fast.rb, line 714
def match_tail?(tail, child)
  tail.each_with_index.all? do |token, i|
    prepare_token(token)
    token.is_a?(Array) ? match?(token, child[i]) : token.match?(child[i])
  end && find_captures
end

Private Instance Methods

prepare_arguments(expression, arguments) click to toggle source

Prepare arguments case the expression needs to bind extra arguments. @return [void]

# File lib/fast.rb, line 752
def prepare_arguments(expression, arguments)
  case expression
  when Array
    expression.each do |item|
      prepare_arguments(item, arguments)
    end
  when Fast::FindFromArgument
    expression.arguments = arguments
  when Fast::Find
    prepare_arguments expression.token, arguments
  end
end
prepare_token(token) click to toggle source

Prepare token with previous captures @param [FindWithCapture] token set the current captures @return [void] @see [FindWithCapture#previous_captures]

# File lib/fast.rb, line 769
def prepare_token(token)
  case token
  when Fast::FindWithCapture
    token.previous_captures = find_captures
  end
end