class RuboCop::AST::NodePattern::Compiler::NodePatternSubcompiler
/docs/modules/ROOT/pages/node_pattern.adoc
Doc on how this fits in the compiling process:
or it’s ‘node.type` if `seq_head` is true
for a given value `var` (typically a RuboCop::AST::Node)
Compiles code that evalues to true or false
def access_element
def access_element seq_head ? "#{access}.type" : access end
def access_node
def access_node return access if seq_head "#{compile_guard_clause} && #{access}" end
def compile_args(arg_list, first: nil)
-
(String, nil)
-
Parameters:
-
(
Array
) --, nil
def compile_args(arg_list, first: nil) args = arg_list&.map { |arg| compiler.compile_as_atom(arg) } args = [first, *args] if first "(#{args.join(', ')})" if args end
def compile_guard_clause
def compile_guard_clause "#{access}.is_a?(::RuboCop::AST::Node)" end
def compile_value_match(value)
def compile_value_match(value) "#{value} === #{access_element}" end
def initialize(compiler, var: nil, access: var, seq_head: false)
def initialize(compiler, var: nil, access: var, seq_head: false) super(compiler) @var = var @access = access @seq_head = seq_head end
def multiple_access(kind)
def multiple_access(kind) return yield @var if @var compiler.with_temp_variables(kind) do |var| memo = "#{var} = #{access}" @var = @access = var "(#{memo}; #{yield @var})" end end
def visit_ascend
def visit_ascend compiler.with_temp_variables do |ascend| expr = compiler.compile_as_node_pattern(node.child, var: ascend) "(#{ascend} = #{access_node}) && (#{ascend} = #{ascend}.parent) && #{expr}" end end
def visit_capture
def visit_capture "(#{compiler.next_capture} = #{access_element}; #{compile(node.child)})" end
def visit_descend
def visit_descend compiler.with_temp_variables { |descendant| <<~RUBY.chomp } ::RuboCop::AST::NodePattern.descend(#{access}).any? do |#{descendant}| #{compiler.compile_as_node_pattern(node.child, var: descendant)} end RUBY end
def visit_function_call
def visit_function_call "#{node.method_name}#{compile_args(node.arg_list, first: access_element)}" end
def visit_intersection
def visit_intersection multiple_access(:intersection) do node.children.map { |child| compile(child) } .join(' && ') end end
def visit_negation
def visit_negation expr = compile(node.child) "!(#{expr})" end
def visit_node_type
def visit_node_type "#{access_node}.#{node.child.to_s.tr('-', '_')}_type?" end
def visit_other_type
def visit_other_type value = compiler.compile_as_atom(node) compile_value_match(value) end
def visit_predicate
def visit_predicate "#{access_element}.#{node.method_name}#{compile_args(node.arg_list)}" end
def visit_sequence
def visit_sequence multiple_access(:sequence) do |var| term = compiler.compile_sequence(node, var: var) "#{compile_guard_clause} && #{term}" end end
def visit_unify
def visit_unify name = compiler.bind(node.child) do |unify_name| # double assign to avoid "assigned but unused variable" return "(#{unify_name} = #{access_element}; #{unify_name} = #{unify_name}; true)" end compile_value_match(name) end
def visit_union
def visit_union multiple_access(:union) do terms = compiler.each_union(node.children) .map { |child| compile(child) } "(#{terms.join(' || ')})" end end
def visit_wildcard
def visit_wildcard 'true' end