OSDN Git Service

Switch the names of unstack and disenstacken.
[joypy/Thun.git] / joy / library.py
1 # -*- coding: utf-8 -*-
2 #
3 #    Copyright © 2014, 2015, 2017, 2018 Simon Forman
4 #
5 #    This file is part of Thun
6 #
7 #    Thun is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU General Public License as published by
9 #    the Free Software Foundation, either version 3 of the License, or
10 #    (at your option) any later version.
11 #
12 #    Thun is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU General Public License for more details.
16 #
17 #    You should have received a copy of the GNU General Public License
18 #    along with Thun.  If not see <http://www.gnu.org/licenses/>. 
19 #
20 '''
21 This module contains the Joy function infrastructure and a library of
22 functions.  Its main export is a Python function initialize() that
23 returns a dictionary of Joy functions suitable for use with the joy()
24 function.
25 '''
26 from __future__ import print_function
27 from builtins import map, object, range, zip
28 from logging import getLogger
29
30 _log = getLogger(__name__)
31 _log.info('Loading library.')
32
33 from inspect import getdoc
34 from functools import wraps
35 from itertools import count
36 from inspect import getmembers, isfunction
37 import operator, math
38
39 from .parser import text_to_expression, Symbol
40 from .utils.stack import expression_to_string, list_to_stack, iter_stack, pick, concat
41 import sys
42 if sys.version_info.major < 3:
43         from .utils.brutal_hackery import rename_code_object
44 else:
45         rename_code_object = lambda _: lambda f: f
46
47 from .utils import generated_library as genlib
48 from .utils.types import (
49         compose,
50         ef,
51         stack_effect,
52         AnyJoyType,
53         AnyStarJoyType,
54         BooleanJoyType,
55         NumberJoyType,
56         NumberStarJoyType,
57         StackJoyType,
58         StackStarJoyType,
59         FloatJoyType,
60         IntJoyType,
61         SymbolJoyType,
62         CombinatorJoyType,
63         TextJoyType,
64         _functions,
65         FUNCTIONS,
66         infer,
67         infer_expression,
68         JoyTypeError,
69         combinator_effect,
70         poly_combinator_effect,
71         doc_from_stack_effect,
72         )
73
74
75 HELP_TEMPLATE = '''\
76
77 ==== Help on %s ====
78
79 %s
80
81 ---- end (%s)
82 '''
83
84
85 _SYM_NUMS = lambda c=count(): next(c)
86 _COMB_NUMS = lambda c=count(): next(c)
87
88
89 _R = list(range(10))
90 A = a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = list(map(AnyJoyType, _R))
91 B = b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = list(map(BooleanJoyType, _R))
92 N = n0, n1, n2, n3, n4, n5, n6, n7, n8, n9 = list(map(NumberJoyType, _R))
93 S = s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 = list(map(StackJoyType, _R))
94 F = f0, f1, f2, f3, f4, f5, f6, f7, f8, f9 = list(map(FloatJoyType, _R))
95 I = i0, i1, i2, i3, i4, i5, i6, i7, i8, i9 = list(map(IntJoyType, _R))
96 T = t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 = list(map(TextJoyType, _R))
97
98
99 _R = list(range(1, 11))
100 As = list(map(AnyStarJoyType, _R))
101 Ns = list(map(NumberStarJoyType, _R))
102 Ss = list(map(StackStarJoyType, _R))
103
104
105 sec0 = stack_effect(t1)()
106 sec1 = stack_effect(s0, i1)(s1)
107 sec2 = stack_effect(s0, i1)(a1)
108 sec_binary_cmp = stack_effect(n1, n2)(b1)
109 sec_binary_ints = stack_effect(i1, i2)(i3)
110 sec_binary_logic = stack_effect(b1, b2)(b3)
111 sec_binary_math = stack_effect(n1, n2)(n3)
112 sec_unary_logic = stack_effect(a1)(b1)
113 sec_unary_math = stack_effect(n1)(n2)
114 sec_Ns_math = stack_effect((Ns[1], s1),)(n0)
115
116 _dictionary = {}
117
118
119 def inscribe(function):
120         '''A decorator to inscribe functions into the default dictionary.'''
121         _dictionary[function.name] = function
122         return function
123
124
125 def initialize():
126         '''Return a dictionary of Joy functions for use with joy().'''
127         return _dictionary.copy()
128
129
130 ALIASES = (
131         ('add', ['+']),
132         ('and', ['&']),
133         ('bool', ['truthy']),
134         ('mul', ['*']),
135         ('floordiv', ['/floor', '//']),
136         ('floor', ['round']),
137         ('truediv', ['/']),
138         ('mod', ['%', 'rem', 'remainder', 'modulus']),
139         ('eq', ['=']),
140         ('ge', ['>=']),
141         ('getitem', ['pick', 'at']),
142         ('gt', ['>']),
143         ('le', ['<=']),
144         ('lshift', ['<<']),
145         ('lt', ['<']),
146         ('ne', ['<>', '!=']),
147         ('rshift', ['>>']),
148         ('sub', ['-']),
149         ('xor', ['^']),
150         ('succ', ['++']),
151         ('pred', ['--']),
152         ('rolldown', ['roll<']),
153         ('rollup', ['roll>']),
154         ('eh', ['?']),
155         ('id', [u'•']),
156         )
157
158
159 def add_aliases(D, A):
160         '''
161         Given a dict and a iterable of (name, [alias, ...]) pairs, create
162         additional entries in the dict mapping each alias to the named function
163         if it's in the dict.  Aliases for functions not in the dict are ignored.
164         '''
165         for name, aliases in A:
166                 try:
167                         F = D[name]
168                 except KeyError:
169                         continue
170                 for alias in aliases:
171                         D[alias] = F
172
173
174 def yin_functions():
175         '''
176         Return a dict of named stack effects.
177
178         "Yin" functions are those that only rearrange items in stacks and
179         can be defined completely by their stack effects.  This means they
180         can be auto-compiled.
181         '''
182         # pylint: disable=unused-variable
183         cons = ef(a1, s0)((a1, s0))
184         ccons = compose(cons, cons)
185         dup = ef(a1)(a1, a1)
186         dupd = ef(a2, a1)(a2, a2, a1)
187         dupdd = ef(a3, a2, a1)(a3, a3, a2, a1)
188         first = ef((a1, s1),)(a1,)
189         over = ef(a2, a1)(a2, a1, a2)
190         pop = ef(a1)()
191         popd = ef(a2, a1,)(a1)
192         popdd = ef(a3, a2, a1,)(a2, a1,)
193         popop = ef(a2, a1,)()
194         popopd = ef(a3, a2, a1,)(a1)
195         popopdd = ef(a4, a3, a2, a1,)(a2, a1)
196         rest = ef((a1, s0),)(s0,)
197         rolldown = ef(a1, a2, a3)(a2, a3, a1)
198         rollup = ef(a1, a2, a3)(a3, a1, a2)
199         rrest = compose(rest, rest)
200         second = compose(rest, first)
201         stack = s0, (s0, s0)
202         swaack = (s1, s0), (s0, s1)
203         swap = ef(a1, a2)(a2, a1)
204         swons = compose(swap, cons)
205         third = compose(rest, second)
206         tuck = ef(a2, a1)(a1, a2, a1)
207         uncons = ef((a1, s0),)(a1, s0)
208         unswons = compose(uncons, swap)
209         stuncons = compose(stack, uncons)
210         stununcons = compose(stack, uncons, uncons)
211         unit = ef(a1)((a1, ()))
212
213         first_two = compose(uncons, uncons, pop)
214         fourth = compose(rest, third)
215
216         _Tree_add_Ee = compose(pop, swap, rolldown, rrest, ccons)
217         _Tree_get_E = compose(popop, second)
218         _Tree_delete_clear_stuff = compose(rollup, popop, rest)
219         _Tree_delete_R0 = compose(over, first, swap, dup)
220
221         return locals()
222
223
224 definitions = ('''\
225 ? == dup truthy
226 *fraction == [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons
227 *fraction0 == concat [[swap] dip * [*] dip] infra
228 anamorphism == [pop []] swap [dip swons] genrec
229 average == [sum 1.0 *] [size] cleave /
230 binary == nullary [popop] dip
231 cleave == fork [popd] dip
232 codireco == cons dip rest cons
233 dinfrirst == dip infra first
234 unstack == ? [uncons ?] loop pop
235 down_to_zero == [0 >] [dup --] while
236 dupdipd == dup dipd
237 enstacken == stack [clear] dip
238 flatten == [] swap [concat] step
239 fork == [i] app2
240 gcd == 1 [tuck modulus dup 0 >] loop pop
241 ifte == [nullary not] dipd branch
242 ii == [dip] dupdip i
243 least_fraction == dup [gcd] infra [div] concat map
244 make_generator == [codireco] ccons
245 nullary == [stack] dinfrirst
246 of == swap at
247 pam == [i] map
248 primrec == [i] genrec
249 product == 1 swap [*] step
250 quoted == [unit] dip
251 range == [0 <=] [1 - dup] anamorphism
252 range_to_zero == unit [down_to_zero] infra
253 run == [] swap infra
254 size == 0 swap [pop ++] step
255 sqr == dup mul
256 step_zero == 0 roll> step
257 swoncat == swap concat
258 ternary == unary [popop] dip
259 unary == nullary popd
260 unquoted == [i] dip
261 while == swap [nullary] cons dup dipd concat loop
262 '''
263 #
264 #
265 # ifte == [nullary] dipd swap branch
266 # genrec == [[genrec] cons cons cons cons] nullary swons concat ifte
267
268 # Another definition for while. FWIW
269 # while == over [[i] dip nullary] ccons [nullary] dip loop
270
271 ##ccons == cons cons
272 ##unit == [] cons
273 ##second == rest first
274 ##third == rest rest first
275 ##swons == swap cons
276
277 ##Zipper
278 ##z-down == [] swap uncons swap
279 ##z-up == swons swap shunt
280 ##z-right == [swons] cons dip uncons swap
281 ##z-left == swons [uncons swap] dip swap
282
283 ##Quadratic Formula
284 ##divisor == popop 2 *
285 ##minusb == pop neg
286 ##radical == swap dup * rollup * 4 * - sqrt
287 ##root1 == + swap /
288 ##root2 == - swap /
289 ##q0 == [[divisor] [minusb] [radical]] pam
290 ##q1 == [[root1] [root2]] pam
291 ##quadratic == [q0] ternary i [q1] ternary
292
293 # Project Euler
294 ##'''\
295 ##PE1.1 == + dup [+] dip
296 ##PE1.2 == dup [3 & PE1.1] dip 2 >>
297 ##PE1.3 == 14811 swap [PE1.2] times pop
298 ##PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop
299 ##'''
300 #PE1.2 == [PE1.1] step
301 #PE1 == 0 0 66 [[3 2 1 3 1 2 3] PE1.2] times [3 2 1 3] PE1.2 pop
302 )
303
304
305 def FunctionWrapper(f):
306         '''Set name attribute.'''
307         if not f.__doc__:
308                 raise ValueError('Function %s must have doc string.' % f.__name__)
309         f.name = f.__name__.rstrip('_')  # Don't shadow builtins.
310         return f
311
312
313 def SimpleFunctionWrapper(f):
314         '''
315         Wrap functions that take and return just a stack.
316         '''
317         @FunctionWrapper
318         @wraps(f)
319         @rename_code_object(f.__name__)
320         def inner(stack, expression, dictionary):
321                 return f(stack), expression, dictionary
322         return inner
323
324
325 def BinaryBuiltinWrapper(f):
326         '''
327         Wrap functions that take two arguments and return a single result.
328         '''
329         @FunctionWrapper
330         @wraps(f)
331         @rename_code_object(f.__name__)
332         def inner(stack, expression, dictionary):
333                 (a, (b, stack)) = stack
334                 result = f(b, a)
335                 return (result, stack), expression, dictionary
336         return inner
337
338
339 def UnaryBuiltinWrapper(f):
340         '''
341         Wrap functions that take one argument and return a single result.
342         '''
343         @FunctionWrapper
344         @wraps(f)
345         @rename_code_object(f.__name__)
346         def inner(stack, expression, dictionary):
347                 (a, stack) = stack
348                 result = f(a)
349                 return (result, stack), expression, dictionary
350         return inner
351
352
353 class DefinitionWrapper(object):
354         '''
355         Provide implementation of defined functions, and some helper methods.
356         '''
357
358         def __init__(self, name, body_text, doc=None):
359                 self.name = self.__name__ = name
360                 self.body = text_to_expression(body_text)
361                 self._body = tuple(iter_stack(self.body))
362                 self.__doc__ = doc or body_text
363                 self._compiled = None
364
365         def __call__(self, stack, expression, dictionary):
366                 if self._compiled:
367                         return self._compiled(stack, expression, dictionary)  # pylint: disable=E1102
368                 expression = list_to_stack(self._body, expression)
369                 return stack, expression, dictionary
370
371         @classmethod
372         def parse_definition(class_, defi):
373                 '''
374                 Given some text describing a Joy function definition parse it and
375                 return a DefinitionWrapper.
376                 '''
377                 name, proper, body_text = (n.strip() for n in defi.partition('=='))
378                 if not proper:
379                         raise ValueError('Definition %r failed' % (defi,))
380                 return class_(name, body_text)
381
382         @classmethod
383         def add_definitions(class_, defs, dictionary):
384                 '''
385                 Scan multi-line string defs for definitions and add them to the
386                 dictionary.
387                 '''
388                 for definition in _text_to_defs(defs):
389                         class_.add_def(definition, dictionary)
390
391         @classmethod
392         def add_def(class_, definition, dictionary, fail_fails=False):
393                 '''
394                 Add the definition to the dictionary.
395                 '''
396                 F = class_.parse_definition(definition)
397                 _log.info('Adding definition %s := %s', F.name, expression_to_string(F.body))
398                 dictionary[F.name] = F
399
400         @classmethod
401         def load_definitions(class_, filename, dictionary):
402                 with open(filename) as f:
403                         lines = [line for line in f if '==' in line]
404                 for line in lines:
405                         class_.add_def(line, dictionary)
406
407
408 def _text_to_defs(text):
409         return (line.strip() for line in text.splitlines() if '==' in line)
410
411
412 #
413 # Functions
414 #
415
416
417 @inscribe
418 @sec0
419 @FunctionWrapper
420 def inscribe_(stack, expression, dictionary):
421         '''
422         Create a new Joy function definition in the Joy dictionary.  A
423         definition is given as a string with a name followed by a double
424         equal sign then one or more Joy functions, the body. for example:
425
426                         sqr == dup mul
427
428         If you want the definition to persist over restarts, enter it into
429         the definitions.txt resource.
430         '''
431         definition, stack = stack
432         DefinitionWrapper.add_def(definition, dictionary, fail_fails=True)
433         return stack, expression, dictionary
434
435
436 @inscribe
437 @SimpleFunctionWrapper
438 def parse(stack):
439         '''Parse the string on the stack to a Joy expression.'''
440         text, stack = stack
441         expression = text_to_expression(text)
442         return expression, stack
443
444
445 @inscribe
446 @SimpleFunctionWrapper
447 def infer_(stack):
448         '''Attempt to infer the stack effect of a Joy expression.'''
449         E, stack = stack
450         effects = infer_expression(E)
451         e = list_to_stack([(fi, (fo, ())) for fi, fo in effects])
452         return e, stack
453
454
455 @inscribe
456 @sec2
457 @SimpleFunctionWrapper
458 def getitem(stack):
459         '''
460         ::
461
462                 getitem == drop first
463
464         Expects an integer and a quote on the stack and returns the item at the
465         nth position in the quote counting from 0.
466         ::
467
468                          [a b c d] 0 getitem
469                 -------------------------
470                                                                 a
471
472         '''
473         n, (Q, stack) = stack
474         return pick(Q, n), stack
475
476
477 @inscribe
478 @sec1
479 @SimpleFunctionWrapper
480 def drop(stack):
481         '''
482         ::
483
484                 drop == [rest] times
485
486         Expects an integer and a quote on the stack and returns the quote with
487         n items removed off the top.
488         ::
489
490                          [a b c d] 2 drop
491                 ----------------------
492                                          [c d]
493
494         '''
495         n, (Q, stack) = stack
496         while n > 0:
497                 try:
498                         _, Q = Q
499                 except ValueError:
500                         raise IndexError
501                 n -= 1
502         return Q, stack
503
504
505 @inscribe
506 @sec1
507 @SimpleFunctionWrapper
508 def take(stack):
509         '''
510         Expects an integer and a quote on the stack and returns the quote with
511         just the top n items in reverse order (because that's easier and you can
512         use reverse if needed.)
513         ::
514
515                          [a b c d] 2 take
516                 ----------------------
517                                          [b a]
518
519         '''
520         n, (Q, stack) = stack
521         x = ()
522         while n > 0:
523                 try:
524                         item, Q = Q
525                 except ValueError:
526                         raise IndexError
527                 x = item, x
528                 n -= 1
529         return x, stack
530
531
532 @inscribe
533 @SimpleFunctionWrapper
534 def choice(stack):
535         '''
536         Use a Boolean value to select one of two items.
537         ::
538
539                                 A B False choice
540                  ----------------------
541                                                          A
542
543
544                                 A B True choice
545                  ---------------------
546                                                          B
547
548         Currently Python semantics are used to evaluate the "truthiness" of the
549         Boolean value (so empty string, zero, etc. are counted as false, etc.)
550         '''
551         (if_, (then, (else_, stack))) = stack
552         return then if if_ else else_, stack
553
554
555 @inscribe
556 @SimpleFunctionWrapper
557 def select(stack):
558         '''
559         Use a Boolean value to select one of two items from a sequence.
560         ::
561
562                                 [A B] False select
563                  ------------------------
564                                                                 A
565
566
567                                 [A B] True select
568                  -----------------------
569                                                          B
570
571         The sequence can contain more than two items but not fewer.
572         Currently Python semantics are used to evaluate the "truthiness" of the
573         Boolean value (so empty string, zero, etc. are counted as false, etc.)
574         '''
575         (flag, (choices, stack)) = stack
576         (else_, (then, _)) = choices
577         return then if flag else else_, stack
578
579
580 @inscribe
581 @sec_Ns_math
582 @SimpleFunctionWrapper
583 def max_(S):
584         '''Given a list find the maximum.'''
585         tos, stack = S
586         return max(iter_stack(tos)), stack
587
588
589 @inscribe
590 @sec_Ns_math
591 @SimpleFunctionWrapper
592 def min_(S):
593         '''Given a list find the minimum.'''
594         tos, stack = S
595         return min(iter_stack(tos)), stack
596
597
598 @inscribe
599 @sec_Ns_math
600 @SimpleFunctionWrapper
601 def sum_(S):
602         '''Given a quoted sequence of numbers return the sum.
603
604         sum == 0 swap [+] step
605         '''
606         tos, stack = S
607         return sum(iter_stack(tos)), stack
608
609
610 @inscribe
611 @SimpleFunctionWrapper
612 def remove(S):
613         '''
614         Expects an item on the stack and a quote under it and removes that item
615         from the the quote.  The item is only removed once.
616         ::
617
618                          [1 2 3 1] 1 remove
619                 ------------------------
620                                                 [2 3 1]
621
622         '''
623         (tos, (second, stack)) = S
624         l = list(iter_stack(second))
625         l.remove(tos)
626         return list_to_stack(l), stack
627
628
629 @inscribe
630 @SimpleFunctionWrapper
631 def unique(S):
632         '''Given a list remove duplicate items.'''
633         tos, stack = S
634         I = list(iter_stack(tos))
635         return list_to_stack(sorted(set(I), key=I.index)), stack
636
637
638 @inscribe
639 @SimpleFunctionWrapper
640 def sort_(S):
641         '''Given a list return it sorted.'''
642         tos, stack = S
643         return list_to_stack(sorted(iter_stack(tos))), stack
644
645
646 _functions['clear'] = s0, s1
647 @inscribe
648 @SimpleFunctionWrapper
649 def clear(stack):
650         '''Clear everything from the stack.
651         ::
652
653                 clear == stack [pop stack] loop
654
655                          ... clear
656                 ---------------
657
658         '''
659         return ()
660
661
662 @inscribe
663 @SimpleFunctionWrapper
664 def disenstacken(stack):
665         '''
666         The disenstacken operator expects a list on top of the stack and makes that
667         the stack discarding the rest of the stack.
668         '''
669         return stack[0]
670
671
672 @inscribe
673 @SimpleFunctionWrapper
674 def reverse(S):
675         '''Reverse the list on the top of the stack.
676         ::
677
678                 reverse == [] swap shunt
679         '''
680         (tos, stack) = S
681         res = ()
682         for term in iter_stack(tos):
683                 res = term, res
684         return res, stack
685
686
687 @inscribe
688 @combinator_effect(_COMB_NUMS(), s7, s6)
689 @SimpleFunctionWrapper
690 def concat_(S):
691         '''Concatinate the two lists on the top of the stack.
692         ::
693
694                          [a b c] [d e f] concat
695                 ----------------------------
696                                          [a b c d e f]
697
698 '''
699         (tos, (second, stack)) = S
700         return concat(second, tos), stack
701
702
703 @inscribe
704 @SimpleFunctionWrapper
705 def shunt(stack):
706         '''Like concat but reverses the top list into the second.
707         ::
708
709                 shunt == [swons] step == reverse swap concat
710
711                          [a b c] [d e f] shunt
712                 ---------------------------
713                                  [f e d a b c] 
714
715         '''
716         (tos, (second, stack)) = stack
717         while tos:
718                 term, tos = tos
719                 second = term, second
720         return second, stack
721
722
723 @inscribe
724 @SimpleFunctionWrapper
725 def zip_(S):
726         '''
727         Replace the two lists on the top of the stack with a list of the pairs
728         from each list.  The smallest list sets the length of the result list.
729         '''
730         (tos, (second, stack)) = S
731         accumulator = [
732                 (a, (b, ()))
733                 for a, b in zip(iter_stack(tos), iter_stack(second))
734                 ]
735         return list_to_stack(accumulator), stack
736
737
738 @inscribe
739 @sec_unary_math
740 @SimpleFunctionWrapper
741 def succ(S):
742         '''Increment TOS.'''
743         (tos, stack) = S
744         return tos + 1, stack
745
746
747 @inscribe
748 @sec_unary_math
749 @SimpleFunctionWrapper
750 def pred(S):
751         '''Decrement TOS.'''
752         (tos, stack) = S
753         return tos - 1, stack
754
755
756 @inscribe
757 @SimpleFunctionWrapper
758 def pm(stack):
759         '''
760         Plus or minus
761         ::
762
763                          a b pm
764                 -------------
765                          a+b a-b
766
767         '''
768         a, (b, stack) = stack
769         p, m, = b + a, b - a
770         return m, (p, stack)
771
772
773 def floor(n):
774         return int(math.floor(n))
775
776 floor.__doc__ = math.floor.__doc__
777
778
779 @inscribe
780 @SimpleFunctionWrapper
781 def divmod_(S):
782         '''
783         divmod(x, y) -> (quotient, remainder)
784
785         Return the tuple (x//y, x%y).  Invariant: div*y + mod == x.
786         '''
787         a, (b, stack) = S
788         d, m = divmod(a, b)
789         return d, (m, stack)
790
791
792 def sqrt(a):
793         '''
794         Return the square root of the number a.
795         Negative numbers return complex roots.
796         '''
797         try:
798                 r = math.sqrt(a)
799         except ValueError:
800                 assert a < 0, repr(a)
801                 r = math.sqrt(-a) * 1j
802         return r
803
804
805 #def execute(S):
806 #  (text, stack) = S
807 #  if isinstance(text, str):
808 #    return run(text, stack)
809 #  return stack
810
811
812 @inscribe
813 @SimpleFunctionWrapper
814 def id_(stack):
815         '''The identity function.'''
816         return stack
817
818
819 @inscribe
820 @SimpleFunctionWrapper
821 def void(stack):
822         '''True if the form on TOS is void otherwise False.'''
823         form, stack = stack
824         return _void(form), stack
825
826
827 def _void(form):
828         return any(not _void(i) for i in iter_stack(form))
829
830
831
832 ##  transpose
833 ##  sign
834 ##  take
835
836
837 @inscribe
838 @FunctionWrapper
839 def words(stack, expression, dictionary):
840         '''Print all the words in alphabetical order.'''
841         print(' '.join(sorted(dictionary)))
842         return stack, expression, dictionary
843
844
845 @inscribe
846 @FunctionWrapper
847 def sharing(stack, expression, dictionary):
848         '''Print redistribution information.'''
849         print("You may convey verbatim copies of the Program's source code as"
850                                 ' you receive it, in any medium, provided that you conspicuously'
851                                 ' and appropriately publish on each copy an appropriate copyright'
852                                 ' notice; keep intact all notices stating that this License and'
853                                 ' any non-permissive terms added in accord with section 7 apply'
854                                 ' to the code; keep intact all notices of the absence of any'
855                                 ' warranty; and give all recipients a copy of this License along'
856                                 ' with the Program.'
857                                 ' You should have received a copy of the GNU General Public License'
858                                 ' along with Thun.  If not see <http://www.gnu.org/licenses/>.')
859         return stack, expression, dictionary
860
861
862 @inscribe
863 @FunctionWrapper
864 def warranty(stack, expression, dictionary):
865         '''Print warranty information.'''
866         print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
867                                 ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
868                                 ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
869                                 ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
870                                 ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
871                                 ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
872                                 ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
873                                 ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
874                                 ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')
875         return stack, expression, dictionary
876
877
878 # def simple_manual(stack):
879 #   '''
880 #   Print words and help for each word.
881 #   '''
882 #   for name, f in sorted(FUNCTIONS.items()):
883 #     d = getdoc(f)
884 #     boxline = '+%s+' % ('-' * (len(name) + 2))
885 #     print('\n'.join((
886 #       boxline,
887 #       '| %s |' % (name,),
888 #       boxline,
889 #       d if d else '   ...',
890 #       '',
891 #       '--' * 40,
892 #       '',
893 #       )))
894 #   return stack
895
896
897 @inscribe
898 @FunctionWrapper
899 def help_(S, expression, dictionary):
900         '''Accepts a quoted symbol on the top of the stack and prints its docs.'''
901         ((symbol, _), stack) = S
902         word = dictionary[symbol]
903         print(HELP_TEMPLATE % (symbol, getdoc(word), symbol))
904         return stack, expression, dictionary
905
906
907 #
908 # § Combinators
909 #
910
911
912 # Several combinators depend on other words in their definitions,
913 # we use symbols to prevent hard-coding these, so in theory, you
914 # could change the word in the dictionary to use different semantics.
915 S_choice = Symbol('choice')
916 S_first = Symbol('first')
917 S_getitem = Symbol('getitem')
918 S_genrec = Symbol('genrec')
919 S_loop = Symbol('loop')
920 S_i = Symbol('i')
921 S_ifte = Symbol('ifte')
922 S_infra = Symbol('infra')
923 S_pop = Symbol('pop')
924 S_step = Symbol('step')
925 S_times = Symbol('times')
926 S_swaack = Symbol('swaack')
927
928
929 @inscribe
930 @combinator_effect(_COMB_NUMS(), s1)
931 @FunctionWrapper
932 def i(stack, expression, dictionary):
933         '''
934         The i combinator expects a quoted program on the stack and unpacks it
935         onto the pending expression for evaluation.
936         ::
937
938                          [Q] i
939                 -----------
940                                 Q
941
942         '''
943         quote, stack = stack
944         return stack, concat(quote, expression), dictionary
945
946
947 @inscribe
948 @combinator_effect(_COMB_NUMS(), s1)
949 @FunctionWrapper
950 def x(stack, expression, dictionary):
951         '''
952         ::
953
954                 x == dup i
955
956                 ... [Q] x = ... [Q] dup i
957                 ... [Q] x = ... [Q] [Q] i
958                 ... [Q] x = ... [Q]  Q
959
960         '''
961         quote, _ = stack
962         return stack, concat(quote, expression), dictionary
963
964
965 @inscribe
966 @combinator_effect(_COMB_NUMS(), s7, s6)
967 @FunctionWrapper
968 def b(stack, expression, dictionary):
969         '''
970         ::
971
972                 b == [i] dip i
973
974                 ... [P] [Q] b == ... [P] i [Q] i
975                 ... [P] [Q] b == ... P Q
976
977         '''
978         q, (p, (stack)) = stack
979         return stack, concat(p, concat(q, expression)), dictionary
980
981
982 @inscribe
983 @combinator_effect(_COMB_NUMS(), a1, s1)
984 @FunctionWrapper
985 def dupdip(stack, expression, dictionary):
986         '''
987         ::
988
989                 [F] dupdip == dup [F] dip
990
991                 ... a [F] dupdip
992                 ... a dup [F] dip
993                 ... a a   [F] dip
994                 ... a F a
995
996         '''
997         F, stack = stack
998         a = stack[0]
999         return stack, concat(F, (a,  expression)), dictionary
1000
1001
1002 @inscribe
1003 @combinator_effect(_COMB_NUMS(), s7, s6)
1004 @FunctionWrapper
1005 def infra(stack, expression, dictionary):
1006         '''
1007         Accept a quoted program and a list on the stack and run the program
1008         with the list as its stack.  Does not affect the rest of the stack.
1009         ::
1010
1011                          ... [a b c] [Q] . infra
1012                 -----------------------------
1013                          c b a . Q [...] swaack
1014
1015         '''
1016         (quote, (aggregate, stack)) = stack
1017         return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary
1018
1019
1020 @inscribe
1021 #@combinator_effect(_COMB_NUMS(), s7, s6, s5, s4)
1022 @FunctionWrapper
1023 def genrec(stack, expression, dictionary):
1024         '''
1025         General Recursion Combinator.
1026         ::
1027
1028                                                                                                         [if] [then] [rec1] [rec2] genrec
1029                 ---------------------------------------------------------------------
1030                          [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
1031
1032         From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
1033         "The genrec combinator takes four program parameters in addition to
1034         whatever data parameters it needs. Fourth from the top is an if-part,
1035         followed by a then-part. If the if-part yields true, then the then-part
1036         is executed and the combinator terminates. The other two parameters are
1037         the rec1-part and the rec2-part. If the if-part yields false, the
1038         rec1-part is executed. Following that the four program parameters and
1039         the combinator are again pushed onto the stack bundled up in a quoted
1040         form. Then the rec2-part is executed, where it will find the bundled
1041         form. Typically it will then execute the bundled form, either with i or
1042         with app2, or some other combinator."
1043
1044         The way to design one of these is to fix your base case [then] and the
1045         test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
1046         a quotation of the whole function.
1047
1048         For example, given a (general recursive) function 'F':
1049         ::
1050
1051                         F == [I] [T] [R1] [R2] genrec
1052
1053         If the [I] if-part fails you must derive R1 and R2 from:
1054         ::
1055
1056                         ... R1 [F] R2
1057
1058         Just set the stack arguments in front, and figure out what R1 and R2
1059         have to do to apply the quoted [F] in the proper way.  In effect, the
1060         genrec combinator turns into an ifte combinator with a quoted copy of
1061         the original definition in the else-part:
1062         ::
1063
1064                         F == [I] [T] [R1]   [R2] genrec
1065                                 == [I] [T] [R1 [F] R2] ifte
1066
1067         Primitive recursive functions are those where R2 == i.
1068         ::
1069
1070                         P == [I] [T] [R] primrec
1071                                 == [I] [T] [R [P] i] ifte
1072                                 == [I] [T] [R P] ifte
1073
1074         '''
1075         (rec2, (rec1, stack)) = stack
1076         (then, (if_, _)) = stack
1077         F = (if_, (then, (rec1, (rec2, (S_genrec, ())))))
1078         else_ = concat(rec1, (F, rec2))
1079         return (else_, stack), (S_ifte, expression), dictionary
1080
1081
1082 @inscribe
1083 @combinator_effect(_COMB_NUMS(), s7, s6)
1084 @FunctionWrapper
1085 def map_(S, expression, dictionary):
1086         '''
1087         Run the quoted program on TOS on the items in the list under it, push a
1088         new list with the results in place of the program and original list.
1089         '''
1090 #  (quote, (aggregate, stack)) = S
1091 #  results = list_to_stack([
1092 #    joy((term, stack), quote, dictionary)[0][0]
1093 #    for term in iter_stack(aggregate)
1094 #    ])
1095 #  return (results, stack), expression, dictionary
1096         (quote, (aggregate, stack)) = S
1097         if not aggregate:
1098                 return (aggregate, stack), expression, dictionary
1099         batch = ()
1100         for term in iter_stack(aggregate):
1101                 s = term, stack
1102                 batch = (s, (quote, (S_infra, (S_first, batch))))
1103         stack = (batch, ((), stack))
1104         return stack, (S_infra, expression), dictionary
1105
1106
1107 #def cleave(S, expression, dictionary):
1108 #  '''
1109 #  The cleave combinator expects two quotations, and below that an item X.
1110 #  It first executes [P], with X on top, and saves the top result element.
1111 #  Then it executes [Q], again with X, and saves the top result.
1112 #  Finally it restores the stack to what it was below X and pushes the two
1113 #  results P(X) and Q(X).
1114 #  '''
1115 #  (Q, (P, (x, stack))) = S
1116 #  p = joy((x, stack), P, dictionary)[0][0]
1117 #  q = joy((x, stack), Q, dictionary)[0][0]
1118 #  return (q, (p, stack)), expression, dictionary
1119
1120
1121 def branch_true(stack, expression, dictionary):
1122         # pylint: disable=unused-variable
1123         (then, (else_, (flag, stack))) = stack
1124         return stack, concat(then, expression), dictionary
1125
1126
1127 def branch_false(stack, expression, dictionary):
1128         # pylint: disable=unused-variable
1129         (then, (else_, (flag, stack))) = stack
1130         return stack, concat(else_, expression), dictionary
1131
1132
1133 @inscribe
1134 @poly_combinator_effect(_COMB_NUMS(), [branch_true, branch_false], b1, s7, s6)
1135 @FunctionWrapper
1136 def branch(stack, expression, dictionary):
1137         '''
1138         Use a Boolean value to select one of two quoted programs to run.
1139
1140         ::
1141
1142                         branch == roll< choice i
1143
1144         ::
1145
1146                                 False [F] [T] branch
1147                  --------------------------
1148                                                          F
1149
1150                                 True [F] [T] branch
1151                  -------------------------
1152                                                                         T
1153
1154         '''
1155         (then, (else_, (flag, stack))) = stack
1156         return stack, concat(then if flag else else_, expression), dictionary
1157
1158
1159 #FUNCTIONS['branch'] = CombinatorJoyType('branch', [branch_true, branch_false], 100)
1160
1161
1162 ##@inscribe
1163 ##@FunctionWrapper
1164 ##def ifte(stack, expression, dictionary):
1165 ##  '''
1166 ##  If-Then-Else Combinator
1167 ##  ::
1168 ##
1169 ##                  ... [if] [then] [else] ifte
1170 ##       ---------------------------------------------------
1171 ##          ... [[else] [then]] [...] [if] infra select i
1172 ##
1173 ##
1174 ##
1175 ##
1176 ##                ... [if] [then] [else] ifte
1177 ##       -------------------------------------------------------
1178 ##          ... [else] [then] [...] [if] infra first choice i
1179 ##
1180 ##
1181 ##  Has the effect of grabbing a copy of the stack on which to run the
1182 ##  if-part using infra.
1183 ##  '''
1184 ##  (else_, (then, (if_, stack))) = stack
1185 ##  expression = (S_infra, (S_first, (S_choice, (S_i, expression))))
1186 ##  stack = (if_, (stack, (then, (else_, stack))))
1187 ##  return stack, expression, dictionary
1188
1189
1190 @inscribe
1191 @FunctionWrapper
1192 def cond(stack, expression, dictionary):
1193         '''
1194         This combinator works like a case statement.  It expects a single quote
1195         on the stack that must contain zero or more condition quotes and a 
1196         default quote.  Each condition clause should contain a quoted predicate
1197         followed by the function expression to run if that predicate returns
1198         true.  If no predicates return true the default function runs.
1199
1200         It works by rewriting into a chain of nested `ifte` expressions, e.g.::
1201
1202                                                 [[[B0] T0] [[B1] T1] [D]] cond
1203                         -----------------------------------------
1204                                  [B0] [T0] [[B1] [T1] [D] ifte] ifte
1205
1206         '''
1207         conditions, stack = stack
1208         if conditions:
1209                 expression = _cond(conditions, expression)
1210                 try:
1211                         # Attempt to preload the args to first ifte.
1212                         (P, (T, (E, expression))) = expression
1213                 except ValueError:
1214                         # If, for any reason, the argument to cond should happen to contain
1215                         # only the default clause then this optimization will fail.
1216                         pass
1217                 else:
1218                         stack = (E, (T, (P, stack)))
1219         return stack, expression, dictionary
1220
1221
1222 def _cond(conditions, expression):
1223         (clause, rest) = conditions
1224         if not rest:  # clause is [D]
1225                 return clause
1226         P, T = clause
1227         return (P, (T, (_cond(rest, ()), (S_ifte, expression))))
1228
1229
1230 @inscribe
1231 @combinator_effect(_COMB_NUMS(), a1, s1)
1232 @FunctionWrapper
1233 def dip(stack, expression, dictionary):
1234         '''
1235         The dip combinator expects a quoted program on the stack and below it
1236         some item, it hoists the item into the expression and runs the program
1237         on the rest of the stack.
1238         ::
1239
1240                          ... x [Q] dip
1241                 -------------------
1242                                  ... Q x
1243
1244         '''
1245         (quote, (x, stack)) = stack
1246         expression = (x, expression)
1247         return stack, concat(quote, expression), dictionary
1248
1249
1250 @inscribe
1251 @combinator_effect(_COMB_NUMS(), a2, a1, s1)
1252 @FunctionWrapper
1253 def dipd(S, expression, dictionary):
1254         '''
1255         Like dip but expects two items.
1256         ::
1257
1258                          ... y x [Q] dip
1259                 ---------------------
1260                                  ... Q y x
1261
1262         '''
1263         (quote, (x, (y, stack))) = S
1264         expression = (y, (x, expression))
1265         return stack, concat(quote, expression), dictionary
1266
1267
1268 @inscribe
1269 @combinator_effect(_COMB_NUMS(), a3, a2, a1, s1)
1270 @FunctionWrapper
1271 def dipdd(S, expression, dictionary):
1272         '''
1273         Like dip but expects three items.
1274         ::
1275
1276                          ... z y x [Q] dip
1277                 -----------------------
1278                                  ... Q z y x
1279
1280         '''
1281         (quote, (x, (y, (z, stack)))) = S
1282         expression = (z, (y, (x, expression)))
1283         return stack, concat(quote, expression), dictionary
1284
1285
1286 @inscribe
1287 @combinator_effect(_COMB_NUMS(), a1, s1)
1288 @FunctionWrapper
1289 def app1(S, expression, dictionary):
1290         '''
1291         Given a quoted program on TOS and anything as the second stack item run
1292         the program and replace the two args with the first result of the
1293         program.
1294         ::
1295
1296                                                         ... x [Q] . app1
1297                  -----------------------------------
1298                                 ... [x ...] [Q] . infra first
1299         '''
1300         (quote, (x, stack)) = S
1301         stack = (quote, ((x, stack), stack))
1302         expression = (S_infra, (S_first, expression))
1303         return stack, expression, dictionary
1304
1305
1306 @inscribe
1307 @combinator_effect(_COMB_NUMS(), a2, a1, s1)
1308 @FunctionWrapper
1309 def app2(S, expression, dictionary):
1310         '''Like app1 with two items.
1311         ::
1312
1313                                                 ... y x [Q] . app2
1314                  -----------------------------------
1315                                 ... [y ...] [Q] . infra first
1316                                                 [x ...] [Q]   infra first
1317
1318         '''
1319         (quote, (x, (y, stack))) = S
1320         expression = (S_infra, (S_first,
1321                 ((x, stack), (quote, (S_infra, (S_first,
1322                         expression))))))
1323         stack = (quote, ((y, stack), stack))
1324         return stack, expression, dictionary
1325
1326
1327 @inscribe
1328 @combinator_effect(_COMB_NUMS(), a3, a2, a1, s1)
1329 @FunctionWrapper
1330 def app3(S, expression, dictionary):
1331         '''Like app1 with three items.
1332         ::
1333
1334                                                 ... z y x [Q] . app3
1335                  -----------------------------------
1336                                 ... [z ...] [Q] . infra first
1337                                                 [y ...] [Q]   infra first
1338                                                 [x ...] [Q]   infra first
1339
1340         '''
1341         (quote, (x, (y, (z, stack)))) = S
1342         expression = (S_infra, (S_first,
1343                 ((y, stack), (quote, (S_infra, (S_first,
1344                 ((x, stack), (quote, (S_infra, (S_first,
1345                         expression))))))))))
1346         stack = (quote, ((z, stack), stack))
1347         return stack, expression, dictionary
1348
1349
1350 @inscribe
1351 @combinator_effect(_COMB_NUMS(), s7, s6)
1352 @FunctionWrapper
1353 def step(S, expression, dictionary):
1354         '''
1355         Run a quoted program on each item in a sequence.
1356         ::
1357
1358                                         ... [] [Q] . step
1359                          -----------------------
1360                                                                  ... .
1361
1362
1363                                  ... [a] [Q] . step
1364                         ------------------------
1365                                                          ... a . Q
1366
1367
1368                          ... [a b c] [Q] . step
1369                 ----------------------------------------
1370                                                                  ... a . Q [b c] [Q] step
1371
1372         The step combinator executes the quotation on each member of the list
1373         on top of the stack.
1374         '''
1375         (quote, (aggregate, stack)) = S
1376         if not aggregate:
1377                 return stack, expression, dictionary
1378         head, tail = aggregate
1379         stack = quote, (head, stack)
1380         if tail:
1381                 expression = tail, (quote, (S_step, expression))
1382         expression = S_i, expression
1383         return stack, expression, dictionary
1384
1385
1386 @inscribe
1387 @combinator_effect(_COMB_NUMS(), i1, s6)
1388 @FunctionWrapper
1389 def times(stack, expression, dictionary):
1390         '''
1391         times == [-- dip] cons [swap] infra [0 >] swap while pop
1392         ::
1393
1394                          ... n [Q] . times
1395                 ---------------------  w/ n <= 0
1396                                                  ... .
1397
1398
1399                          ... 1 [Q] . times
1400                 ---------------------------------
1401                                                  ... . Q
1402
1403
1404                          ... n [Q] . times
1405                 ---------------------------------  w/ n > 1
1406                                                  ... . Q (n - 1) [Q] times
1407
1408         '''
1409         # times == [-- dip] cons [swap] infra [0 >] swap while pop
1410         (quote, (n, stack)) = stack
1411         if n <= 0:
1412                 return stack, expression, dictionary
1413         n -= 1
1414         if n:
1415                 expression = n, (quote, (S_times, expression))
1416         expression = concat(quote, expression)
1417         return stack, expression, dictionary
1418
1419
1420 # The current definition above works like this:
1421
1422 #             [P] [Q] while
1423 # --------------------------------------
1424 #    [P] nullary [Q [P] nullary] loop
1425
1426 #   while == [pop i not] [popop] [dudipd] primrec
1427
1428 #def while_(S, expression, dictionary):
1429 #  '''[if] [body] while'''
1430 #  (body, (if_, stack)) = S
1431 #  while joy(stack, if_, dictionary)[0][0]:
1432 #    stack = joy(stack, body, dictionary)[0]
1433 #  return stack, expression, dictionary
1434
1435
1436 def loop_true(stack, expression, dictionary):
1437                 quote, (flag, stack) = stack  # pylint: disable=unused-variable
1438                 return stack, concat(quote, (S_pop, expression)), dictionary
1439
1440 def loop_two_true(stack, expression, dictionary):
1441                 quote, (flag, stack) = stack  # pylint: disable=unused-variable
1442                 return stack, concat(quote, (S_pop, concat(quote, (S_pop, expression)))), dictionary
1443
1444 def loop_false(stack, expression, dictionary):
1445                 quote, (flag, stack) = stack  # pylint: disable=unused-variable
1446                 return stack, expression, dictionary
1447
1448
1449 @inscribe
1450 @poly_combinator_effect(_COMB_NUMS(), [loop_two_true, loop_true, loop_false], b1, s6)
1451 @FunctionWrapper
1452 def loop(stack, expression, dictionary):
1453         '''
1454         Basic loop combinator.
1455         ::
1456
1457                          ... True [Q] loop
1458                 -----------------------
1459                                         ... Q [Q] loop
1460
1461                          ... False [Q] loop
1462                 ------------------------
1463                                                         ...
1464
1465         '''
1466         quote, (flag, stack) = stack
1467         if flag:
1468                 expression = concat(quote, (quote, (S_loop, expression)))
1469         return stack, expression, dictionary
1470
1471
1472 @inscribe
1473 @combinator_effect(_COMB_NUMS(), a1, a2, s6, s7, s8)
1474 @FunctionWrapper
1475 def cmp_(stack, expression, dictionary):
1476         '''
1477         cmp takes two values and three quoted programs on the stack and runs
1478         one of the three depending on the results of comparing the two values:
1479         ::
1480
1481                                          a b [G] [E] [L] cmp
1482                                 ------------------------- a > b
1483                                                                 G
1484
1485                                          a b [G] [E] [L] cmp
1486                                 ------------------------- a = b
1487                                                                                 E
1488
1489                                          a b [G] [E] [L] cmp
1490                                 ------------------------- a < b
1491                                                                                                 L
1492         '''
1493         L, (E, (G, (b, (a, stack)))) = stack
1494         expression = concat(G if a > b else L if a < b else E, expression)
1495         return stack, expression, dictionary
1496
1497
1498 #  FunctionWrapper(cleave),
1499 #  FunctionWrapper(while_),
1500
1501
1502 for F in (
1503
1504         #divmod_ = pm = __(n2, n1), __(n4, n3)
1505
1506         sec_binary_cmp(BinaryBuiltinWrapper(operator.eq)),
1507         sec_binary_cmp(BinaryBuiltinWrapper(operator.ge)),
1508         sec_binary_cmp(BinaryBuiltinWrapper(operator.gt)),
1509         sec_binary_cmp(BinaryBuiltinWrapper(operator.le)),
1510         sec_binary_cmp(BinaryBuiltinWrapper(operator.lt)),
1511         sec_binary_cmp(BinaryBuiltinWrapper(operator.ne)),
1512
1513         sec_binary_ints(BinaryBuiltinWrapper(operator.xor)),
1514         sec_binary_ints(BinaryBuiltinWrapper(operator.lshift)),
1515         sec_binary_ints(BinaryBuiltinWrapper(operator.rshift)),
1516
1517         sec_binary_logic(BinaryBuiltinWrapper(operator.and_)),
1518         sec_binary_logic(BinaryBuiltinWrapper(operator.or_)),
1519
1520         sec_binary_math(BinaryBuiltinWrapper(operator.add)),
1521         sec_binary_math(BinaryBuiltinWrapper(operator.floordiv)),
1522         sec_binary_math(BinaryBuiltinWrapper(operator.mod)),
1523         sec_binary_math(BinaryBuiltinWrapper(operator.mul)),
1524         sec_binary_math(BinaryBuiltinWrapper(operator.pow)),
1525         sec_binary_math(BinaryBuiltinWrapper(operator.sub)),
1526         sec_binary_math(BinaryBuiltinWrapper(operator.truediv)),
1527
1528         sec_unary_logic(UnaryBuiltinWrapper(bool)),
1529         sec_unary_logic(UnaryBuiltinWrapper(operator.not_)),
1530
1531         sec_unary_math(UnaryBuiltinWrapper(abs)),
1532         sec_unary_math(UnaryBuiltinWrapper(operator.neg)),
1533         sec_unary_math(UnaryBuiltinWrapper(sqrt)),
1534
1535         stack_effect(n1)(i1)(UnaryBuiltinWrapper(floor)),
1536         ):
1537         inscribe(F)
1538 del F  # Otherwise Sphinx autodoc will pick it up.
1539
1540
1541 YIN_STACK_EFFECTS = yin_functions()
1542 add_aliases(YIN_STACK_EFFECTS, ALIASES)
1543
1544 # Load the auto-generated primitives into the dictionary.
1545 _functions.update(YIN_STACK_EFFECTS)
1546 # exec '''
1547
1548 # eh = compose(dup, bool)
1549 # sqr = compose(dup, mul)
1550 # of = compose(swap, at)
1551
1552 # ''' in dict(compose=compose), _functions
1553 for name in sorted(_functions):
1554         sec = _functions[name]
1555         F = FUNCTIONS[name] = SymbolJoyType(name, [sec], _SYM_NUMS())
1556         if name in YIN_STACK_EFFECTS:
1557                 _log.info('Setting stack effect for Yin function %s := %s', F.name, doc_from_stack_effect(*sec))
1558
1559 for name, primitive in getmembers(genlib, isfunction):
1560         inscribe(SimpleFunctionWrapper(primitive))
1561
1562
1563 add_aliases(_dictionary, ALIASES)
1564 add_aliases(_functions, ALIASES)
1565 add_aliases(FUNCTIONS, ALIASES)
1566
1567
1568 DefinitionWrapper.add_definitions(definitions, _dictionary)
1569
1570
1571 EXPECTATIONS = dict(
1572         ifte=(s7, (s6, (s5, s4))),
1573         nullary=(s7, s6),
1574         run=(s7, s6),
1575 )
1576 EXPECTATIONS['while'] = (s7, (s6, s5))
1577
1578
1579 for name in '''
1580         dinfrirst
1581         nullary
1582         ifte
1583         run
1584         dupdipd codireco
1585         while
1586         '''.split():
1587         C = _dictionary[name]
1588         expect = EXPECTATIONS.get(name)
1589         if expect:
1590                 sec = doc_from_stack_effect(expect)
1591                 _log.info('Setting stack EXPECT for combinator %s := %s', C.name, sec)
1592         else:
1593                 _log.info('combinator %s', C.name)
1594         FUNCTIONS[name] = CombinatorJoyType(name, [C], _COMB_NUMS(), expect)
1595
1596
1597 for name in ('''
1598         of quoted enstacken ?
1599         unary binary ternary
1600         sqr unquoted
1601         '''.split()):
1602         of_ = _dictionary[name]
1603         secs = infer_expression(of_.body)
1604         assert len(secs) == 1, repr(secs)
1605         _log.info(
1606                 'Setting stack effect for definition %s := %s',
1607                 name,
1608                 doc_from_stack_effect(*secs[0]),
1609                 )
1610         FUNCTIONS[name] = SymbolJoyType(name, infer_expression(of_.body), _SYM_NUMS())
1611
1612
1613 #sec_Ns_math(_dictionary['product'])
1614
1615 ##  product == 1 swap [*] step
1616 ##  flatten == [] swap [concat] step
1617 ##  pam == [i] map
1618 ##  size == 0 swap [pop ++] step
1619 ##  fork == [i] app2
1620 ##  cleave == fork [popd] dip
1621 ##  average == [sum 1.0 *] [size] cleave /
1622 ##  gcd == 1 [tuck modulus dup 0 >] loop pop
1623 ##  least_fraction == dup [gcd] infra [div] concat map
1624 ##  *fraction == [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons
1625 ##  *fraction0 == concat [[swap] dip * [*] dip] infra
1626 ##  down_to_zero == [0 >] [dup --] while
1627 ##  range_to_zero == unit [down_to_zero] infra
1628 ##  anamorphism == [pop []] swap [dip swons] genrec
1629 ##  range == [0 <=] [1 - dup] anamorphism
1630 ##  while == swap [nullary] cons dup dipd concat loop
1631 ##  dupdipd == dup dipd
1632 ##  primrec == [i] genrec
1633 ##  step_zero == 0 roll> step
1634 ##  codireco == cons dip rest cons
1635 ##  make_generator == [codireco] ccons
1636 ##  ifte == [nullary not] dipd branch