free_mutant/lib/mutant/mutator.rb
John Backus 523bad401e
Refactor to add Mutant::Mutator.mutate(...)
Refactors Registry to be more generic
2016-05-07 15:03:45 -07:00

91 lines
1.8 KiB
Ruby

module Mutant
# Generator for mutations
class Mutator
REGISTRY = Registry.new
include Adamantium::Flat, Concord.new(:input, :parent), AbstractType, Procto.call(:output)
# Lookup and invoke dedicated AST mutator
#
# @param node [Parser::AST::Node]
# @param parent [nil,Mutant::Mutator::Node]
#
# @return [Set<Parser::AST::Node>]
def self.mutate(node, parent = nil)
self::REGISTRY.lookup(node.type).call(node, parent)
end
# Register node class handler
#
# @return [undefined]
def self.handle(*types)
types.each do |type|
self::REGISTRY.register(type, self)
end
end
private_class_method :handle
# Return output
#
# @return [Set<Parser::AST::Node>]
attr_reader :output
private
# Initialize object
#
# @param [Object] input
# @param [Object] parent
# @param [#call(node)] block
#
# @return [undefined]
def initialize(_input, _parent = nil)
super
@output = Set.new
dispatch
end
# Test if generated object is not guarded from emitting
#
# @param [Object] object
#
# @return [Boolean]
def new?(object)
!object.eql?(input)
end
# Dispatch node generations
#
# @return [undefined]
abstract_method :dispatch
# Emit generated mutation if object is not equivalent to input
#
# @param [Object] object
#
# @return [undefined]
def emit(object)
return unless new?(object)
output << object
end
# Run input with mutator
#
# @return [undefined]
def run(mutator)
mutator.call(input).each(&method(:emit))
end
# Shortcut to create a new unfrozen duplicate of input
#
# @return [Object]
def dup_input
input.dup
end
end # Mutator
end # Mutant