class Prism::Pattern

模式是一个包装 Ruby 模式匹配表达式的对象。该表达式通常会传递给 `case` 表达式或右向赋值表达式中的 `in` 子句。例如,在以下代码片段中

case node
in ConstantPathNode[ConstantReadNode[name: :Prism], ConstantReadNode[name: :Pattern]]
end

模式是 ConstantPathNode[...] 表达式。

模式会被编译成一个对象,通过运行 compile 方法来响应调用。此方法本身会回调 Prism 来将表达式解析成一个树,然后遍历树来生成必要的可调用对象。例如,如果你想将上面的表达式编译成可调用对象,你需要

callable = Prism::Pattern.new("ConstantPathNode[ConstantReadNode[name: :Prism], ConstantReadNode[name: :Pattern]]").compile
callable.call(node)

compile 返回的可调用对象保证以一个参数响应调用,该参数是要匹配的节点。它还保证响应 ===,这意味着它本身可以在 `case` 表达式中使用,如下所示

case node
when callable
end

如果给初始化器的查询无法编译成有效的匹配器(要么是因为语法错误,要么是因为它正在使用我们尚不支持的语法),则会引发 Prism::Pattern::CompilationError

属性

query[R]

此模式初始化的查询。

公共类方法

new(query) 点击切换源代码

使用给定的查询创建一个新模式。查询应该是一个包含 Ruby 模式匹配表达式的字符串。

# File prism/pattern.rb, line 63
def initialize(query)
  @query = query
  @compiled = nil
end

公共实例方法

compile() 点击切换源代码

将查询编译成一个可调用对象,可用于匹配节点。

# File prism/pattern.rb, line 70
def compile
  result = Prism.parse("case nil\nin #{query}\nend")

  case_match_node = result.value.statements.body.last
  raise CompilationError, case_match_node.inspect unless case_match_node.is_a?(CaseMatchNode)

  in_node = case_match_node.conditions.last
  raise CompilationError, in_node.inspect unless in_node.is_a?(InNode)

  compile_node(in_node.pattern)
end
scan(root) { |node| ... } 点击切换源代码

扫描给定节点及其所有子节点中与该模式匹配的节点。如果给定了块,则会为每个匹配的节点调用该块。如果没有给出块,则会返回一个枚举器,该枚举器将产生每个匹配该模式的节点。

# File prism/pattern.rb, line 86
def scan(root)
  return to_enum(:scan, root) unless block_given?

  @compiled ||= compile
  queue = [root]

  while (node = queue.shift)
    yield node if @compiled.call(node) # steep:ignore
    queue.concat(node.compact_child_nodes)
  end
end

私有实例方法

combine_and(left, right) 点击切换源代码

将两个 proc 合并为一个的快捷方式,如果两者都返回 true,则返回 true。

# File prism/pattern.rb, line 102
def combine_and(left, right)
  ->(other) { left.call(other) && right.call(other) }
end
combine_or(left, right) 点击切换源代码

将两个 proc 合并为一个的快捷方式,如果任何一个返回 true,则返回 true。

# File prism/pattern.rb, line 108
def combine_or(left, right)
  ->(other) { left.call(other) || right.call(other) }
end
compile_alternation_pattern_node(node) 点击切换源代码

in foo | bar

# File prism/pattern.rb, line 143
def compile_alternation_pattern_node(node)
  combine_or(compile_node(node.left), compile_node(node.right))
end
compile_array_pattern_node(node) 点击切换源代码

in [foo, bar, baz]

# File prism/pattern.rb, line 118
def compile_array_pattern_node(node)
  compile_error(node) if !node.rest.nil? || node.posts.any?

  constant = node.constant
  compiled_constant = compile_node(constant) if constant

  preprocessed = node.requireds.map { |required| compile_node(required) }

  compiled_requireds = ->(other) do
    deconstructed = other.deconstruct

    deconstructed.length == preprocessed.length &&
      preprocessed
        .zip(deconstructed)
        .all? { |(matcher, value)| matcher.call(value) }
  end

  if compiled_constant
    combine_and(compiled_constant, compiled_requireds)
  else
    compiled_requireds
  end
end
compile_constant_name(node, name) 点击切换源代码

编译与常量关联的名称。

# File prism/pattern.rb, line 168
def compile_constant_name(node, name)
  if Prism.const_defined?(name, false)
    clazz = Prism.const_get(name)

    ->(other) { clazz === other }
  elsif Object.const_defined?(name, false)
    clazz = Object.const_get(name)

    ->(other) { clazz === other }
  else
    compile_error(node)
  end
end
compile_constant_path_node(node) 点击切换源代码

in Prism::ConstantReadNode

# File prism/pattern.rb, line 148
def compile_constant_path_node(node)
  parent = node.parent

  if parent.is_a?(ConstantReadNode) && parent.slice == "Prism"
    name = node.name
    raise CompilationError, node.inspect if name.nil?

    compile_constant_name(node, name)
  else
    compile_error(node)
  end
end
compile_constant_read_node(node) 点击切换源代码

在 String 中的 ConstantReadNode

# File prism/pattern.rb, line 163
def compile_constant_read_node(node)
  compile_constant_name(node, node.name)
end
compile_error(node) 点击切换源代码

引发错误,因为不支持给定的节点。

# File prism/pattern.rb, line 113
def compile_error(node)
  raise CompilationError, node.inspect
end
compile_hash_pattern_node(node) 点击切换源代码

在 { name: Symbol } 中的 InstanceVariableReadNode[name: Symbol] 中

# File prism/pattern.rb, line 184
def compile_hash_pattern_node(node)
  compile_error(node) if node.rest
  compiled_constant = compile_node(node.constant) if node.constant

  preprocessed =
    node.elements.to_h do |element|
      key = element.key
      if key.is_a?(SymbolNode)
        [key.unescaped.to_sym, compile_node(element.value)]
      else
        raise CompilationError, element.inspect
      end
    end

  compiled_keywords = ->(other) do
    deconstructed = other.deconstruct_keys(preprocessed.keys)

    preprocessed.all? do |keyword, matcher|
      deconstructed.key?(keyword) && matcher.call(deconstructed[keyword])
    end
  end

  if compiled_constant
    combine_and(compiled_constant, compiled_keywords)
  else
    compiled_keywords
  end
end
compile_nil_node(node) 点击切换源代码

在 nil 中

# File prism/pattern.rb, line 214
def compile_nil_node(node)
  ->(attribute) { attribute.nil? }
end
compile_node(node) 点击切换源代码

编译任何类型的节点。根据节点类型分派到各个编译方法。

# File prism/pattern.rb, line 243
def compile_node(node)
  case node
  when AlternationPatternNode
    compile_alternation_pattern_node(node)
  when ArrayPatternNode
    compile_array_pattern_node(node)
  when ConstantPathNode
    compile_constant_path_node(node)
  when ConstantReadNode
    compile_constant_read_node(node)
  when HashPatternNode
    compile_hash_pattern_node(node)
  when NilNode
    compile_nil_node(node)
  when RegularExpressionNode
    compile_regular_expression_node(node)
  when StringNode
    compile_string_node(node)
  when SymbolNode
    compile_symbol_node(node)
  else
    compile_error(node)
  end
end
compile_regular_expression_node(node) 点击切换源代码

在 /foo/ 中

# File prism/pattern.rb, line 219
def compile_regular_expression_node(node)
  regexp = Regexp.new(node.unescaped, node.closing[1..])

  ->(attribute) { regexp === attribute }
end
compile_string_node(node) 点击切换源代码

在 “” 中,在 “foo” 中

# File prism/pattern.rb, line 227
def compile_string_node(node)
  string = node.unescaped

  ->(attribute) { string === attribute }
end
compile_symbol_node(node) 点击切换源代码

在 :+ 中,在 :foo 中

# File prism/pattern.rb, line 235
def compile_symbol_node(node)
  symbol = node.unescaped.to_sym

  ->(attribute) { symbol === attribute }
end