We have to we find the highest (right-most) node in our lower (left) sub-tree:

Replace current node key and value, recursively delete rightmost

# -*- coding: utf-8 -*-
#
#    Copyright © 2014, 2015, 2017, 2018 Simon Forman
#
#    This file is part of Thun
#
#    Thun is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Thun is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with Thun.  If not see <http://www.gnu.org/licenses/>.
#
'''
This module implements an interpreter for a dialect of Joy that
attempts to stay very close to the spirit of Joy but does not precisely
match the behaviour of the original version(s) written in C.

'''
from builtins import input
from traceback import print_exc
from .parser import text_to_expression, ParseError, Symbol
from .utils.stack import stack_to_string
from .utils.errors import (
    NotAListError,
    NotAnIntError,
    StackUnderflowError,
    )

class UnknownSymbolError(KeyError): pass

def joy(stack, expression, dictionary, viewer=None):
    '''Evaluate a Joy expression on a stack.

  This function iterates through a sequence of terms which are either
  literals (strings, numbers, sequences of terms) or function symbols.
  Literals are put onto the stack and functions are looked up in the
  dictionary and executed.

    The viewer is a function that is called with the stack and expression
    on every iteration, its return value is ignored.

    :param stack stack: The stack.
    :param stack expression: The expression to evaluate.
    :param dict dictionary: A ``dict`` mapping names to Joy functions.
    :param function viewer: Optional viewer function.
    :rtype: (stack, (), dictionary)

    '''
    while expression:

        if viewer: viewer(stack, expression)

        term, expression = expression
        if isinstance(term, Symbol):
            if term not in dictionary:
                raise UnknownSymbolError(term)
            func = dictionary[term]
            stack, expression, dictionary = func(stack, expression, dictionary)
        else:
            stack = term, stack

    if viewer: viewer(stack, expression)
    return stack, expression, dictionary

def run(text, stack, dictionary, viewer=None):
    '''
    Return the stack resulting from running the Joy code text on the stack.

    :param str text: Joy code.

    '''
    expression = text_to_expression(text)
    return joy(stack, expression, dictionary, viewer)

def repl(stack=(), dictionary=None):
    '''
    Read-Evaluate-Print Loop

    Accept input and run it on the stack, loop.

    :rtype: stack

    '''
    if dictionary is None:
        dictionary = {}
    try:
        while True:
            print()
            print(stack_to_string(stack), '<-top')
            print()
            try:
                text = input('joy? ')
            except (EOFError, KeyboardInterrupt):
                break
            try:
                stack, _, dictionary = run(text, stack, dictionary)
            except:
                print_exc()
    except:
        print_exc()
    print()
    return stack

def interp(stack=(), dictionary=None):
    '''
    Simple REPL with no extra output, suitable for use in scripts.
    '''
    try:
            try:
                text = input()
                break
            try:
            except UnknownSymbolError as sym:
                print('Unknown:', sym)
            except StackUnderflowError as e:
                print(e)  # 'Not enough values on stack.'
            except NotAnIntError:
                print('Not an integer.')
            except NotAListError as e:
                print(e)  # 'Not a list.'
            except:
            print(stack_to_string(stack))
    except:

#
#    Copyright © 2014-2020 Simon Forman
#
#
#
#
#    along with Thun.  If not see <http://www.gnu.org/licenses/>. 
#
'''
This module contains the Joy function infrastructure and a library of
functions.  Its main export is a Python function initialize() that
returns a dictionary of Joy functions suitable for use with the joy()
function.
'''
from pkg_resources import resource_stream
from io import TextIOWrapper
from inspect import getdoc, getmembers, isfunction
from functools import wraps
from itertools import count
import operator, math

from . import __name__ as _joy_package_name
from .parser import text_to_expression, Symbol
from .utils import generated_library as genlib
    )
from .utils.stack import (
    concat,
    expression_to_string,
    iter_stack,
    list_to_stack,
    pick,
    )

def default_defs(dictionary):
    def_stream = TextIOWrapper(
        resource_stream(_joy_package_name, 'defs.txt'),
        encoding='UTF_8',
        )
    Def.load_definitions(def_stream, dictionary)

HELP_TEMPLATE = '''\

==== Help on %s ====

%s

---- end (%s)
'''

# This is the main dict we're building.
_dictionary = {}

def inscribe(function, d=_dictionary):
    '''A decorator to inscribe functions into the default dictionary.'''
    d[function.name] = function
    return function

def initialize():
    '''Return a dictionary of Joy functions for use with joy().'''
    return _dictionary.copy()

ALIASES = (
    ('add', ['+']),
    ('and', ['&']),
    ('bool', ['truthy']),
    ('mul', ['*']),
    ('floordiv', ['/floor', '//', '/', 'div']),
    ('mod', ['%', 'rem', 'remainder', 'modulus']),
    ('eq', ['=']),
    ('ge', ['>=']),
    ('getitem', ['pick', 'at']),
    ('gt', ['>']),
    ('le', ['<=']),
    ('lshift', ['<<']),
    ('lt', ['<']),
    ('ne', ['<>', '!=']),
    ('rshift', ['>>']),
    ('sub', ['-']),
    ('xor', ['^']),
    ('succ', ['++']),
    ('pred', ['--']),
    ('rolldown', ['roll<']),
    ('rollup', ['roll>']),
    ('eh', ['?']),
    ('id', [u'•']),
    )

def add_aliases(D, A):
    '''
    Given a dict and a iterable of (name, [alias, ...]) pairs, create
    additional entries in the dict mapping each alias to the named function
    if it's in the dict.  Aliases for functions not in the dict are ignored.
    '''
    for name, aliases in A:
        try:
            F = D[name]
        except KeyError:
            continue
        for alias in aliases:
            D[alias] = F

def FunctionWrapper(f):
    '''Set name attribute.'''
    if not f.__doc__:
        raise ValueError('Function %s must have doc string.' % f.__name__)
    f.name = f.__name__.rstrip('_')  # Don't shadow builtins.
    return f

def SimpleFunctionWrapper(f):
    '''
    Wrap functions that take and return just a stack.
    '''
    @FunctionWrapper
    @wraps(f)
    def inner(stack, expression, dictionary):
        return f(stack), expression, dictionary
    return inner

def BinaryBuiltinWrapper(f):
    '''
    Wrap functions that take two arguments and return a single result.
    '''
    @wraps(f)
        try:
            (a, (b, stack)) = stack
        except ValueError:
            raise StackUnderflowError('Not enough values on stack.')
        # Boolean predicates like "or" fail here.  :(
##        if (   not isinstance(a, int)
##            or not isinstance(b, int)
##            or isinstance(a, bool)  # Because bools are ints in Python.
##            or isinstance(b, bool)
##            ):
##            raise NotAnIntError
        result = f(b, a)
        return (result, stack), expression, dictionary

def UnaryBuiltinWrapper(f):
    '''
    Wrap functions that take one argument and return a single result.
    '''
    @wraps(f)
        (a, stack) = stack
        result = f(a)

class Def(object):
    '''
    Definitions created by inscribe.
    '''

    def __init__(self, name, body):
        self.name = name
        self.body = body
        self._body = tuple(iter_stack(body))
        self.__doc__ = expression_to_string(body)
        self._compiled = None

    def __call__(self, stack, expression, dictionary):
        if self._compiled:
            return self._compiled(stack, expression, dictionary)  # pylint: disable=E1102
        expression = list_to_stack(self._body, expression)
        return stack, expression, dictionary

    @classmethod
    def load_definitions(class_, stream, dictionary):
        for line in stream:
            if line.lstrip().startswith('#'):
                continue
            name, body = text_to_expression(line)
            if name not in dictionary:
                inscribe(class_(name, body), dictionary)
##            inscribe(class_(name, body), dictionary)

#
# Functions
#

@inscribe
@FunctionWrapper
def inscribe_(stack, expression, dictionary):
    '''
    Create a new Joy function definition in the Joy dictionary.  A
    definition is given as a quote with a name followed by a Joy
    expression. for example:

        [sqr dup mul] inscribe

    '''
    (name, body), stack = stack
    inscribe(Def(name, body), dictionary)

@inscribe
@SimpleFunctionWrapper
def parse(stack):
    '''Parse the string on the stack to a Joy expression.'''
    text, stack = stack
    return expression, stack

# @inscribe
# @SimpleFunctionWrapper
# def infer_(stack):
#     '''Attempt to infer the stack effect of a Joy expression.'''
#     E, stack = stack
#     effects = infer_expression(E)
#     e = list_to_stack([(fi, (fo, ())) for fi, fo in effects])
#     return e, stack

@inscribe
def getitem(stack):
    '''
    ::

        getitem == drop first

    Expects an integer and a quote on the stack and returns the item at the
    nth position in the quote counting from 0.
    ::

           [a b c d] 0 getitem
        -------------------------
            a

    '''
    n, (Q, stack) = stack
    return pick(Q, n), stack

@inscribe
def drop(stack):
    '''
    ::

        drop == [rest] times

    Expects an integer and a quote on the stack and returns the quote with
    n items removed off the top.
    ::

           [a b c d] 2 drop
        ----------------------
               [c d]

    '''
    while n > 0:
        try:
            _, Q = Q
            raise IndexError
        n -= 1
    return Q, stack

@inscribe
def take(stack):
    '''
    just the top n items in reverse order (because that's easier and you can
    use reverse if needed.)
    ::

           [a b c d] 2 take
               [b a]

    '''
    x = ()
    while n > 0:
        try:
            item, Q = Q
        x = item, x
        n -= 1
    return x, stack

@inscribe
def gcd2(stack, expression, dictionary):
    '''Compiled GCD function.'''
    (v1, (v2, stack)) = stack
    tos = True
    while tos:
        v3 = v2 % v1
        tos = v3 > 0
        (v1, (v2, stack)) = (v3, (v1, stack))
    return (v2, stack), expression, dictionary

@inscribe
def choice(stack):
    '''
    Use a Boolean value to select one of two items.
    ::

           A B False choice
           A

           A B True choice
        ---------------------
             B

    Currently Python semantics are used to evaluate the "truthiness" of the
    Boolean value (so empty string, zero, etc. are counted as false, etc.)
    '''
    (if_, (then, (else_, stack))) = stack
    return then if if_ else else_, stack

@inscribe
def select(stack):
    '''
    Use a Boolean value to select one of two items from a sequence.
    ::

           [A B] False select
        ------------------------
            A

           [A B] True select
        -----------------------
              B

    The sequence can contain more than two items but not fewer.
    '''
    (flag, (choices, stack)) = stack
    (else_, (then, _)) = choices
    return then if flag else else_, stack

@inscribe
def max_(S):
    '''Given a list find the maximum.'''
    tos, stack = S
    return max(iter_stack(tos)), stack

@inscribe
def min_(S):
    '''Given a list find the minimum.'''
    return min(iter_stack(tos)), stack

@inscribe
def sum_(S):
    '''
    Given a quoted sequence of numbers return the sum.
    ::

        sum == 0 swap [+] step

    '''
    return sum(iter_stack(tos)), stack

@inscribe
def remove(S):
    '''
    Expects an item on the stack and a quote under it and removes that item
    from the the quote.  The item is only removed once.  If the list is
    empty or the item isn't in the list then the list is unchanged.
    ::

           [1 2 3 1] 1 remove
             [2 3 1]

    '''
    (item, (quote, stack)) = S
    return _remove(item, quote), stack

def _remove(item, quote):
    try: head, tail = quote
    except ValueError: return quote
    return tail if head == item else (head, _remove(item, tail))

@inscribe
def unique(S):
    '''Given a list remove duplicate items.'''
    I = list(iter_stack(tos))
    return list_to_stack(sorted(set(I), key=I.index)), stack

@inscribe
def sort_(S):
    '''Given a list return it sorted.'''
    return list_to_stack(sorted(iter_stack(tos))), stack

@inscribe
def clear(stack):
    '''Clear everything from the stack.
    ::

        clear == stack [pop stack] loop

           ... clear
        ---------------

    '''
    return ()

@inscribe
def disenstacken(stack):
    '''
    The disenstacken operator expects a list on top of the stack and makes that
    the stack discarding the rest of the stack.
    '''
    return stack[0]

@inscribe
def reverse(S):
    '''
    Reverse the list on the top of the stack.
    ::

        reverse == [] swap shunt
    '''
    (tos, stack) = S
    res = ()
    for term in iter_stack(tos):
        res = term, res
    return res, stack

@inscribe
def concat_(S):
    '''
    Concatinate the two lists on the top of the stack.
    ::

           [a b c] [d e f] concat
        ----------------------------
               [a b c d e f]

    '''
    (tos, (second, stack)) = S
    return concat(second, tos), stack

@inscribe
def shunt(stack):
    '''
    Like concat but reverses the top list into the second.
    ::

        shunt == [swons] step == reverse swap concat

           [a b c] [d e f] shunt
        ---------------------------
               [f e d a b c] 

    '''
    (tos, (second, stack)) = stack
    while tos:
        term, tos = tos
        second = term, second
    return second, stack

@inscribe
def zip_(S):
    '''
    Replace the two lists on the top of the stack with a list of the pairs
    from each list.  The smallest list sets the length of the result list.
    '''
    accumulator = [
        (a, (b, ()))
        for a, b in zip(iter_stack(tos), iter_stack(second))
        ]
    return list_to_stack(accumulator), stack

@inscribe
def succ(S):
    '''Increment TOS.'''
    return tos + 1, stack

@inscribe
def pred(S):
    '''Decrement TOS.'''
    return tos - 1, stack

@inscribe
def pm(stack):
    '''
    Plus or minus
    ::

           a b pm
        -------------
           a+b a-b

    '''
    a, (b, stack) = stack
    p, m, = b + a, b - a
    return m, (p, stack)

def floor(n):
    return int(math.floor(n))

floor.__doc__ = math.floor.__doc__

@inscribe
def divmod_(S):
    '''
    divmod(x, y) -> (quotient, remainder)

    Return the tuple (x//y, x%y).  Invariant: q * y + r == x.
    '''
    a, (b, stack) = S
    d, m = divmod(a, b)
    return d, (m, stack)

def sqrt(a):
    '''
    Return the square root of the number a.
    Negative numbers return complex roots.
    '''
    try:
        r = math.sqrt(a)
    except ValueError:
        assert a < 0, repr(a)
        r = math.sqrt(-a) * 1j
    return r

#def execute(S):
#  (text, stack) = S
#  if isinstance(text, str):
#    return run(text, stack)
#  return stack

@inscribe
def id_(stack):
    '''The identity function.'''

@inscribe
def void(stack):
    '''True if the form on TOS is void otherwise False.'''
    form, stack = stack
    return _void(form), stack

def _void(form):
    return any(not _void(i) for i in iter_stack(form))

##  transpose
##  sign
##  take

@inscribe
def words(stack, expression, dictionary):
    '''Print all the words in alphabetical order.'''
    print(' '.join(sorted(dictionary)))

@inscribe
def sharing(stack, expression, dictionary):
    '''Print redistribution information.'''
    print("You may convey verbatim copies of the Program's source code as"
    ' you receive it, in any medium, provided that you conspicuously'
    ' and appropriately publish on each copy an appropriate copyright'
    ' notice; keep intact all notices stating that this License and'
    ' any non-permissive terms added in accord with section 7 apply'
    ' to the code; keep intact all notices of the absence of any'
    ' warranty; and give all recipients a copy of this License along'
    ' with the Program.'
    ' You should have received a copy of the GNU General Public License'
    ' along with Thun.  If not see <http://www.gnu.org/licenses/>.')

@inscribe
def warranty(stack, expression, dictionary):
    '''Print warranty information.'''
    print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
    ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
    ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
    ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
    ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
    ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
    ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
    ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
    ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')

# def simple_manual(stack):
#   '''
#   Print words and help for each word.
#   '''
#   for name, f in sorted(FUNCTIONS.items()):
#     d = getdoc(f)
#     boxline = '+%s+' % ('-' * (len(name) + 2))
#     print('\n'.join((
#       boxline,
#       '| %s |' % (name,),
#       boxline,
#       d if d else '   ...',
#       '',
#       '--' * 40,
#       '',
#       )))
#   return stack

@inscribe
def help_(S, expression, dictionary):
    '''Accepts a quoted symbol on the top of the stack and prints its docs.'''
    ((symbol, _), stack) = S
    word = dictionary[symbol]
    print(HELP_TEMPLATE % (symbol, getdoc(word), symbol))

#
# § Combinators
#

# Several combinators depend on other words in their definitions,
# we use symbols to prevent hard-coding these, so in theory, you
# could change the word in the dictionary to use different semantics.
S_choice = Symbol('choice')
S_first = Symbol('first')
S_genrec = Symbol('genrec')
S_getitem = Symbol('getitem')
S_i = Symbol('i')
S_ifte = Symbol('ifte')
S_infra = Symbol('infra')
S_loop = Symbol('loop')
S_pop = Symbol('pop')
S_primrec = Symbol('primrec')
S_step = Symbol('step')
S_swaack = Symbol('swaack')
S_times = Symbol('times')

@inscribe
def i(stack, expression, dictionary):
    '''
    The i combinator expects a quoted program on the stack and unpacks it
    onto the pending expression for evaluation.
    ::

           [Q] i
        -----------
            Q

    '''
    try:
        quote, stack = stack
        raise StackUnderflowError('Not enough values on stack.')
    return stack, concat(quote, expression), dictionary

@inscribe
def x(stack, expression, dictionary):
    '''
    ::

        x == dup i

        ... [Q] x = ... [Q] dup i
        ... [Q] x = ... [Q] [Q] i
        ... [Q] x = ... [Q]  Q

    '''
    quote, _ = stack

@inscribe
def b(stack, expression, dictionary):
    '''
    ::

        b == [i] dip i

        ... [P] [Q] b == ... [P] i [Q] i
        ... [P] [Q] b == ... P Q

    '''
    q, (p, (stack)) = stack
    return stack, concat(p, concat(q, expression)), dictionary

@inscribe
def ii(stack, expression, dictionary):
    '''
    ::

           ... a [Q] ii
        ------------------
            ... Q a Q

    '''
    quote, (a, stack) = stack
    expression = concat(quote, (a, concat(quote, expression)))

@inscribe
def dupdip(stack, expression, dictionary):
    '''
    ::

        [F] dupdip == dup [F] dip

        ... a [F] dupdip
        ... a dup [F] dip
        ... a a   [F] dip
        ... a F a

    '''
    F, stack = stack
    a = stack[0]
    return stack, concat(F, (a,  expression)), dictionary

@inscribe
def infra(stack, expression, dictionary):
    '''
    Accept a quoted program and a list on the stack and run the program
    with the list as its stack.  Does not affect the rest of the stack.
    ::

           ... [a b c] [Q] . infra
        -----------------------------
            c b a . Q [...] swaack

    '''
    (quote, (aggregate, stack)) = stack
    return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary

@inscribe
def genrec(stack, expression, dictionary):
    '''
    General Recursion Combinator.
    ::

                  [if] [then] [rec1] [rec2] genrec
        ---------------------------------------------------------------------
           [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte

    From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
    "The genrec combinator takes four program parameters in addition to
    whatever data parameters it needs. Fourth from the top is an if-part,
    followed by a then-part. If the if-part yields true, then the then-part
    is executed and the combinator terminates. The other two parameters are
    the rec1-part and the rec2-part. If the if-part yields false, the
    rec1-part is executed. Following that the four program parameters and
    the combinator are again pushed onto the stack bundled up in a quoted
    form. Then the rec2-part is executed, where it will find the bundled
    form. Typically it will then execute the bundled form, either with i or
    with app2, or some other combinator."

    The way to design one of these is to fix your base case [then] and the
    test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
    a quotation of the whole function.

    For example, given a (general recursive) function 'F':
    ::

        F == [I] [T] [R1] [R2] genrec

    If the [I] if-part fails you must derive R1 and R2 from:
    ::

        ... R1 [F] R2

    Just set the stack arguments in front, and figure out what R1 and R2
    have to do to apply the quoted [F] in the proper way.  In effect, the
    genrec combinator turns into an ifte combinator with a quoted copy of
    the original definition in the else-part:
    ::

        F == [I] [T] [R1]   [R2] genrec
          == [I] [T] [R1 [F] R2] ifte

    Primitive recursive functions are those where R2 == i.
    ::

        P == [I] [T] [R] tailrec
          == [I] [T] [R [P] i] ifte
          == [I] [T] [R P] ifte

    '''
    (rec2, (rec1, stack)) = stack
    (then, (if_, _)) = stack
    F = (if_, (then, (rec1, (rec2, (S_genrec, ())))))
    else_ = concat(rec1, (F, rec2))
    return (else_, stack), (S_ifte, expression), dictionary

@inscribe
def map_(S, expression, dictionary):
    '''
    Run the quoted program on TOS on the items in the list under it, push a
    new list with the results in place of the program and original list.
    '''
    # (quote, (aggregate, stack)) = S
    # results = list_to_stack([
    # joy((term, stack), quote, dictionary)[0][0]
    # for term in iter_stack(aggregate)
    # ])
    # return (results, stack), expression, dictionary
    (quote, (aggregate, stack)) = S
    if not aggregate:
        return (aggregate, stack), expression, dictionary
    batch = ()
    for term in iter_stack(aggregate):
        s = term, stack
        batch = (s, (quote, (S_infra, (S_first, batch))))
    stack = (batch, ((), stack))
    return stack, (S_infra, expression), dictionary

@inscribe
def primrec(stack, expression, dictionary):
    '''
    From the "Overview of the language JOY":

    > The primrec combinator expects two quoted programs in addition to a
    data parameter. For an integer data parameter it works like this: If
    the data parameter is zero, then the first quotation has to produce
    the value to be returned. If the data parameter is positive then the
    second has to combine the data parameter with the result of applying
    the function to its predecessor.::

        5  [1]  [*]  primrec

    > Then primrec tests whether the top element on the stack (initially
    the 5) is equal to zero. If it is, it pops it off and executes one of
    the quotations, the [1] which leaves 1 on the stack as the result.
    Otherwise it pushes a decremented copy of the top element and
    recurses. On the way back from the recursion it uses the other
    quotation, [*], to multiply what is now a factorial on top of the
    stack by the second element on the stack.::

        n [Base] [Recur] primrec

           0 [Base] [Recur] primrec
        ------------------------------
              Base

             n [Base] [Recur] primrec
        ------------------------------------------ n > 0
           n (n-1) [Base] [Recur] primrec Recur

    '''
    recur, (base, (n, stack)) = stack
    if n <= 0:
        expression = concat(base, expression)
    else:
        expression = S_primrec, concat(recur, expression)
        stack = recur, (base, (n - 1, (n, stack)))

#def cleave(S, expression, dictionary):
#  '''
#  The cleave combinator expects two quotations, and below that an item X.
#  It first executes [P], with X on top, and saves the top result element.
#  Then it executes [Q], again with X, and saves the top result.
#  Finally it restores the stack to what it was below X and pushes the two
#  results P(X) and Q(X).
#  '''
#  (Q, (P, (x, stack))) = S
#  p = joy((x, stack), P, dictionary)[0][0]
#  q = joy((x, stack), Q, dictionary)[0][0]
#  return (q, (p, stack)), expression, dictionary

@inscribe
def branch(stack, expression, dictionary):
    '''
    Use a Boolean value to select one of two quoted programs to run.

    ::

        branch == roll< choice i

    ::

           False [F] [T] branch
        --------------------------
              F

           True [F] [T] branch
                 T

    '''
    (then, (else_, (flag, stack))) = stack
    return stack, concat(then if flag else else_, expression), dictionary

@inscribe
def ifte(stack, expression, dictionary):
  '''
  If-Then-Else Combinator
  ::

                  ... [if] [then] [else] ifte
       ---------------------------------------------------
          ... [[else] [then]] [...] [if] infra select i

                ... [if] [then] [else] ifte
       -------------------------------------------------------
          ... [else] [then] [...] [if] infra first choice i

  Has the effect of grabbing a copy of the stack on which to run the
  if-part using infra.
  '''
  (else_, (then, (if_, stack))) = stack
  expression = (S_infra, (S_first, (S_choice, (S_i, expression))))
  stack = (if_, (stack, (then, (else_, stack))))
  return stack, expression, dictionary

@inscribe
def cond(stack, expression, dictionary):
    '''
    This combinator works like a case statement.  It expects a single quote
    on the stack that must contain zero or more condition quotes and a 
    default quote.  Each condition clause should contain a quoted predicate
    followed by the function expression to run if that predicate returns
    true.  If no predicates return true the default function runs.

    It works by rewriting into a chain of nested `ifte` expressions, e.g.::

              [[[B0] T0] [[B1] T1] [D]] cond
        -----------------------------------------
           [B0] [T0] [[B1] [T1] [D] ifte] ifte

    '''
    conditions, stack = stack
    if conditions:
        expression = _cond(conditions, expression)
        try:
            # Attempt to preload the args to first ifte.
            (P, (T, (E, expression))) = expression
            # If, for any reason, the argument to cond should happen to contain
            # only the default clause then this optimization will fail.
            pass
        else:
            stack = (E, (T, (P, stack)))

def _cond(conditions, expression):
    (clause, rest) = conditions
    if not rest:  # clause is [D]
        return clause
    P, T = clause
    return (P, (T, (_cond(rest, ()), (S_ifte, expression))))

@inscribe
def dip(stack, expression, dictionary):
    '''
    The dip combinator expects a quoted program on the stack and below it
    some item, it hoists the item into the expression and runs the program
    on the rest of the stack.
    ::

           ... x [Q] dip
        -------------------
             ... Q x

    '''
    try:
        (quote, (x, stack)) = stack
    expression = (x, expression)

@inscribe
def dipd(S, expression, dictionary):
    '''
    Like dip but expects two items.
    ::

           ... y x [Q] dip
             ... Q y x

    '''
    (quote, (x, (y, stack))) = S
    expression = (y, (x, expression))

@inscribe
def dipdd(S, expression, dictionary):
    '''
    Like dip but expects three items.
    ::

           ... z y x [Q] dip
             ... Q z y x

    '''
    (quote, (x, (y, (z, stack)))) = S
    expression = (z, (y, (x, expression)))

@inscribe
def app1(S, expression, dictionary):
    '''
    Given a quoted program on TOS and anything as the second stack item run
    the program and replace the two args with the first result of the
    program.
    ::

             ... x [Q] . app1
        -----------------------------------
           ... [x ...] [Q] . infra first

    '''
    (quote, (x, stack)) = S
    stack = (quote, ((x, stack), stack))
    expression = (S_infra, (S_first, expression))

@inscribe
def app2(S, expression, dictionary):
    '''Like app1 with two items.
    ::

               ... y x [Q] . app2
           ... [y ...] [Q] . infra first
               [x ...] [Q]   infra first

    '''
    expression = (S_infra, (S_first,
        ((x, stack), (quote, (S_infra, (S_first,
            expression))))))
    stack = (quote, ((y, stack), stack))

@inscribe
def app3(S, expression, dictionary):
    '''Like app1 with three items.
    ::

             ... z y x [Q] . app3
           ... [z ...] [Q] . infra first
               [y ...] [Q]   infra first

    '''
        ((y, stack), (quote, (S_infra, (S_first,
            expression))))))))))
    stack = (quote, ((z, stack), stack))

@inscribe
def step(S, expression, dictionary):
    '''
    Run a quoted program on each item in a sequence.
    ::

           ... [] [Q] . step
              ... .

           ... [a] [Q] . step
             ... a . Q

           ... [a b c] [Q] . step
        ----------------------------------------
                 ... a . Q [b c] [Q] step

    The step combinator executes the quotation on each member of the list
    on top of the stack.
    '''
    head, tail = aggregate
    stack = quote, (head, stack)
    if tail:
        expression = tail, (quote, (S_step, expression))
    expression = S_i, expression

@inscribe
def times(stack, expression, dictionary):
    '''
    times == [-- dip] cons [swap] infra [0 >] swap while pop
    ::

           ... n [Q] . times
        ---------------------  w/ n <= 0
             ... .

           ... 1 [Q] . times
             ... . Q

        -------------------------------------  w/ n > 1
             ... . Q (n - 1) [Q] times

    '''
    # times == [-- dip] cons [swap] infra [0 >] swap while pop
    (quote, (n, stack)) = stack
    if n <= 0:
    n -= 1
    if n:
        expression = n, (quote, (S_times, expression))
    expression = concat(quote, expression)

# The current definition above works like this:

#             [P] [Q] while
# --------------------------------------
#    [P] nullary [Q [P] nullary] loop

#   while == [pop i not] [popop] [dudipd] tailrec

#def while_(S, expression, dictionary):
#  '''[if] [body] while'''
#  (body, (if_, stack)) = S
#  while joy(stack, if_, dictionary)[0][0]:
#    stack = joy(stack, body, dictionary)[0]
#  return stack, expression, dictionary

@inscribe
def loop(stack, expression, dictionary):
    '''
    Basic loop combinator.
    ::

           ... True [Q] loop
              ... Q [Q] loop

           ... False [Q] loop
              ...

    '''
    try:
    if not isinstance(quote, tuple):
        raise NotAListError('Loop body not a list.')
    try:
        (flag, stack) = stack
    if flag:
        expression = concat(quote, (quote, (S_loop, expression)))

@inscribe
def cmp_(stack, expression, dictionary):
    '''
    cmp takes two values and three quoted programs on the stack and runs
    one of the three depending on the results of comparing the two values:
    ::

           a b [G] [E] [L] cmp
        ------------------------- a > b
            G

        ------------------------- a = b
                E

        ------------------------- a < b
                L
    '''
    L, (E, (G, (b, (a, stack)))) = stack
    expression = concat(G if a > b else L if a < b else E, expression)

#  FunctionWrapper(cleave),
#  FunctionWrapper(while_),

for F in (

    #divmod_ = pm = __(n2, n1), __(n4, n3)

    BinaryBuiltinWrapper(operator.eq),
    BinaryBuiltinWrapper(operator.ge),
    BinaryBuiltinWrapper(operator.gt),
    BinaryBuiltinWrapper(operator.le),
    BinaryBuiltinWrapper(operator.lt),
    BinaryBuiltinWrapper(operator.ne),

    BinaryBuiltinWrapper(operator.xor),
    BinaryBuiltinWrapper(operator.lshift),
    BinaryBuiltinWrapper(operator.rshift),

    BinaryBuiltinWrapper(operator.and_),
    BinaryBuiltinWrapper(operator.or_),

    BinaryBuiltinWrapper(operator.add),
    BinaryBuiltinWrapper(operator.floordiv),
    BinaryBuiltinWrapper(operator.mod),
    BinaryBuiltinWrapper(operator.mul),
    BinaryBuiltinWrapper(operator.pow),
    BinaryBuiltinWrapper(operator.sub),
##    BinaryBuiltinWrapper(operator.truediv),

    UnaryBuiltinWrapper(bool),
    UnaryBuiltinWrapper(operator.not_),

    UnaryBuiltinWrapper(abs),
    UnaryBuiltinWrapper(operator.neg),
    UnaryBuiltinWrapper(sqrt),

    UnaryBuiltinWrapper(floor),
    UnaryBuiltinWrapper(round),
    ):
    inscribe(F)
del F  # Otherwise Sphinx autodoc will pick it up.

for name, primitive in getmembers(genlib, isfunction):
    inscribe(SimpleFunctionWrapper(primitive))

add_aliases(_dictionary, ALIASES)

# GENERATED FILE. DO NOT EDIT.
# The code that generated these functions is in the repo history
# at the v0.4.0 tag.
from .errors import NotAListError, StackUnderflowError

def _Tree_add_Ee(stack):
  """
  ::

    ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])

  """
  (a1, (a2, (a3, ((a4, (a5, s1)), s2)))) = stack
  return ((a2, (a3, s1)), s2)

def _Tree_delete_R0(stack):
  """
  ::

    ([a2 ...1] a1 -- [a2 ...1] a2 a1 a1)

  """
  (a1, ((a2, s1), s2)) = stack
  return (a1, (a1, (a2, ((a2, s1), s2))))

def _Tree_delete_clear_stuff(stack):
  """
  ::

    (a3 a2 [a1 ...1] -- [...1])

  """
  ((a1, s1), (a2, (a3, s2))) = stack
  return (s1, s2)

def _Tree_get_E(stack):
  """
  ::

    ([a3 a4 ...1] a2 a1 -- a4)

  """
  (a1, (a2, ((a3, (a4, s1)), s2))) = stack
  return (a4, s2)

def ccons(stack):
  """
  ::

    (a2 a1 [...1] -- [a2 a1 ...1])

  """
  (s1, (a1, (a2, s2))) = stack
  return ((a2, (a1, s1)), s2)

def cons(stack):
  """
  ::

    (a1 [...0] -- [a1 ...0])

  """
  try: s0, stack = stack
  except ValueError: raise StackUnderflowError('Not enough values on stack.')
  if not isinstance(s0, tuple): raise NotAListError('Not a list.')
  try: a1, s23 = stack
  return ((a1, s0), s23)

def dup(stack):
  """
  ::

    (a1 -- a1 a1)

  """
  (a1, s23) = stack
  return (a1, (a1, s23))

def dupd(stack):
  """
  ::

    (a2 a1 -- a2 a2 a1)

  """
  (a1, (a2, s23)) = stack
  return (a1, (a2, (a2, s23)))

def dupdd(stack):
  """
  ::

    (a3 a2 a1 -- a3 a3 a2 a1)

  """
  (a1, (a2, (a3, s23))) = stack
  return (a1, (a2, (a3, (a3, s23))))

def first(stack):
  """
  ::

    ([a1 ...1] -- a1)

  """
  ((a1, s1), s23) = stack
  return (a1, s23)

def first_two(stack):
  """
  ::

    ([a1 a2 ...1] -- a1 a2)

  """
  ((a1, (a2, s1)), s2) = stack
  return (a2, (a1, s2))

def fourth(stack):
  """
  ::

    ([a1 a2 a3 a4 ...1] -- a4)

  """
  ((a1, (a2, (a3, (a4, s1)))), s2) = stack

def over(stack):
  """
  ::

    (a2 a1 -- a2 a1 a2)

  """
  return (a2, (a1, (a2, s23)))

def pop(stack):
  """
  ::

    (a1 --)

  """
  try:
    (a1, s23) = stack
  except ValueError:
    raise StackUnderflowError('Cannot pop empty stack.')
  return s23

def popd(stack):
  """
  ::

    (a2 a1 -- a1)

  """

def popdd(stack):
  """
  ::

    (a3 a2 a1 -- a2 a1)

  """
  return (a1, (a2, s23))

def popop(stack):
  """
  ::

    (a2 a1 --)

  """
  return s23

def popopd(stack):
  """
  ::

    (a3 a2 a1 -- a1)

  """

def popopdd(stack):
  """
  ::

    (a4 a3 a2 a1 -- a2 a1)

  """
  (a1, (a2, (a3, (a4, s23)))) = stack

def rest(stack):
  """
  ::

    ([a1 ...0] -- [...0])

  """
  try:
    s0, stack = stack
    raise StackUnderflowError
  if not isinstance(s0, tuple):
    raise NotAListError('Not a list.')
  try:
    _, s1 = s0
    raise StackUnderflowError('Cannot take rest of empty list.')
  return (s1, stack)

def rolldown(stack):
  """
  ::

    (a1 a2 a3 -- a2 a3 a1)

  """
  (a3, (a2, (a1, s23))) = stack
  return (a1, (a3, (a2, s23)))

def rollup(stack):
  """
  ::

    (a1 a2 a3 -- a3 a1 a2)

  """
  return (a2, (a1, (a3, s23)))

def rrest(stack):
  """
  ::

    ([a1 a2 ...1] -- [...1])

  """

def second(stack):
  """
  ::

    ([a1 a2 ...1] -- a2)

  """
  return (a2, s2)

def stack(stack):
  """
  ::

    (... -- ... [...])

  """
  s0 = stack
  return (s0, s0)

def stuncons(stack):
  """
  ::

    (... a1 -- ... a1 a1 [...])

  """
  (a1, s1) = stack
  return (s1, (a1, (a1, s1)))

def stununcons(stack):
  """
  ::

    (... a2 a1 -- ... a2 a1 a1 a2 [...])

  """
  (a1, (a2, s1)) = stack
  return (s1, (a2, (a1, (a1, (a2, s1)))))

def swaack(stack):
  """
  ::

    ([...1] -- [...0])

  """
  try:
    (s1, s0) = stack
    raise StackUnderflowError('Not enough values on stack.')
  if not isinstance(s1, tuple):
  return (s0, s1)

def swap(stack):
  """
  ::

    (a1 a2 -- a2 a1)

  """
  try:
    (a2, (a1, s23)) = stack

def swons(stack):
  """
  ::

    ([...1] a1 -- [a1 ...1])

  """
  (a1, (s1, s2)) = stack
  return ((a1, s1), s2)

def third(stack):
  """
  ::

    ([a1 a2 a3 ...1] -- a3)

  """
  ((a1, (a2, (a3, s1))), s2) = stack
  return (a3, s2)

def tuck(stack):
  """
  ::

    (a2 a1 -- a1 a2 a1)

  """
  return (a1, (a2, (a1, s23)))

def uncons(stack):
  """
  ::

    ([a1 ...0] -- a1 [...0])

  """
  ((a1, s0), s23) = stack
  return (s0, (a1, s23))

def unit(stack):
  """
  ::

    (a1 -- [a1 ])

  """
  return ((a1, ()), s23)

def unswons(stack):
  """
  ::

    ([a1 ...1] -- [...1] a1)

  """
  ((a1, s1), s2) = stack
  return (a1, (s1, s2))

#
#    Copyright © 2014, 2015, 2016, 2017 Simon Forman
#
#    This file is part of Thun.
#
#
#
#
'''
This module exports a single function for converting text to a joy
expression as well as a single Symbol class and a single Exception type.

The Symbol string class is used by the interpreter to recognize literals
by the fact that they are not Symbol objects.

A crude grammar::

    joy = term*
    term = int | float | string | '[' joy ']' | symbol

A Joy expression is a sequence of zero or more terms.  A term is a
literal value (integer, float, string, or Joy expression) or a function
symbol.  Function symbols are unquoted strings and cannot contain square
brackets.   Terms must be separated by blanks, which can be omitted
around square brackets.

'''
from re import Scanner
from .utils.stack import list_to_stack

BRACKETS = r'\[|\]'
BLANKS = r'\s+'
WORDS = r'[^[\]\s]+'

token_scanner = Scanner([
    (BRACKETS, lambda _, token: token),
    (BLANKS, None),
    (WORDS, lambda _, token: token),
    ])

class Symbol(str):
    '''A string class that represents Joy function names.'''
    __repr__ = str.__str__

def text_to_expression(text):
    '''Convert a string to a Joy expression.

    When supplied with a string this function returns a Python datastructure
    that represents the Joy datastructure described by the text expression.
    Any unbalanced square brackets will raise a ParseError.

    :param str text: Text to convert.
    :raises ParseError: if the parse fails.
    '''
    return _parse(_tokenize(text))

class ParseError(ValueError):
    '''Raised when there is a error while parsing text.'''

def _tokenize(text):
    '''Convert a text into a stream of tokens.

    Converts function names to Symbols.

    Raise ParseError (with some of the failing text) if the scan fails.
    '''
    tokens, rest = token_scanner.scan(text)
    if rest:
        raise ParseError(
            'Scan failed at position %i, %r'
            % (len(text) - len(rest), rest[:10])
            )
    return tokens

def _parse(tokens):
    '''
    Return a stack/list expression of the tokens.
    '''
    frame = []
    stack = []
    for tok in tokens:
        if tok == '[':
            stack.append(frame)
            frame = []
        elif tok == ']':
            v = frame
            try: frame = stack.pop()
            except IndexError:
                raise ParseError('Extra closing bracket.')
            frame.append(list_to_stack(v))
        elif tok == 'true':
            frame.append(True)
        elif tok == 'false':
            frame.append(False)
        else:
            try:
                thing = int(tok)
            except ValueError:
                thing = Symbol(tok)
            frame.append(thing)
    if stack:
        raise ParseError('Unclosed bracket.')
    return list_to_stack(frame)

#
#    Copyright © 2014, 2015, 2017 Simon Forman
#
#
#
#
#
'''
When talking about Joy we use the terms "stack", "quote", "sequence",
"list", and others to mean the same thing: a simple linear datatype that
permits certain operations such as iterating and pushing and popping
values from (at least) one end.

There is no "Stack" Python class, instead we use the  `cons list`_, a 
venerable two-tuple recursive sequence datastructure, where the
empty tuple ``()`` is the empty stack and ``(head, rest)`` gives the
recursive form of a stack with one or more items on it::

    stack := () | (item, stack)

Putting some numbers onto a stack::

    ()
    (1, ())
    (2, (1, ()))
    (3, (2, (1, ())))
    ...

Python has very nice "tuple packing and unpacking" in its syntax which
means we can directly "unpack" the expected arguments to a Joy function.

For example::

    def dup((head, tail)):
        return head, (head, tail)

We replace the argument "stack" by the expected structure of the stack,
in this case "(head, tail)", and Python takes care of unpacking the
incoming tuple and assigning values to the names.  (Note that Python
syntax doesn't require parentheses around tuples used in expressions
where they would be redundant.)

Unfortunately, the Sphinx documentation generator, which is used to generate this
web page, doesn't handle tuples in the function parameters.  And in Python 3, this
syntax was removed entirely.  Instead you would have to write::

    def dup(stack):
        head, tail = stack

We have two very simple functions, one to build up a stack from a Python
iterable and another to iterate through a stack and yield its items
one-by-one in order.  There are also two functions to generate string representations
of stacks.  They only differ in that one prints the terms in stack from left-to-right while the other prints from right-to-left.  In both functions *internal stacks* are
printed left-to-right.  These functions are written to support :doc:`../pretty`.

.. _cons list: https://en.wikipedia.org/wiki/Cons#Lists

'''
from .errors import NotAListError

def list_to_stack(el, stack=()):
    '''Convert a Python list (or other sequence) to a Joy stack::

    [1, 2, 3] -> (1, (2, (3, ())))

    :param list el: A Python list or other sequence (iterators and generators
             won't work because ``reverse()`` is called on ``el``.)
    :param stack stack: A stack, optional, defaults to the empty stack.

    '''
    for item in reversed(el):
        stack = item, stack

def iter_stack(stack):
    '''Iterate through the items on the stack.

    :param stack stack: A stack.
    :rtype: iterator
    '''
    while stack:
        item, stack = stack
        yield item

def stack_to_string(stack):
    '''
    Return a "pretty print" string for a stack.

    The items are written right-to-left::

        (top, (second, ...)) -> '... second top'

    :rtype: str
    '''
    f = lambda stack: reversed(list(iter_stack(stack)))
    return _to_string(stack, f)

def expression_to_string(expression):
    '''
    Return a "pretty print" string for a expression.

    The items are written left-to-right::

        (top, (second, ...)) -> 'top second ...'

    :param stack expression: A stack.
    '''
    return _to_string(expression, iter_stack)

_JOY_BOOL_LITS = 'false', 'true'

def _joy_repr(thing):
        if isinstance(thing, bool):
                return _JOY_BOOL_LITS[thing]
        return repr(thing)

def _to_string(stack, f):
    if not isinstance(stack, tuple): return _joy_repr(stack)
    if not stack: return ''  # shortcut
    return ' '.join(map(_s, f(stack)))

_s = lambda s: (
    '[%s]' % expression_to_string(s)
        if isinstance(s, tuple)
    else _joy_repr(s)
    )

def concat(quote, expression):
    '''Concatinate quote onto expression.

    In joy [1 2] [3 4] would become [1 2 3 4].

    :param stack quote: A stack.
    :raises RuntimeError: if quote is larger than sys.getrecursionlimit().
    '''
    # This is the fastest implementation, but will trigger
    # RuntimeError: maximum recursion depth exceeded
    # on quotes longer than sys.getrecursionlimit().

##    return (quote[0], concat(quote[1], expression)) if quote else expression

    # Original implementation.

##  return list_to_stack(list(iter_stack(quote)), expression)

    # In-lining is slightly faster (and won't break the
    # recursion limit on long quotes.)

    temp = []
    while quote:
        if not isinstance(quote, tuple):
            raise NotAListError(repr(quote))
        item, quote = quote
        temp.append(item)
    for item in reversed(temp):
        expression = item, expression
    return expression

def dnd(stack, from_index, to_index):
    '''
    Given a stack and two indices return a rearranged stack.
    First remove the item at from_index and then insert it at to_index,
    the second index is relative to the stack after removal of the item
    at from_index.

    This function reuses all of the items and as much of the stack as it
    can.  It's meant to be used by remote clients to support drag-n-drop
    rearranging of the stack from e.g. the StackListbox.
    '''
    assert 0 <= from_index
    assert 0 <= to_index
    if from_index == to_index:
        return stack
    head, n = [], from_index
    while True:
        n -= 1
        if n < 0:
            break
        head.append(item)
    assert len(head) == from_index
    # now we have two cases:
    diff = from_index - to_index
    if diff < 0:
        # from < to
        # so the destination index is still in the stack
        while diff:
            h, stack = stack
            head.append(h)
            diff += 1
    else:
        # from > to
        # so the destination is in the head list
            stack = head.pop(), stack
            diff -= 1
    stack = item, stack
    while head:
        stack = head.pop(), stack

def pick(stack, n):
    '''
    Return the nth item on the stack.

    :param int n: An index into the stack.
    :raises ValueError: if ``n`` is less than zero.
    :raises IndexError: if ``n`` is equal to or greater than the length of ``stack``.
    :rtype: whatever
    '''
    if n < 0:
        raise ValueError
        try:
            item, stack = stack
        n -= 1
        if n < 0:
            break
    return item

#
#    Copyright © 2016 Simon Forman
#
#
#
#
#
'''
Pretty printing support, e.g.::

    Joy? [23 18 * 99 +] trace
           • 23 18 mul 99 add
        23 • 18 mul 99 add
     23 18 • mul 99 add
       414 • 99 add
    414 99 • add
       513 • 

    513 <-top

    joy? 

On each line the stack is printed with the top to the left, then a
bullet symbol, •, to represent the current locus of processing, then
the pending expression to the right.
'''
# (Kinda clunky and hacky.  This should be swapped out in favor of much
# smarter stuff.)
from .stack import expression_to_string, stack_to_string
from ..joy import joy
from ..library import FunctionWrapper

def trace(stack, expression, dictionary):
    '''Evaluate a Joy expression on a stack and print a trace.

    This function is just like the `i` combinator but it also prints a
    trace of the evaluation

    '''
    tp = TracePrinter()
    quote, stack = stack
    try:
        s, _, d = joy(stack, quote, dictionary, tp.viewer)
    except:
        tp.print_()
        print('-' * 73)
        raise
    else:
    return s, expression, d

class TracePrinter(object):
    '''
    This is what does the formatting.  You instantiate it and pass the ``viewer()``
    method to the :py:func:`joy.joy.joy` function, then print it to see the
    trace.
    '''

    def __init__(self):
        self.history = []

    def viewer(self, stack, expression):
        '''
        Record the current stack and expression in the TracePrinter's history.
        Pass this method as the ``viewer`` argument to the :py:func:`joy.joy.joy` function.

        :param stack quote: A stack.
        :param stack expression: A stack.
        '''
        self.history.append((stack, expression))

    def __str__(self):
        return '\n'.join(self.go())

    def go(self):
        '''
        Return a list of strings, one for each entry in the history, prefixed
        with enough spaces to align all the interpreter dots.

        This method is called internally by the ``__str__()`` method.

        :rtype: list(str)
        '''
        max_stack_length = 0
        lines = []
        for stack, expression in self.history:
            stack = stack_to_string(stack)
            expression = expression_to_string(expression)
            n = len(stack)
            if n > max_stack_length:
                max_stack_length = n
            lines.append((n, '%s • %s' % (stack, expression)))
        for i in range(len(lines)):  # Prefix spaces to line up '•'s.
            length, line = lines[i]
            lines[i] =  (' ' * (max_stack_length - length) + line)
        return lines

    def print_(self):
        try:
            print(self)
        except:
            print_exc()
            print('Exception while printing viewer.')