free_mutant/lib/mutant/mutator/node.rb

233 lines
4.6 KiB
Ruby
Raw Normal View History

module Mutant
2013-07-29 17:34:02 -07:00
# Generator for mutations
class Mutator
2013-07-29 17:34:02 -07:00
# Abstract base class for node mutators
class Node < self
include AbstractType, Unparser::Constants
include AST::NamedChildren, AST::NodePredicates, AST::Sexp, AST::Nodes
TAUTOLOGY = ->(_input) { true }
# Helper to define a named child
#
# @param [Parser::AST::Node] node
2013-06-21 15:02:47 +02:00
#
# @param [Fixnum] index
#
# @return [undefined]
#
# @api private
#
def self.define_named_child(name, index)
super
2013-06-21 15:02:47 +02:00
define_method("emit_#{name}_mutations") do |&block|
mutate_child(index, &block)
2013-06-21 15:02:47 +02:00
end
define_method("emit_#{name}") do |node|
emit_child_update(index, node)
end
end
2014-07-14 13:46:55 +00:00
private_class_method :define_named_child
2013-06-21 15:02:47 +02:00
private
# Return mutated node
#
# @return [Parser::AST::Node]
#
# @api private
#
alias_method :node, :input
# Return duplicated node
#
# @return [Parser::AST::Node]
#
# @api private
#
alias_method :dup_node, :dup_input
# Return ast meta description
#
# @return [AST::Meta]
#
# @api private
#
def meta
AST::Meta.for(node)
end
memoize :meta
# Return children
#
# @return [Array<Parser::AST::Node>]
#
# @api private
#
def children
node.children
end
# Dispatch on child index
#
# @param [Fixnum] index
#
# @return [undefined]
#
# @api private
#
def mutate_child(index, mutator = Mutator, &block)
block ||= TAUTOLOGY
2013-07-05 03:01:59 +02:00
child = children.at(index)
mutator.each(child, self) do |mutation|
2014-06-08 13:01:26 +00:00
next unless block.call(mutation)
emit_child_update(index, mutation)
end
end
2013-06-14 18:22:34 +02:00
# Emit delete child mutation
#
# @param [Fixnum] index
#
# @return [undefined]
#
# @api private
#
def delete_child(index)
dup_children = children.dup
dup_children.delete_at(index)
2014-06-03 03:51:22 +00:00
emit_type(*dup_children)
2013-06-14 18:22:34 +02:00
end
# Emit updated child
#
# @param [Fixnum] index
2013-07-05 03:01:59 +02:00
# @param [Parser::AST::Node] node
#
# @return [undefined]
#
# @api private
#
2013-07-05 03:01:59 +02:00
def emit_child_update(index, node)
new_children = children.dup
new_children[index] = node
2014-06-03 03:51:22 +00:00
emit_type(*new_children)
end
# Emit a new AST node with same class as wrapped node
#
# @param [Array<Parser::AST::Node>] children
#
2013-06-15 16:37:43 +02:00
# @return [undefined]
#
# @api private
#
2014-06-03 03:51:22 +00:00
def emit_type(*children)
emit(Parser::AST::Node.new(node.type, children))
end
# Emit singleton literals
#
# @return [undefined]
#
# @api private
#
def emit_singletons
emit_nil
emit_self
end
# Emit a literal self
#
# @return [undefined]
#
# @api private
#
def emit_self
emit(N_SELF)
end
# Emit a literal nil
#
2013-06-25 00:33:28 -07:00
# @return [undefined]
#
# @api private
#
def emit_nil
2014-04-04 14:35:42 -04:00
emit(N_NIL) unless asgn_left?
end
2013-06-12 19:09:43 +02:00
# Emit values
#
# @param [Array<Object>] values
#
# @return [undefined]
#
# @api private
#
def emit_values(values)
values.each do |value|
2014-06-03 03:51:22 +00:00
emit_type(value)
2013-06-12 19:09:43 +02:00
end
end
2014-06-10 13:42:47 +00:00
# Return parent node
#
# @return [Parser::AST::Node] node
2014-08-07 09:35:30 -07:00
# if parent with node is present
2014-06-10 13:42:47 +00:00
#
# @return [nil]
# otherwise
#
# @api private
#
def parent_node
2014-06-10 15:43:38 +00:00
parent.node if parent
2014-06-10 13:42:47 +00:00
end
# Return parent type
#
# @return [Symbol] type
# if parent with type is present
#
# @return [nil]
# otherwise
#
# @api private
#
def parent_type
2014-06-10 15:43:38 +00:00
parent_node.type if parent_node
end
# Test if the node is the left of an or_asgn or op_asgn
#
# @return [Boolean]
#
# @api private
#
2014-04-04 14:35:42 -04:00
def asgn_left?
AST::Types::OP_ASSIGN.include?(parent_type) && parent.node.children.first.equal?(node)
end
# Return children indices
#
# @param [Range] range
#
# @return [Enumerable<Fixnum>]
#
# @api pirvate
#
def children_indices(range)
range_end = range.end
last_index = range_end >= 0 ? range_end : children.length + range_end
range.begin.upto(last_index)
end
end # Node
end # Mutator
end # Mutant