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
36 from .utils.types import (
49 A = a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = map(AnyJoyType, _R)
50 B = b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = map(BooleanJoyType, _R)
51 N = n0, n1, n2, n3, n4, n5, n6, n7, n8, n9 = map(NumberJoyType, _R)
52 S = s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 = map(StackJoyType, _R)
53 F = f0, f1, f2, f3, f4, f5, f6, f7, f8, f9 = map(FloatJoyType, _R)
54 I = i0, i1, i2, i3, i4, i5, i6, i7, i8, i9 = map(IntJoyType, _R)
55 T = t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 = map(TextJoyType, _R)
61 def inscribe(function):
62 '''A decorator to inscribe functions into the default dictionary.'''
63 _dictionary[function.name] = function
68 '''Return a dictionary of Joy functions for use with joy().'''
69 return _dictionary.copy()
78 ('mod', ['%', 'rem', 'remainder', 'modulus']),
81 ('getitem', ['pick', 'at']),
92 ('rolldown', ['roll<']),
93 ('rollup', ['roll>']),
99 def add_aliases(D, A):
101 Given a dict and a iterable of (name, [alias, ...]) pairs, create
102 additional entries in the dict mapping each alias to the named function
103 if it's in the dict. Aliases for functions not in the dict are ignored.
105 for name, aliases in A:
110 for alias in aliases:
116 product == 1 swap [*] step
117 flatten == [] swap [concat] step
120 enstacken == stack [clear] dip
121 disenstacken == ? [uncons ?] loop pop
123 dinfrirst == dip infra first
124 nullary == [stack] dinfrirst
125 unary == nullary popd
126 binary == nullary [popop] dip
127 ternary == unary [popop] dip
131 size == 0 swap [pop ++] step
133 cleave == fork [popd] dip
134 average == [sum 1.0 *] [size] cleave /
135 gcd == 1 [tuck modulus dup 0 >] loop pop
136 least_fraction == dup [gcd] infra [div] concat map
137 *fraction == [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons
138 *fraction0 == concat [[swap] dip * [*] dip] infra
139 down_to_zero == [0 >] [dup --] while
140 range_to_zero == unit [down_to_zero] infra
141 anamorphism == [pop []] swap [dip swons] genrec
142 range == [0 <=] [1 - dup] anamorphism
143 while == swap [nullary] cons dup dipd concat loop
145 primrec == [i] genrec
146 step_zero == 0 roll> step
147 codireco == cons dip rest cons
148 make_generator == [codireco] ccons
149 ifte == [nullary not] dipd branch
152 # ifte == [nullary] dipd swap branch
153 # genrec == [[genrec] cons cons cons cons] nullary swons concat ifte
155 # Another definition for while. FWIW
156 # while == over [[i] dip nullary] ccons [nullary] dip loop
160 ##second == rest first
161 ##third == rest rest first
163 ##swoncat == swap concat
166 ##z-down == [] swap uncons swap
167 ##z-up == swons swap shunt
168 ##z-right == [swons] cons dip uncons swap
169 ##z-left == swons [uncons swap] dip swap
172 ##divisor == popop 2 *
174 ##radical == swap dup * rollup * 4 * - sqrt
177 ##q0 == [[divisor] [minusb] [radical]] pam
178 ##q1 == [[root1] [root2]] pam
179 ##quadratic == [q0] ternary i [q1] ternary
183 ##PE1.1 == + dup [+] dip
184 ##PE1.2 == dup [3 & PE1.1] dip 2 >>
185 ##PE1.3 == 14811 swap [PE1.2] times pop
186 ##PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop
188 #PE1.2 == [PE1.1] step
189 #PE1 == 0 0 66 [[3 2 1 3 1 2 3] PE1.2] times [3 2 1 3] PE1.2 pop
193 def FunctionWrapper(f):
194 '''Set name attribute.'''
196 raise ValueError('Function %s must have doc string.' % f.__name__)
197 f.name = f.__name__.rstrip('_') # Don't shadow builtins.
201 def SimpleFunctionWrapper(f):
203 Wrap functions that take and return just a stack.
207 @rename_code_object(f.__name__)
208 def inner(stack, expression, dictionary):
209 return f(stack), expression, dictionary
213 def BinaryBuiltinWrapper(f):
215 Wrap functions that take two arguments and return a single result.
219 @rename_code_object(f.__name__)
220 def inner(stack, expression, dictionary):
221 (a, (b, stack)) = stack
223 return (result, stack), expression, dictionary
227 def UnaryBuiltinWrapper(f):
229 Wrap functions that take one argument and return a single result.
233 @rename_code_object(f.__name__)
234 def inner(stack, expression, dictionary):
237 return (result, stack), expression, dictionary
241 class DefinitionWrapper(object):
243 Provide implementation of defined functions, and some helper methods.
246 def __init__(self, name, body_text, doc=None):
247 self.name = self.__name__ = name
248 self.body = text_to_expression(body_text)
249 self._body = tuple(iter_stack(self.body))
250 self.__doc__ = doc or body_text
251 self._compiled = None
253 def __call__(self, stack, expression, dictionary):
255 return self._compiled(stack, expression, dictionary)
256 expression = list_to_stack(self._body, expression)
257 return stack, expression, dictionary
260 def parse_definition(class_, defi):
262 Given some text describing a Joy function definition parse it and
263 return a DefinitionWrapper.
265 name, proper, body_text = (n.strip() for n in defi.partition('=='))
267 raise ValueError('Definition %r failed' % (defi,))
268 return class_(name, body_text)
271 def add_definitions(class_, defs, dictionary):
273 Scan multi-line string defs for definitions and add them to the
276 for definition in _text_to_defs(defs):
277 class_.add_def(definition, dictionary)
280 def add_def(class_, definition, dictionary):
282 Add the definition to the dictionary.
284 F = class_.parse_definition(definition)
285 dictionary[F.name] = F
288 def _text_to_defs(text):
289 return (line.strip() for line in text.splitlines() if '==' in line)
297 # Load the auto-generated primitives into the dictionary.
298 for name, primitive in getmembers(genlib, isfunction):
299 inscribe(SimpleFunctionWrapper(primitive))
304 def inscribe_(stack, expression, dictionary):
306 Create a new Joy function definition in the Joy dictionary. A
307 definition is given as a string with a name followed by a double
308 equal sign then one or more Joy functions, the body. for example:
312 If you want the definition to persist over restarts, enter it into
313 the definitions.txt resource.
315 definition, stack = stack
316 DefinitionWrapper.add_def(definition, dictionary)
317 return stack, expression, dictionary
321 @SimpleFunctionWrapper
323 '''Parse the string on the stack to a Joy expression.'''
325 expression = text_to_expression(text)
326 return expression, stack
330 @SimpleFunctionWrapper
335 getitem == drop first
337 Expects an integer and a quote on the stack and returns the item at the
338 nth position in the quote counting from 0.
342 -------------------------
346 n, (Q, stack) = stack
347 return pick(Q, n), stack
351 @SimpleFunctionWrapper
358 Expects an integer and a quote on the stack and returns the quote with
359 n items removed off the top.
363 ----------------------
367 n, (Q, stack) = stack
378 @SimpleFunctionWrapper
381 Expects an integer and a quote on the stack and returns the quote with
382 just the top n items in reverse order (because that's easier and you can
383 use reverse if needed.)
387 ----------------------
391 n, (Q, stack) = stack
404 @SimpleFunctionWrapper
407 Use a Boolean value to select one of two items.
411 ----------------------
416 ---------------------
419 Currently Python semantics are used to evaluate the "truthiness" of the
420 Boolean value (so empty string, zero, etc. are counted as false, etc.)
422 (if_, (then, (else_, stack))) = stack
423 return then if if_ else else_, stack
427 @SimpleFunctionWrapper
430 Use a Boolean value to select one of two items from a sequence.
434 ------------------------
439 -----------------------
442 The sequence can contain more than two items but not fewer.
443 Currently Python semantics are used to evaluate the "truthiness" of the
444 Boolean value (so empty string, zero, etc. are counted as false, etc.)
446 (flag, (choices, stack)) = stack
447 (else_, (then, _)) = choices
448 return then if flag else else_, stack
452 @SimpleFunctionWrapper
454 '''Given a list find the maximum.'''
456 return max(iter_stack(tos)), stack
460 @SimpleFunctionWrapper
462 '''Given a list find the minimum.'''
464 return min(iter_stack(tos)), stack
468 @SimpleFunctionWrapper
470 '''Given a quoted sequence of numbers return the sum.
472 sum == 0 swap [+] step
475 return sum(iter_stack(tos)), stack
479 @SimpleFunctionWrapper
482 Expects an item on the stack and a quote under it and removes that item
483 from the the quote. The item is only removed once.
487 ------------------------
491 (tos, (second, stack)) = S
492 l = list(iter_stack(second))
494 return list_to_stack(l), stack
498 @SimpleFunctionWrapper
500 '''Given a list remove duplicate items.'''
502 I = list(iter_stack(tos))
503 list_to_stack(sorted(set(I), key=I.index))
504 return list_to_stack(sorted(set(I), key=I.index)), stack
508 @SimpleFunctionWrapper
510 '''Given a list return it sorted.'''
512 return list_to_stack(sorted(iter_stack(tos))), stack
516 @SimpleFunctionWrapper
518 '''Clear everything from the stack.
521 clear == stack [pop stack] loop
531 @SimpleFunctionWrapper
534 The unstack operator expects a list on top of the stack and makes that
535 the stack discarding the rest of the stack.
541 @SimpleFunctionWrapper
543 '''Reverse the list on the top of the stack.
546 reverse == [] swap shunt
550 for term in iter_stack(tos):
556 @SimpleFunctionWrapper
558 '''Concatinate the two lists on the top of the stack.
561 [a b c] [d e f] concat
562 ----------------------------
566 (tos, (second, stack)) = S
567 return concat(second, tos), stack
571 @SimpleFunctionWrapper
573 '''Like concat but reverses the top list into the second.
576 shunt == [swons] step == reverse swap concat
578 [a b c] [d e f] shunt
579 ---------------------------
583 (tos, (second, stack)) = stack
586 second = term, second
591 @SimpleFunctionWrapper
594 Replace the two lists on the top of the stack with a list of the pairs
595 from each list. The smallest list sets the length of the result list.
597 (tos, (second, stack)) = S
600 for a, b in zip(iter_stack(tos), iter_stack(second))
602 return list_to_stack(accumulator), stack
606 @SimpleFunctionWrapper
610 return tos + 1, stack
614 @SimpleFunctionWrapper
618 return tos - 1, stack
622 @SimpleFunctionWrapper
633 a, (b, stack) = stack
639 return int(math.floor(n))
641 floor.__doc__ = math.floor.__doc__
645 @SimpleFunctionWrapper
648 divmod(x, y) -> (quotient, remainder)
650 Return the tuple (x//y, x%y). Invariant: div*y + mod == x.
659 Return the square root of the number a.
660 Negative numbers return complex roots.
665 assert a < 0, repr(a)
666 r = math.sqrt(-a) * 1j
672 # if isinstance(text, str):
673 # return run(text, stack)
678 @SimpleFunctionWrapper
680 '''The identity function.'''
685 @SimpleFunctionWrapper
687 '''True if the form on TOS is void otherwise False.'''
689 return _void(form), stack
693 return any(not _void(i) for i in iter_stack(form))
704 def words(stack, expression, dictionary):
705 '''Print all the words in alphabetical order.'''
706 print(' '.join(sorted(dictionary)))
707 return stack, expression, dictionary
712 def sharing(stack, expression, dictionary):
713 '''Print redistribution information.'''
714 print("You may convey verbatim copies of the Program's source code as"
715 ' you receive it, in any medium, provided that you conspicuously'
716 ' and appropriately publish on each copy an appropriate copyright'
717 ' notice; keep intact all notices stating that this License and'
718 ' any non-permissive terms added in accord with section 7 apply'
719 ' to the code; keep intact all notices of the absence of any'
720 ' warranty; and give all recipients a copy of this License along'
722 ' You should have received a copy of the GNU General Public License'
723 ' along with Thun. If not see <http://www.gnu.org/licenses/>.')
724 return stack, expression, dictionary
729 def warranty(stack, expression, dictionary):
730 '''Print warranty information.'''
731 print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
732 ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
733 ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
734 ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
735 ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
736 ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
737 ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
738 ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
739 ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')
740 return stack, expression, dictionary
743 # def simple_manual(stack):
745 # Print words and help for each word.
747 # for name, f in sorted(FUNCTIONS.items()):
749 # boxline = '+%s+' % ('-' * (len(name) + 2))
752 # '| %s |' % (name,),
754 # d if d else ' ...',
764 def help_(S, expression, dictionary):
765 '''Accepts a quoted symbol on the top of the stack and prints its docs.'''
766 ((symbol, _), stack) = S
767 word = dictionary[symbol]
769 return stack, expression, dictionary
777 # Several combinators depend on other words in their definitions,
778 # we use symbols to prevent hard-coding these, so in theory, you
779 # could change the word in the dictionary to use different semantics.
780 S_choice = Symbol('choice')
781 S_first = Symbol('first')
782 S_getitem = Symbol('getitem')
783 S_genrec = Symbol('genrec')
784 S_loop = Symbol('loop')
786 S_ifte = Symbol('ifte')
787 S_infra = Symbol('infra')
788 S_step = Symbol('step')
789 S_times = Symbol('times')
790 S_swaack = Symbol('swaack')
791 S_truthy = Symbol('truthy')
796 def i(stack, expression, dictionary):
798 The i combinator expects a quoted program on the stack and unpacks it
799 onto the pending expression for evaluation.
808 return stack, concat(quote, expression), dictionary
813 def x(stack, expression, dictionary):
819 ... [Q] x = ... [Q] dup i
820 ... [Q] x = ... [Q] [Q] i
821 ... [Q] x = ... [Q] Q
825 return stack, concat(quote, expression), dictionary
830 def b(stack, expression, dictionary):
836 ... [P] [Q] b == ... [P] i [Q] i
837 ... [P] [Q] b == ... P Q
840 q, (p, (stack)) = stack
841 return stack, concat(p, concat(q, expression)), dictionary
846 def dupdip(stack, expression, dictionary):
850 [F] dupdip == dup [F] dip
860 return stack, concat(F, (a, expression)), dictionary
865 def infra(stack, expression, dictionary):
867 Accept a quoted program and a list on the stack and run the program
868 with the list as its stack.
871 ... [a b c] [Q] . infra
872 -----------------------------
873 c b a . Q [...] swaack
876 (quote, (aggregate, stack)) = stack
877 return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary
882 def genrec(stack, expression, dictionary):
884 General Recursion Combinator.
887 [if] [then] [rec1] [rec2] genrec
888 ---------------------------------------------------------------------
889 [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
891 From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
892 "The genrec combinator takes four program parameters in addition to
893 whatever data parameters it needs. Fourth from the top is an if-part,
894 followed by a then-part. If the if-part yields true, then the then-part
895 is executed and the combinator terminates. The other two parameters are
896 the rec1-part and the rec2-part. If the if-part yields false, the
897 rec1-part is executed. Following that the four program parameters and
898 the combinator are again pushed onto the stack bundled up in a quoted
899 form. Then the rec2-part is executed, where it will find the bundled
900 form. Typically it will then execute the bundled form, either with i or
901 with app2, or some other combinator."
903 The way to design one of these is to fix your base case [then] and the
904 test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
905 a quotation of the whole function.
907 For example, given a (general recursive) function 'F':
910 F == [I] [T] [R1] [R2] genrec
912 If the [I] if-part fails you must derive R1 and R2 from:
917 Just set the stack arguments in front, and figure out what R1 and R2
918 have to do to apply the quoted [F] in the proper way. In effect, the
919 genrec combinator turns into an ifte combinator with a quoted copy of
920 the original definition in the else-part:
923 F == [I] [T] [R1] [R2] genrec
924 == [I] [T] [R1 [F] R2] ifte
926 Primitive recursive functions are those where R2 == i.
929 P == [I] [T] [R] primrec
930 == [I] [T] [R [P] i] ifte
931 == [I] [T] [R P] ifte
934 (rec2, (rec1, stack)) = stack
935 (then, (if_, _)) = stack
936 F = (if_, (then, (rec1, (rec2, (S_genrec, ())))))
937 else_ = concat(rec1, (F, rec2))
938 return (else_, stack), (S_ifte, expression), dictionary
943 def map_(S, expression, dictionary):
945 Run the quoted program on TOS on the items in the list under it, push a
946 new list with the results (in place of the program and original list.
948 # (quote, (aggregate, stack)) = S
949 # results = list_to_stack([
950 # joy((term, stack), quote, dictionary)[0][0]
951 # for term in iter_stack(aggregate)
953 # return (results, stack), expression, dictionary
954 (quote, (aggregate, stack)) = S
956 return (aggregate, stack), expression, dictionary
958 for term in iter_stack(aggregate):
960 batch = (s, (quote, (S_infra, (S_first, batch))))
961 stack = (batch, ((), stack))
962 return stack, (S_infra, expression), dictionary
965 #def cleave(S, expression, dictionary):
967 # The cleave combinator expects two quotations, and below that an item X.
968 # It first executes [P], with X on top, and saves the top result element.
969 # Then it executes [Q], again with X, and saves the top result.
970 # Finally it restores the stack to what it was below X and pushes the two
971 # results P(X) and Q(X).
973 # (Q, (P, (x, stack))) = S
974 # p = joy((x, stack), P, dictionary)[0][0]
975 # q = joy((x, stack), Q, dictionary)[0][0]
976 # return (q, (p, stack)), expression, dictionary
981 def branch(stack, expression, dictionary):
983 Use a Boolean value to select one of two quoted programs to run.
987 branch == roll< choice i
992 --------------------------
996 -------------------------
1000 (then, (else_, (flag, stack))) = stack
1001 return stack, concat(then if flag else else_, expression), dictionary
1006 ##def ifte(stack, expression, dictionary):
1008 ## If-Then-Else Combinator
1011 ## ... [if] [then] [else] ifte
1012 ## ---------------------------------------------------
1013 ## ... [[else] [then]] [...] [if] infra select i
1018 ## ... [if] [then] [else] ifte
1019 ## -------------------------------------------------------
1020 ## ... [else] [then] [...] [if] infra first choice i
1023 ## Has the effect of grabbing a copy of the stack on which to run the
1024 ## if-part using infra.
1026 ## (else_, (then, (if_, stack))) = stack
1027 ## expression = (S_infra, (S_first, (S_choice, (S_i, expression))))
1028 ## stack = (if_, (stack, (then, (else_, stack))))
1029 ## return stack, expression, dictionary
1034 def cond(stack, expression, dictionary):
1036 This combinator works like a case statement. It expects a single quote
1037 on the stack that must contain zero or more condition quotes and a
1038 default quote. Each condition clause should contain a quoted predicate
1039 followed by the function expression to run if that predicate returns
1040 true. If no predicates return true the default function runs.
1042 It works by rewriting into a chain of nested `ifte` expressions, e.g.::
1044 [[[B0] T0] [[B1] T1] [D]] cond
1045 -----------------------------------------
1046 [B0] [T0] [[B1] [T1] [D] ifte] ifte
1049 conditions, stack = stack
1051 expression = _cond(conditions, expression)
1053 # Attempt to preload the args to first ifte.
1054 (P, (T, (E, expression))) = expression
1056 # If, for any reason, the argument to cond should happen to contain
1057 # only the default clause then this optimization will fail.
1060 stack = (E, (T, (P, stack)))
1061 return stack, expression, dictionary
1064 def _cond(conditions, expression):
1065 (clause, rest) = conditions
1066 if not rest: # clause is [D]
1069 return (P, (T, (_cond(rest, ()), (S_ifte, expression))))
1074 def dip(stack, expression, dictionary):
1076 The dip combinator expects a quoted program on the stack and below it
1077 some item, it hoists the item into the expression and runs the program
1078 on the rest of the stack.
1086 (quote, (x, stack)) = stack
1087 expression = (x, expression)
1088 return stack, concat(quote, expression), dictionary
1093 def dipd(S, expression, dictionary):
1095 Like dip but expects two items.
1099 ---------------------
1103 (quote, (x, (y, stack))) = S
1104 expression = (y, (x, expression))
1105 return stack, concat(quote, expression), dictionary
1110 def dipdd(S, expression, dictionary):
1112 Like dip but expects three items.
1116 -----------------------
1120 (quote, (x, (y, (z, stack)))) = S
1121 expression = (z, (y, (x, expression)))
1122 return stack, concat(quote, expression), dictionary
1127 def app1(S, expression, dictionary):
1129 Given a quoted program on TOS and anything as the second stack item run
1130 the program and replace the two args with the first result of the
1135 -----------------------------------
1136 ... [x ...] [Q] . infra first
1138 (quote, (x, stack)) = S
1139 stack = (quote, ((x, stack), stack))
1140 expression = (S_infra, (S_first, expression))
1141 return stack, expression, dictionary
1146 def app2(S, expression, dictionary):
1147 '''Like app1 with two items.
1151 -----------------------------------
1152 ... [y ...] [Q] . infra first
1153 [x ...] [Q] infra first
1156 (quote, (x, (y, stack))) = S
1157 expression = (S_infra, (S_first,
1158 ((x, stack), (quote, (S_infra, (S_first,
1160 stack = (quote, ((y, stack), stack))
1161 return stack, expression, dictionary
1166 def app3(S, expression, dictionary):
1167 '''Like app1 with three items.
1170 ... z y x [Q] . app3
1171 -----------------------------------
1172 ... [z ...] [Q] . infra first
1173 [y ...] [Q] infra first
1174 [x ...] [Q] infra first
1177 (quote, (x, (y, (z, stack)))) = S
1178 expression = (S_infra, (S_first,
1179 ((y, stack), (quote, (S_infra, (S_first,
1180 ((x, stack), (quote, (S_infra, (S_first,
1181 expression))))))))))
1182 stack = (quote, ((z, stack), stack))
1183 return stack, expression, dictionary
1188 def step(S, expression, dictionary):
1190 Run a quoted program on each item in a sequence.
1194 -----------------------
1199 ------------------------
1203 ... [a b c] [Q] . step
1204 ----------------------------------------
1205 ... a . Q [b c] [Q] step
1207 The step combinator executes the quotation on each member of the list
1208 on top of the stack.
1210 (quote, (aggregate, stack)) = S
1212 return stack, expression, dictionary
1213 head, tail = aggregate
1214 stack = quote, (head, stack)
1216 expression = tail, (quote, (S_step, expression))
1217 expression = S_i, expression
1218 return stack, expression, dictionary
1223 def times(stack, expression, dictionary):
1225 times == [-- dip] cons [swap] infra [0 >] swap while pop
1229 --------------------- w/ n <= 0
1234 ---------------------------------
1239 --------------------------------- w/ n > 1
1240 ... . Q (n - 1) [Q] times
1243 # times == [-- dip] cons [swap] infra [0 >] swap while pop
1244 (quote, (n, stack)) = stack
1246 return stack, expression, dictionary
1249 expression = n, (quote, (S_times, expression))
1250 expression = concat(quote, expression)
1251 return stack, expression, dictionary
1254 # The current definition above works like this:
1257 # --------------------------------------
1258 # [P] nullary [Q [P] nullary] loop
1260 # while == [pop i not] [popop] [dudipd] primrec
1262 #def while_(S, expression, dictionary):
1263 # '''[if] [body] while'''
1264 # (body, (if_, stack)) = S
1265 # while joy(stack, if_, dictionary)[0][0]:
1266 # stack = joy(stack, body, dictionary)[0]
1267 # return stack, expression, dictionary
1272 def loop(stack, expression, dictionary):
1274 Basic loop combinator.
1278 -----------------------
1282 ------------------------
1286 quote, (flag, stack) = stack
1288 expression = concat(quote, (quote, (S_loop, expression)))
1289 return stack, expression, dictionary
1294 def cmp_(stack, expression, dictionary):
1296 cmp takes two values and three quoted programs on the stack and runs
1297 one of the three depending on the results of comparing the two values:
1301 ------------------------- a > b
1305 ------------------------- a = b
1309 ------------------------- a < b
1312 L, (E, (G, (b, (a, stack)))) = stack
1313 expression = concat(G if a > b else L if a < b else E, expression)
1314 return stack, expression, dictionary
1317 # FunctionWrapper(cleave),
1318 # FunctionWrapper(while_),
1322 BinaryBuiltinWrapper(operator.add),
1323 BinaryBuiltinWrapper(operator.and_),
1324 BinaryBuiltinWrapper(operator.div),
1325 BinaryBuiltinWrapper(operator.eq),
1326 BinaryBuiltinWrapper(operator.floordiv),
1327 BinaryBuiltinWrapper(operator.ge),
1328 BinaryBuiltinWrapper(operator.gt),
1329 BinaryBuiltinWrapper(operator.le),
1330 BinaryBuiltinWrapper(operator.lshift),
1331 BinaryBuiltinWrapper(operator.lt),
1332 BinaryBuiltinWrapper(operator.mod),
1333 BinaryBuiltinWrapper(operator.mul),
1334 BinaryBuiltinWrapper(operator.ne),
1335 BinaryBuiltinWrapper(operator.or_),
1336 BinaryBuiltinWrapper(operator.pow),
1337 BinaryBuiltinWrapper(operator.rshift),
1338 BinaryBuiltinWrapper(operator.sub),
1339 BinaryBuiltinWrapper(operator.truediv),
1340 BinaryBuiltinWrapper(operator.xor),
1342 UnaryBuiltinWrapper(abs),
1343 UnaryBuiltinWrapper(bool),
1344 UnaryBuiltinWrapper(floor),
1345 UnaryBuiltinWrapper(operator.neg),
1346 UnaryBuiltinWrapper(operator.not_),
1347 UnaryBuiltinWrapper(sqrt),
1350 del F # Otherwise Sphinx autodoc will pick it up.
1353 add_aliases(_dictionary, ALIASES)
1356 DefinitionWrapper.add_definitions(definitions, _dictionary)