diff --git a/implementations/Python/joy/utils/generated_library.py b/implementations/Python/generated_library.py similarity index 100% rename from implementations/Python/joy/utils/generated_library.py rename to implementations/Python/generated_library.py diff --git a/implementations/Python/joy/joy.py b/implementations/Python/joy/joy.py deleted file mode 100644 index 76fc8f9..0000000 --- a/implementations/Python/joy/joy.py +++ /dev/null @@ -1,150 +0,0 @@ -# -*- 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 . -# -''' -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 diff --git a/implementations/Python/joy/library.py b/implementations/Python/joy/library.py index f556f73..6cd5e0c 100644 --- a/implementations/Python/joy/library.py +++ b/implementations/Python/joy/library.py @@ -1,236 +1,12 @@ -# -*- 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 . -# -''' -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): @@ -247,16 +23,6 @@ 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): @@ -462,21 +228,6 @@ def sort_(S): return list_to_stack(sorted(iter_stack(tos))), stack -@inscribe -@SimpleFunctionWrapper -def clear(stack): - '''Clear everything from the stack. - :: - - clear == stack [pop stack] loop - - ... clear - --------------- - - ''' - return () - - @inscribe @SimpleFunctionWrapper def disenstacken(stack): @@ -503,22 +254,6 @@ def reverse(S): return res, stack -@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): @@ -607,26 +342,6 @@ def divmod_(S): 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): @@ -634,31 +349,6 @@ 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 @@ -693,35 +383,6 @@ def warranty(stack, expression, dictionary): 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 # @@ -1347,48 +1008,8 @@ def cmp_(stack, expression, dictionary): # 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) diff --git a/implementations/Python/joy/parser.py b/implementations/Python/joy/parser.py deleted file mode 100644 index 4b6f2f0..0000000 --- a/implementations/Python/joy/parser.py +++ /dev/null @@ -1,131 +0,0 @@ -# -*- 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 . -# -''' -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) diff --git a/implementations/Python/joy/utils/errors.py b/implementations/Python/joy/utils/errors.py deleted file mode 100644 index 4834ad3..0000000 --- a/implementations/Python/joy/utils/errors.py +++ /dev/null @@ -1,5 +0,0 @@ -class NotAListError(Exception): pass -class NotAnIntError(Exception): pass -class StackUnderflowError(Exception): pass - - diff --git a/implementations/Python/joy/utils/stack.py b/implementations/Python/joy/utils/stack.py deleted file mode 100644 index c722bf4..0000000 --- a/implementations/Python/joy/utils/stack.py +++ /dev/null @@ -1,272 +0,0 @@ -# -*- 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 . -# -''' -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 `_ - -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