# frozen_string_literal: true# Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com># # Permission is hereby granted, free of charge, to any person obtaining a copy# of this software and associated documentation files (the "Software"), to deal# in the Software without restriction, including without limitation the rights# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell# copies of the Software, and to permit persons to whom the Software is# furnished to do so, subject to the following conditions:# # The above copyright notice and this permission notice shall be included in# all copies or substantial portions of the Software.# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN# THE SOFTWARE.moduleAsync# A double linked list.classListdefinitialize@head=nil@tail=nil@size=0endattr:sizeattr_accessor:headattr_accessor:tail# Inserts an item at the end of the list.definsert(item)unless@head@head=item@tail=item# Consistency:item.head=nilitem.tail=nilelse@tail.tail=itemitem.head=@tail# Consistency:item.tail=nil@tail=itemend@size+=1returnselfenddefdelete(item)if@head.equal?(item)@head=@head.tailelseitem.head.tail=item.tailendif@tail.equal?(item)@tail=@tail.headelseitem.tail.head=item.headenditem.head=nilitem.tail=nil@size-=1returnselfenddefeachreturnto_enumunlessblock_given?item=@headwhileitem# We store the tail pointer so we can remove the current item from the linked list:tail=item.tailyielditemitem=tailendenddefinclude?(needle)self.eachdo|item|returntrueifneedle.equal?(item)endreturnfalseenddeffirst@headenddeflast@tailenddefempty?@head.nil?enddefnil?@head.nil?endendprivate_constant:ListclassChildren<Listdefinitializesuper@transient_count=0end# Does this node have (direct) transient children?deftransients?@transient_count>0enddefinsert(item)ifitem.transient?@transient_count+=1endsuperenddefdelete(item)ifitem.transient?@transient_count-=1endsuperenddeffinished?@size==@transient_countendend# Represents a node in a tree, used for nested {Task} instances.classNode# Create a new node in the tree.# @param parent [Node, nil] This node will attach to the given parent.definitialize(parent=nil,annotation: nil,transient: false)@parent=nil@children=nil@annotation=annotation@object_name=nil@transient=transient@head=nil@tail=nilifparentparent.add_child(self)endend# You should not directly rely on these pointers but instead use `#children`.# List pointers:attr_accessor:headattr_accessor:tail# @attr parent [Node, nil]attr:parent# @attr children [List<Node>] Optional list of children.attr:children# A useful identifier for the current node.attr:annotation# Whether there are children?defchildren?@children!=nil&&!@children.empty?end# Is this node transient?deftransient?@transientenddefannotate(annotation)ifblock_given?previous_annotation=@annotation@annotation=annotationyield@annotation=previous_annotationelse@annotation=annotationendenddefdescription@object_name||="#{self.class}:0x#{object_id.to_s(16)}#{@transient?' transient':nil}"if@annotation"#{@object_name}#{@annotation}"else@object_nameendenddefbacktrace(*arguments)nilenddefto_s"\#<#{description}>"end# Change the parent of this node.# @param parent [Node, nil] the parent to attach to, or nil to detach.# @return [self]defparent=(parent)returnif@parent.equal?(parent)if@parent@parent.delete_child(self)@parent=nilendifparentparent.add_child(self)endreturnselfendprotecteddefset_parentparent@parent=parentendprotecteddefadd_childchild@children||=Children.new@children.insert(child)child.set_parent(self)endprotecteddefdelete_child(child)@children.delete(child)child.set_parent(nil)end# Whether the node can be consumed safely. By default, checks if the# children set is empty.# @return [Boolean]deffinished?@children.nil?||@children.finished?end# If the node has a parent, and is {finished?}, then remove this node from# the parent.defconsumeifparent=@parentandfinished?parent.delete_child(self)if@children@children.eachdo|child|ifchild.finished?delete_child(child)elseparent.add_child(child)endend@children=nilendparent.consumeendend# Traverse the tree.# @yield [node, level] The node and the level relative to the given root.deftraverse(level=0,&block)yieldself,level@children&.eachdo|child|child.traverse(level+1,&block)endenddefstop@children&.each(&:stop)enddefprint_hierarchy(out=$stdout,backtrace: true)self.traversedo|node,level|indent="\t"*levelout.puts"#{indent}#{node}"print_backtrace(out,indent,node)ifbacktraceendendprivatedefprint_backtrace(out,indent,node)ifbacktrace=node.backtracebacktrace.each_with_indexdo|line,index|out.puts"#{indent}#{index.zero??"→ ":" "}#{line}"endendendendend