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 inspect import getmembers, isfunction
31 from .parser import text_to_expression, Symbol
32 from .utils.stack import list_to_stack, iter_stack, pick, concat
33 from .utils.brutal_hackery import rename_code_object
35 from .utils import generated_library as genlib
41 def inscribe(function):
42 '''A decorator to inscribe functions into the default dictionary.'''
43 _dictionary[function.name] = function
48 '''Return a dictionary of Joy functions for use with joy().'''
49 return _dictionary.copy()
58 ('mod', ['%', 'rem', 'remainder', 'modulus']),
61 ('getitem', ['pick', 'at']),
72 ('rolldown', ['roll<']),
73 ('rollup', ['roll>']),
78 def add_aliases(D, A):
80 Given a dict and a iterable of (name, [alias, ...]) pairs, create
81 additional entries in the dict mapping each alias to the named function
82 if it's in the dict. Aliases for functions not in the dict are ignored.
84 for name, aliases in A:
95 product == 1 swap [*] step
96 flatten == [] swap [concat] step
100 enstacken == stack [clear] dip
101 disenstacken == ? [uncons ?] loop pop
103 dinfrirst == dip infra first
104 nullary == [stack] dinfrirst
105 unary == nullary popd
106 binary == nullary [popop] dip
107 ternary == unary [popop] dip
111 size == 0 swap [pop ++] step
112 cleave == [i] app2 [popd] dip
113 average == [sum 1.0 *] [size] cleave /
114 gcd == 1 [tuck modulus dup 0 >] loop pop
115 least_fraction == dup [gcd] infra [div] concat map
116 *fraction == [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons
117 *fraction0 == concat [[swap] dip * [*] dip] infra
118 down_to_zero == [0 >] [dup --] while
119 range_to_zero == unit [down_to_zero] infra
120 anamorphism == [pop []] swap [dip swons] genrec
121 range == [0 <=] [1 - dup] anamorphism
122 while == swap [nullary] cons dup dipd concat loop
124 primrec == [i] genrec
125 step_zero == 0 roll> step
126 codireco == cons dip rest cons
127 make_generator == [codireco] ccons
130 ##second == rest first
131 ##third == rest rest first
133 ##swoncat == swap concat
136 ##z-down == [] swap uncons swap
137 ##z-up == swons swap shunt
138 ##z-right == [swons] cons dip uncons swap
139 ##z-left == swons [uncons swap] dip swap
142 ##divisor == popop 2 *
144 ##radical == swap dup * rollup * 4 * - sqrt
147 ##q0 == [[divisor] [minusb] [radical]] pam
148 ##q1 == [[root1] [root2]] pam
149 ##quadratic == [q0] ternary i [q1] ternary
153 ##PE1.1 == + dup [+] dip
154 ##PE1.2 == dup [3 & PE1.1] dip 2 >>
155 ##PE1.3 == 14811 swap [PE1.2] times pop
156 ##PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop
158 #PE1.2 == [PE1.1] step
159 #PE1 == 0 0 66 [[3 2 1 3 1 2 3] PE1.2] times [3 2 1 3] PE1.2 pop
163 def FunctionWrapper(f):
164 '''Set name attribute.'''
166 raise ValueError('Function %s must have doc string.' % f.__name__)
167 f.name = f.__name__.rstrip('_') # Don't shadow builtins.
171 def SimpleFunctionWrapper(f):
173 Wrap functions that take and return just a stack.
177 @rename_code_object(f.__name__)
178 def inner(stack, expression, dictionary):
179 return f(stack), expression, dictionary
183 def BinaryBuiltinWrapper(f):
185 Wrap functions that take two arguments and return a single result.
189 @rename_code_object(f.__name__)
190 def inner(stack, expression, dictionary):
191 (a, (b, stack)) = stack
193 return (result, stack), expression, dictionary
197 def UnaryBuiltinWrapper(f):
199 Wrap functions that take one argument and return a single result.
203 @rename_code_object(f.__name__)
204 def inner(stack, expression, dictionary):
207 return (result, stack), expression, dictionary
211 class DefinitionWrapper(object):
213 Provide implementation of defined functions, and some helper methods.
216 def __init__(self, name, body_text, doc=None):
217 self.name = self.__name__ = name
218 self.body = text_to_expression(body_text)
219 self._body = tuple(iter_stack(self.body))
220 self.__doc__ = doc or body_text
222 def __call__(self, stack, expression, dictionary):
223 expression = list_to_stack(self._body, expression)
224 return stack, expression, dictionary
227 def parse_definition(class_, defi):
229 Given some text describing a Joy function definition parse it and
230 return a DefinitionWrapper.
232 name, proper, body_text = (n.strip() for n in defi.partition('=='))
234 raise ValueError('Definition %r failed' % (defi,))
235 return class_(name, body_text)
238 def add_definitions(class_, defs, dictionary):
240 Scan multi-line string defs for definitions and add them to the
243 for definition in _text_to_defs(defs):
244 class_.add_def(definition, dictionary)
247 def add_def(class_, definition, dictionary):
249 Add the definition to the dictionary.
251 F = class_.parse_definition(definition)
252 dictionary[F.name] = F
255 def _text_to_defs(text):
256 return (line.strip() for line in text.splitlines() if '==' in line)
264 # Load the auto-generated primitives into the dictionary.
265 for name, primitive in getmembers(genlib, isfunction):
266 inscribe(SimpleFunctionWrapper(primitive))
270 @SimpleFunctionWrapper
272 '''Parse the string on the stack to a Joy expression.'''
274 expression = text_to_expression(text)
275 return expression, stack
279 ##@SimpleFunctionWrapper
284 ## first == uncons pop
287 ## ((head, tail), stack) = stack
288 ## return head, stack
292 ##@SimpleFunctionWrapper
297 ## rest == uncons popd
300 ## ((head, tail), stack) = stack
301 ## return tail, stack
305 @SimpleFunctionWrapper
310 getitem == drop first
312 Expects an integer and a quote on the stack and returns the item at the
313 nth position in the quote counting from 0.
317 -------------------------
321 n, (Q, stack) = stack
322 return pick(Q, n), stack
326 @SimpleFunctionWrapper
333 Expects an integer and a quote on the stack and returns the quote with
334 n items removed off the top.
338 ----------------------
342 n, (Q, stack) = stack
353 @SimpleFunctionWrapper
356 Expects an integer and a quote on the stack and returns the quote with
357 just the top n items in reverse order (because that's easier and you can
358 use reverse if needed.)
362 ----------------------
366 n, (Q, stack) = stack
379 @SimpleFunctionWrapper
382 Use a Boolean value to select one of two items.
386 ----------------------
391 ---------------------
394 Currently Python semantics are used to evaluate the "truthiness" of the
395 Boolean value (so empty string, zero, etc. are counted as false, etc.)
397 (if_, (then, (else_, stack))) = stack
398 return then if if_ else else_, stack
402 @SimpleFunctionWrapper
405 Use a Boolean value to select one of two items from a sequence.
409 ------------------------
414 -----------------------
417 The sequence can contain more than two items but not fewer.
418 Currently Python semantics are used to evaluate the "truthiness" of the
419 Boolean value (so empty string, zero, etc. are counted as false, etc.)
421 (flag, (choices, stack)) = stack
422 (else_, (then, _)) = choices
423 return then if flag else else_, stack
427 @SimpleFunctionWrapper
429 '''Given a list find the maximum.'''
431 return max(iter_stack(tos)), stack
435 @SimpleFunctionWrapper
437 '''Given a list find the minimum.'''
439 return min(iter_stack(tos)), stack
443 @SimpleFunctionWrapper
445 '''Given a quoted sequence of numbers return the sum.
447 sum == 0 swap [+] step
450 return sum(iter_stack(tos)), stack
454 @SimpleFunctionWrapper
457 Expects an item on the stack and a quote under it and removes that item
458 from the the quote. The item is only removed once.
462 ------------------------
466 (tos, (second, stack)) = S
467 l = list(iter_stack(second))
469 return list_to_stack(l), stack
473 @SimpleFunctionWrapper
475 '''Given a list remove duplicate items.'''
477 I = list(iter_stack(tos))
478 list_to_stack(sorted(set(I), key=I.index))
479 return list_to_stack(sorted(set(I), key=I.index)), stack
483 @SimpleFunctionWrapper
485 '''Given a list return it sorted.'''
487 return list_to_stack(sorted(iter_stack(tos))), stack
491 ##@SimpleFunctionWrapper
494 ## The cons operator expects a list on top of the stack and the potential
495 ## member below. The effect is to add the potential member into the
498 ## (tos, (second, stack)) = S
499 ## return (second, tos), stack
503 ##@SimpleFunctionWrapper
506 ## Inverse of cons, removes an item from the top of the list on the stack
507 ## and places it under the remaining list.
511 ## return tos, (item, stack)
515 @SimpleFunctionWrapper
517 '''Clear everything from the stack.
528 ##@SimpleFunctionWrapper
530 ## '''Duplicate the top item on the stack.'''
532 ## return tos, (tos, stack)
536 ##@SimpleFunctionWrapper
539 ## Copy the second item down on the stack to the top of the stack.
552 ##@SimpleFunctionWrapper
555 ## Copy the item at TOS under the second item of the stack.
563 ## (tos, (second, stack)) = S
564 ## return tos, (second, (tos, stack))
568 ##@SimpleFunctionWrapper
570 ## '''Swap the top two items on stack.'''
571 ## (tos, (second, stack)) = S
572 ## return second, (tos, stack)
576 @SimpleFunctionWrapper
579 old_stack, stack = stack
580 return stack, old_stack
584 ##@SimpleFunctionWrapper
587 ## The stack operator pushes onto the stack a list containing all the
588 ## elements of the stack.
590 ## return stack, stack
594 @SimpleFunctionWrapper
597 The unstack operator expects a list on top of the stack and makes that
598 the stack discarding the rest of the stack.
604 ##@SimpleFunctionWrapper
606 ## '''Pop and discard the top item from the stack.'''
611 ##@SimpleFunctionWrapper
613 ## '''Pop and discard the second item from the stack.'''
614 ## (tos, (_, stack)) = stack
619 ##@SimpleFunctionWrapper
621 ## '''Pop and discard the third item from the stack.'''
622 ## (tos, (second, (_, stack))) = stack
623 ## return tos, (second, stack)
627 ##@SimpleFunctionWrapper
629 ## '''Pop and discard the first and second items from the stack.'''
630 ## return stack[1][1]
634 ##@SimpleFunctionWrapper
636 ## '''Duplicate the second item on the stack.'''
637 ## (tos, (second, stack)) = S
638 ## return tos, (second, (second, stack))
642 @SimpleFunctionWrapper
644 '''Reverse the list on the top of the stack.
647 reverse == [] swap shunt
651 for term in iter_stack(tos):
657 @SimpleFunctionWrapper
659 '''Concatinate the two lists on the top of the stack.
662 [a b c] [d e f] concat
663 ----------------------------
667 (tos, (second, stack)) = S
668 return concat(second, tos), stack
672 @SimpleFunctionWrapper
674 '''Like concat but reverses the top list into the second.
677 shunt == [swons] step == reverse swap concat
679 [a b c] [d e f] shunt
680 ---------------------------
684 (tos, (second, stack)) = stack
687 second = term, second
692 @SimpleFunctionWrapper
695 Replace the two lists on the top of the stack with a list of the pairs
696 from each list. The smallest list sets the length of the result list.
698 (tos, (second, stack)) = S
701 for a, b in zip(iter_stack(tos), iter_stack(second))
703 return list_to_stack(accumulator), stack
707 @SimpleFunctionWrapper
711 return tos + 1, stack
715 @SimpleFunctionWrapper
719 return tos - 1, stack
723 @SimpleFunctionWrapper
734 a, (b, stack) = stack
740 return int(math.floor(n))
742 floor.__doc__ = math.floor.__doc__
746 @SimpleFunctionWrapper
749 divmod(x, y) -> (quotient, remainder)
751 Return the tuple (x//y, x%y). Invariant: div*y + mod == x.
760 Return the square root of the number a.
761 Negative numbers return complex roots.
766 assert a < 0, repr(a)
767 r = math.sqrt(-a) * 1j
772 ##@SimpleFunctionWrapper
782 ## (a, (b, (c, stack))) = S
783 ## return b, (c, (a, stack))
787 ##@SimpleFunctionWrapper
797 ## (a, (b, (c, stack))) = S
798 ## return c, (a, (b, stack))
803 # if isinstance(text, str):
804 # return run(text, stack)
809 @SimpleFunctionWrapper
811 '''The identity function.'''
816 @SimpleFunctionWrapper
818 '''True if the form on TOS is void otherwise False.'''
820 return _void(form), stack
824 return any(not _void(i) for i in iter_stack(form))
835 def words(stack, expression, dictionary):
836 '''Print all the words in alphabetical order.'''
837 print(' '.join(sorted(dictionary)))
838 return stack, expression, dictionary
843 def sharing(stack, expression, dictionary):
844 '''Print redistribution information.'''
845 print("You may convey verbatim copies of the Program's source code as"
846 ' you receive it, in any medium, provided that you conspicuously'
847 ' and appropriately publish on each copy an appropriate copyright'
848 ' notice; keep intact all notices stating that this License and'
849 ' any non-permissive terms added in accord with section 7 apply'
850 ' to the code; keep intact all notices of the absence of any'
851 ' warranty; and give all recipients a copy of this License along'
853 ' You should have received a copy of the GNU General Public License'
854 ' along with Thun. If not see <http://www.gnu.org/licenses/>.')
855 return stack, expression, dictionary
860 def warranty(stack, expression, dictionary):
861 '''Print warranty information.'''
862 print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
863 ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
864 ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
865 ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
866 ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
867 ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
868 ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
869 ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
870 ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')
871 return stack, expression, dictionary
874 # def simple_manual(stack):
876 # Print words and help for each word.
878 # for name, f in sorted(FUNCTIONS.items()):
880 # boxline = '+%s+' % ('-' * (len(name) + 2))
883 # '| %s |' % (name,),
885 # d if d else ' ...',
895 def help_(S, expression, dictionary):
896 '''Accepts a quoted symbol on the top of the stack and prints its docs.'''
897 ((symbol, _), stack) = S
898 word = dictionary[symbol]
900 return stack, expression, dictionary
908 # Several combinators depend on other words in their definitions,
909 # we use symbols to prevent hard-coding these, so in theory, you
910 # could change the word in the dictionary to use different semantics.
911 S_choice = Symbol('choice')
912 S_first = Symbol('first')
913 S_getitem = Symbol('getitem')
914 S_genrec = Symbol('genrec')
915 S_loop = Symbol('loop')
917 S_ifte = Symbol('ifte')
918 S_infra = Symbol('infra')
919 S_step = Symbol('step')
920 S_times = Symbol('times')
921 S_swaack = Symbol('swaack')
922 S_truthy = Symbol('truthy')
927 def i(stack, expression, dictionary):
929 The i combinator expects a quoted program on the stack and unpacks it
930 onto the pending expression for evaluation.
939 return stack, concat(quote, expression), dictionary
944 def x(stack, expression, dictionary):
950 ... [Q] x = ... [Q] dup i
951 ... [Q] x = ... [Q] [Q] i
952 ... [Q] x = ... [Q] Q
956 return stack, concat(quote, expression), dictionary
961 def b(stack, expression, dictionary):
967 ... [P] [Q] b == ... [P] i [Q] i
968 ... [P] [Q] b == ... P Q
971 q, (p, (stack)) = stack
972 return stack, concat(p, concat(q, expression)), dictionary
977 def dupdip(stack, expression, dictionary):
981 [F] dupdip == dup [F] dip
991 return stack, concat(F, (a, expression)), dictionary
996 def infra(stack, expression, dictionary):
998 Accept a quoted program and a list on the stack and run the program
999 with the list as its stack.
1002 ... [a b c] [Q] . infra
1003 -----------------------------
1004 c b a . Q [...] swaack
1007 (quote, (aggregate, stack)) = stack
1008 return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary
1013 def genrec(stack, expression, dictionary):
1015 General Recursion Combinator.
1018 [if] [then] [rec1] [rec2] genrec
1019 ---------------------------------------------------------------------
1020 [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
1022 From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
1023 "The genrec combinator takes four program parameters in addition to
1024 whatever data parameters it needs. Fourth from the top is an if-part,
1025 followed by a then-part. If the if-part yields true, then the then-part
1026 is executed and the combinator terminates. The other two parameters are
1027 the rec1-part and the rec2-part. If the if-part yields false, the
1028 rec1-part is executed. Following that the four program parameters and
1029 the combinator are again pushed onto the stack bundled up in a quoted
1030 form. Then the rec2-part is executed, where it will find the bundled
1031 form. Typically it will then execute the bundled form, either with i or
1032 with app2, or some other combinator."
1034 The way to design one of these is to fix your base case [then] and the
1035 test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
1036 a quotation of the whole function.
1038 For example, given a (general recursive) function 'F':
1041 F == [I] [T] [R1] [R2] genrec
1043 If the [I] if-part fails you must derive R1 and R2 from:
1048 Just set the stack arguments in front, and figure out what R1 and R2
1049 have to do to apply the quoted [F] in the proper way. In effect, the
1050 genrec combinator turns into an ifte combinator with a quoted copy of
1051 the original definition in the else-part:
1054 F == [I] [T] [R1] [R2] genrec
1055 == [I] [T] [R1 [F] R2] ifte
1057 Primitive recursive functions are those where R2 == i.
1060 P == [I] [T] [R] primrec
1061 == [I] [T] [R [P] i] ifte
1062 == [I] [T] [R P] ifte
1065 (rec2, (rec1, stack)) = stack
1066 (then, (if_, _)) = stack
1067 F = (if_, (then, (rec1, (rec2, (S_genrec, ())))))
1068 else_ = concat(rec1, (F, rec2))
1069 return (else_, stack), (S_ifte, expression), dictionary
1074 def map_(S, expression, dictionary):
1076 Run the quoted program on TOS on the items in the list under it, push a
1077 new list with the results (in place of the program and original list.
1079 # (quote, (aggregate, stack)) = S
1080 # results = list_to_stack([
1081 # joy((term, stack), quote, dictionary)[0][0]
1082 # for term in iter_stack(aggregate)
1084 # return (results, stack), expression, dictionary
1085 (quote, (aggregate, stack)) = S
1087 return (aggregate, stack), expression, dictionary
1089 for term in iter_stack(aggregate):
1091 batch = (s, (quote, (S_infra, (S_first, batch))))
1092 stack = (batch, ((), stack))
1093 return stack, (S_infra, expression), dictionary
1096 #def cleave(S, expression, dictionary):
1098 # The cleave combinator expects two quotations, and below that an item X.
1099 # It first executes [P], with X on top, and saves the top result element.
1100 # Then it executes [Q], again with X, and saves the top result.
1101 # Finally it restores the stack to what it was below X and pushes the two
1102 # results P(X) and Q(X).
1104 # (Q, (P, (x, stack))) = S
1105 # p = joy((x, stack), P, dictionary)[0][0]
1106 # q = joy((x, stack), Q, dictionary)[0][0]
1107 # return (q, (p, stack)), expression, dictionary
1112 def branch(stack, expression, dictionary):
1114 Use a Boolean value to select one of two quoted programs to run.
1118 branch == roll< choice i
1122 False [F] [T] branch
1123 --------------------------
1127 -------------------------
1131 (then, (else_, (flag, stack))) = stack
1132 return stack, concat(then if flag else else_, expression), dictionary
1137 def ifte(stack, expression, dictionary):
1139 If-Then-Else Combinator
1142 ... [if] [then] [else] ifte
1143 ---------------------------------------------------
1144 ... [[else] [then]] [...] [if] infra select i
1149 ... [if] [then] [else] ifte
1150 -------------------------------------------------------
1151 ... [else] [then] [...] [if] infra first choice i
1154 Has the effect of grabbing a copy of the stack on which to run the
1155 if-part using infra.
1157 (else_, (then, (if_, stack))) = stack
1158 expression = (S_infra, (S_first, (S_choice, (S_i, expression))))
1159 stack = (if_, (stack, (then, (else_, stack))))
1160 return stack, expression, dictionary
1165 def cond(stack, expression, dictionary):
1167 This combinator works like a case statement. It expects a single quote
1168 on the stack that must contain zero or more condition quotes and a
1169 default quote. Each condition clause should contain a quoted predicate
1170 followed by the function expression to run if that predicate returns
1171 true. If no predicates return true the default function runs.
1173 It works by rewriting into a chain of nested `ifte` expressions, e.g.::
1175 [[[B0] T0] [[B1] T1] [D]] cond
1176 -----------------------------------------
1177 [B0] [T0] [[B1] [T1] [D] ifte] ifte
1180 conditions, stack = stack
1182 expression = _cond(conditions, expression)
1184 # Attempt to preload the args to first ifte.
1185 (P, (T, (E, expression))) = expression
1187 # If, for any reason, the argument to cond should happen to contain
1188 # only the default clause then this optimization will fail.
1191 stack = (E, (T, (P, stack)))
1192 return stack, expression, dictionary
1195 def _cond(conditions, expression):
1196 (clause, rest) = conditions
1197 if not rest: # clause is [D]
1200 return (P, (T, (_cond(rest, ()), (S_ifte, expression))))
1205 def dip(stack, expression, dictionary):
1207 The dip combinator expects a quoted program on the stack and below it
1208 some item, it hoists the item into the expression and runs the program
1209 on the rest of the stack.
1217 (quote, (x, stack)) = stack
1218 expression = (x, expression)
1219 return stack, concat(quote, expression), dictionary
1224 def dipd(S, expression, dictionary):
1226 Like dip but expects two items.
1230 ---------------------
1234 (quote, (x, (y, stack))) = S
1235 expression = (y, (x, expression))
1236 return stack, concat(quote, expression), dictionary
1241 def dipdd(S, expression, dictionary):
1243 Like dip but expects three items.
1247 -----------------------
1251 (quote, (x, (y, (z, stack)))) = S
1252 expression = (z, (y, (x, expression)))
1253 return stack, concat(quote, expression), dictionary
1258 def app1(S, expression, dictionary):
1260 Given a quoted program on TOS and anything as the second stack item run
1261 the program and replace the two args with the first result of the
1266 -----------------------------------
1267 ... [x ...] [Q] . infra first
1269 (quote, (x, stack)) = S
1270 stack = (quote, ((x, stack), stack))
1271 expression = (S_infra, (S_first, expression))
1272 return stack, expression, dictionary
1277 def app2(S, expression, dictionary):
1278 '''Like app1 with two items.
1282 -----------------------------------
1283 ... [y ...] [Q] . infra first
1284 [x ...] [Q] infra first
1287 (quote, (x, (y, stack))) = S
1288 expression = (S_infra, (S_first,
1289 ((x, stack), (quote, (S_infra, (S_first,
1291 stack = (quote, ((y, stack), stack))
1292 return stack, expression, dictionary
1297 def app3(S, expression, dictionary):
1298 '''Like app1 with three items.
1301 ... z y x [Q] . app3
1302 -----------------------------------
1303 ... [z ...] [Q] . infra first
1304 [y ...] [Q] infra first
1305 [x ...] [Q] infra first
1308 (quote, (x, (y, (z, stack)))) = S
1309 expression = (S_infra, (S_first,
1310 ((y, stack), (quote, (S_infra, (S_first,
1311 ((x, stack), (quote, (S_infra, (S_first,
1312 expression))))))))))
1313 stack = (quote, ((z, stack), stack))
1314 return stack, expression, dictionary
1319 def step(S, expression, dictionary):
1321 Run a quoted program on each item in a sequence.
1325 -----------------------
1330 ------------------------
1334 ... [a b c] [Q] . step
1335 ----------------------------------------
1336 ... a . Q [b c] [Q] step
1338 The step combinator executes the quotation on each member of the list
1339 on top of the stack.
1341 (quote, (aggregate, stack)) = S
1343 return stack, expression, dictionary
1344 head, tail = aggregate
1345 stack = quote, (head, stack)
1347 expression = tail, (quote, (S_step, expression))
1348 expression = S_i, expression
1349 return stack, expression, dictionary
1354 def times(stack, expression, dictionary):
1356 times == [-- dip] cons [swap] infra [0 >] swap while pop
1360 --------------------- w/ n <= 0
1365 ---------------------------------
1370 --------------------------------- w/ n > 1
1371 ... . Q (n - 1) [Q] times
1374 # times == [-- dip] cons [swap] infra [0 >] swap while pop
1375 (quote, (n, stack)) = stack
1377 return stack, expression, dictionary
1380 expression = n, (quote, (S_times, expression))
1381 expression = concat(quote, expression)
1382 return stack, expression, dictionary
1385 # The current definition above works like this:
1388 # --------------------------------------
1389 # [P] nullary [Q [P] nullary] loop
1391 # while == [pop i not] [popop] [dudipd] primrec
1393 #def while_(S, expression, dictionary):
1394 # '''[if] [body] while'''
1395 # (body, (if_, stack)) = S
1396 # while joy(stack, if_, dictionary)[0][0]:
1397 # stack = joy(stack, body, dictionary)[0]
1398 # return stack, expression, dictionary
1403 def loop(stack, expression, dictionary):
1405 Basic loop combinator.
1409 -----------------------
1413 ------------------------
1417 quote, (flag, stack) = stack
1419 expression = concat(quote, (quote, (S_loop, expression)))
1420 return stack, expression, dictionary
1425 def cmp_(stack, expression, dictionary):
1427 cmp takes two values and three quoted programs on the stack and runs
1428 one of the three depending on the results of comparing the two values:
1432 ------------------------- a > b
1436 ------------------------- a = b
1440 ------------------------- a < b
1443 L, (E, (G, (b, (a, stack)))) = stack
1444 expression = concat(G if a > b else L if a < b else E, expression)
1445 return stack, expression, dictionary
1448 #def nullary(S, expression, dictionary):
1450 # Run the program on TOS and return its first result without consuming
1451 # any of the stack (except the program on TOS.)
1453 # (quote, stack) = S
1454 # result = joy(stack, quote, dictionary)
1455 # return (result[0][0], stack), expression, dictionary
1458 #def unary(S, expression, dictionary):
1459 # (quote, stack) = S
1460 # _, return_stack = stack
1461 # result = joy(stack, quote, dictionary)[0]
1462 # return (result[0], return_stack), expression, dictionary
1465 #def binary(S, expression, dictionary):
1466 # (quote, stack) = S
1467 # _, (_, return_stack) = stack
1468 # result = joy(stack, quote, dictionary)[0]
1469 # return (result[0], return_stack), expression, dictionary
1472 #def ternary(S, expression, dictionary):
1473 # (quote, stack) = S
1474 # _, (_, (_, return_stack)) = stack
1475 # result = joy(stack, quote, dictionary)[0]
1476 # return (result[0], return_stack), expression, dictionary
1479 # FunctionWrapper(binary),
1480 # FunctionWrapper(cleave),
1481 # FunctionWrapper(nullary),
1482 # FunctionWrapper(ternary),
1483 # FunctionWrapper(unary),
1484 # FunctionWrapper(while_),
1488 BinaryBuiltinWrapper(operator.add),
1489 BinaryBuiltinWrapper(operator.and_),
1490 BinaryBuiltinWrapper(operator.div),
1491 BinaryBuiltinWrapper(operator.eq),
1492 BinaryBuiltinWrapper(operator.floordiv),
1493 BinaryBuiltinWrapper(operator.ge),
1494 BinaryBuiltinWrapper(operator.gt),
1495 BinaryBuiltinWrapper(operator.le),
1496 BinaryBuiltinWrapper(operator.lshift),
1497 BinaryBuiltinWrapper(operator.lt),
1498 BinaryBuiltinWrapper(operator.mod),
1499 BinaryBuiltinWrapper(operator.mul),
1500 BinaryBuiltinWrapper(operator.ne),
1501 BinaryBuiltinWrapper(operator.or_),
1502 BinaryBuiltinWrapper(operator.pow),
1503 BinaryBuiltinWrapper(operator.rshift),
1504 BinaryBuiltinWrapper(operator.sub),
1505 BinaryBuiltinWrapper(operator.truediv),
1506 BinaryBuiltinWrapper(operator.xor),
1508 UnaryBuiltinWrapper(abs),
1509 UnaryBuiltinWrapper(bool),
1510 UnaryBuiltinWrapper(floor),
1511 UnaryBuiltinWrapper(operator.neg),
1512 UnaryBuiltinWrapper(operator.not_),
1513 UnaryBuiltinWrapper(sqrt),
1516 del F # Otherwise Sphinx autodoc will pick it up.
1519 add_aliases(_dictionary, ALIASES)
1522 DefinitionWrapper.add_definitions(definitions, _dictionary)