X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=joy%2Flibrary.py;h=800ff3b76fca339294dcdcdd2bc7a1ff3b616c29;hb=fcd4c613e4a049a21fe0ed5bed42770390bc1b80;hp=2e57aad0d605d91ab20520cd479bbad9f2bd9f4e;hpb=0588496ca52d48038558dac610c67494ffb94102;p=joypy%2FThun.git
diff --git a/joy/library.py b/joy/library.py
index 2e57aad..800ff3b 100644
--- a/joy/library.py
+++ b/joy/library.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright © 2014, 2015, 2017, 2018 Simon Forman
+# Copyright © 2014-2020 Simon Forman
#
# This file is part of Thun
#
@@ -23,374 +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 logging import getLogger
-
-_log = getLogger(__name__)
-_log.info('Loading library.')
-
-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
-from inspect import getmembers, isfunction
import operator, math
+from . import __name__ as _joy_package_name
from .parser import text_to_expression, Symbol
-from .utils.stack import expression_to_string, list_to_stack, iter_stack, pick, concat
-from .utils.brutal_hackery import rename_code_object
-
from .utils import generated_library as genlib
-from .utils.types import (
- compose,
- ef,
- stack_effect,
- AnyJoyType,
- AnyStarJoyType,
- BooleanJoyType,
- NumberJoyType,
- NumberStarJoyType,
- StackJoyType,
- StackStarJoyType,
- FloatJoyType,
- IntJoyType,
- SymbolJoyType,
- CombinatorJoyType,
- TextJoyType,
- _functions,
- FUNCTIONS,
- infer,
- infer_expression,
- JoyTypeError,
- combinator_effect,
- poly_combinator_effect,
- doc_from_stack_effect,
- )
-
-
-_SYM_NUMS = count().next
-_COMB_NUMS = count().next
-
-
-_R = range(10)
-A = a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = map(AnyJoyType, _R)
-B = b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = map(BooleanJoyType, _R)
-N = n0, n1, n2, n3, n4, n5, n6, n7, n8, n9 = map(NumberJoyType, _R)
-S = s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 = map(StackJoyType, _R)
-F = f0, f1, f2, f3, f4, f5, f6, f7, f8, f9 = map(FloatJoyType, _R)
-I = i0, i1, i2, i3, i4, i5, i6, i7, i8, i9 = map(IntJoyType, _R)
-T = t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 = map(TextJoyType, _R)
-
-
-_R = range(1, 11)
-As = map(AnyStarJoyType, _R)
-Ns = map(NumberStarJoyType, _R)
-Ss = map(StackStarJoyType, _R)
-
-
-sec0 = stack_effect(t1)()
-sec1 = stack_effect(s0, i1)(s1)
-sec2 = stack_effect(s0, i1)(a1)
-sec_binary_cmp = stack_effect(n1, n2)(b1)
-sec_binary_ints = stack_effect(i1, i2)(i3)
-sec_binary_logic = stack_effect(b1, b2)(b3)
-sec_binary_math = stack_effect(n1, n2)(n3)
-sec_unary_logic = stack_effect(a1)(b1)
-sec_unary_math = stack_effect(n1)(n2)
-sec_Ns_math = stack_effect((Ns[1], s1),)(n0)
+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', ['*']),
- ('floordiv', ['/floor', '//']),
- ('floor', ['round']),
- ('truediv', ['/']),
- ('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'â¢']),
- )
+ ('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 yin_functions():
- '''
- Return a dict of named stack effects.
-
- "Yin" functions are those that only rearrange items in stacks and
- can be defined completely by their stack effects. This means they
- can be auto-compiled.
- '''
- # pylint: disable=unused-variable
- cons = ef(a1, s0)((a1, s0))
- ccons = compose(cons, cons)
- dup = ef(a1)(a1, a1)
- dupd = ef(a2, a1)(a2, a2, a1)
- dupdd = ef(a3, a2, a1)(a3, a3, a2, a1)
- first = ef((a1, s1),)(a1,)
- over = ef(a2, a1)(a2, a1, a2)
- pop = ef(a1)()
- popd = ef(a2, a1,)(a1)
- popdd = ef(a3, a2, a1,)(a2, a1,)
- popop = ef(a2, a1,)()
- popopd = ef(a3, a2, a1,)(a1)
- popopdd = ef(a4, a3, a2, a1,)(a2, a1)
- rest = ef((a1, s0),)(s0,)
- rolldown = ef(a1, a2, a3)(a2, a3, a1)
- rollup = ef(a1, a2, a3)(a3, a1, a2)
- rrest = compose(rest, rest)
- second = compose(rest, first)
- stack = s0, (s0, s0)
- swaack = (s1, s0), (s0, s1)
- swap = ef(a1, a2)(a2, a1)
- swons = compose(swap, cons)
- third = compose(rest, second)
- tuck = ef(a2, a1)(a1, a2, a1)
- uncons = ef((a1, s0),)(a1, s0)
- unswons = compose(uncons, swap)
- stuncons = compose(stack, uncons)
- stununcons = compose(stack, uncons, uncons)
- unit = ef(a1)((a1, ()))
-
- first_two = compose(uncons, uncons, pop)
- fourth = compose(rest, third)
-
- _Tree_add_Ee = compose(pop, swap, rolldown, rrest, ccons)
- _Tree_get_E = compose(popop, second)
- _Tree_delete_clear_stuff = compose(rollup, popop, rest)
- _Tree_delete_R0 = compose(over, first, swap, dup)
-
- return locals()
-
-
-definitions = ('''\
-ii == [dip] dupdip i
-of == swap at
-product == 1 swap [*] step
-flatten == [] swap [concat] step
-quoted == [unit] dip
-unquoted == [i] dip
-enstacken == stack [clear] dip
-? == dup truthy
-disenstacken == ? [uncons ?] loop pop
-dinfrirst == dip infra first
-nullary == [stack] dinfrirst
-unary == nullary popd
-binary == nullary [popop] dip
-ternary == unary [popop] dip
-pam == [i] map
-run == [] swap infra
-sqr == dup mul
-size == 0 swap [pop ++] step
-fork == [i] app2
-cleave == fork [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
-dupdipd == dup dipd
-primrec == [i] genrec
-step_zero == 0 roll> step
-codireco == cons dip rest cons
-make_generator == [codireco] ccons
-ifte == [nullary not] dipd branch
-'''
-#
-#
-# ifte == [nullary] dipd swap branch
-# genrec == [[genrec] cons cons cons cons] nullary swons concat ifte
-
-# Another definition for while. FWIW
-# while == over [[i] dip nullary] ccons [nullary] dip loop
-
-##ccons == cons cons
-##unit == [] cons
-##second == rest first
-##third == rest rest first
-##swons == swap cons
-##swoncat == swap concat
-
-##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
-)
+ '''
+ 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
- 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 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):
- '''
- Scan multi-line string defs for definitions and add them to the
- dictionary.
- '''
- for definition in _text_to_defs(defs):
- class_.add_def(definition, dictionary)
- @classmethod
- def add_def(class_, definition, dictionary, fail_fails=False):
+class Def(object):
'''
- Add the definition to the dictionary.
+ Definitions created by inscribe.
'''
- F = class_.parse_definition(definition)
- _log.info('Adding definition %s := %s', F.name, expression_to_string(F.body))
- dictionary[F.name] = F
- @classmethod
- def load_definitions(class_, filename, dictionary):
- with open(filename) as f:
- lines = [line for line in f if '==' in line]
- for line in lines:
- class_.add_def(line, dictionary)
-
-
-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)
#
@@ -399,363 +212,369 @@ def _text_to_defs(text):
@inscribe
-@sec0
@FunctionWrapper
def inscribe_(stack, expression, dictionary):
- '''
- Create a new Joy function definition in the Joy dictionary. A
- definition is given as a string with a name followed by a double
- equal sign then one or more Joy functions, the body. for example:
+ '''
+ 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
+ [sqr dup mul] inscribe
- If you want the definition to persist over restarts, enter it into
- the definitions.txt resource.
- '''
- definition, stack = stack
- DefinitionWrapper.add_def(definition, dictionary, fail_fails=True)
- return stack, expression, dictionary
+ '''
+ (name, body), stack = stack
+ inscribe(Def(name, body), dictionary)
+ return stack, expression, dictionary
@inscribe
@SimpleFunctionWrapper
def parse(stack):
- '''Parse the string on the stack to a Joy expression.'''
- text, stack = stack
- expression = text_to_expression(text)
- return expression, 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 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
-@sec2
@SimpleFunctionWrapper
def getitem(stack):
- '''
- ::
+ '''
+ ::
- getitem == drop first
+ 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.
- ::
+ 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
+ [a b c d] 0 getitem
+ -------------------------
+ a
- '''
- n, (Q, stack) = stack
- return pick(Q, n), stack
+ '''
+ n, (Q, stack) = stack
+ return pick(Q, n), stack
@inscribe
-@sec1
@SimpleFunctionWrapper
def drop(stack):
- '''
- ::
+ '''
+ ::
- drop == [rest] times
+ drop == [rest] times
- Expects an integer and a quote on the stack and returns the quote with
- n items removed off the top.
- ::
+ 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]
+ [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
+ '''
+ n, (Q, stack) = stack
+ while n > 0:
+ try:
+ _, Q = Q
+ except ValueError:
+ raise IndexError
+ n -= 1
+ return Q, stack
@inscribe
-@sec1
@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
-@sec_Ns_math
@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
-@sec_Ns_math
@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
-@sec_Ns_math
@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))
- 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
+ '''Given a list return it sorted.'''
+ tos, stack = S
+ return list_to_stack(sorted(iter_stack(tos))), stack
-_functions['clear'] = s0, s1
@inscribe
@SimpleFunctionWrapper
def clear(stack):
- '''Clear everything from the stack.
- ::
+ '''Clear everything from the stack.
+ ::
- clear == stack [pop stack] loop
+ clear == stack [pop stack] loop
- ... clear
- ---------------
+ ... clear
+ ---------------
- '''
- return ()
+ '''
+ return ()
@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]
+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 reverse(S):
- '''Reverse the list on the top of the stack.
- ::
+ '''
+ 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
+ reverse == [] swap shunt
+ '''
+ (tos, stack) = S
+ res = ()
+ for term in iter_stack(tos):
+ res = term, res
+ return res, stack
@inscribe
-@combinator_effect(_COMB_NUMS(), s7, s6)
@SimpleFunctionWrapper
def concat_(S):
- '''Concatinate the two lists on the top of the stack.
- ::
+ '''
+ Concatinate the two lists on the top of the stack.
+ ::
- [a b c] [d e f] concat
- ----------------------------
- [a b c d e f]
+ [a b c] [d e f] concat
+ ----------------------------
+ [a b c d e f]
-'''
- (tos, (second, stack)) = S
- return concat(second, tos), stack
+ '''
+ (tos, (second, stack)) = S
+ return concat(second, tos), stack
@inscribe
@SimpleFunctionWrapper
def shunt(stack):
- '''Like concat but reverses the top list into the second.
- ::
+ '''
+ Like concat but reverses the top list into the second.
+ ::
- shunt == [swons] step == reverse swap concat
+ shunt == [swons] step == reverse swap concat
- [a b c] [d e f] shunt
- ---------------------------
- [f e d a b c]
+ [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
+ '''
+ (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
-@sec_unary_math
@SimpleFunctionWrapper
def succ(S):
- '''Increment TOS.'''
- (tos, stack) = S
- return tos + 1, stack
+ '''Increment TOS.'''
+ (tos, stack) = S
+ return tos + 1, stack
@inscribe
-@sec_unary_math
@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__
@@ -763,27 +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
+ '''
+ 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):
@@ -796,20 +615,20 @@ def sqrt(a):
@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))
@@ -821,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 Thun. 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):
@@ -881,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
#
@@ -898,194 +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_times = Symbol('times')
@inscribe
-@combinator_effect(_COMB_NUMS(), s1)
@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, concat(quote, expression), dictionary
+ '''
+ try:
+ quote, stack = stack
+ except ValueError:
+ raise StackUnderflowError('Not enough values on stack.')
+ return stack, concat(quote, expression), dictionary
@inscribe
-@combinator_effect(_COMB_NUMS(), s1)
@FunctionWrapper
def x(stack, expression, dictionary):
- '''
- ::
+ '''
+ ::
- x == dup i
+ x == dup i
- ... [Q] x = ... [Q] dup i
- ... [Q] x = ... [Q] [Q] i
- ... [Q] x = ... [Q] Q
+ ... [Q] x = ... [Q] dup i
+ ... [Q] x = ... [Q] [Q] i
+ ... [Q] x = ... [Q] Q
- '''
- quote, _ = stack
- return stack, concat(quote, expression), dictionary
+ '''
+ quote, _ = stack
+ return stack, concat(quote, expression), dictionary
@inscribe
-@combinator_effect(_COMB_NUMS(), s7, s6)
@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
+ ... [P] [Q] b == ... [P] i [Q] i
+ ... [P] [Q] b == ... P Q
- '''
- q, (p, (stack)) = stack
- return stack, concat(p, concat(q, expression)), dictionary
+ '''
+ q, (p, (stack)) = stack
+ return stack, concat(p, concat(q, expression)), dictionary
+
+
+@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
@inscribe
-@combinator_effect(_COMB_NUMS(), a1, s1)
@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, concat(F, (a, expression)), dictionary
+ '''
+ F, stack = stack
+ a = stack[0]
+ return stack, concat(F, (a, expression)), dictionary
@inscribe
-@combinator_effect(_COMB_NUMS(), s7, s6)
@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.
- ::
+ '''
+ 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, concat(quote, (stack, (S_swaack, expression))), dictionary
+ '''
+ (quote, (aggregate, stack)) = stack
+ return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary
@inscribe
-#@combinator_effect(_COMB_NUMS(), s7, s6, s5, s4)
@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
+ 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
+ '''
+ (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
-@combinator_effect(_COMB_NUMS(), s7, s6)
@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):
@@ -1102,45 +977,29 @@ def map_(S, expression, dictionary):
# return (q, (p, stack)), expression, dictionary
-def branch_true(stack, expression, dictionary):
- # pylint: disable=unused-variable
- (then, (else_, (flag, stack))) = stack
- return stack, concat(then, expression), dictionary
-
-
-def branch_false(stack, expression, dictionary):
- # pylint: disable=unused-variable
- (then, (else_, (flag, stack))) = stack
- return stack, concat(else_, expression), dictionary
-
-
@inscribe
-@poly_combinator_effect(_COMB_NUMS(), [branch_true, branch_false], b1, s7, s6)
@FunctionWrapper
def branch(stack, expression, dictionary):
- '''
- Use a Boolean value to select one of two quoted programs to run.
-
- ::
-
- branch == roll< choice i
+ '''
+ Use a Boolean value to select one of two quoted programs to run.
- ::
+ ::
- False [F] [T] branch
- --------------------------
- F
+ branch == roll< choice i
- True [F] [T] branch
- -------------------------
- T
+ ::
- '''
- (then, (else_, (flag, stack))) = stack
- return stack, concat(then if flag else else_, expression), dictionary
+ False [F] [T] branch
+ --------------------------
+ F
+ True [F] [T] branch
+ -------------------------
+ T
-#FUNCTIONS['branch'] = CombinatorJoyType('branch', [branch_true, branch_false], 100)
+ '''
+ (then, (else_, (flag, stack))) = stack
+ return stack, concat(then if flag else else_, expression), dictionary
##@inscribe
@@ -1174,231 +1033,227 @@ def branch(stack, expression, dictionary):
@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
+ '''
+ 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))))
+ (clause, rest) = conditions
+ if not rest: # clause is [D]
+ return clause
+ P, T = clause
+ return (P, (T, (_cond(rest, ()), (S_ifte, expression))))
@inscribe
-@combinator_effect(_COMB_NUMS(), a1, s1)
@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, concat(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
-@combinator_effect(_COMB_NUMS(), a2, a1, s1)
@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, concat(quote, expression), dictionary
+ '''
+ (quote, (x, (y, stack))) = S
+ expression = (y, (x, expression))
+ return stack, concat(quote, expression), dictionary
@inscribe
-@combinator_effect(_COMB_NUMS(), a3, a2, a1, s1)
@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, concat(quote, expression), dictionary
+ '''
+ (quote, (x, (y, (z, stack)))) = S
+ expression = (z, (y, (x, expression)))
+ return stack, concat(quote, expression), dictionary
@inscribe
-@combinator_effect(_COMB_NUMS(), a1, s1)
@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
+ '''
+ 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
@inscribe
-@combinator_effect(_COMB_NUMS(), a2, a1, s1)
@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
-@combinator_effect(_COMB_NUMS(), a3, a2, a1, s1)
@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
-@combinator_effect(_COMB_NUMS(), s7, s6)
@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 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
-@combinator_effect(_COMB_NUMS(), i1, s6)
@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
- ---------------------------------
+ ... 1 [Q] . times
+ -----------------------
... . Q
- ... n [Q] . times
- --------------------------------- w/ n > 1
+ ... 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 = concat(quote, expression)
- return stack, expression, dictionary
# The current definition above works like this:
@@ -1407,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'''
@@ -1417,66 +1272,60 @@ def times(stack, expression, dictionary):
# return stack, expression, dictionary
-def loop_true(stack, expression, dictionary):
- quote, (flag, stack) = stack # pylint: disable=unused-variable
- return stack, concat(quote, (S_pop, expression)), dictionary
-
-def loop_two_true(stack, expression, dictionary):
- quote, (flag, stack) = stack # pylint: disable=unused-variable
- return stack, concat(quote, (S_pop, concat(quote, (S_pop, expression)))), dictionary
-
-def loop_false(stack, expression, dictionary):
- quote, (flag, stack) = stack # pylint: disable=unused-variable
- return stack, expression, dictionary
-
-
@inscribe
-@poly_combinator_effect(_COMB_NUMS(), [loop_two_true, loop_true, loop_false], b1, s6)
@FunctionWrapper
def loop(stack, expression, dictionary):
- '''
- Basic loop combinator.
- ::
+ '''
+ Basic loop combinator.
+ ::
- ... True [Q] loop
- -----------------------
- ... Q [Q] loop
+ ... True [Q] loop
+ -----------------------
+ ... Q [Q] loop
- ... False [Q] loop
- ------------------------
+ ... False [Q] loop
+ ------------------------
...
- '''
- quote, (flag, stack) = stack
- if flag:
- expression = concat(quote, (quote, (S_loop, expression)))
- return stack, expression, dictionary
+ '''
+ 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
@inscribe
-@combinator_effect(_COMB_NUMS(), a1, a2, s6, s7, s8)
@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:
- ::
+ '''
+ 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
+ G
a b [G] [E] [L] cmp
------------------------- a = b
- E
+ 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
+ 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),
@@ -1485,137 +1334,46 @@ def cmp_(stack, expression, dictionary):
for F in (
- #divmod_ = pm = __(n2, n1), __(n4, n3)
-
- sec_binary_cmp(BinaryBuiltinWrapper(operator.eq)),
- sec_binary_cmp(BinaryBuiltinWrapper(operator.ge)),
- sec_binary_cmp(BinaryBuiltinWrapper(operator.gt)),
- sec_binary_cmp(BinaryBuiltinWrapper(operator.le)),
- sec_binary_cmp(BinaryBuiltinWrapper(operator.lt)),
- sec_binary_cmp(BinaryBuiltinWrapper(operator.ne)),
-
- sec_binary_ints(BinaryBuiltinWrapper(operator.xor)),
- sec_binary_ints(BinaryBuiltinWrapper(operator.lshift)),
- sec_binary_ints(BinaryBuiltinWrapper(operator.rshift)),
-
- sec_binary_logic(BinaryBuiltinWrapper(operator.and_)),
- sec_binary_logic(BinaryBuiltinWrapper(operator.or_)),
-
- sec_binary_math(BinaryBuiltinWrapper(operator.add)),
- sec_binary_math(BinaryBuiltinWrapper(operator.floordiv)),
- sec_binary_math(BinaryBuiltinWrapper(operator.mod)),
- sec_binary_math(BinaryBuiltinWrapper(operator.mul)),
- sec_binary_math(BinaryBuiltinWrapper(operator.pow)),
- sec_binary_math(BinaryBuiltinWrapper(operator.sub)),
- sec_binary_math(BinaryBuiltinWrapper(operator.truediv)),
-
- sec_unary_logic(UnaryBuiltinWrapper(bool)),
- sec_unary_logic(UnaryBuiltinWrapper(operator.not_)),
-
- sec_unary_math(UnaryBuiltinWrapper(abs)),
- sec_unary_math(UnaryBuiltinWrapper(operator.neg)),
- sec_unary_math(UnaryBuiltinWrapper(sqrt)),
-
- stack_effect(n1)(i1)(UnaryBuiltinWrapper(floor)),
- ):
- inscribe(F)
+ #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.
-YIN_STACK_EFFECTS = yin_functions()
-add_aliases(YIN_STACK_EFFECTS, ALIASES)
-
-# Load the auto-generated primitives into the dictionary.
-_functions.update(YIN_STACK_EFFECTS)
-# exec '''
-
-# eh = compose(dup, bool)
-# sqr = compose(dup, mul)
-# of = compose(swap, at)
-
-# ''' in dict(compose=compose), _functions
-for name in sorted(_functions):
- sec = _functions[name]
- F = FUNCTIONS[name] = SymbolJoyType(name, [sec], _SYM_NUMS())
- if name in YIN_STACK_EFFECTS:
- _log.info('Setting stack effect for Yin function %s := %s', F.name, doc_from_stack_effect(*sec))
-
for name, primitive in getmembers(genlib, isfunction):
- inscribe(SimpleFunctionWrapper(primitive))
+ inscribe(SimpleFunctionWrapper(primitive))
add_aliases(_dictionary, ALIASES)
-add_aliases(_functions, ALIASES)
-add_aliases(FUNCTIONS, ALIASES)
-
-
-DefinitionWrapper.add_definitions(definitions, _dictionary)
-
-
-EXPECTATIONS = dict(
- ifte=(s7, (s6, (s5, s4))),
- nullary=(s7, s6),
- run=(s7, s6),
-)
-EXPECTATIONS['while'] = (s7, (s6, s5))
-
-
-for name in '''
- dinfrirst
- nullary
- ifte
- run
- dupdipd codireco
- while
- '''.split():
- C = _dictionary[name]
- expect = EXPECTATIONS.get(name)
- if expect:
- sec = doc_from_stack_effect(expect)
- _log.info('Setting stack EXPECT for combinator %s := %s', C.name, sec)
- else:
- _log.info('combinator %s', C.name)
- FUNCTIONS[name] = CombinatorJoyType(name, [C], _COMB_NUMS(), expect)
-
-
-for name in ('''
- of quoted enstacken ?
- unary binary ternary
- sqr unquoted
- '''.split()):
- of_ = _dictionary[name]
- secs = infer_expression(of_.body)
- assert len(secs) == 1, repr(secs)
- _log.info(
- 'Setting stack effect for definition %s := %s',
- name,
- doc_from_stack_effect(*secs[0]),
- )
- FUNCTIONS[name] = SymbolJoyType(name, infer_expression(of_.body), _SYM_NUMS())
-
-
-#sec_Ns_math(_dictionary['product'])
-
-## product == 1 swap [*] step
-## flatten == [] swap [concat] step
-## disenstacken == ? [uncons ?] loop pop
-## pam == [i] map
-## size == 0 swap [pop ++] step
-## fork == [i] app2
-## cleave == fork [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
-## dupdipd == dup dipd
-## primrec == [i] genrec
-## step_zero == 0 roll> step
-## codireco == cons dip rest cons
-## make_generator == [codireco] ccons
-## ifte == [nullary not] dipd branch