OSDN Git Service

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