class Fzip::Zipper

Attributes

adapter[R]
at_end[R]
lefts[R]
node[R]
parent[R]
path[R]
rights[R]

Public Class Methods

new(adapter, node, lefts = nil, path = nil, parent = nil, rights = nil, changed = false, at_end = false) click to toggle source
# File lib/fzip/zipper.rb, line 11
def initialize(adapter, node, lefts = nil, path = nil, parent = nil, rights = nil, changed = false, at_end = false)
  @adapter   = adapter

  @node      = node
  @lefts     = lefts
  @rights    = rights

  @path      = path    # Array[Node]
  @parent    = parent  # Zipper
  @changed   = changed
  @at_end    = at_end
end

Public Instance Methods

append_child(item) click to toggle source
# File lib/fzip/zipper.rb, line 158
def append_child(item)
  replace(make_node(node, children + [item]))
end
branch?() click to toggle source
# File lib/fzip/zipper.rb, line 37
def branch?
  adapter.branch?(node)
end
changed?() click to toggle source
# File lib/fzip/zipper.rb, line 50
def changed?
  @changed
end
children() click to toggle source
# File lib/fzip/zipper.rb, line 41
def children
  raise "called children on a leaf node" unless branch?
  @cs ||= adapter.children(node)
end
down() click to toggle source
# File lib/fzip/zipper.rb, line 58
def down
  if branch? && children.any?
    new(
      node: children.first,
      lefts: [],
      path: path ? [node] + path : [node],
      parent: self,
      rights: children.drop(1)
    )
  end
end
each() { |loc| ... } click to toggle source
# File lib/fzip/zipper.rb, line 210
def each
  return to_enum unless block_given?
  loc = self
  until (loc = loc.next).end?
    yield loc
  end
end
edit() { |node)| ... } click to toggle source
# File lib/fzip/zipper.rb, line 150
def edit
  replace(yield node)
end
end?() click to toggle source
# File lib/fzip/zipper.rb, line 54
def end?
  @at_end
end
insert_child(item) click to toggle source
# File lib/fzip/zipper.rb, line 154
def insert_child(item)
  replace(make_node(node, [item] + children))
end
insert_left(item) click to toggle source
# File lib/fzip/zipper.rb, line 127
def insert_left(item)
  raise "insert at top" unless path
  new(
    lefts:   lefts + [item],
    changed: true
  )
end
insert_right(item) click to toggle source
# File lib/fzip/zipper.rb, line 135
def insert_right(item)
  raise "insert at top" unless path
  new(
    rights:  [item] + rights,
    changed: true
  )
end
left() click to toggle source
# File lib/fzip/zipper.rb, line 108
def left
  if path && lefts && !lefts.empty?
    new(
      node:   lefts.last,
      lefts:  lefts[0...-1],
      rights: [node] + rights
    )
  end
end
leftmost() click to toggle source
# File lib/fzip/zipper.rb, line 118
def leftmost
  return self unless path && lefts && !lefts.empty?
  new(
    node: lefts.first,
    lefts: [],
    rights: (lefts + [node] + rights).drop(1)
  )
end
make_node(node, children) click to toggle source
# File lib/fzip/zipper.rb, line 46
def make_node(node, children)
  adapter.make_node(node, children)
end
new(changes = {}) click to toggle source
# File lib/fzip/zipper.rb, line 24
def new(changes = {})
  self.class.new(
    @adapter,
    changes.fetch(:node)    { self.node     },
    changes.fetch(:lefts)   { self.lefts    },
    changes.fetch(:path)    { self.path     },
    changes.fetch(:parent)  { self.parent   },
    changes.fetch(:rights)  { self.rights   },
    changes.fetch(:changed) { self.changed? },
    changes.fetch(:end)     { self.end?     }
  )
end
next() click to toggle source
# File lib/fzip/zipper.rb, line 162
def next
  backtrack = ->(node) {
    if node.up
      node.up.right || backtrack.(node.up)
    else
      node.new(end: true)
    end
  }

  (self if end?) ||
    (branch? && down) ||
    right ||
    backtrack.(self)
end
prev() click to toggle source
# File lib/fzip/zipper.rb, line 177
def prev
  return up unless loc = left
  loop do
    if child = loc.branch? && loc.down
      loc = child.rightmost
    else
      return loc
    end
  end
end
remove() click to toggle source

Removes the node at loc, returning the loc that would have preceded it in a depth-first walk.

# File lib/fzip/zipper.rb, line 190
def remove
  raise "Remove at top" unless path
  if lefts.empty?
    parent.new(
      node: make_node(parent.node, rights),
      changed: true
    )
  else
    loc = new(
      node: lefts.first,
      lefts: lefts.drop(1),
      changed: true
    )
    loop do
      return loc unless child = loc.branch? && loc.down
      loc = child.rightmost
    end
  end
end
replace(item) click to toggle source
# File lib/fzip/zipper.rb, line 143
def replace(item)
  new(
    node: item,
    changed: true
  )
end
right() click to toggle source
# File lib/fzip/zipper.rb, line 89
def right
  if path && rights && !rights.empty?
    new(
      node:   rights.first,
      lefts:  lefts + [node],
      rights: rights.drop(1)
    )
  end
end
rightmost() click to toggle source
# File lib/fzip/zipper.rb, line 99
def rightmost
  return self unless path && rights && !rights.empty?
  new(
    node: rights.last,
    lefts: (lefts + [node] + rights)[0..-2],
    rights: []
  )
end
root() click to toggle source
# File lib/fzip/zipper.rb, line 84
def root
  return node unless path
  up.root
end
up() click to toggle source
# File lib/fzip/zipper.rb, line 70
def up
  if path
    return parent unless changed?
    parent_path = path.drop(1)
    new(
      node:   make_node(parent.node, lefts + [node] + rights),
      lefts:  parent.lefts,
      path:   parent_path.empty? ? nil : parent_path,
      parent: parent.parent,
      rights: parent.rights
    )
  end
end