class Prism::Pattern
Pattern 是一个封装了 Ruby 模式匹配表达式的对象。该表达式通常会传递给 `case` 表达式内的 `in` 子句或向右赋值表达式。例如,在以下代码片段中
case node in ConstantPathNode[ConstantReadNode[name: :Prism], ConstantReadNode[name: :Pattern]] end
模式是 ConstantPathNode[...]
表达式。
模式会被编译成一个可以通过运行 compile
方法来响应 `call` 的对象。此方法本身将再次通过 Prism
来解析表达式为一棵树,然后遍历该树以生成必要的调用对象。例如,如果你想将上面的表达式编译为可调用对象,你可以这样做:
callable = Prism::Pattern.new("ConstantPathNode[ConstantReadNode[name: :Prism], ConstantReadNode[name: :Pattern]]").compile callable.call(node)
compile
返回的可调用对象保证使用一个参数响应 `call`,该参数是要匹配的节点。它也保证响应 ===
,这意味着它本身可以在 `case` 表达式中使用,例如:
case node when callable end
如果给初始化器的查询无法编译成有效的匹配器(要么是因为语法错误,要么是因为使用了我们尚不支持的语法),则会抛出 Prism::Pattern::CompilationError
异常。
属性
此模式初始化时使用的查询。
公共类方法
源码
# File lib/prism/pattern.rb, line 63 def initialize(query) @query = query @compiled = nil end
使用给定的查询创建一个新的模式。查询应为一个包含 Ruby 模式匹配表达式的字符串。
公共实例方法
源码
# File lib/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
将查询编译为可调用对象,该对象可用于匹配节点。
源码
# File lib/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
扫描给定节点及其所有子节点,查找与模式匹配的节点。如果给定一个块,则将使用每个匹配的节点调用该块。如果没有给定块,则将返回一个枚举器,该枚举器将产生每个匹配的节点。
私有实例方法
源码
# File lib/prism/pattern.rb, line 102 def combine_and(left, right) ->(other) { left.call(other) && right.call(other) } end
将两个 proc 合并为一个,如果两者都返回 true,则返回 true 的快捷方式。
源码
# File lib/prism/pattern.rb, line 108 def combine_or(left, right) ->(other) { left.call(other) || right.call(other) } end
将两个 proc 合并为一个,如果其中任何一个返回 true,则返回 true 的快捷方式。
源码
# File lib/prism/pattern.rb, line 143 def compile_alternation_pattern_node(node) combine_or(compile_node(node.left), compile_node(node.right)) end
in foo | bar
源码
# File lib/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
in [foo, bar, baz]
源码
# File lib/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
编译与常量关联的名称。
源码
# File lib/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
源码
# File lib/prism/pattern.rb, line 163 def compile_constant_read_node(node) compile_constant_name(node, node.name) end
in ConstantReadNode
in String
源码
# File lib/prism/pattern.rb, line 113 def compile_error(node) raise CompilationError, node.inspect end
由于给定的节点不受支持,因此引发错误。
源码
# File lib/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
in InstanceVariableReadNode[name: Symbol] in { name: Symbol
}
源码
# File lib/prism/pattern.rb, line 214 def compile_nil_node(node) ->(attribute) { attribute.nil? } end
in nil
源码
# File lib/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
编译任何类型的节点。根据节点类型分派到各个编译方法。
源码
# File lib/prism/pattern.rb, line 219 def compile_regular_expression_node(node) regexp = Regexp.new(node.unescaped, node.closing[1..]) ->(attribute) { regexp === attribute } end
in /foo/
源码
# File lib/prism/pattern.rb, line 227 def compile_string_node(node) string = node.unescaped ->(attribute) { string === attribute } end
in “” in “foo”
源码
# File lib/prism/pattern.rb, line 235 def compile_symbol_node(node) symbol = node.unescaped.to_sym ->(attribute) { symbol === attribute } end
in :+ in :foo