Class: Prism::Pattern
| Relationships & Source Files | |
| Namespace Children | |
| Exceptions: | |
| Inherits: | Object | 
| Defined in: | lib/prism/pattern.rb | 
Overview
A pattern is an object that wraps a Ruby pattern matching expression. The expression would normally be passed to an in clause within a case expression or a rightward assignment expression. For example, in the following snippet:
case node
in ConstantPathNode[ConstantReadNode[name: :Prism], ConstantReadNode[name: :Pattern]]
endthe pattern is the ConstantPathNode[...] expression.
The pattern gets compiled into an object that responds to #call by running the #compile method. This method itself will run back through ::Prism to parse the expression into a tree, then walk the tree to generate the necessary callable objects. For example, if you wanted to compile the expression above into a callable, you would:
callable = Prism::Pattern.new("ConstantPathNode[ConstantReadNode[name: :Prism], ConstantReadNode[name: :Pattern]]").compile
callable.call(node)The callable object returned by #compile is guaranteed to respond to #call with a single argument, which is the node to match against. It also is guaranteed to respond to #===, which means it itself can be used in a case expression, as in:
case node
when callable
endIf the query given to the initializer cannot be compiled into a valid matcher (either because of a syntax error or because it is using syntax we do not yet support) then a CompilationError will be raised.
Class Method Summary
- 
    
      .new(query)  ⇒ Pattern 
    
    constructor
    Create a new pattern with the given query. 
Instance Attribute Summary
- 
    
      #query  
    
    readonly
    The query that this pattern was initialized with. 
Instance Method Summary
- 
    
      #compile  
    
    Compile the query into a callable object that can be used to match against nodes. 
- 
    
      #scan(root)  
    
    Scan the given node and all of its children for nodes that match the pattern. 
- 
    
      #combine_and(left, right)  
    
    private
    Shortcut for combining two procs into one that returns true if both return true. 
- 
    
      #combine_or(left, right)  
    
    private
    Shortcut for combining two procs into one that returns true if either returns true. 
- 
    
      #compile_alternation_pattern_node(node)  
    
    private
    in foo | bar. 
- 
    
      #compile_array_pattern_node(node)  
    
    private
    in [foo, bar, baz]. 
- #compile_constant_path_node(node) private
- 
    
      #compile_constant_read_node(node)  
    
    private
    in ConstantReadNodein String.
- 
    
      #compile_error(node)  
    
    private
    Raise an error because the given node is not supported. 
- 
    
      #compile_hash_pattern_node(node)  
    
    private
    in InstanceVariableReadNode[name: Symbol] in { name: Symbol } 
- 
    
      #compile_nil_node(node)  
    
    private
    in nil. 
- 
    
      #compile_node(node)  
    
    private
    Compile any kind of node. 
- 
    
      #compile_regular_expression_node(node)  
    
    private
    in /foo/. 
- 
    
      #compile_string_node(node)  
    
    private
    in “” in “foo”. 
- 
    
      #compile_symbol_node(node)  
    
    private
    in :+ in :foo.
Constructor Details
    .new(query)  ⇒ Pattern 
  
Create a new pattern with the given query. The query should be a string containing a Ruby pattern matching expression.
Instance Attribute Details
#query (readonly)
The query that this pattern was initialized with.
# File 'lib/prism/pattern.rb', line 59
attr_reader :query
Instance Method Details
#combine_and(left, right) (private)
Shortcut for combining two procs into one that returns true if both return true.
# File 'lib/prism/pattern.rb', line 95
def combine_and(left, right) ->(other) { left.call(other) && right.call(other) } end
#combine_or(left, right) (private)
Shortcut for combining two procs into one that returns true if either returns true.
# File 'lib/prism/pattern.rb', line 101
def combine_or(left, right) ->(other) { left.call(other) || right.call(other) } end
#compile
Compile the query into a callable object that can be used to match against nodes.
# File 'lib/prism/pattern.rb', line 70
def compile result = Prism.parse("case nil\nin #{query}\nend") compile_node(result.value.statements.body.last.conditions.last.pattern) end
#compile_alternation_pattern_node(node) (private)
in foo | bar
# File 'lib/prism/pattern.rb', line 136
def compile_alternation_pattern_node(node) combine_or(compile_node(node.left), compile_node(node.right)) end
#compile_array_pattern_node(node) (private)
in [foo, bar, baz]
# File 'lib/prism/pattern.rb', line 111
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_path_node(node) (private)
[ GitHub ]# File 'lib/prism/pattern.rb', line 141
def compile_constant_path_node(node) parent = node.parent if parent.is_a?(ConstantReadNode) && parent.slice == "Prism" compile_node(node.child) else compile_error(node) end end
#compile_constant_read_node(node) (private)
in ConstantReadNode in String
# File 'lib/prism/pattern.rb', line 153
def compile_constant_read_node(node) value = node.slice if Prism.const_defined?(value, false) clazz = Prism.const_get(value) ->(other) { clazz === other } elsif Object.const_defined?(value, false) clazz = Object.const_get(value) ->(other) { clazz === other } else compile_error(node) end end
#compile_error(node) (private)
Raise an error because the given node is not supported.
# File 'lib/prism/pattern.rb', line 106
def compile_error(node) raise CompilationError, node.inspect end
#compile_hash_pattern_node(node) (private)
in InstanceVariableReadNode[name: Symbol] in { name: Symbol }
# File 'lib/prism/pattern.rb', line 171
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| [element.key.unescaped.to_sym, compile_node(element.value)] 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) (private)
in nil
# File 'lib/prism/pattern.rb', line 196
def compile_nil_node(node) ->(attribute) { attribute.nil? } end
#compile_node(node) (private)
Compile any kind of node. Dispatch out to the individual compilation methods based on the type of node.
# File 'lib/prism/pattern.rb', line 225
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) (private)
in /foo/
# File 'lib/prism/pattern.rb', line 201
def compile_regular_expression_node(node) regexp = Regexp.new(node.unescaped, node.closing[1..]) ->(attribute) { regexp === attribute } end
#compile_string_node(node) (private)
in “” in “foo”
# File 'lib/prism/pattern.rb', line 209
def compile_string_node(node) string = node.unescaped ->(attribute) { string === attribute } end
#compile_symbol_node(node) (private)
in :+ in :foo
# File 'lib/prism/pattern.rb', line 217
def compile_symbol_node(node) symbol = node.unescaped.to_sym ->(attribute) { symbol === attribute } end
#scan(root)
Scan the given node and all of its children for nodes that match the pattern. If a block is given, it will be called with each node that matches the pattern. If no block is given, an enumerator will be returned that will yield each node that matches the pattern.