class YARP::Pattern
raised.
do not yet support) then a YARP::Pattern::CompilationError will be
matcher (either because of a syntax error or because it is using syntax we
If the query given to the initializer cannot be compiled into a valid
end
when callable
case node
expression, as in:
guaranteed to respond to #===, which means it itself can be used in a ‘case`
with a single argument, which is the node to match against. It also is
The callable object returned by #compile is guaranteed to respond to #call
callable.call(node)
callable = YARP::Pattern.new(“ConstantPathNode[ConstantReadNode[name: :YARP], ConstantReadNode[name: :Pattern]]”).compile
expression above into a callable, you would:
necessary callable objects. For example, if you wanted to compile the
parse the expression into a tree, then walk the tree to generate the
the #compile method. This method itself will run back through YARP to
The pattern gets compiled into an object that responds to #call by running
the pattern is the `ConstantPathNode` expression.
end
in ConstantPathNode[ConstantReadNode[name: :YARP], ConstantReadNode[name: :Pattern]]
case node
following snippet:
expression or a rightward assignment expression. For example, in the
expression would normally be passed to an `in` clause within a `case`
A pattern is an object that wraps a Ruby pattern matching expression. The
def combine_and(left, right)
Shortcut for combining two procs into one that returns true if both return
def combine_and(left, right) ->(other) { left.call(other) && right.call(other) } end
def combine_or(left, right)
Shortcut for combining two procs into one that returns true if either
def combine_or(left, right) ->(other) { left.call(other) || right.call(other) } end
def compile
def compile result = YARP.parse("case nil\nin #{query}\nend") compile_node(result.value.statements.body.last.conditions.last.pattern) end
def compile_alternation_pattern_node(node)
def compile_alternation_pattern_node(node) combine_or(compile_node(node.left), compile_node(node.right)) end
def compile_array_pattern_node(node)
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
def compile_constant_path_node(node)
def compile_constant_path_node(node) parent = node.parent if parent.is_a?(ConstantReadNode) && parent.slice == "YARP" compile_node(node.child) else compile_error(node) end end
def compile_constant_read_node(node)
in ConstantReadNode
def compile_constant_read_node(node) value = node.slice if YARP.const_defined?(value, false) clazz = YARP.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
def compile_error(node)
def compile_error(node) raise CompilationError, node.inspect end
def compile_hash_pattern_node(node)
in InstanceVariableReadNode[name: Symbol]
def compile_hash_pattern_node(node) compile_error(node) unless node.kwrest.nil? compiled_constant = compile_node(node.constant) if node.constant preprocessed = node.assocs.to_h do |assoc| [assoc.key.unescaped.to_sym, compile_node(assoc.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
def compile_nil_node(node)
def compile_nil_node(node) ->(attribute) { attribute.nil? } end
def compile_node(node)
Compile any kind of node. Dispatch out to the individual compilation
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
def compile_regular_expression_node(node)
def compile_regular_expression_node(node) regexp = Regexp.new(node.unescaped, node.closing[1..]) ->(attribute) { regexp === attribute } end
def compile_string_node(node)
in ""
def compile_string_node(node) string = node.unescaped ->(attribute) { string === attribute } end
def compile_symbol_node(node)
in :+
def compile_symbol_node(node) symbol = node.unescaped.to_sym ->(attribute) { symbol === attribute } end
def initialize(query)
def initialize(query) @query = query @compiled = nil end
def scan(root)
def scan(root) return to_enum(__method__, root) unless block_given? @compiled ||= compile queue = [root] while (node = queue.shift) yield node if @compiled.call(node) queue.concat(node.child_nodes.compact) end end