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
。
属性
此模式初始化的查询。
公共类方法
使用给定的查询创建一个新模式。查询应该是一个包含 Ruby 模式匹配表达式的字符串。
# File prism/pattern.rb, line 63 def initialize(query) @query = query @compiled = nil end
公共实例方法
将查询编译成一个可调用对象,可用于匹配节点。
# 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
扫描给定节点及其所有子节点中与该模式匹配的节点。如果给定了块,则会为每个匹配的节点调用该块。如果没有给出块,则会返回一个枚举器,该枚举器将产生每个匹配该模式的节点。
# 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
私有实例方法
将两个 proc 合并为一个的快捷方式,如果两者都返回 true,则返回 true。
# File prism/pattern.rb, line 102 def combine_and(left, right) ->(other) { left.call(other) && right.call(other) } end
将两个 proc 合并为一个的快捷方式,如果任何一个返回 true,则返回 true。
# File prism/pattern.rb, line 108 def combine_or(left, right) ->(other) { left.call(other) || right.call(other) } end
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
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
编译与常量关联的名称。
# 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
# 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
在 String 中的 ConstantReadNode
中
# File prism/pattern.rb, line 163 def compile_constant_read_node(node) compile_constant_name(node, node.name) end
引发错误,因为不支持给定的节点。
# File prism/pattern.rb, line 113 def compile_error(node) raise CompilationError, node.inspect end
在 { 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
在 nil 中
# File prism/pattern.rb, line 214 def compile_nil_node(node) ->(attribute) { attribute.nil? } end
编译任何类型的节点。根据节点类型分派到各个编译方法。
# 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
在 /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
在 “” 中,在 “foo” 中
# File prism/pattern.rb, line 227 def compile_string_node(node) string = node.unescaped ->(attribute) { string === attribute } end
在 :+ 中,在 :foo 中
# File prism/pattern.rb, line 235 def compile_symbol_node(node) symbol = node.unescaped.to_sym ->(attribute) { symbol === attribute } end