+++ /dev/null
-# -*- 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 joy.parser import text_to_expression, ParseError, Symbol
-from joy.utils.stack import stack_to_string
-from joy.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.
- :param stack stack: The stack.
- :param dict dictionary: A ``dict`` mapping names to Joy functions.
- :param function viewer: Optional viewer function.
- :rtype: (stack, (), dictionary)
-
- '''
- 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.
-
- :param stack stack: The stack.
- :param dict dictionary: A ``dict`` mapping names to Joy functions.
- :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.
- '''
- if dictionary is None:
- dictionary = {}
- try:
- while True:
- try:
- text = input()
- except (EOFError, KeyboardInterrupt):
- break
- try:
- stack, _, dictionary = run(text, stack, dictionary)
- 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)
- except:
- print_exc()
- print(stack_to_string(stack))
- except:
- print_exc()
- return stack
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2014-2020 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 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.errors import (
- NotAListError,
- NotAnIntError,
- StackUnderflowError,
- )
-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 BinaryMathWrapper(f):
- '''
- Wrap functions that take two numbers and return a single result.
- '''
- @FunctionWrapper
- @wraps(f)
- def inner(stack, expression, dictionary):
- try:
- (a, (b, stack)) = stack
- except ValueError:
- raise StackUnderflowError('Not enough values on stack.')
- if ( not isinstance(a, int)
- or not isinstance(b, int)
- # bool is int in Python.
- or isinstance(a, bool)
- or isinstance(b, bool)
- ):
- raise NotAnIntError
- result = f(b, a)
- return (result, stack), expression, dictionary
- return inner
-
-
-def BinaryLogicWrapper(f):
- '''
- Wrap functions that take two numbers and return a single result.
- '''
- @FunctionWrapper
- @wraps(f)
- def inner(stack, expression, dictionary):
- try:
- (a, (b, stack)) = stack
- except ValueError:
- raise StackUnderflowError('Not enough values on stack.')
-## if (not isinstance(a, bool)
-## or not isinstance(b, bool)
-## ):
-## raise NotABoolError
- result = f(b, a)
- return (result, stack), expression, dictionary
- return inner
-
-
-def UnaryBuiltinWrapper(f):
- '''
- Wrap functions that take one argument and return a single result.
- '''
- @FunctionWrapper
- @wraps(f)
- def inner(stack, expression, dictionary):
- (a, stack) = stack
- result = f(a)
- return (result, stack), expression, dictionary
- return inner
-
-
-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):
return stack, expression, dictionary
-# @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
@SimpleFunctionWrapper
def getitem(stack):
@inscribe
@SimpleFunctionWrapper
-def clear(stack):
- '''Clear everything from the stack.
- ::
-
- clear == stack [pop stack] loop
-
- ... clear
- ---------------
-
- '''
- return ()
-
-
-@inscribe
-@SimpleFunctionWrapper
def disenstacken(stack):
'''
The disenstacken operator expects a list on top of the stack and makes that
@inscribe
@SimpleFunctionWrapper
-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
-@SimpleFunctionWrapper
def shunt(stack):
'''
Like concat but reverses the top list into the second.
return r, (q, 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
@SimpleFunctionWrapper
def id_(stack):
return stack
-@inscribe
-@SimpleFunctionWrapper
-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
-@FunctionWrapper
-def words(stack, expression, dictionary):
- '''Print all the words in alphabetical order.'''
- print(' '.join(sorted(dictionary)))
- return stack, expression, dictionary
-
@inscribe
@FunctionWrapper
return stack, expression, dictionary
-# 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
-@FunctionWrapper
-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))
- return stack, expression, dictionary
-
-
#
# § Combinators
#
# FunctionWrapper(while_),
-for F in (
-
- #divmod_ = pm = __(n2, n1), __(n4, n3)
-
- BinaryMathWrapper(operator.eq),
- BinaryMathWrapper(operator.ge),
- BinaryMathWrapper(operator.gt),
- BinaryMathWrapper(operator.le),
- BinaryMathWrapper(operator.lt),
- BinaryMathWrapper(operator.ne),
-
- BinaryMathWrapper(operator.xor),
- BinaryMathWrapper(operator.lshift),
- BinaryMathWrapper(operator.rshift),
-
- BinaryLogicWrapper(operator.and_),
- BinaryLogicWrapper(operator.or_),
-
- BinaryMathWrapper(operator.add),
- BinaryMathWrapper(operator.floordiv),
- BinaryMathWrapper(operator.mod),
- BinaryMathWrapper(operator.mul),
- BinaryMathWrapper(operator.pow),
- BinaryMathWrapper(operator.sub),
-## BinaryMathWrapper(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)
+++ /dev/null
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2014, 2015, 2016, 2017 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 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 = integer | '[' joy ']' | symbol
-
-A Joy expression is a sequence of zero or more terms. A term is a
-literal value (integer or Joy expression) or a function symbol.
-Function symbols are sequences of non-blanks and cannot contain square
-brackets. Terms must be separated by blanks, which can be omitted
-around square brackets.
-
-'''
-from re import Scanner
-from joy.utils.stack import list_to_stack
-from joy.utils.snippets import (
- pat as SNIPPETS,
- from_string,
- Snippet,
- )
-
-
-BRACKETS = r'\[|\]' # Left or right square bracket.
-BLANKS = r'\s+' # One-or-more blankspace.
-WORDS = (
- '[' # Character class
- '^' # not a
- '[' # left square bracket nor a
- '\]' # right square bracket (escaped so it doesn't close the character class)
- '\s' # nor blankspace
- ']+' # end character class, one-or-more.
- )
-
-
-token_scanner = Scanner([
- (SNIPPETS, lambda _, token: from_string(token)),
- (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.
- :rtype: stack
- :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.') from None
- frame.append(list_to_stack(v))
- elif tok == 'true': frame.append(True)
- elif tok == 'false': frame.append(False)
- elif isinstance(tok, Snippet): frame.append(tok)
- else:
- try: thing = int(tok)
- except ValueError: thing = Symbol(tok)
- frame.append(thing)
- if stack: raise ParseError('Unclosed bracket.')
- return list_to_stack(frame)
+++ /dev/null
-class NotAListError(Exception): pass
-class NotAnIntError(Exception): pass
-class StackUnderflowError(Exception): pass
-
-
+++ /dev/null
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2014, 2015, 2017 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/>.
-#
-'''
-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.
-
- In describing Joy I have used the term quotation to describe all of the
- above, because I needed a word to describe the arguments to combinators
- which fulfill the same role in Joy as lambda abstractions (with
- variables) fulfill in the more familiar functional languages. I use the
- term list for those quotations whose members are what I call literals:
- numbers, characters, truth values, sets, strings and other quotations.
- All these I call literals because their occurrence in code results in
- them being pushed onto the stack. But I also call [London Paris] a list.
- So, [dup \*] is a quotation but not a list.
-
-`"A Conversation with Manfred von Thun" w/ Stevan Apter <http://archive.vector.org.uk/art10000350>`_
-
-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
- return head, (head, tail)
-
-
-We have two very simple functions, one to build up a stack from a Python
-list 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
-from .snippets import Snippet, to_string as snip_to_string
-
-
-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. This
- allows for concatinating Python lists (or other sequence objects)
- onto an existing Joy stack.
- :rtype: stack
-
- '''
- for item in reversed(el):
- stack = item, stack
- return 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'
-
- :param stack stack: A stack.
- :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.
- :rtype: str
- '''
- return _to_string(expression, iter_stack)
-
-
-JOY_BOOL_LITERALS = 'false', 'true'
-
-
-def _joy_repr(thing):
- if isinstance(thing, bool): return JOY_BOOL_LITERALS[thing]
- if isinstance(thing, Snippet): return snip_to_string(thing)
- return repr(thing)
-
-
-def _to_string(stack, f):
- if not isinstance(stack, tuple): return _joy_repr(stack)
- if not stack: return '' # shortcut
- if isinstance(stack, Snippet): return snip_to_string(stack)
- return ' '.join(map(_s, f(stack)))
-
-
-_s = lambda s: (
- '[%s]' % expression_to_string(s)
- if isinstance(s, tuple)
- and not isinstance(s, Snippet)
- # Is it worth making a non-tuple class for Snippet?
- # Doing this check on each tuple seems a bit much.
- 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.
- :param stack expression: A stack.
- :rtype: stack
- '''
- # This is the fastest implementation, but will trigger
- # RuntimeError: maximum recursion depth exceeded
- # on quotes longer than sys.getrecursionlimit().
- # :raises RuntimeError: if quote is larger 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.)
-
- if not isinstance(quote, tuple):
- raise NotAListError('Not a list.')
- temp = []
- while 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:
- item, stack = stack
- 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
- while diff:
- stack = head.pop(), stack
- diff -= 1
- stack = item, stack
- while head:
- stack = head.pop(), stack
- return stack
-
-
-def pick(stack, n):
- '''
- Return the nth item on the stack.
-
- :param stack stack: A 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
- while True:
- try:
- item, stack = stack
- except ValueError:
- raise IndexError
- n -= 1
- if n < 0:
- break
- return item