X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=joy%2Flibrary.py;h=800ff3b76fca339294dcdcdd2bc7a1ff3b616c29;hb=fcd4c613e4a049a21fe0ed5bed42770390bc1b80;hp=4b62c71513082aa8c0e2620e2c08ff8c293a1895;hpb=a3c3709e4c3252d698690f8a0978b046f83c9712;p=joypy%2FThun.git diff --git a/joy/library.py b/joy/library.py index 4b62c71..800ff3b 100644 --- a/joy/library.py +++ b/joy/library.py @@ -1,21 +1,21 @@ # -*- coding: utf-8 -*- # -# Copyright © 2014, 2015, 2017, 2018 Simon Forman +# Copyright © 2014-2020 Simon Forman # -# This file is part of Joypy +# This file is part of Thun # -# Joypy is free software: you can redistribute it and/or modify +# 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. # -# Joypy is distributed in the hope that it will be useful, +# 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 Joypy. If not see . +# along with Thun. If not see . # ''' This module contains the Joy function infrastructure and a library of @@ -23,224 +23,187 @@ functions. Its main export is a Python function initialize() that returns a dictionary of Joy functions suitable for use with the joy() function. ''' -from inspect import getdoc +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.stack import list_to_stack, iter_stack, pick, pushback -from .utils.brutal_hackery import rename_code_object +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): - '''A decorator to inscribe functions into the default dictionary.''' - _dictionary[function.name] = function - return function +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() + '''Return a dictionary of Joy functions for use with joy().''' + return _dictionary.copy() ALIASES = ( - ('add', ['+']), - ('and', ['&']), - ('bool', ['truthy']), - ('mul', ['*']), - ('truediv', ['/']), - ('mod', ['%', 'rem', 'remainder', 'modulus']), - ('eq', ['=']), - ('ge', ['>=']), - ('getitem', ['pick', 'at']), - ('gt', ['>']), - ('le', ['<=']), - ('lshift', ['<<']), - ('lt', ['<']), - ('ne', ['<>', '!=']), - ('rshift', ['>>']), - ('sub', ['-']), - ('xor', ['^']), - ('succ', ['++']), - ('pred', ['--']), - ('rolldown', ['roll<']), - ('rollup', ['roll>']), - ('id', ['•']), - ) - - -def add_aliases(D, A=ALIASES): - ''' - 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 - - -definitions = ('''\ -second == rest first -third == rest rest first -of == swap at -product == 1 swap [*] step -swons == swap cons -swoncat == swap concat -flatten == [] swap [concat] step -unit == [] cons -quoted == [unit] dip -unquoted == [i] dip -enstacken == stack [clear] dip -disenstacken == ? [uncons ?] loop pop -? == dup truthy -dinfrirst == dip infra first -nullary == [stack] dinfrirst -unary == [stack [pop] dip] dinfrirst -binary == [stack [popop] dip] dinfrirst -ternary == [stack [popop pop] dip] dinfrirst -pam == [i] map -run == [] swap infra -sqr == dup mul -size == 0 swap [pop ++] step -cleave == [i] app2 [popd] dip -average == [sum 1.0 *] [size] cleave / -gcd == 1 [tuck modulus dup 0 >] loop pop -least_fraction == dup [gcd] infra [div] concat map -*fraction == [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons -*fraction0 == concat [[swap] dip * [*] dip] infra -down_to_zero == [0 >] [dup --] while -range_to_zero == unit [down_to_zero] infra -anamorphism == [pop []] swap [dip swons] genrec -range == [0 <=] [1 - dup] anamorphism -while == swap [nullary] cons dup dipd concat loop -dudipd == dup dipd -primrec == [i] genrec -step_zero == 0 roll> step -''' - -##Zipper -##z-down == [] swap uncons swap -##z-up == swons swap shunt -##z-right == [swons] cons dip uncons swap -##z-left == swons [uncons swap] dip swap - -##Quadratic Formula -##divisor == popop 2 * -##minusb == pop neg -##radical == swap dup * rollup * 4 * - sqrt -##root1 == + swap / -##root2 == - swap / -##q0 == [[divisor] [minusb] [radical]] pam -##q1 == [[root1] [root2]] pam -##quadratic == [q0] ternary i [q1] ternary - -# Project Euler -##'''\ -##PE1.1 == + dup [+] dip -##PE1.2 == dup [3 & PE1.1] dip 2 >> -##PE1.3 == 14811 swap [PE1.2] times pop -##PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop -##''' -#PE1.2 == [PE1.1] step -#PE1 == 0 0 66 [[3 2 1 3 1 2 3] PE1.2] times [3 2 1 3] PE1.2 pop -) + ('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 + '''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) - @rename_code_object(f.__name__) - def inner(stack, expression, dictionary): - return f(stack), expression, dictionary - return inner + ''' + Wrap functions that take and return just a stack. + ''' + @FunctionWrapper + @wraps(f) + def inner(stack, expression, dictionary): + return f(stack), expression, dictionary + return inner def BinaryBuiltinWrapper(f): - ''' - Wrap functions that take two arguments and return a single result. - ''' - @FunctionWrapper - @wraps(f) - @rename_code_object(f.__name__) - def inner(stack, expression, dictionary): - (a, (b, stack)) = stack - result = f(b, a) - return (result, stack), expression, dictionary - return inner + ''' + Wrap functions that take two arguments 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.') + # Boolean predicates like "or" fail here. :( +## if ( not isinstance(a, int) +## or not isinstance(b, int) +## or isinstance(a, bool) # Because bools are ints in Python. +## or isinstance(b, bool) +## ): +## raise NotAnIntError + result = f(b, a) + return (result, stack), expression, dictionary + return inner def UnaryBuiltinWrapper(f): - ''' - Wrap functions that take one argument and return a single result. - ''' - @FunctionWrapper - @wraps(f) - @rename_code_object(f.__name__) - def inner(stack, expression, dictionary): - (a, stack) = stack - result = f(a) - return (result, stack), expression, dictionary - return inner - - -class DefinitionWrapper(object): - ''' - Provide implementation of defined functions, and some helper methods. - ''' - - def __init__(self, name, body_text, doc=None): - self.name = self.__name__ = name - self.body = text_to_expression(body_text) - self._body = tuple(iter_stack(self.body)) - self.__doc__ = doc or body_text - - def __call__(self, stack, expression, dictionary): - expression = list_to_stack(self._body, expression) - return stack, expression, dictionary - - @classmethod - def parse_definition(class_, defi): ''' - Given some text describing a Joy function definition parse it and - return a DefinitionWrapper. + Wrap functions that take one argument and return a single result. ''' - name, proper, body_text = (n.strip() for n in defi.partition('==')) - if not proper: - raise ValueError('Definition %r failed' % (defi,)) - return class_(name, body_text) + @FunctionWrapper + @wraps(f) + def inner(stack, expression, dictionary): + (a, stack) = stack + result = f(a) + return (result, stack), expression, dictionary + return inner - @classmethod - def add_definitions(class_, defs, dictionary): - for definition in _text_to_defs(defs): - class_.add_def(definition, dictionary) - - @classmethod - def add_def(class_, definition, dictionary): - F = class_.parse_definition(definition) - dictionary[F.name] = F +class Def(object): + ''' + Definitions created by inscribe. + ''' -def _text_to_defs(text): - return (line.strip() for line in text.splitlines() if '==' in line) + 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) # @@ -249,438 +212,369 @@ def _text_to_defs(text): @inscribe -@SimpleFunctionWrapper -def parse((text, stack)): - '''Parse the string on the stack to a Joy expression.''' - expression = text_to_expression(text) - return expression, stack +@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 -@inscribe -@SimpleFunctionWrapper -def first(((head, tail), stack)): - '''first == uncons pop''' - return head, stack + ''' + (name, body), stack = stack + inscribe(Def(name, body), dictionary) + return stack, expression, dictionary @inscribe @SimpleFunctionWrapper -def rest(((head, tail), stack)): - '''rest == uncons popd''' - return tail, stack +def parse(stack): + '''Parse the string on the stack to a Joy expression.''' + text, stack = stack + expression = text_to_expression(text) + return expression, stack + + +# @inscribe +# @SimpleFunctionWrapper +# def infer_(stack): +# '''Attempt to infer the stack effect of a Joy expression.''' +# E, stack = stack +# effects = infer_expression(E) +# e = list_to_stack([(fi, (fo, ())) for fi, fo in effects]) +# return e, stack @inscribe @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. + getitem == drop first - [a b c d] 0 getitem - ------------------------- - a + Expects an integer and a quote on the stack and returns the item at the + nth position in the quote counting from 0. + :: - ''' - n, (Q, stack) = stack - return pick(Q, n), stack + [a b c d] 0 getitem + ------------------------- + a + + ''' + n, (Q, stack) = stack + return pick(Q, n), stack @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. + drop == [rest] times - [a b c d] 2 drop - ---------------------- - [c d] + Expects an integer and a quote on the stack and returns the quote with + n items removed off the top. + :: - ''' - n, (Q, stack) = stack - while n > 0: - try: - _, Q = Q - except ValueError: - raise IndexError - n -= 1 - return Q, stack + [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 @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 + ''' + 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 + + +@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 @inscribe @SimpleFunctionWrapper def choice(stack): - ''' - Use a Boolean value to select one of two items. + ''' + Use a Boolean value to select one of two items. + :: - A B False choice - ---------------------- - A + A B False choice + ---------------------- + A - A B True choice - --------------------- - B + A B True choice + --------------------- + B - Currently Python semantics are used to evaluate the "truthiness" of the - Boolean value (so empty string, zero, etc. are counted as false, etc.) - ''' - (if_, (then, (else_, stack))) = stack - return then if if_ else else_, stack + Currently Python semantics are used to evaluate the "truthiness" of the + Boolean value (so empty string, zero, etc. are counted as false, etc.) + ''' + (if_, (then, (else_, stack))) = stack + return then if if_ else else_, stack @inscribe @SimpleFunctionWrapper def select(stack): - ''' - Use a Boolean value to select one of two items from a sequence. + ''' + Use a Boolean value to select one of two items from a sequence. + :: - [A B] False select - ------------------------ - A + [A B] False select + ------------------------ + A - [A B] True select - ----------------------- - B + [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 + 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 @inscribe @SimpleFunctionWrapper def max_(S): - '''Given a list find the maximum.''' - tos, stack = S - return max(iter_stack(tos)), stack + '''Given a list find the maximum.''' + tos, stack = S + return max(iter_stack(tos)), stack @inscribe @SimpleFunctionWrapper def min_(S): - '''Given a list find the minimum.''' - tos, stack = S - return min(iter_stack(tos)), stack + '''Given a list find the minimum.''' + tos, stack = S + return min(iter_stack(tos)), stack @inscribe @SimpleFunctionWrapper def sum_(S): - '''Given a quoted sequence of numbers return the sum. + ''' + Given a quoted sequence of numbers return the sum. + :: + + sum == 0 swap [+] step - sum == 0 swap [+] step - ''' - tos, stack = S - return sum(iter_stack(tos)), stack + ''' + tos, stack = S + return sum(iter_stack(tos)), stack @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. + ''' + 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. + :: - [1 2 3 1] 1 remove - ------------------------ - [2 3 1] + [1 2 3 1] 1 remove + ------------------------ + [2 3 1] - ''' - (tos, (second, stack)) = S - l = list(iter_stack(second)) - l.remove(tos) - return list_to_stack(l), stack + ''' + (tos, (second, stack)) = S + l = list(iter_stack(second)) + l.remove(tos) + return list_to_stack(l), stack @inscribe @SimpleFunctionWrapper def unique(S): - '''Given a list remove duplicate items.''' - tos, stack = S - I = list(iter_stack(tos)) - list_to_stack(sorted(set(I), key=I.index)) - return list_to_stack(sorted(set(I), key=I.index)), stack + '''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 @inscribe @SimpleFunctionWrapper def sort_(S): - '''Given a list return it sorted.''' - tos, stack = S - return list_to_stack(sorted(iter_stack(tos))), stack - - -@inscribe -@SimpleFunctionWrapper -def cons(S): - ''' - The cons operator expects a list on top of the stack and the potential - member below. The effect is to add the potential member into the - aggregate. - ''' - (tos, (second, stack)) = S - return (second, tos), stack - - -@inscribe -@SimpleFunctionWrapper -def uncons(S): - ''' - Inverse of cons, removes an item from the top of the list on the stack - and places it under the remaining list. - ''' - (tos, stack) = S - item, tos = tos - return tos, (item, stack) + '''Given a list return it sorted.''' + tos, stack = S + return list_to_stack(sorted(iter_stack(tos))), stack @inscribe @SimpleFunctionWrapper def clear(stack): - '''Clear everything from the stack. - - ... clear - --------------- - - ''' - return () - - -@inscribe -@SimpleFunctionWrapper -def dup(S): - '''Duplicate the top item on the stack.''' - (tos, stack) = S - return tos, (tos, stack) - - -@inscribe -@SimpleFunctionWrapper -def over(S): - ''' - Copy the second item down on the stack to the top of the stack. - - a b over - -------------- - a b a - - ''' - second = S[1][0] - return second, S - - -@inscribe -@SimpleFunctionWrapper -def tuck(S): - ''' - Copy the item at TOS under the second item of the stack. - - a b tuck - -------------- - b a b - - ''' - (tos, (second, stack)) = S - return tos, (second, (tos, stack)) - - -@inscribe -@SimpleFunctionWrapper -def swap(S): - '''Swap the top two items on stack.''' - (tos, (second, stack)) = S - return second, (tos, stack) - - -@inscribe -@SimpleFunctionWrapper -def swaack(stack): - '''swap stack''' - old_stack, stack = stack - return stack, old_stack - - -@inscribe -@SimpleFunctionWrapper -def stack_(stack): - ''' - The stack operator pushes onto the stack a list containing all the - elements of the stack. - ''' - return stack, stack - + '''Clear everything from the stack. + :: -@inscribe -@SimpleFunctionWrapper -def unstack(stack): - ''' - The unstack operator expects a list on top of the stack and makes that - the stack discarding the rest of the stack. - ''' - return stack[0] + clear == stack [pop stack] loop + ... clear + --------------- -@inscribe -@SimpleFunctionWrapper -def pop(stack): - '''Pop and discard the top item from the stack.''' - return stack[1] + ''' + return () @inscribe @SimpleFunctionWrapper -def popd(stack): - '''Pop and discard the second item from the stack.''' - (tos, (_, stack)) = stack - return tos, stack +def disenstacken(stack): + ''' + The disenstacken operator expects a list on top of the stack and makes that + the stack discarding the rest of the stack. + ''' + return stack[0] @inscribe @SimpleFunctionWrapper -def popdd(stack): - '''Pop and discard the third item from the stack.''' - (tos, (second, (_, stack))) = stack - return tos, (second, stack) - +def reverse(S): + ''' + Reverse the list on the top of the stack. + :: -@inscribe -@SimpleFunctionWrapper -def popop(stack): - '''Pop and discard the first and second items from the stack.''' - return stack[1][1] + reverse == [] swap shunt + ''' + (tos, stack) = S + res = () + for term in iter_stack(tos): + res = term, res + return res, stack @inscribe @SimpleFunctionWrapper -def dupd(S): - '''Duplicate the second item on the stack.''' - (tos, (second, stack)) = S - return tos, (second, (second, stack)) - +def concat_(S): + ''' + Concatinate the two lists on the top of the stack. + :: -@inscribe -@SimpleFunctionWrapper -def reverse(S): - '''Reverse the list on the top of the stack. + [a b c] [d e f] concat + ---------------------------- + [a b c d e f] - reverse == [] swap shunt - ''' - (tos, stack) = S - res = () - for term in iter_stack(tos): - res = term, res - return res, stack + ''' + (tos, (second, stack)) = S + return concat(second, tos), stack @inscribe @SimpleFunctionWrapper -def concat(S): - '''Concatinate the two lists on the top of the stack.''' - (tos, (second, stack)) = S - for term in reversed(list(iter_stack(second))): - tos = term, tos - return tos, stack +def shunt(stack): + ''' + Like concat but reverses the top list into the second. + :: + shunt == [swons] step == reverse swap concat -@inscribe -@SimpleFunctionWrapper -def shunt((tos, (second, stack))): - ''' - shunt == [swons] step + [a b c] [d e f] shunt + --------------------------- + [f e d a b c] - Like concat but reverses the top list into the second. - ''' - while tos: - term, tos = tos - second = term, second - return second, stack + ''' + (tos, (second, stack)) = stack + while tos: + term, tos = tos + second = term, second + return second, stack @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 + ''' + 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 @inscribe @SimpleFunctionWrapper def succ(S): - '''Increment TOS.''' - (tos, stack) = S - return tos + 1, stack + '''Increment TOS.''' + (tos, stack) = S + return tos + 1, stack @inscribe @SimpleFunctionWrapper def pred(S): - '''Decrement TOS.''' - (tos, stack) = S - return tos - 1, stack + '''Decrement TOS.''' + (tos, stack) = S + return tos - 1, stack @inscribe @SimpleFunctionWrapper def pm(stack): - ''' - Plus or minus + ''' + Plus or minus + :: - a b pm - ------------- - a+b a-b + a b pm + ------------- + a+b a-b - ''' - a, (b, stack) = stack - p, m, = b + a, b - a - return m, (p, stack) + ''' + a, (b, stack) = stack + p, m, = b + a, b - a + return m, (p, stack) def floor(n): - return int(math.floor(n)) + return int(math.floor(n)) floor.__doc__ = math.floor.__doc__ @@ -688,43 +582,27 @@ floor.__doc__ = math.floor.__doc__ @inscribe @SimpleFunctionWrapper def divmod_(S): - ''' - divmod(x, y) -> (quotient, remainder) + ''' + divmod(x, y) -> (quotient, remainder) - Return the tuple (x//y, x%y). Invariant: div*y + mod == x. - ''' - a, (b, stack) = S - d, m = divmod(a, b) - return d, (m, stack) + Return the tuple (x//y, x%y). Invariant: q * y + r == x. + ''' + a, (b, stack) = S + d, m = divmod(a, b) + return d, (m, stack) def sqrt(a): - ''' - Return the square root of the number a. - Negative numbers return complex roots. - ''' - try: - r = math.sqrt(a) - except ValueError: - assert a < 0, repr(a) - r = math.sqrt(-a) * 1j - return r - - -@inscribe -@SimpleFunctionWrapper -def rollup(S): - '''a b c -> b c a''' - (a, (b, (c, stack))) = S - return b, (c, (a, stack)) - - -@inscribe -@SimpleFunctionWrapper -def rolldown(S): - '''a b c -> c a b''' - (a, (b, (c, stack))) = S - return c, (a, (b, stack)) + ''' + 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): @@ -737,20 +615,20 @@ def rolldown(S): @inscribe @SimpleFunctionWrapper def id_(stack): - '''The identity function.''' - return stack + '''The identity function.''' + return stack @inscribe @SimpleFunctionWrapper def void(stack): - '''True if the form on TOS is void otherwise False.''' - form, stack = stack - return _void(form), 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)) + return any(not _void(i) for i in iter_stack(form)) @@ -762,42 +640,42 @@ def _void(form): @inscribe @FunctionWrapper def words(stack, expression, dictionary): - '''Print all the words in alphabetical order.''' - print(' '.join(sorted(dictionary))) - return stack, expression, dictionary + '''Print all the words in alphabetical order.''' + print(' '.join(sorted(dictionary))) + return stack, expression, dictionary @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 Joypy. If not see .') - return 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 .') + return stack, expression, dictionary @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 + '''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): @@ -822,11 +700,11 @@ def warranty(stack, expression, dictionary): @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(getdoc(word)) - return stack, 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 # @@ -839,174 +717,250 @@ def help_(S, expression, dictionary): # could change the word in the dictionary to use different semantics. S_choice = Symbol('choice') S_first = Symbol('first') -S_getitem = Symbol('getitem') S_genrec = Symbol('genrec') -S_loop = Symbol('loop') +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_times = Symbol('times') S_swaack = Symbol('swaack') -S_truthy = Symbol('truthy') +S_times = Symbol('times') @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. + ''' + The i combinator expects a quoted program on the stack and unpacks it + onto the pending expression for evaluation. + :: - [Q] i - ----------- - Q + [Q] i + ----------- + Q - ''' - quote, stack = stack - return stack, pushback(quote, expression), dictionary + ''' + try: + quote, stack = stack + except ValueError: + raise StackUnderflowError('Not enough values on stack.') + return stack, concat(quote, expression), dictionary @inscribe @FunctionWrapper def x(stack, expression, dictionary): - ''' - x == dup i + ''' + :: - ... [Q] x = ... [Q] dup i - ... [Q] x = ... [Q] [Q] i - ... [Q] x = ... [Q] Q + x == dup i - ''' - quote, _ = stack - return stack, pushback(quote, expression), dictionary + ... [Q] x = ... [Q] dup i + ... [Q] x = ... [Q] [Q] i + ... [Q] x = ... [Q] Q + + ''' + quote, _ = stack + return stack, concat(quote, expression), dictionary @inscribe @FunctionWrapper def b(stack, expression, dictionary): - ''' - b == [i] dip i + ''' + :: + + b == [i] dip i + + ... [P] [Q] b == ... [P] i [Q] i + ... [P] [Q] b == ... P Q + + ''' + q, (p, (stack)) = stack + return stack, concat(p, concat(q, expression)), dictionary + + +@inscribe +@FunctionWrapper +def ii(stack, expression, dictionary): + ''' + :: - ... [P] [Q] b == ... [P] i [Q] i - ... [P] [Q] b == ... P Q + ... a [Q] ii + ------------------ + ... Q a Q - ''' - q, (p, (stack)) = stack - return stack, pushback(p, pushback(q, expression)), dictionary + ''' + quote, (a, stack) = stack + expression = concat(quote, (a, concat(quote, expression))) + return stack, expression, dictionary @inscribe @FunctionWrapper def dupdip(stack, expression, dictionary): - ''' - [F] dupdip == dup [F] dip + ''' + :: + + [F] dupdip == dup [F] dip - ... a [F] dupdip - ... a dup [F] dip - ... a a [F] dip - ... a F a + ... a [F] dupdip + ... a dup [F] dip + ... a a [F] dip + ... a F a - ''' - F, stack = stack - a = stack[0] - return stack, pushback(F, (a, expression)), dictionary + ''' + F, stack = stack + a = stack[0] + return stack, concat(F, (a, expression)), dictionary @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. + ''' + 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 + ... [a b c] [Q] . infra + ----------------------------- + c b a . Q [...] swaack - ''' - (quote, (aggregate, stack)) = stack - return aggregate, pushback(quote, (stack, (S_swaack, expression))), dictionary + ''' + (quote, (aggregate, stack)) = stack + return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary @inscribe @FunctionWrapper def genrec(stack, expression, dictionary): - ''' - General Recursion Combinator. + ''' + General Recursion Combinator. + :: - [if] [then] [rec1] [rec2] genrec - --------------------------------------------------------------------- - [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte + [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." + 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. + 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': + For example, given a (general recursive) function 'F': + :: - F == [I] [T] [R1] [R2] genrec + F == [I] [T] [R1] [R2] genrec - If the [I] if-part fails you must derive R1 and R2 from: + If the [I] if-part fails you must derive R1 and R2 from: + :: - ... R1 [F] R2 + ... 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: + 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 + F == [I] [T] [R1] [R2] genrec + == [I] [T] [R1 [F] R2] ifte - (Primitive recursive functions are those where R2 == i. + Primitive recursive functions are those where R2 == i. + :: - P == [I] [T] [R] primrec - == [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_ = pushback(rec1, (F, rec2)) - return (else_, stack), (S_ifte, expression), dictionary + P == [I] [T] [R] tailrec + == [I] [T] [R [P] i] ifte + == [I] [T] [R P] ifte + + ''' + (rec2, (rec1, stack)) = stack + (then, (if_, _)) = stack + F = (if_, (then, (rec1, (rec2, (S_genrec, ()))))) + else_ = concat(rec1, (F, rec2)) + return (else_, stack), (S_ifte, expression), dictionary @inscribe @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 + ''' + Run the quoted program on TOS on the items in the list under it, push a + new list with the results in place of the program and original list. + ''' + # (quote, (aggregate, stack)) = S + # results = list_to_stack([ + # joy((term, stack), quote, dictionary)[0][0] + # for term in iter_stack(aggregate) + # ]) + # return (results, stack), expression, dictionary + (quote, (aggregate, stack)) = S + if not aggregate: + return (aggregate, stack), expression, dictionary + batch = () + for term in iter_stack(aggregate): + s = term, stack + batch = (s, (quote, (S_infra, (S_first, batch)))) + stack = (batch, ((), stack)) + return stack, (S_infra, expression), dictionary + + +@inscribe +@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): @@ -1026,224 +980,280 @@ def map_(S, expression, dictionary): @inscribe @FunctionWrapper def branch(stack, expression, dictionary): - ''' - Use a Boolean value to select one of two quoted programs to run. + ''' + Use a Boolean value to select one of two quoted programs to run. + + :: - branch == roll< choice i + branch == roll< choice i + :: - False [F] [T] branch - -------------------------- - F + False [F] [T] branch + -------------------------- + F - True [F] [T] branch - ------------------------- - T + True [F] [T] branch + ------------------------- + T - ''' - (then, (else_, (flag, stack))) = stack - return stack, pushback(then if flag else else_, expression), dictionary + ''' + (then, (else_, (flag, stack))) = stack + return stack, concat(then if flag else else_, expression), dictionary + + +##@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 @inscribe @FunctionWrapper -def ifte(stack, expression, dictionary): - ''' - If-Then-Else Combinator - - ... [if] [then] [else] ifte - --------------------------------------------------- - ... [[else] [then]] [...] [if] infra select i - +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 - ... [if] [then] [else] ifte - ------------------------------------------------------- - ... [else] [then] [...] [if] infra first choice i + ''' + 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 - 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 +def _cond(conditions, expression): + (clause, rest) = conditions + if not rest: # clause is [D] + return clause + P, T = clause + return (P, (T, (_cond(rest, ()), (S_ifte, expression)))) @inscribe @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. + ''' + 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 + ... x [Q] dip + ------------------- + ... Q x - ''' - (quote, (x, stack)) = stack - expression = (x, expression) - return stack, pushback(quote, expression), dictionary + ''' + try: + (quote, (x, stack)) = stack + except ValueError: + raise StackUnderflowError('Not enough values on stack.') + expression = (x, expression) + return stack, concat(quote, expression), dictionary @inscribe @FunctionWrapper def dipd(S, expression, dictionary): - ''' - Like dip but expects two items. + ''' + Like dip but expects two items. + :: - ... y x [Q] dip - --------------------- - ... Q y x + ... y x [Q] dip + --------------------- + ... Q y x - ''' - (quote, (x, (y, stack))) = S - expression = (y, (x, expression)) - return stack, pushback(quote, expression), dictionary + ''' + (quote, (x, (y, stack))) = S + expression = (y, (x, expression)) + return stack, concat(quote, expression), dictionary @inscribe @FunctionWrapper def dipdd(S, expression, dictionary): - ''' - Like dip but expects three items. + ''' + Like dip but expects three items. + :: - ... z y x [Q] dip - ----------------------- - ... Q z y x + ... z y x [Q] dip + ----------------------- + ... Q z y x - ''' - (quote, (x, (y, (z, stack)))) = S - expression = (z, (y, (x, expression))) - return stack, pushback(quote, expression), dictionary + ''' + (quote, (x, (y, (z, stack)))) = S + expression = (z, (y, (x, expression))) + return stack, concat(quote, expression), dictionary @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. + ''' + 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 - ... 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 + ''' + (quote, (x, stack)) = S + stack = (quote, ((x, stack), stack)) + expression = (S_infra, (S_first, expression)) + return stack, expression, dictionary @inscribe @FunctionWrapper def app2(S, expression, dictionary): - '''Like app1 with two items. + '''Like app1 with two items. + :: - ... y x [Q] . app2 - ----------------------------------- - ... [y ...] [Q] . infra first - [x ...] [Q] infra first + ... 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 + ''' + (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 @inscribe @FunctionWrapper def app3(S, expression, dictionary): - '''Like app1 with three items. + '''Like app1 with three items. + :: - ... z y x [Q] . app3 - ----------------------------------- - ... [z ...] [Q] . infra first - [y ...] [Q] infra first - [x ...] [Q] infra first + ... 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 + ''' + (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 @inscribe @FunctionWrapper def step(S, expression, dictionary): - ''' - Run a quoted program on each item in a sequence. + ''' + Run a quoted program on each item in a sequence. + :: - ... [] [Q] . step - ----------------------- - ... . + ... [] [Q] . step + ----------------------- + ... . - ... [a] [Q] . step - ------------------------ - ... a . Q + ... [a] [Q] . step + ------------------------ + ... a . Q - ... [a b c] [Q] . step - ---------------------------------------- - ... a . Q [b c] [Q] step + ... [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: + 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 - head, tail = aggregate - stack = quote, (head, stack) - if tail: - expression = tail, (quote, (S_step, expression)) - expression = S_i, expression - return stack, expression, dictionary @inscribe @FunctionWrapper def times(stack, expression, dictionary): - ''' - times == [-- dip] cons [swap] infra [0 >] swap while pop + ''' + times == [-- dip] cons [swap] infra [0 >] swap while pop + :: - ... n [Q] . times - --------------------- w/ n <= 0 - ... . + ... n [Q] . times + --------------------- w/ n <= 0 + ... . - ... 1 [Q] . times - --------------------------------- - ... . Q + ... 1 [Q] . times + ----------------------- + ... . Q - ... n [Q] . times - --------------------------------- w/ n > 1 - ... . Q (n - 1) [Q] times + ... 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: + ''' + # 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 - n -= 1 - if n: - expression = n, (quote, (S_times, expression)) - expression = pushback(quote, expression) - return stack, expression, dictionary # The current definition above works like this: @@ -1252,7 +1262,7 @@ def times(stack, expression, dictionary): # -------------------------------------- # [P] nullary [Q [P] nullary] loop -# while == [pop i not] [popop] [dudipd] primrec +# while == [pop i not] [popop] [dudipd] tailrec #def while_(S, expression, dictionary): # '''[if] [body] while''' @@ -1265,95 +1275,105 @@ def times(stack, expression, dictionary): @inscribe @FunctionWrapper def loop(stack, expression, dictionary): - ''' - Basic loop combinator. + ''' + Basic loop combinator. + :: + + ... True [Q] loop + ----------------------- + ... Q [Q] loop + + ... False [Q] loop + ------------------------ + ... - ... True [Q] loop - ----------------------- - ... Q [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 - ... False [Q] loop - ------------------------ - ... - ''' - quote, (flag, stack) = stack - if flag: - expression = pushback(quote, (quote, (S_loop, expression))) - return stack, expression, dictionary +@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 -#def nullary(S, expression, dictionary): -# ''' -# Run the program on TOS and return its first result without consuming -# any of the stack (except the program on TOS.) -# ''' -# (quote, stack) = S -# result = joy(stack, quote, dictionary) -# return (result[0][0], stack), expression, dictionary -# -# -#def unary(S, expression, dictionary): -# (quote, stack) = S -# _, return_stack = stack -# result = joy(stack, quote, dictionary)[0] -# return (result[0], return_stack), expression, dictionary -# -# -#def binary(S, expression, dictionary): -# (quote, stack) = S -# _, (_, return_stack) = stack -# result = joy(stack, quote, dictionary)[0] -# return (result[0], return_stack), expression, dictionary -# -# -#def ternary(S, expression, dictionary): -# (quote, stack) = S -# _, (_, (_, return_stack)) = stack -# result = joy(stack, quote, dictionary)[0] -# return (result[0], return_stack), expression, dictionary + 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(binary), # FunctionWrapper(cleave), -# FunctionWrapper(nullary), -# FunctionWrapper(ternary), -# FunctionWrapper(unary), # FunctionWrapper(while_), for F in ( - BinaryBuiltinWrapper(operator.add), - BinaryBuiltinWrapper(operator.and_), - BinaryBuiltinWrapper(operator.div), - BinaryBuiltinWrapper(operator.eq), - BinaryBuiltinWrapper(operator.floordiv), - BinaryBuiltinWrapper(operator.ge), - BinaryBuiltinWrapper(operator.gt), - BinaryBuiltinWrapper(operator.le), - BinaryBuiltinWrapper(operator.lshift), - BinaryBuiltinWrapper(operator.lt), - BinaryBuiltinWrapper(operator.mod), - BinaryBuiltinWrapper(operator.mul), - BinaryBuiltinWrapper(operator.ne), - BinaryBuiltinWrapper(operator.or_), - BinaryBuiltinWrapper(operator.pow), - BinaryBuiltinWrapper(operator.rshift), - BinaryBuiltinWrapper(operator.sub), - BinaryBuiltinWrapper(operator.truediv), - BinaryBuiltinWrapper(operator.xor), - - UnaryBuiltinWrapper(abs), - UnaryBuiltinWrapper(bool), - UnaryBuiltinWrapper(floor), - UnaryBuiltinWrapper(operator.neg), - UnaryBuiltinWrapper(operator.not_), - UnaryBuiltinWrapper(sqrt), - ): - inscribe(F) - - -add_aliases(_dictionary) - - -DefinitionWrapper.add_definitions(definitions, _dictionary) + + #divmod_ = pm = __(n2, n1), __(n4, n3) + + BinaryBuiltinWrapper(operator.eq), + BinaryBuiltinWrapper(operator.ge), + BinaryBuiltinWrapper(operator.gt), + BinaryBuiltinWrapper(operator.le), + BinaryBuiltinWrapper(operator.lt), + BinaryBuiltinWrapper(operator.ne), + + BinaryBuiltinWrapper(operator.xor), + BinaryBuiltinWrapper(operator.lshift), + BinaryBuiltinWrapper(operator.rshift), + + BinaryBuiltinWrapper(operator.and_), + BinaryBuiltinWrapper(operator.or_), + + BinaryBuiltinWrapper(operator.add), + BinaryBuiltinWrapper(operator.floordiv), + BinaryBuiltinWrapper(operator.mod), + BinaryBuiltinWrapper(operator.mul), + BinaryBuiltinWrapper(operator.pow), + BinaryBuiltinWrapper(operator.sub), +## BinaryBuiltinWrapper(operator.truediv), + + UnaryBuiltinWrapper(bool), + UnaryBuiltinWrapper(operator.not_), + + UnaryBuiltinWrapper(abs), + UnaryBuiltinWrapper(operator.neg), + UnaryBuiltinWrapper(sqrt), + + UnaryBuiltinWrapper(floor), + UnaryBuiltinWrapper(round), + ): + inscribe(F) +del F # Otherwise Sphinx autodoc will pick it up. + + +for name, primitive in getmembers(genlib, isfunction): + inscribe(SimpleFunctionWrapper(primitive)) + + +add_aliases(_dictionary, ALIASES)