free_mutant/lib/mutant/ast/meta.rb
Markus Schirp 13cd04d9be Introduce AST::Meta to externalize semantic analysis
* Add Mutant::AST namespace to hold all AST related data / helpers.
* Mutant::AST will be externalized into an ast-meta gem that can be
  shared with unparser for deduplication.
* Over the time the mutators itself will not need to deal with semantic
  analysis of the AST anymore by themselves.
* Move AST analysis for send nodes to AST::Meta
* Fix #209
2014-06-29 21:26:58 +00:00

131 lines
2.9 KiB
Ruby

module Mutant
module AST
# Node meta information mixin
module Meta
REGISTRY = {}
# Return meta for node
#
# @param [Parser::AST::Node] node
#
# @return [Meta]
#
# @api private
#
def self.for(node)
REGISTRY.fetch(node.type, Generic).new(node)
end
# Generic metadata for send nodes
class Send
include Concord.new(:node), NamedChildren
children :receiver, :selector
REGISTRY[:send] = self
INDEX_ASSIGNMENT_SELECTOR = :[]=
ATTRIBUTE_ASSIGNMENT_SELECTOR_SUFFIX = '='.freeze
# Return arguments
#
# @return [Enumerable<Parser::AST::Node>]
#
# @api private
#
alias_method :arguments, :remaining_children
# Test if AST node is a valid assignment target
#
# @return [Boolean]
#
# @api private
#
def assignment?
index_assignment? || attribute_assignment?
end
# Test if AST node is an attribute assignment?
#
# @return [Boolean]
#
# @api private
#
def attribute_assignment?
arguments.one? && attribute_assignment_selector?
end
# Test if AST node is an index assign
#
# @return [Boolean]
#
# @api private
#
def index_assignment?
arguments.length.equal?(2) && index_assignment_selector?
end
# Test for binary operator implemented as method
#
# @return [Boolean]
#
# @api private
#
def binary_method_operator?
arguments.one? && Types::BINARY_METHOD_OPERATORS.include?(selector)
end
# Test if node is part of an mlhs
#
# @return [Boolean]
#
# @api private
#
def mlhs?
(index_assignment_selector? && arguments.one?) || (arguments.empty? && attribute_assignment_selector?)
end
private
# Test for index assignment operator
#
# @return [Boolean]
#
# @api private
#
def index_assignment_selector?
selector.equal?(INDEX_ASSIGNMENT_SELECTOR)
end
# Test for attribute assignment selector
#
# @return [Boolean]
#
# @api private
#
def attribute_assignment_selector?
!Types::METHOD_OPERATORS.include?(selector) && selector.to_s.end_with?(ATTRIBUTE_ASSIGNMENT_SELECTOR_SUFFIX)
end
end # Send
# Generic node metatada
class Generic
include Adamantium, Concord.new(:node)
# Test if AST node is a valid assign target
#
# @return [Boolean]
#
# @api private
#
def assignment?
Types::ASSIGNABLE_VARIABLES.include?(node.type)
end
end # Generic
end #
end # AST
end # Mutant