diff --git a/docs/sphinx_docs/Makefile b/docs/sphinx_docs/Makefile deleted file mode 100644 index 9602036..0000000 --- a/docs/sphinx_docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = Thun -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/sphinx_docs/_build/doctrees/environment.pickle b/docs/sphinx_docs/_build/doctrees/environment.pickle deleted file mode 100644 index 1f9dd0d..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/environment.pickle and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/index.doctree b/docs/sphinx_docs/_build/doctrees/index.doctree deleted file mode 100644 index bca829d..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/index.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/joy.doctree b/docs/sphinx_docs/_build/doctrees/joy.doctree deleted file mode 100644 index a3e0fc8..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/joy.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/lib.doctree b/docs/sphinx_docs/_build/doctrees/lib.doctree deleted file mode 100644 index 66ed7a5..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/lib.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/library.doctree b/docs/sphinx_docs/_build/doctrees/library.doctree deleted file mode 100644 index 49aebd8..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/library.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Categorical.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Categorical.doctree deleted file mode 100644 index f9267f3..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Categorical.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Derivatives_of_Regular_Expressions.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Derivatives_of_Regular_Expressions.doctree deleted file mode 100644 index 3bd7342..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Derivatives_of_Regular_Expressions.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Developing.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Developing.doctree deleted file mode 100644 index 8bfaf10..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Developing.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Generator_Programs.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Generator_Programs.doctree deleted file mode 100644 index 033c340..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Generator_Programs.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Intro.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Intro.doctree deleted file mode 100644 index 14627f9..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Intro.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Newton-Raphson.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Newton-Raphson.doctree deleted file mode 100644 index 690f96e..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Newton-Raphson.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/NoUpdates.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/NoUpdates.doctree deleted file mode 100644 index 2210df9..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/NoUpdates.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Ordered_Binary_Trees.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Ordered_Binary_Trees.doctree deleted file mode 100644 index c90dfb8..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Ordered_Binary_Trees.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Quadratic.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Quadratic.doctree deleted file mode 100644 index f3e6e76..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Quadratic.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Recursion_Combinators.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Recursion_Combinators.doctree deleted file mode 100644 index f44277e..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Recursion_Combinators.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Replacing.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Replacing.doctree deleted file mode 100644 index af1b3bc..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Replacing.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Square_Spiral.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Square_Spiral.doctree deleted file mode 100644 index 32f73e8..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Square_Spiral.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/The_Four_Operations.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/The_Four_Operations.doctree deleted file mode 100644 index d232c6e..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/The_Four_Operations.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Treestep.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Treestep.doctree deleted file mode 100644 index b50dd7f..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Treestep.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/TypeChecking.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/TypeChecking.doctree deleted file mode 100644 index c0f5ebb..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/TypeChecking.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Types.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Types.doctree deleted file mode 100644 index 2709a75..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Types.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/Zipper.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/Zipper.doctree deleted file mode 100644 index 984d622..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/Zipper.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/notebooks/index.doctree b/docs/sphinx_docs/_build/doctrees/notebooks/index.doctree deleted file mode 100644 index 2bf2595..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/notebooks/index.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/parser.doctree b/docs/sphinx_docs/_build/doctrees/parser.doctree deleted file mode 100644 index 7db8efb..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/parser.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/pretty.doctree b/docs/sphinx_docs/_build/doctrees/pretty.doctree deleted file mode 100644 index 043c6a5..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/pretty.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/stack.doctree b/docs/sphinx_docs/_build/doctrees/stack.doctree deleted file mode 100644 index 77c61da..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/stack.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/doctrees/types.doctree b/docs/sphinx_docs/_build/doctrees/types.doctree deleted file mode 100644 index dbb3caf..0000000 Binary files a/docs/sphinx_docs/_build/doctrees/types.doctree and /dev/null differ diff --git a/docs/sphinx_docs/_build/html/.buildinfo b/docs/sphinx_docs/_build/html/.buildinfo deleted file mode 100644 index 73c8c3e..0000000 --- a/docs/sphinx_docs/_build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: d581c6009ec2b560e43b1edcc2912d38 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/sphinx_docs/_build/html/_images/omg.svg b/docs/sphinx_docs/_build/html/_images/omg.svg deleted file mode 100644 index a3be69c..0000000 --- a/docs/sphinx_docs/_build/html/_images/omg.svg +++ /dev/null @@ -1,185 +0,0 @@ - - - - - diff --git a/docs/sphinx_docs/_build/html/_modules/index.html b/docs/sphinx_docs/_build/html/_modules/index.html deleted file mode 100644 index 9ee35ef..0000000 --- a/docs/sphinx_docs/_build/html/_modules/index.html +++ /dev/null @@ -1,109 +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 <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,
- )
-
-
-
-
-
-[docs]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
-
-
-[docs]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)
-
-
-[docs]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
-
-
-[docs]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 = {}
-
-
-[docs]def inscribe(function, d=_dictionary):
- '''A decorator to inscribe functions into the default dictionary.'''
- d[function.name] = function
- return function
-
-
-[docs]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'•']),
- )
-
-
-[docs]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
-
-
-[docs]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
-
-
-[docs]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
-
-
-[docs]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
-
-
-[docs]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
-
-
-[docs]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
-
-
-[docs]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
-#
-
-
-[docs]@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)
- 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
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-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
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-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]
-
- '''
- n, (Q, stack) = stack
- while n > 0:
- try:
- _, Q = Q
- except ValueError:
- raise IndexError
- n -= 1
- return Q, stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def take(stack):
- '''
- Expects an integer and a quote on the stack and returns the quote with
- 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]
-
- '''
- n, (Q, stack) = stack
- x = ()
- while n > 0:
- try:
- item, Q = Q
- except ValueError:
- raise IndexError
- x = item, x
- n -= 1
- return x, stack
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def choice(stack):
- '''
- Use a Boolean value to select one of two items.
- ::
-
- A B false choice
- ----------------------
- A
-
-
- A B true choice
- ---------------------
- B
-
- '''
- (if_, (then, (else_, stack))) = stack
- assert isinstance(if_, bool), repr(if_)
- return then if if_ else else_, stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-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.
- Currently Python semantics are used to evaluate the "truthiness" of the
- Boolean value (so empty string, zero, etc. are counted as false, etc.)
- '''
- (flag, (choices, stack)) = stack
- (else_, (then, _)) = choices
- return then if flag else else_, stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def max_(S):
- '''Given a list find the maximum.'''
- tos, stack = S
- return max(iter_stack(tos)), stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def min_(S):
- '''Given a list find the minimum.'''
- tos, stack = S
- return min(iter_stack(tos)), stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def sum_(S):
- '''
- Given a quoted sequence of numbers return the sum.
- ::
-
- sum == 0 swap [+] step
-
- '''
- tos, stack = S
- return sum(iter_stack(tos)), stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-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))
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def unique(S):
- '''Given a list remove duplicate items.'''
- tos, stack = S
- I = list(iter_stack(tos))
- return list_to_stack(sorted(set(I), key=I.index)), stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def sort_(S):
- '''Given a list return it sorted.'''
- tos, stack = S
- return list_to_stack(sorted(iter_stack(tos))), stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def clear(stack):
- '''Clear everything from the stack.
- ::
-
- clear == stack [pop stack] loop
-
- ... clear
- ---------------
-
- '''
- return ()
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-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]
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-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
-
-
-[docs]@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
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-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
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-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.
- '''
- (tos, (second, stack)) = S
- accumulator = [
- (a, (b, ()))
- for a, b in zip(iter_stack(tos), iter_stack(second))
- ]
- return list_to_stack(accumulator), stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def succ(S):
- '''Increment TOS.'''
- (tos, stack) = S
- return tos + 1, stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def pred(S):
- '''Decrement TOS.'''
- (tos, stack) = S
- return tos - 1, stack
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-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)
-
-
-
-
-floor.__doc__ = math.floor.__doc__
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def divmod_(S):
- '''
- divmod(x, y) -> (quotient, remainder)
-
- Return the tuple (x//y, x%y). Invariant: q * y + r == x.
- '''
- y, (x, stack) = S
- q, r = divmod(x, y)
- return r, (q, stack)
-
-
-[docs]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
-
-
-[docs]@inscribe
-@SimpleFunctionWrapper
-def id_(stack):
- '''The identity function.'''
- return stack
-
-
-[docs]@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
-
-
-[docs]@inscribe
-@FunctionWrapper
-def words(stack, expression, dictionary):
- '''Print all the words in alphabetical order.'''
- print(' '.join(sorted(dictionary)))
- return stack, expression, dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-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/>.')
- return stack, expression, dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-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.')
- 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
-
-
-[docs]@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
-#
-
-
-# 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')
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
- except ValueError:
- raise StackUnderflowError('Not enough values on stack.')
- return stack, concat(quote, expression), dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-def x(stack, expression, dictionary):
- '''
- ::
-
- x == dup i
-
- ... [Q] x = ... [Q] dup i
- ... [Q] x = ... [Q] [Q] i
- ... [Q] x = ... [Q] Q
-
- '''
- quote, _ = stack
- return stack, concat(quote, expression), dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
-
-
-[docs]@inscribe
-@FunctionWrapper
-def ii(stack, expression, dictionary):
- '''
- ::
-
- ... a [Q] ii
- ------------------
- ... Q a Q
-
- '''
- quote, (a, stack) = stack
- expression = concat(quote, (a, concat(quote, expression)))
- return stack, expression, dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
-
-
-[docs]@inscribe
-@FunctionWrapper
-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)))
- return stack, expression, dictionary
-
-
-#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
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
- except ValueError:
- # 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)))
- return stack, expression, dictionary
-
-
-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))))
-
-
-[docs]@inscribe
-@FunctionWrapper
-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
- except ValueError:
- raise StackUnderflowError('Not enough values on stack.')
- expression = (x, expression)
- return stack, concat(quote, expression), dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-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))
- return stack, concat(quote, expression), dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-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)))
- return stack, concat(quote, expression), dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-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))
- return stack, expression, dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-def app2(S, expression, dictionary):
- '''Like app1 with two items.
- ::
-
- ... y x [Q] . app2
- -----------------------------------
- ... [y ...] [Q] . infra first
- [x ...] [Q] infra first
-
- '''
- (quote, (x, (y, stack))) = S
- expression = (S_infra, (S_first,
- ((x, stack), (quote, (S_infra, (S_first,
- expression))))))
- stack = (quote, ((y, stack), stack))
- return stack, expression, dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-def app3(S, expression, dictionary):
- '''Like app1 with three items.
- ::
-
- ... z y x [Q] . app3
- -----------------------------------
- ... [z ...] [Q] . infra first
- [y ...] [Q] infra first
- [x ...] [Q] infra first
-
- '''
- (quote, (x, (y, (z, stack)))) = S
- expression = (S_infra, (S_first,
- ((y, stack), (quote, (S_infra, (S_first,
- ((x, stack), (quote, (S_infra, (S_first,
- expression))))))))))
- stack = (quote, ((z, stack), stack))
- return stack, expression, dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-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.
- '''
- (quote, (aggregate, stack)) = S
- if not aggregate:
- return stack, expression, dictionary
- head, tail = aggregate
- stack = quote, (head, stack)
- if tail:
- expression = tail, (quote, (S_step, expression))
- expression = S_i, expression
- return stack, expression, dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-def times(stack, expression, dictionary):
- '''
- times == [-- dip] cons [swap] infra [0 >] swap while pop
- ::
-
- ... n [Q] . times
- --------------------- w/ n <= 0
- ... .
-
-
- ... 1 [Q] . times
- -----------------------
- ... . Q
-
-
- ... n [Q] . times
- ------------------------------------- w/ n > 1
- ... . Q (n - 1) [Q] times
-
- '''
- # times == [-- dip] cons [swap] infra [0 >] swap while pop
- (quote, (n, stack)) = stack
- if n <= 0:
- return stack, expression, dictionary
- n -= 1
- if n:
- expression = n, (quote, (S_times, expression))
- expression = concat(quote, expression)
- return stack, expression, dictionary
-
-
-# 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
-
-
-[docs]@inscribe
-@FunctionWrapper
-def loop(stack, expression, dictionary):
- '''
- Basic loop combinator.
- ::
-
- ... True [Q] loop
- -----------------------
- ... Q [Q] loop
-
- ... False [Q] loop
- ------------------------
- ...
-
- '''
- try:
- quote, stack = stack
- except ValueError:
- raise StackUnderflowError('Not enough values on stack.')
- if not isinstance(quote, tuple):
- raise NotAListError('Loop body not a list.')
- try:
- (flag, stack) = stack
- except ValueError:
- raise StackUnderflowError('Not enough values on stack.')
- if flag:
- expression = concat(quote, (quote, (S_loop, expression)))
- return stack, expression, dictionary
-
-
-[docs]@inscribe
-@FunctionWrapper
-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 [G] [E] [L] cmp
- ------------------------- a = b
- E
-
- a b [G] [E] [L] cmp
- ------------------------- a < b
- L
- '''
- L, (E, (G, (b, (a, stack)))) = stack
- expression = concat(G if a > b else L if a < b else E, expression)
- return stack, expression, dictionary
-
-
-# FunctionWrapper(cleave),
-# 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)
-
-# -*- 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'\[|\]'
-BLANKS = r'\s+'
-WORDS = r'[^[\]\s]+'
-
-
-token_scanner = Scanner([
- (SNIPPETS, lambda _, token: from_string(token)),
- (BRACKETS, lambda _, token: token),
- (BLANKS, None),
- (WORDS, lambda _, token: token),
- ])
-
-
-[docs]class Symbol(str):
- '''A string class that represents Joy function names.'''
- __repr__ = str.__str__
-
-
-[docs]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))
-
-
-
-
-
-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)
-
-# 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)
-
-
-[docs]def ccons(stack):
- """
- ::
-
- (a2 a1 [...1] -- [a2 a1 ...1])
-
- """
- (s1, (a1, (a2, s2))) = stack
- return ((a2, (a1, s1)), s2)
-
-
-[docs]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
- except ValueError: raise StackUnderflowError('Not enough values on stack.')
- return ((a1, s0), s23)
-
-
-[docs]def dup(stack):
- """
- ::
-
- (a1 -- a1 a1)
-
- """
- (a1, s23) = stack
- return (a1, (a1, s23))
-
-
-[docs]def dupd(stack):
- """
- ::
-
- (a2 a1 -- a2 a2 a1)
-
- """
- (a1, (a2, s23)) = stack
- return (a1, (a2, (a2, s23)))
-
-
-[docs]def dupdd(stack):
- """
- ::
-
- (a3 a2 a1 -- a3 a3 a2 a1)
-
- """
- (a1, (a2, (a3, s23))) = stack
- return (a1, (a2, (a3, (a3, s23))))
-
-
-[docs]def first(stack):
- """
- ::
-
- ([a1 ...1] -- a1)
-
- """
- ((a1, s1), s23) = stack
- return (a1, s23)
-
-
-[docs]def first_two(stack):
- """
- ::
-
- ([a1 a2 ...1] -- a1 a2)
-
- """
- ((a1, (a2, s1)), s2) = stack
- return (a2, (a1, s2))
-
-
-[docs]def fourth(stack):
- """
- ::
-
- ([a1 a2 a3 a4 ...1] -- a4)
-
- """
- ((a1, (a2, (a3, (a4, s1)))), s2) = stack
- return (a4, s2)
-
-
-[docs]def over(stack):
- """
- ::
-
- (a2 a1 -- a2 a1 a2)
-
- """
- (a1, (a2, s23)) = stack
- return (a2, (a1, (a2, s23)))
-
-
-[docs]def pop(stack):
- """
- ::
-
- (a1 --)
-
- """
- try:
- (a1, s23) = stack
- except ValueError:
- raise StackUnderflowError('Cannot pop empty stack.')
- return s23
-
-
-[docs]def popd(stack):
- """
- ::
-
- (a2 a1 -- a1)
-
- """
- (a1, (a2, s23)) = stack
- return (a1, s23)
-
-
-[docs]def popdd(stack):
- """
- ::
-
- (a3 a2 a1 -- a2 a1)
-
- """
- (a1, (a2, (a3, s23))) = stack
- return (a1, (a2, s23))
-
-
-
-
-
-[docs]def popopd(stack):
- """
- ::
-
- (a3 a2 a1 -- a1)
-
- """
- (a1, (a2, (a3, s23))) = stack
- return (a1, s23)
-
-
-[docs]def popopdd(stack):
- """
- ::
-
- (a4 a3 a2 a1 -- a2 a1)
-
- """
- (a1, (a2, (a3, (a4, s23)))) = stack
- return (a1, (a2, s23))
-
-
-[docs]def rest(stack):
- """
- ::
-
- ([a1 ...0] -- [...0])
-
- """
- try:
- s0, stack = stack
- except ValueError:
- raise StackUnderflowError
- if not isinstance(s0, tuple):
- raise NotAListError('Not a list.')
- try:
- _, s1 = s0
- except ValueError:
- raise StackUnderflowError('Cannot take rest of empty list.')
- return (s1, stack)
-
-
-[docs]def rolldown(stack):
- """
- ::
-
- (a1 a2 a3 -- a2 a3 a1)
-
- """
- (a3, (a2, (a1, s23))) = stack
- return (a1, (a3, (a2, s23)))
-
-
-[docs]def rollup(stack):
- """
- ::
-
- (a1 a2 a3 -- a3 a1 a2)
-
- """
- (a3, (a2, (a1, s23))) = stack
- return (a2, (a1, (a3, s23)))
-
-
-[docs]def rrest(stack):
- """
- ::
-
- ([a1 a2 ...1] -- [...1])
-
- """
- ((a1, (a2, s1)), s2) = stack
- return (s1, s2)
-
-
-[docs]def second(stack):
- """
- ::
-
- ([a1 a2 ...1] -- a2)
-
- """
- ((a1, (a2, s1)), s2) = stack
- return (a2, s2)
-
-
-
-
-
-[docs]def stuncons(stack):
- """
- ::
-
- (... a1 -- ... a1 a1 [...])
-
- """
- (a1, s1) = stack
- return (s1, (a1, (a1, s1)))
-
-
-[docs]def stununcons(stack):
- """
- ::
-
- (... a2 a1 -- ... a2 a1 a1 a2 [...])
-
- """
- (a1, (a2, s1)) = stack
- return (s1, (a2, (a1, (a1, (a2, s1)))))
-
-
-[docs]def swaack(stack):
- """
- ::
-
- ([...1] -- [...0])
-
- """
- try:
- (s1, s0) = stack
- except ValueError:
- raise StackUnderflowError('Not enough values on stack.')
- if not isinstance(s1, tuple):
- raise NotAListError('Not a list.')
- return (s0, s1)
-
-
-[docs]def swap(stack):
- """
- ::
-
- (a1 a2 -- a2 a1)
-
- """
- try:
- (a2, (a1, s23)) = stack
- except ValueError:
- raise StackUnderflowError('Not enough values on stack.')
- return (a1, (a2, s23))
-
-
-[docs]def swons(stack):
- """
- ::
-
- ([...1] a1 -- [a1 ...1])
-
- """
- (a1, (s1, s2)) = stack
- return ((a1, s1), s2)
-
-
-[docs]def third(stack):
- """
- ::
-
- ([a1 a2 a3 ...1] -- a3)
-
- """
- ((a1, (a2, (a3, s1))), s2) = stack
- return (a3, s2)
-
-
-[docs]def tuck(stack):
- """
- ::
-
- (a2 a1 -- a1 a2 a1)
-
- """
- (a1, (a2, s23)) = stack
- return (a1, (a2, (a1, s23)))
-
-
-[docs]def uncons(stack):
- """
- ::
-
- ([a1 ...0] -- a1 [...0])
-
- """
- ((a1, s0), s23) = stack
- return (s0, (a1, s23))
-
-
-[docs]def unit(stack):
- """
- ::
-
- (a1 -- [a1 ])
-
- """
- (a1, s23) = stack
- return ((a1, ()), s23)
-
-
-[docs]def unswons(stack):
- """
- ::
-
- ([a1 ...1] -- [...1] a1)
-
- """
- ((a1, s1), s2) = stack
- return (a1, (s1, s2))
-
-
-# -*- coding: utf-8 -*-
-#
-# Copyright © 2016 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/>.
-#
-'''
-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 traceback import print_exc
-from .stack import expression_to_string, stack_to_string
-from ..joy import joy
-from ..library import FunctionWrapper
-
-
-[docs]@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
-
- :param stack stack: The stack.
- :param stack expression: The expression to evaluate.
- :param dict dictionary: A ``dict`` mapping names to Joy functions.
- :rtype: (stack, (), dictionary)
-
- '''
- tp = TracePrinter()
- quote, stack = stack
- try:
- s, _, d = joy(stack, quote, dictionary, tp.viewer)
- except:
- tp.print_()
- print('-' * 73)
- raise
- else:
- tp.print_()
- return s, expression, d
-
-
-[docs]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 = []
-
-[docs] 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())
-
-[docs] 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.')
-
-# -*- 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
-
-
-[docs]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
-
-
-[docs]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
-
-
-[docs]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)
-
-
-[docs]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)
- )
-
-
-[docs]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
-
-
-
-[docs]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
-
-
-[docs]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
-