Class: Fast::Matcher

Inherits:
Object
  • Object
show all
Defined in:
lib/fast.rb

Overview

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.

Examples:

simple match

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

simple capture

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

Instance Method Summary collapse

Constructor Details

#initialize(pattern, ast, *args) ⇒ Matcher

Returns a new instance of Matcher.



846
847
848
849
850
851
852
853
854
855
# File 'lib/fast.rb', line 846

def initialize(pattern, ast, *args)
  @ast = ast
  @expression = if pattern.is_a?(String)
                  Array(Fast.expression(pattern))
                else
                  [*pattern].map(&Find.method(:new))
                end
  @captures = []
  prepare_arguments(@expression, args) if args.any?
end

Instance Method Details

#captures?(expression = @expression) ⇒ true

Look recursively into @param expression to check if the expression is have captures.

Returns:

  • (true)

    if any sub expression have captures.



885
886
887
888
889
890
891
# File 'lib/fast.rb', line 885

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) ⇒ Array<Object>, true

Find search captures recursively.

Returns:

  • (Array<Object>)

    of captures from the expression

  • (true)

    in case of no captures in the expression

See Also:



899
900
901
902
903
904
905
906
907
# File 'lib/fast.rb', line 899

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) ⇒ true

Returns:

  • (true)

    if the @param ast recursively matches with expression.

  • #find_captures case matches



859
860
861
862
863
864
865
# File 'lib/fast.rb', line 859

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) ⇒ true

Returns if all children matches with tail.

Returns:

  • (true)

    if all children matches with tail



868
869
870
871
872
873
874
875
876
877
878
879
880
# File 'lib/fast.rb', line 868

def match_tail?(tail, child)
  if tail.last.is_a?(Find) && tail.last.token == '...'
    tail[0...-1].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
  else
    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
end