OSDN Git Service

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