module Unparser # Preprocessor to normalize AST generated by parser class Preprocessor include Adamantium::Flat, NodeHelpers, AbstractType, Concord.new(:node, :parent_type), Procto.call(:result) # Return preprocessor result # # @return [Parser::AST::Node] # # @api private # abstract_method :result EMPTY = Parser::AST::Node.new(:empty) # Run preprocessor for node # # @param [Parser::AST::Node, nil] node # # @return [Parser::AST::Node, nil] # # @api private # def self.run(node, parent_type = nil) return EMPTY if node.nil? REGISTRY.fetch(node.type, [Noop]).reduce(node) do |current, processor| processor.call(current, parent_type) end end REGISTRY = Hash.new { |hash, key| hash[key] = [] } # Register preprocessor # # @param [Symbol] type # # @return [undefined] # # @api private # def self.register(type) REGISTRY[type] << self end private_class_method :register private # Visit node # # @param [Parser::AST::Node] child # # @return [undefined] # # @api private # def visit(child) self.class.run(child, node.type) end # Return children # # @return [Array] # # @api private # def children node.children end # Return visited children # # @return [Array] # # @api private # def visited_children children.map do |node| if node.is_a?(Parser::AST::Node) visit(node) else node end end end # Noop preprocessor that just passes node through. class Noop < self register :int register :str # Return preprocessor result # # @return [Parser::AST::Node] # # @api private # def result node.updated(nil, visited_children) end end # Noop # Preprocessor transforming numeric nodes with infinity as value to round trippable equivalent. class Infinity < self register :float register :int NEG_INFINITY = -(Float::INFINITY - 1) # Return preprocessor result # # @param [Parser::AST::Node] # # @api private # def result value = node.children.first case value when Float::INFINITY s(:const, s(:const, nil, :Float), :INFINITY) when NEG_INFINITY s(:send, s(:const, s(:const, nil, :Float), :INFINITY), :-@) else node end end end # Preprocessor for begin nodes. Removes begin nodes with one child. # # This reduces the amount of complex logic needed inside unparser to emit "nice" syntax with minimal # tokens. # class Begin < self register :begin # Return preprocessor result # # @return [Parser::AST::Node] # # @api private # def result if children.one? && !parent_type.equal?(:regexp) visit(children.first) else Noop.call(node, parent_type) end end end # Begin end # Preprocessor end # Unparser