1 # -*- coding: utf-8 -*-
3 # Copyright © 2014, 2015, 2017, 2018 Simon Forman
5 # This file is part of Thun
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.
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.
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/>.
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()
26 from inspect import getdoc
27 from functools import wraps
28 from itertools import count
29 from inspect import getmembers, isfunction
32 from .parser import text_to_expression, Symbol
33 from .utils.stack import expression_to_string, list_to_stack, iter_stack, pick, concat
34 from .utils.brutal_hackery import rename_code_object
36 from .utils import generated_library as genlib
37 from .utils.types import (
60 _SYM_NUMS = count().next
61 _COMB_NUMS = count().next
65 A = a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = map(AnyJoyType, _R)
66 B = b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = map(BooleanJoyType, _R)
67 N = n0, n1, n2, n3, n4, n5, n6, n7, n8, n9 = map(NumberJoyType, _R)
68 S = s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 = map(StackJoyType, _R)
69 F = f0, f1, f2, f3, f4, f5, f6, f7, f8, f9 = map(FloatJoyType, _R)
70 I = i0, i1, i2, i3, i4, i5, i6, i7, i8, i9 = map(IntJoyType, _R)
71 T = t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 = map(TextJoyType, _R)
75 As = map(AnyStarJoyType, _R)
76 Ns = map(NumberStarJoyType, _R)
77 Ss = map(StackStarJoyType, _R)
80 sec0 = stack_effect(t1)()
81 sec1 = stack_effect(s0, i1)(s1)
82 sec2 = stack_effect(s0, i1)(a1)
83 sec_binary_cmp = stack_effect(n1, n2)(b1)
84 sec_binary_ints = stack_effect(i1, i2)(i3)
85 sec_binary_logic = stack_effect(b1, b2)(b3)
86 sec_binary_math = stack_effect(n1, n2)(n3)
87 sec_unary_logic = stack_effect(a1)(b1)
88 sec_unary_math = stack_effect(n1)(n2)
89 sec_Ns_math = stack_effect((Ns[1], s1),)(n0)
94 def inscribe(function):
95 '''A decorator to inscribe functions into the default dictionary.'''
96 _dictionary[function.name] = function
101 '''Return a dictionary of Joy functions for use with joy().'''
102 return _dictionary.copy()
108 ('bool', ['truthy']),
110 ('floordiv', ['/floor', '//']),
111 ('floor', ['round']),
113 ('mod', ['%', 'rem', 'remainder', 'modulus']),
116 ('getitem', ['pick', 'at']),
121 ('ne', ['<>', '!=']),
127 ('rolldown', ['roll<']),
128 ('rollup', ['roll>']),
134 def add_aliases(D, A):
136 Given a dict and a iterable of (name, [alias, ...]) pairs, create
137 additional entries in the dict mapping each alias to the named function
138 if it's in the dict. Aliases for functions not in the dict are ignored.
140 for name, aliases in A:
145 for alias in aliases:
151 Return a dict of named stack effects.
153 "Yin" functions are those that only rearrange items in stacks and
154 can be defined completely by their stack effects. This means they
155 can be auto-compiled.
157 cons = ef(a1, s0)((a1, s0))
158 ccons = compose(cons, cons)
160 dupd = ef(a2, a1)(a2, a2, a1)
161 dupdd = ef(a3, a2, a1)(a3, a3, a2, a1)
162 first = ef((a1, s1),)(a1,)
163 over = ef(a2, a1)(a2, a1, a2)
165 popd = ef(a2, a1,)(a1)
166 popdd = ef(a3, a2, a1,)(a2, a1,)
167 popop = ef(a2, a1,)()
168 popopd = ef(a3, a2, a1,)(a1)
169 popopdd = ef(a4, a3, a2, a1,)(a2, a1)
170 rest = ef((a1, s0),)(s0,)
171 rolldown = ef(a1, a2, a3)(a2, a3, a1)
172 rollup = ef(a1, a2, a3)(a3, a1, a2)
173 rrest = compose(rest, rest)
174 second = compose(rest, first)
176 swaack = (s1, s0), (s0, s1)
177 swap = ef(a1, a2)(a2, a1)
178 swons = compose(swap, cons)
179 third = compose(rest, second)
180 tuck = ef(a2, a1)(a1, a2, a1)
181 uncons = ef((a1, s0),)(a1, s0)
182 unswons = compose(uncons, swap)
183 stuncons = compose(stack, uncons)
184 stununcons = compose(stack, uncons, uncons)
185 unit = ef(a1)((a1, ()))
187 first_two = compose(uncons, uncons, pop)
188 fourth = compose(rest, third)
190 _Tree_add_Ee = compose(pop, swap, rolldown, rrest, ccons)
191 _Tree_get_E = compose(popop, second)
192 _Tree_delete_clear_stuff = compose(rollup, popop, rest)
193 _Tree_delete_R0 = compose(over, first, swap, dup)
196 name.rstrip('_'): stack_effect
197 for name, stack_effect in locals().iteritems()
203 product == 1 swap [*] step
204 flatten == [] swap [concat] step
207 enstacken == stack [clear] dip
208 disenstacken == ? [uncons ?] loop pop
210 dinfrirst == dip infra first
211 nullary == [stack] dinfrirst
212 unary == nullary popd
213 binary == nullary [popop] dip
214 ternary == unary [popop] dip
218 size == 0 swap [pop ++] step
220 cleave == fork [popd] dip
221 average == [sum 1.0 *] [size] cleave /
222 gcd == 1 [tuck modulus dup 0 >] loop pop
223 least_fraction == dup [gcd] infra [div] concat map
224 *fraction == [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons
225 *fraction0 == concat [[swap] dip * [*] dip] infra
226 down_to_zero == [0 >] [dup --] while
227 range_to_zero == unit [down_to_zero] infra
228 anamorphism == [pop []] swap [dip swons] genrec
229 range == [0 <=] [1 - dup] anamorphism
230 while == swap [nullary] cons dup dipd concat loop
232 primrec == [i] genrec
233 step_zero == 0 roll> step
234 codireco == cons dip rest cons
235 make_generator == [codireco] ccons
236 ifte == [nullary not] dipd branch
239 # ifte == [nullary] dipd swap branch
240 # genrec == [[genrec] cons cons cons cons] nullary swons concat ifte
242 # Another definition for while. FWIW
243 # while == over [[i] dip nullary] ccons [nullary] dip loop
247 ##second == rest first
248 ##third == rest rest first
250 ##swoncat == swap concat
253 ##z-down == [] swap uncons swap
254 ##z-up == swons swap shunt
255 ##z-right == [swons] cons dip uncons swap
256 ##z-left == swons [uncons swap] dip swap
259 ##divisor == popop 2 *
261 ##radical == swap dup * rollup * 4 * - sqrt
264 ##q0 == [[divisor] [minusb] [radical]] pam
265 ##q1 == [[root1] [root2]] pam
266 ##quadratic == [q0] ternary i [q1] ternary
270 ##PE1.1 == + dup [+] dip
271 ##PE1.2 == dup [3 & PE1.1] dip 2 >>
272 ##PE1.3 == 14811 swap [PE1.2] times pop
273 ##PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop
275 #PE1.2 == [PE1.1] step
276 #PE1 == 0 0 66 [[3 2 1 3 1 2 3] PE1.2] times [3 2 1 3] PE1.2 pop
280 def FunctionWrapper(f):
281 '''Set name attribute.'''
283 raise ValueError('Function %s must have doc string.' % f.__name__)
284 f.name = f.__name__.rstrip('_') # Don't shadow builtins.
288 def SimpleFunctionWrapper(f):
290 Wrap functions that take and return just a stack.
294 @rename_code_object(f.__name__)
295 def inner(stack, expression, dictionary):
296 return f(stack), expression, dictionary
300 def BinaryBuiltinWrapper(f):
302 Wrap functions that take two arguments and return a single result.
306 @rename_code_object(f.__name__)
307 def inner(stack, expression, dictionary):
308 (a, (b, stack)) = stack
310 return (result, stack), expression, dictionary
314 def UnaryBuiltinWrapper(f):
316 Wrap functions that take one argument and return a single result.
320 @rename_code_object(f.__name__)
321 def inner(stack, expression, dictionary):
324 return (result, stack), expression, dictionary
328 class DefinitionWrapper(object):
330 Provide implementation of defined functions, and some helper methods.
333 def __init__(self, name, body_text, doc=None):
334 self.name = self.__name__ = name
335 self.body = text_to_expression(body_text)
336 self._body = tuple(iter_stack(self.body))
337 self.__doc__ = doc or body_text
338 self._compiled = None
340 def __call__(self, stack, expression, dictionary):
342 return self._compiled(stack, expression, dictionary)
343 expression = list_to_stack(self._body, expression)
344 return stack, expression, dictionary
347 def parse_definition(class_, defi):
349 Given some text describing a Joy function definition parse it and
350 return a DefinitionWrapper.
352 name, proper, body_text = (n.strip() for n in defi.partition('=='))
354 raise ValueError('Definition %r failed' % (defi,))
355 return class_(name, body_text)
358 def add_definitions(class_, defs, dictionary):
360 Scan multi-line string defs for definitions and add them to the
363 for definition in _text_to_defs(defs):
364 class_.add_def(definition, dictionary)
367 def add_def(class_, definition, dictionary):
369 Add the definition to the dictionary.
371 F = class_.parse_definition(definition)
374 secs = infer(*F._body)
377 print F.name, '==', expression_to_string(F.body), ' --failed to infer stack effect.'
379 FUNCTIONS[F.name] = SymbolJoyType(F.name, secs, _SYM_NUMS())
380 dictionary[F.name] = F
383 def _text_to_defs(text):
384 return (line.strip() for line in text.splitlines() if '==' in line)
395 def inscribe_(stack, expression, dictionary):
397 Create a new Joy function definition in the Joy dictionary. A
398 definition is given as a string with a name followed by a double
399 equal sign then one or more Joy functions, the body. for example:
403 If you want the definition to persist over restarts, enter it into
404 the definitions.txt resource.
406 definition, stack = stack
407 DefinitionWrapper.add_def(definition, dictionary)
408 return stack, expression, dictionary
412 @SimpleFunctionWrapper
414 '''Parse the string on the stack to a Joy expression.'''
416 expression = text_to_expression(text)
417 return expression, stack
422 @SimpleFunctionWrapper
427 getitem == drop first
429 Expects an integer and a quote on the stack and returns the item at the
430 nth position in the quote counting from 0.
434 -------------------------
438 n, (Q, stack) = stack
439 return pick(Q, n), stack
444 @SimpleFunctionWrapper
451 Expects an integer and a quote on the stack and returns the quote with
452 n items removed off the top.
456 ----------------------
460 n, (Q, stack) = stack
472 @SimpleFunctionWrapper
475 Expects an integer and a quote on the stack and returns the quote with
476 just the top n items in reverse order (because that's easier and you can
477 use reverse if needed.)
481 ----------------------
485 n, (Q, stack) = stack
498 @SimpleFunctionWrapper
501 Use a Boolean value to select one of two items.
505 ----------------------
510 ---------------------
513 Currently Python semantics are used to evaluate the "truthiness" of the
514 Boolean value (so empty string, zero, etc. are counted as false, etc.)
516 (if_, (then, (else_, stack))) = stack
517 return then if if_ else else_, stack
521 @SimpleFunctionWrapper
524 Use a Boolean value to select one of two items from a sequence.
528 ------------------------
533 -----------------------
536 The sequence can contain more than two items but not fewer.
537 Currently Python semantics are used to evaluate the "truthiness" of the
538 Boolean value (so empty string, zero, etc. are counted as false, etc.)
540 (flag, (choices, stack)) = stack
541 (else_, (then, _)) = choices
542 return then if flag else else_, stack
547 @SimpleFunctionWrapper
549 '''Given a list find the maximum.'''
551 return max(iter_stack(tos)), stack
556 @SimpleFunctionWrapper
558 '''Given a list find the minimum.'''
560 return min(iter_stack(tos)), stack
565 @SimpleFunctionWrapper
567 '''Given a quoted sequence of numbers return the sum.
569 sum == 0 swap [+] step
572 return sum(iter_stack(tos)), stack
576 @SimpleFunctionWrapper
579 Expects an item on the stack and a quote under it and removes that item
580 from the the quote. The item is only removed once.
584 ------------------------
588 (tos, (second, stack)) = S
589 l = list(iter_stack(second))
591 return list_to_stack(l), stack
595 @SimpleFunctionWrapper
597 '''Given a list remove duplicate items.'''
599 I = list(iter_stack(tos))
600 list_to_stack(sorted(set(I), key=I.index))
601 return list_to_stack(sorted(set(I), key=I.index)), stack
605 @SimpleFunctionWrapper
607 '''Given a list return it sorted.'''
609 return list_to_stack(sorted(iter_stack(tos))), stack
612 _functions['clear'] = s0, s1
614 @SimpleFunctionWrapper
616 '''Clear everything from the stack.
619 clear == stack [pop stack] loop
629 @SimpleFunctionWrapper
632 The unstack operator expects a list on top of the stack and makes that
633 the stack discarding the rest of the stack.
639 @SimpleFunctionWrapper
641 '''Reverse the list on the top of the stack.
644 reverse == [] swap shunt
648 for term in iter_stack(tos):
654 @SimpleFunctionWrapper
656 '''Concatinate the two lists on the top of the stack.
659 [a b c] [d e f] concat
660 ----------------------------
664 (tos, (second, stack)) = S
665 return concat(second, tos), stack
669 @SimpleFunctionWrapper
671 '''Like concat but reverses the top list into the second.
674 shunt == [swons] step == reverse swap concat
676 [a b c] [d e f] shunt
677 ---------------------------
681 (tos, (second, stack)) = stack
684 second = term, second
689 @SimpleFunctionWrapper
692 Replace the two lists on the top of the stack with a list of the pairs
693 from each list. The smallest list sets the length of the result list.
695 (tos, (second, stack)) = S
698 for a, b in zip(iter_stack(tos), iter_stack(second))
700 return list_to_stack(accumulator), stack
704 @SimpleFunctionWrapper
708 return tos + 1, stack
712 @SimpleFunctionWrapper
716 return tos - 1, stack
720 @SimpleFunctionWrapper
731 a, (b, stack) = stack
737 return int(math.floor(n))
739 floor.__doc__ = math.floor.__doc__
743 @SimpleFunctionWrapper
746 divmod(x, y) -> (quotient, remainder)
748 Return the tuple (x//y, x%y). Invariant: div*y + mod == x.
757 Return the square root of the number a.
758 Negative numbers return complex roots.
763 assert a < 0, repr(a)
764 r = math.sqrt(-a) * 1j
770 # if isinstance(text, str):
771 # return run(text, stack)
776 @SimpleFunctionWrapper
778 '''The identity function.'''
783 @SimpleFunctionWrapper
785 '''True if the form on TOS is void otherwise False.'''
787 return _void(form), stack
791 return any(not _void(i) for i in iter_stack(form))
802 def words(stack, expression, dictionary):
803 '''Print all the words in alphabetical order.'''
804 print(' '.join(sorted(dictionary)))
805 return stack, expression, dictionary
810 def sharing(stack, expression, dictionary):
811 '''Print redistribution information.'''
812 print("You may convey verbatim copies of the Program's source code as"
813 ' you receive it, in any medium, provided that you conspicuously'
814 ' and appropriately publish on each copy an appropriate copyright'
815 ' notice; keep intact all notices stating that this License and'
816 ' any non-permissive terms added in accord with section 7 apply'
817 ' to the code; keep intact all notices of the absence of any'
818 ' warranty; and give all recipients a copy of this License along'
820 ' You should have received a copy of the GNU General Public License'
821 ' along with Thun. If not see <http://www.gnu.org/licenses/>.')
822 return stack, expression, dictionary
827 def warranty(stack, expression, dictionary):
828 '''Print warranty information.'''
829 print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
830 ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
831 ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
832 ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
833 ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
834 ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
835 ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
836 ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
837 ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')
838 return stack, expression, dictionary
841 # def simple_manual(stack):
843 # Print words and help for each word.
845 # for name, f in sorted(FUNCTIONS.items()):
847 # boxline = '+%s+' % ('-' * (len(name) + 2))
850 # '| %s |' % (name,),
852 # d if d else ' ...',
862 def help_(S, expression, dictionary):
863 '''Accepts a quoted symbol on the top of the stack and prints its docs.'''
864 ((symbol, _), stack) = S
865 word = dictionary[symbol]
867 return stack, expression, dictionary
875 # Several combinators depend on other words in their definitions,
876 # we use symbols to prevent hard-coding these, so in theory, you
877 # could change the word in the dictionary to use different semantics.
878 S_choice = Symbol('choice')
879 S_first = Symbol('first')
880 S_getitem = Symbol('getitem')
881 S_genrec = Symbol('genrec')
882 S_loop = Symbol('loop')
884 S_ifte = Symbol('ifte')
885 S_infra = Symbol('infra')
886 S_step = Symbol('step')
887 S_times = Symbol('times')
888 S_swaack = Symbol('swaack')
889 S_truthy = Symbol('truthy')
893 @combinator_effect(_COMB_NUMS(), s1)
895 def i(stack, expression, dictionary):
897 The i combinator expects a quoted program on the stack and unpacks it
898 onto the pending expression for evaluation.
907 return stack, concat(quote, expression), dictionary
911 @combinator_effect(_COMB_NUMS(), s1)
913 def x(stack, expression, dictionary):
919 ... [Q] x = ... [Q] dup i
920 ... [Q] x = ... [Q] [Q] i
921 ... [Q] x = ... [Q] Q
925 return stack, concat(quote, expression), dictionary
929 #@combinator_effect(_COMB_NUMS(), s7, s6)
931 def b(stack, expression, dictionary):
937 ... [P] [Q] b == ... [P] i [Q] i
938 ... [P] [Q] b == ... P Q
941 q, (p, (stack)) = stack
942 return stack, concat(p, concat(q, expression)), dictionary
947 def dupdip(stack, expression, dictionary):
951 [F] dupdip == dup [F] dip
961 return stack, concat(F, (a, expression)), dictionary
965 #@combinator_effect(_COMB_NUMS(), s7, s6)
967 def infra(stack, expression, dictionary):
969 Accept a quoted program and a list on the stack and run the program
970 with the list as its stack.
973 ... [a b c] [Q] . infra
974 -----------------------------
975 c b a . Q [...] swaack
978 (quote, (aggregate, stack)) = stack
979 return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary
984 def genrec(stack, expression, dictionary):
986 General Recursion Combinator.
989 [if] [then] [rec1] [rec2] genrec
990 ---------------------------------------------------------------------
991 [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
993 From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
994 "The genrec combinator takes four program parameters in addition to
995 whatever data parameters it needs. Fourth from the top is an if-part,
996 followed by a then-part. If the if-part yields true, then the then-part
997 is executed and the combinator terminates. The other two parameters are
998 the rec1-part and the rec2-part. If the if-part yields false, the
999 rec1-part is executed. Following that the four program parameters and
1000 the combinator are again pushed onto the stack bundled up in a quoted
1001 form. Then the rec2-part is executed, where it will find the bundled
1002 form. Typically it will then execute the bundled form, either with i or
1003 with app2, or some other combinator."
1005 The way to design one of these is to fix your base case [then] and the
1006 test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
1007 a quotation of the whole function.
1009 For example, given a (general recursive) function 'F':
1012 F == [I] [T] [R1] [R2] genrec
1014 If the [I] if-part fails you must derive R1 and R2 from:
1019 Just set the stack arguments in front, and figure out what R1 and R2
1020 have to do to apply the quoted [F] in the proper way. In effect, the
1021 genrec combinator turns into an ifte combinator with a quoted copy of
1022 the original definition in the else-part:
1025 F == [I] [T] [R1] [R2] genrec
1026 == [I] [T] [R1 [F] R2] ifte
1028 Primitive recursive functions are those where R2 == i.
1031 P == [I] [T] [R] primrec
1032 == [I] [T] [R [P] i] ifte
1033 == [I] [T] [R P] ifte
1036 (rec2, (rec1, stack)) = stack
1037 (then, (if_, _)) = stack
1038 F = (if_, (then, (rec1, (rec2, (S_genrec, ())))))
1039 else_ = concat(rec1, (F, rec2))
1040 return (else_, stack), (S_ifte, expression), dictionary
1045 def map_(S, expression, dictionary):
1047 Run the quoted program on TOS on the items in the list under it, push a
1048 new list with the results (in place of the program and original list.
1050 # (quote, (aggregate, stack)) = S
1051 # results = list_to_stack([
1052 # joy((term, stack), quote, dictionary)[0][0]
1053 # for term in iter_stack(aggregate)
1055 # return (results, stack), expression, dictionary
1056 (quote, (aggregate, stack)) = S
1058 return (aggregate, stack), expression, dictionary
1060 for term in iter_stack(aggregate):
1062 batch = (s, (quote, (S_infra, (S_first, batch))))
1063 stack = (batch, ((), stack))
1064 return stack, (S_infra, expression), dictionary
1067 #def cleave(S, expression, dictionary):
1069 # The cleave combinator expects two quotations, and below that an item X.
1070 # It first executes [P], with X on top, and saves the top result element.
1071 # Then it executes [Q], again with X, and saves the top result.
1072 # Finally it restores the stack to what it was below X and pushes the two
1073 # results P(X) and Q(X).
1075 # (Q, (P, (x, stack))) = S
1076 # p = joy((x, stack), P, dictionary)[0][0]
1077 # q = joy((x, stack), Q, dictionary)[0][0]
1078 # return (q, (p, stack)), expression, dictionary
1083 def branch(stack, expression, dictionary):
1085 Use a Boolean value to select one of two quoted programs to run.
1089 branch == roll< choice i
1093 False [F] [T] branch
1094 --------------------------
1098 -------------------------
1102 (then, (else_, (flag, stack))) = stack
1103 return stack, concat(then if flag else else_, expression), dictionary
1108 ##def ifte(stack, expression, dictionary):
1110 ## If-Then-Else Combinator
1113 ## ... [if] [then] [else] ifte
1114 ## ---------------------------------------------------
1115 ## ... [[else] [then]] [...] [if] infra select i
1120 ## ... [if] [then] [else] ifte
1121 ## -------------------------------------------------------
1122 ## ... [else] [then] [...] [if] infra first choice i
1125 ## Has the effect of grabbing a copy of the stack on which to run the
1126 ## if-part using infra.
1128 ## (else_, (then, (if_, stack))) = stack
1129 ## expression = (S_infra, (S_first, (S_choice, (S_i, expression))))
1130 ## stack = (if_, (stack, (then, (else_, stack))))
1131 ## return stack, expression, dictionary
1136 def cond(stack, expression, dictionary):
1138 This combinator works like a case statement. It expects a single quote
1139 on the stack that must contain zero or more condition quotes and a
1140 default quote. Each condition clause should contain a quoted predicate
1141 followed by the function expression to run if that predicate returns
1142 true. If no predicates return true the default function runs.
1144 It works by rewriting into a chain of nested `ifte` expressions, e.g.::
1146 [[[B0] T0] [[B1] T1] [D]] cond
1147 -----------------------------------------
1148 [B0] [T0] [[B1] [T1] [D] ifte] ifte
1151 conditions, stack = stack
1153 expression = _cond(conditions, expression)
1155 # Attempt to preload the args to first ifte.
1156 (P, (T, (E, expression))) = expression
1158 # If, for any reason, the argument to cond should happen to contain
1159 # only the default clause then this optimization will fail.
1162 stack = (E, (T, (P, stack)))
1163 return stack, expression, dictionary
1166 def _cond(conditions, expression):
1167 (clause, rest) = conditions
1168 if not rest: # clause is [D]
1171 return (P, (T, (_cond(rest, ()), (S_ifte, expression))))
1175 @combinator_effect(_COMB_NUMS(), a1, s1)
1177 def dip(stack, expression, dictionary):
1179 The dip combinator expects a quoted program on the stack and below it
1180 some item, it hoists the item into the expression and runs the program
1181 on the rest of the stack.
1189 (quote, (x, stack)) = stack
1190 expression = (x, expression)
1191 return stack, concat(quote, expression), dictionary
1196 def dipd(S, expression, dictionary):
1198 Like dip but expects two items.
1202 ---------------------
1206 (quote, (x, (y, stack))) = S
1207 expression = (y, (x, expression))
1208 return stack, concat(quote, expression), dictionary
1213 def dipdd(S, expression, dictionary):
1215 Like dip but expects three items.
1219 -----------------------
1223 (quote, (x, (y, (z, stack)))) = S
1224 expression = (z, (y, (x, expression)))
1225 return stack, concat(quote, expression), dictionary
1230 def app1(S, expression, dictionary):
1232 Given a quoted program on TOS and anything as the second stack item run
1233 the program and replace the two args with the first result of the
1238 -----------------------------------
1239 ... [x ...] [Q] . infra first
1241 (quote, (x, stack)) = S
1242 stack = (quote, ((x, stack), stack))
1243 expression = (S_infra, (S_first, expression))
1244 return stack, expression, dictionary
1249 def app2(S, expression, dictionary):
1250 '''Like app1 with two items.
1254 -----------------------------------
1255 ... [y ...] [Q] . infra first
1256 [x ...] [Q] infra first
1259 (quote, (x, (y, stack))) = S
1260 expression = (S_infra, (S_first,
1261 ((x, stack), (quote, (S_infra, (S_first,
1263 stack = (quote, ((y, stack), stack))
1264 return stack, expression, dictionary
1269 def app3(S, expression, dictionary):
1270 '''Like app1 with three items.
1273 ... z y x [Q] . app3
1274 -----------------------------------
1275 ... [z ...] [Q] . infra first
1276 [y ...] [Q] infra first
1277 [x ...] [Q] infra first
1280 (quote, (x, (y, (z, stack)))) = S
1281 expression = (S_infra, (S_first,
1282 ((y, stack), (quote, (S_infra, (S_first,
1283 ((x, stack), (quote, (S_infra, (S_first,
1284 expression))))))))))
1285 stack = (quote, ((z, stack), stack))
1286 return stack, expression, dictionary
1291 def step(S, expression, dictionary):
1293 Run a quoted program on each item in a sequence.
1297 -----------------------
1302 ------------------------
1306 ... [a b c] [Q] . step
1307 ----------------------------------------
1308 ... a . Q [b c] [Q] step
1310 The step combinator executes the quotation on each member of the list
1311 on top of the stack.
1313 (quote, (aggregate, stack)) = S
1315 return stack, expression, dictionary
1316 head, tail = aggregate
1317 stack = quote, (head, stack)
1319 expression = tail, (quote, (S_step, expression))
1320 expression = S_i, expression
1321 return stack, expression, dictionary
1326 def times(stack, expression, dictionary):
1328 times == [-- dip] cons [swap] infra [0 >] swap while pop
1332 --------------------- w/ n <= 0
1337 ---------------------------------
1342 --------------------------------- w/ n > 1
1343 ... . Q (n - 1) [Q] times
1346 # times == [-- dip] cons [swap] infra [0 >] swap while pop
1347 (quote, (n, stack)) = stack
1349 return stack, expression, dictionary
1352 expression = n, (quote, (S_times, expression))
1353 expression = concat(quote, expression)
1354 return stack, expression, dictionary
1357 # The current definition above works like this:
1360 # --------------------------------------
1361 # [P] nullary [Q [P] nullary] loop
1363 # while == [pop i not] [popop] [dudipd] primrec
1365 #def while_(S, expression, dictionary):
1366 # '''[if] [body] while'''
1367 # (body, (if_, stack)) = S
1368 # while joy(stack, if_, dictionary)[0][0]:
1369 # stack = joy(stack, body, dictionary)[0]
1370 # return stack, expression, dictionary
1375 def loop(stack, expression, dictionary):
1377 Basic loop combinator.
1381 -----------------------
1385 ------------------------
1389 quote, (flag, stack) = stack
1391 expression = concat(quote, (quote, (S_loop, expression)))
1392 return stack, expression, dictionary
1397 def cmp_(stack, expression, dictionary):
1399 cmp takes two values and three quoted programs on the stack and runs
1400 one of the three depending on the results of comparing the two values:
1404 ------------------------- a > b
1408 ------------------------- a = b
1412 ------------------------- a < b
1415 L, (E, (G, (b, (a, stack)))) = stack
1416 expression = concat(G if a > b else L if a < b else E, expression)
1417 return stack, expression, dictionary
1420 # FunctionWrapper(cleave),
1421 # FunctionWrapper(while_),
1426 #divmod_ = pm = __(n2, n1), __(n4, n3)
1428 sec_binary_cmp(BinaryBuiltinWrapper(operator.eq)),
1429 sec_binary_cmp(BinaryBuiltinWrapper(operator.ge)),
1430 sec_binary_cmp(BinaryBuiltinWrapper(operator.gt)),
1431 sec_binary_cmp(BinaryBuiltinWrapper(operator.le)),
1432 sec_binary_cmp(BinaryBuiltinWrapper(operator.lt)),
1433 sec_binary_cmp(BinaryBuiltinWrapper(operator.ne)),
1435 sec_binary_ints(BinaryBuiltinWrapper(operator.xor)),
1436 sec_binary_ints(BinaryBuiltinWrapper(operator.lshift)),
1437 sec_binary_ints(BinaryBuiltinWrapper(operator.rshift)),
1439 sec_binary_logic(BinaryBuiltinWrapper(operator.and_)),
1440 sec_binary_logic(BinaryBuiltinWrapper(operator.or_)),
1442 sec_binary_math(BinaryBuiltinWrapper(operator.add)),
1443 sec_binary_math(BinaryBuiltinWrapper(operator.floordiv)),
1444 sec_binary_math(BinaryBuiltinWrapper(operator.mod)),
1445 sec_binary_math(BinaryBuiltinWrapper(operator.mul)),
1446 sec_binary_math(BinaryBuiltinWrapper(operator.pow)),
1447 sec_binary_math(BinaryBuiltinWrapper(operator.sub)),
1448 sec_binary_math(BinaryBuiltinWrapper(operator.truediv)),
1450 sec_unary_logic(UnaryBuiltinWrapper(bool)),
1451 sec_unary_logic(UnaryBuiltinWrapper(operator.not_)),
1453 sec_unary_math(UnaryBuiltinWrapper(abs)),
1454 sec_unary_math(UnaryBuiltinWrapper(operator.neg)),
1455 sec_unary_math(UnaryBuiltinWrapper(sqrt)),
1457 stack_effect(n1)(i1)(UnaryBuiltinWrapper(floor)),
1460 del F # Otherwise Sphinx autodoc will pick it up.
1463 YIN_STACK_EFFECTS = yin_functions()
1465 # Load the auto-generated primitives into the dictionary.
1466 _functions.update(YIN_STACK_EFFECTS)
1469 # eh = compose(dup, bool)
1470 # sqr = compose(dup, mul)
1471 # of = compose(swap, at)
1473 # ''' in dict(compose=compose), _functions
1476 (name, SymbolJoyType(name, [_functions[name]], _SYM_NUMS()))
1477 for name in sorted(_functions)
1479 for name, primitive in getmembers(genlib, isfunction):
1480 inscribe(SimpleFunctionWrapper(primitive))
1483 add_aliases(_dictionary, ALIASES)
1484 add_aliases(_functions, ALIASES)
1485 add_aliases(FUNCTIONS, ALIASES)
1488 DefinitionWrapper.add_definitions(definitions, _dictionary)
1490 #sec_Ns_math(_dictionary['product'])