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
99 enstacken == stack [clear] dip
100 disenstacken == ? [uncons ?] loop pop
102 dinfrirst == dip infra first
103 nullary == [stack] dinfrirst
104 unary == nullary popd
105 binary == nullary [popop] dip
106 ternary == unary [popop] dip
110 size == 0 swap [pop ++] step
112 cleave == fork [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
128 ifte == [nullary not] dipd branch
131 # ifte == [nullary] dipd swap branch
132 # genrec == [[genrec] cons cons cons cons] nullary swons concat ifte
134 # Another definition for while. FWIW
135 # while == over [[i] dip nullary] ccons [nullary] dip loop
139 ##second == rest first
140 ##third == rest rest first
142 ##swoncat == swap concat
145 ##z-down == [] swap uncons swap
146 ##z-up == swons swap shunt
147 ##z-right == [swons] cons dip uncons swap
148 ##z-left == swons [uncons swap] dip swap
151 ##divisor == popop 2 *
153 ##radical == swap dup * rollup * 4 * - sqrt
156 ##q0 == [[divisor] [minusb] [radical]] pam
157 ##q1 == [[root1] [root2]] pam
158 ##quadratic == [q0] ternary i [q1] ternary
162 ##PE1.1 == + dup [+] dip
163 ##PE1.2 == dup [3 & PE1.1] dip 2 >>
164 ##PE1.3 == 14811 swap [PE1.2] times pop
165 ##PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop
167 #PE1.2 == [PE1.1] step
168 #PE1 == 0 0 66 [[3 2 1 3 1 2 3] PE1.2] times [3 2 1 3] PE1.2 pop
172 def FunctionWrapper(f):
173 '''Set name attribute.'''
175 raise ValueError('Function %s must have doc string.' % f.__name__)
176 f.name = f.__name__.rstrip('_') # Don't shadow builtins.
180 def SimpleFunctionWrapper(f):
182 Wrap functions that take and return just a stack.
186 @rename_code_object(f.__name__)
187 def inner(stack, expression, dictionary):
188 return f(stack), expression, dictionary
192 def BinaryBuiltinWrapper(f):
194 Wrap functions that take two arguments and return a single result.
198 @rename_code_object(f.__name__)
199 def inner(stack, expression, dictionary):
200 (a, (b, stack)) = stack
202 return (result, stack), expression, dictionary
206 def UnaryBuiltinWrapper(f):
208 Wrap functions that take one argument and return a single result.
212 @rename_code_object(f.__name__)
213 def inner(stack, expression, dictionary):
216 return (result, stack), expression, dictionary
220 class DefinitionWrapper(object):
222 Provide implementation of defined functions, and some helper methods.
225 def __init__(self, name, body_text, doc=None):
226 self.name = self.__name__ = name
227 self.body = text_to_expression(body_text)
228 self._body = tuple(iter_stack(self.body))
229 self.__doc__ = doc or body_text
230 self._compiled = None
232 def __call__(self, stack, expression, dictionary):
234 return self._compiled(stack, expression, dictionary)
235 expression = list_to_stack(self._body, expression)
236 return stack, expression, dictionary
239 def parse_definition(class_, defi):
241 Given some text describing a Joy function definition parse it and
242 return a DefinitionWrapper.
244 name, proper, body_text = (n.strip() for n in defi.partition('=='))
246 raise ValueError('Definition %r failed' % (defi,))
247 return class_(name, body_text)
250 def add_definitions(class_, defs, dictionary):
252 Scan multi-line string defs for definitions and add them to the
255 for definition in _text_to_defs(defs):
256 class_.add_def(definition, dictionary)
259 def add_def(class_, definition, dictionary):
261 Add the definition to the dictionary.
263 F = class_.parse_definition(definition)
264 dictionary[F.name] = F
267 def _text_to_defs(text):
268 return (line.strip() for line in text.splitlines() if '==' in line)
276 # Load the auto-generated primitives into the dictionary.
277 for name, primitive in getmembers(genlib, isfunction):
278 inscribe(SimpleFunctionWrapper(primitive))
282 @SimpleFunctionWrapper
284 '''Parse the string on the stack to a Joy expression.'''
286 expression = text_to_expression(text)
287 return expression, stack
291 @SimpleFunctionWrapper
296 getitem == drop first
298 Expects an integer and a quote on the stack and returns the item at the
299 nth position in the quote counting from 0.
303 -------------------------
307 n, (Q, stack) = stack
308 return pick(Q, n), stack
312 @SimpleFunctionWrapper
319 Expects an integer and a quote on the stack and returns the quote with
320 n items removed off the top.
324 ----------------------
328 n, (Q, stack) = stack
339 @SimpleFunctionWrapper
342 Expects an integer and a quote on the stack and returns the quote with
343 just the top n items in reverse order (because that's easier and you can
344 use reverse if needed.)
348 ----------------------
352 n, (Q, stack) = stack
365 @SimpleFunctionWrapper
368 Use a Boolean value to select one of two items.
372 ----------------------
377 ---------------------
380 Currently Python semantics are used to evaluate the "truthiness" of the
381 Boolean value (so empty string, zero, etc. are counted as false, etc.)
383 (if_, (then, (else_, stack))) = stack
384 return then if if_ else else_, stack
388 @SimpleFunctionWrapper
391 Use a Boolean value to select one of two items from a sequence.
395 ------------------------
400 -----------------------
403 The sequence can contain more than two items but not fewer.
404 Currently Python semantics are used to evaluate the "truthiness" of the
405 Boolean value (so empty string, zero, etc. are counted as false, etc.)
407 (flag, (choices, stack)) = stack
408 (else_, (then, _)) = choices
409 return then if flag else else_, stack
413 @SimpleFunctionWrapper
415 '''Given a list find the maximum.'''
417 return max(iter_stack(tos)), stack
421 @SimpleFunctionWrapper
423 '''Given a list find the minimum.'''
425 return min(iter_stack(tos)), stack
429 @SimpleFunctionWrapper
431 '''Given a quoted sequence of numbers return the sum.
433 sum == 0 swap [+] step
436 return sum(iter_stack(tos)), stack
440 @SimpleFunctionWrapper
443 Expects an item on the stack and a quote under it and removes that item
444 from the the quote. The item is only removed once.
448 ------------------------
452 (tos, (second, stack)) = S
453 l = list(iter_stack(second))
455 return list_to_stack(l), stack
459 @SimpleFunctionWrapper
461 '''Given a list remove duplicate items.'''
463 I = list(iter_stack(tos))
464 list_to_stack(sorted(set(I), key=I.index))
465 return list_to_stack(sorted(set(I), key=I.index)), stack
469 @SimpleFunctionWrapper
471 '''Given a list return it sorted.'''
473 return list_to_stack(sorted(iter_stack(tos))), stack
477 @SimpleFunctionWrapper
479 '''Clear everything from the stack.
482 clear == stack [pop stack] loop
492 @SimpleFunctionWrapper
495 The unstack operator expects a list on top of the stack and makes that
496 the stack discarding the rest of the stack.
502 @SimpleFunctionWrapper
504 '''Reverse the list on the top of the stack.
507 reverse == [] swap shunt
511 for term in iter_stack(tos):
517 @SimpleFunctionWrapper
519 '''Concatinate the two lists on the top of the stack.
522 [a b c] [d e f] concat
523 ----------------------------
527 (tos, (second, stack)) = S
528 return concat(second, tos), stack
532 @SimpleFunctionWrapper
534 '''Like concat but reverses the top list into the second.
537 shunt == [swons] step == reverse swap concat
539 [a b c] [d e f] shunt
540 ---------------------------
544 (tos, (second, stack)) = stack
547 second = term, second
552 @SimpleFunctionWrapper
555 Replace the two lists on the top of the stack with a list of the pairs
556 from each list. The smallest list sets the length of the result list.
558 (tos, (second, stack)) = S
561 for a, b in zip(iter_stack(tos), iter_stack(second))
563 return list_to_stack(accumulator), stack
567 @SimpleFunctionWrapper
571 return tos + 1, stack
575 @SimpleFunctionWrapper
579 return tos - 1, stack
583 @SimpleFunctionWrapper
594 a, (b, stack) = stack
600 return int(math.floor(n))
602 floor.__doc__ = math.floor.__doc__
606 @SimpleFunctionWrapper
609 divmod(x, y) -> (quotient, remainder)
611 Return the tuple (x//y, x%y). Invariant: div*y + mod == x.
620 Return the square root of the number a.
621 Negative numbers return complex roots.
626 assert a < 0, repr(a)
627 r = math.sqrt(-a) * 1j
633 # if isinstance(text, str):
634 # return run(text, stack)
639 @SimpleFunctionWrapper
641 '''The identity function.'''
646 @SimpleFunctionWrapper
648 '''True if the form on TOS is void otherwise False.'''
650 return _void(form), stack
654 return any(not _void(i) for i in iter_stack(form))
665 def words(stack, expression, dictionary):
666 '''Print all the words in alphabetical order.'''
667 print(' '.join(sorted(dictionary)))
668 return stack, expression, dictionary
673 def sharing(stack, expression, dictionary):
674 '''Print redistribution information.'''
675 print("You may convey verbatim copies of the Program's source code as"
676 ' you receive it, in any medium, provided that you conspicuously'
677 ' and appropriately publish on each copy an appropriate copyright'
678 ' notice; keep intact all notices stating that this License and'
679 ' any non-permissive terms added in accord with section 7 apply'
680 ' to the code; keep intact all notices of the absence of any'
681 ' warranty; and give all recipients a copy of this License along'
683 ' You should have received a copy of the GNU General Public License'
684 ' along with Thun. If not see <http://www.gnu.org/licenses/>.')
685 return stack, expression, dictionary
690 def warranty(stack, expression, dictionary):
691 '''Print warranty information.'''
692 print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY'
693 ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE'
694 ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM'
695 ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR'
696 ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES'
697 ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE'
698 ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS'
699 ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE'
700 ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.')
701 return stack, expression, dictionary
704 # def simple_manual(stack):
706 # Print words and help for each word.
708 # for name, f in sorted(FUNCTIONS.items()):
710 # boxline = '+%s+' % ('-' * (len(name) + 2))
713 # '| %s |' % (name,),
715 # d if d else ' ...',
725 def help_(S, expression, dictionary):
726 '''Accepts a quoted symbol on the top of the stack and prints its docs.'''
727 ((symbol, _), stack) = S
728 word = dictionary[symbol]
730 return stack, expression, dictionary
738 # Several combinators depend on other words in their definitions,
739 # we use symbols to prevent hard-coding these, so in theory, you
740 # could change the word in the dictionary to use different semantics.
741 S_choice = Symbol('choice')
742 S_first = Symbol('first')
743 S_getitem = Symbol('getitem')
744 S_genrec = Symbol('genrec')
745 S_loop = Symbol('loop')
747 S_ifte = Symbol('ifte')
748 S_infra = Symbol('infra')
749 S_step = Symbol('step')
750 S_times = Symbol('times')
751 S_swaack = Symbol('swaack')
752 S_truthy = Symbol('truthy')
757 def i(stack, expression, dictionary):
759 The i combinator expects a quoted program on the stack and unpacks it
760 onto the pending expression for evaluation.
769 return stack, concat(quote, expression), dictionary
774 def x(stack, expression, dictionary):
780 ... [Q] x = ... [Q] dup i
781 ... [Q] x = ... [Q] [Q] i
782 ... [Q] x = ... [Q] Q
786 return stack, concat(quote, expression), dictionary
791 def b(stack, expression, dictionary):
797 ... [P] [Q] b == ... [P] i [Q] i
798 ... [P] [Q] b == ... P Q
801 q, (p, (stack)) = stack
802 return stack, concat(p, concat(q, expression)), dictionary
807 def dupdip(stack, expression, dictionary):
811 [F] dupdip == dup [F] dip
821 return stack, concat(F, (a, expression)), dictionary
826 def infra(stack, expression, dictionary):
828 Accept a quoted program and a list on the stack and run the program
829 with the list as its stack.
832 ... [a b c] [Q] . infra
833 -----------------------------
834 c b a . Q [...] swaack
837 (quote, (aggregate, stack)) = stack
838 return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary
843 def genrec(stack, expression, dictionary):
845 General Recursion Combinator.
848 [if] [then] [rec1] [rec2] genrec
849 ---------------------------------------------------------------------
850 [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
852 From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun:
853 "The genrec combinator takes four program parameters in addition to
854 whatever data parameters it needs. Fourth from the top is an if-part,
855 followed by a then-part. If the if-part yields true, then the then-part
856 is executed and the combinator terminates. The other two parameters are
857 the rec1-part and the rec2-part. If the if-part yields false, the
858 rec1-part is executed. Following that the four program parameters and
859 the combinator are again pushed onto the stack bundled up in a quoted
860 form. Then the rec2-part is executed, where it will find the bundled
861 form. Typically it will then execute the bundled form, either with i or
862 with app2, or some other combinator."
864 The way to design one of these is to fix your base case [then] and the
865 test [if], and then treat rec1 and rec2 as an else-part "sandwiching"
866 a quotation of the whole function.
868 For example, given a (general recursive) function 'F':
871 F == [I] [T] [R1] [R2] genrec
873 If the [I] if-part fails you must derive R1 and R2 from:
878 Just set the stack arguments in front, and figure out what R1 and R2
879 have to do to apply the quoted [F] in the proper way. In effect, the
880 genrec combinator turns into an ifte combinator with a quoted copy of
881 the original definition in the else-part:
884 F == [I] [T] [R1] [R2] genrec
885 == [I] [T] [R1 [F] R2] ifte
887 Primitive recursive functions are those where R2 == i.
890 P == [I] [T] [R] primrec
891 == [I] [T] [R [P] i] ifte
892 == [I] [T] [R P] ifte
895 (rec2, (rec1, stack)) = stack
896 (then, (if_, _)) = stack
897 F = (if_, (then, (rec1, (rec2, (S_genrec, ())))))
898 else_ = concat(rec1, (F, rec2))
899 return (else_, stack), (S_ifte, expression), dictionary
904 def map_(S, expression, dictionary):
906 Run the quoted program on TOS on the items in the list under it, push a
907 new list with the results (in place of the program and original list.
909 # (quote, (aggregate, stack)) = S
910 # results = list_to_stack([
911 # joy((term, stack), quote, dictionary)[0][0]
912 # for term in iter_stack(aggregate)
914 # return (results, stack), expression, dictionary
915 (quote, (aggregate, stack)) = S
917 return (aggregate, stack), expression, dictionary
919 for term in iter_stack(aggregate):
921 batch = (s, (quote, (S_infra, (S_first, batch))))
922 stack = (batch, ((), stack))
923 return stack, (S_infra, expression), dictionary
926 #def cleave(S, expression, dictionary):
928 # The cleave combinator expects two quotations, and below that an item X.
929 # It first executes [P], with X on top, and saves the top result element.
930 # Then it executes [Q], again with X, and saves the top result.
931 # Finally it restores the stack to what it was below X and pushes the two
932 # results P(X) and Q(X).
934 # (Q, (P, (x, stack))) = S
935 # p = joy((x, stack), P, dictionary)[0][0]
936 # q = joy((x, stack), Q, dictionary)[0][0]
937 # return (q, (p, stack)), expression, dictionary
942 def branch(stack, expression, dictionary):
944 Use a Boolean value to select one of two quoted programs to run.
948 branch == roll< choice i
953 --------------------------
957 -------------------------
961 (then, (else_, (flag, stack))) = stack
962 return stack, concat(then if flag else else_, expression), dictionary
967 ##def ifte(stack, expression, dictionary):
969 ## If-Then-Else Combinator
972 ## ... [if] [then] [else] ifte
973 ## ---------------------------------------------------
974 ## ... [[else] [then]] [...] [if] infra select i
979 ## ... [if] [then] [else] ifte
980 ## -------------------------------------------------------
981 ## ... [else] [then] [...] [if] infra first choice i
984 ## Has the effect of grabbing a copy of the stack on which to run the
985 ## if-part using infra.
987 ## (else_, (then, (if_, stack))) = stack
988 ## expression = (S_infra, (S_first, (S_choice, (S_i, expression))))
989 ## stack = (if_, (stack, (then, (else_, stack))))
990 ## return stack, expression, dictionary
995 def cond(stack, expression, dictionary):
997 This combinator works like a case statement. It expects a single quote
998 on the stack that must contain zero or more condition quotes and a
999 default quote. Each condition clause should contain a quoted predicate
1000 followed by the function expression to run if that predicate returns
1001 true. If no predicates return true the default function runs.
1003 It works by rewriting into a chain of nested `ifte` expressions, e.g.::
1005 [[[B0] T0] [[B1] T1] [D]] cond
1006 -----------------------------------------
1007 [B0] [T0] [[B1] [T1] [D] ifte] ifte
1010 conditions, stack = stack
1012 expression = _cond(conditions, expression)
1014 # Attempt to preload the args to first ifte.
1015 (P, (T, (E, expression))) = expression
1017 # If, for any reason, the argument to cond should happen to contain
1018 # only the default clause then this optimization will fail.
1021 stack = (E, (T, (P, stack)))
1022 return stack, expression, dictionary
1025 def _cond(conditions, expression):
1026 (clause, rest) = conditions
1027 if not rest: # clause is [D]
1030 return (P, (T, (_cond(rest, ()), (S_ifte, expression))))
1035 def dip(stack, expression, dictionary):
1037 The dip combinator expects a quoted program on the stack and below it
1038 some item, it hoists the item into the expression and runs the program
1039 on the rest of the stack.
1047 (quote, (x, stack)) = stack
1048 expression = (x, expression)
1049 return stack, concat(quote, expression), dictionary
1054 def dipd(S, expression, dictionary):
1056 Like dip but expects two items.
1060 ---------------------
1064 (quote, (x, (y, stack))) = S
1065 expression = (y, (x, expression))
1066 return stack, concat(quote, expression), dictionary
1071 def dipdd(S, expression, dictionary):
1073 Like dip but expects three items.
1077 -----------------------
1081 (quote, (x, (y, (z, stack)))) = S
1082 expression = (z, (y, (x, expression)))
1083 return stack, concat(quote, expression), dictionary
1088 def app1(S, expression, dictionary):
1090 Given a quoted program on TOS and anything as the second stack item run
1091 the program and replace the two args with the first result of the
1096 -----------------------------------
1097 ... [x ...] [Q] . infra first
1099 (quote, (x, stack)) = S
1100 stack = (quote, ((x, stack), stack))
1101 expression = (S_infra, (S_first, expression))
1102 return stack, expression, dictionary
1107 def app2(S, expression, dictionary):
1108 '''Like app1 with two items.
1112 -----------------------------------
1113 ... [y ...] [Q] . infra first
1114 [x ...] [Q] infra first
1117 (quote, (x, (y, stack))) = S
1118 expression = (S_infra, (S_first,
1119 ((x, stack), (quote, (S_infra, (S_first,
1121 stack = (quote, ((y, stack), stack))
1122 return stack, expression, dictionary
1127 def app3(S, expression, dictionary):
1128 '''Like app1 with three items.
1131 ... z y x [Q] . app3
1132 -----------------------------------
1133 ... [z ...] [Q] . infra first
1134 [y ...] [Q] infra first
1135 [x ...] [Q] infra first
1138 (quote, (x, (y, (z, stack)))) = S
1139 expression = (S_infra, (S_first,
1140 ((y, stack), (quote, (S_infra, (S_first,
1141 ((x, stack), (quote, (S_infra, (S_first,
1142 expression))))))))))
1143 stack = (quote, ((z, stack), stack))
1144 return stack, expression, dictionary
1149 def step(S, expression, dictionary):
1151 Run a quoted program on each item in a sequence.
1155 -----------------------
1160 ------------------------
1164 ... [a b c] [Q] . step
1165 ----------------------------------------
1166 ... a . Q [b c] [Q] step
1168 The step combinator executes the quotation on each member of the list
1169 on top of the stack.
1171 (quote, (aggregate, stack)) = S
1173 return stack, expression, dictionary
1174 head, tail = aggregate
1175 stack = quote, (head, stack)
1177 expression = tail, (quote, (S_step, expression))
1178 expression = S_i, expression
1179 return stack, expression, dictionary
1184 def times(stack, expression, dictionary):
1186 times == [-- dip] cons [swap] infra [0 >] swap while pop
1190 --------------------- w/ n <= 0
1195 ---------------------------------
1200 --------------------------------- w/ n > 1
1201 ... . Q (n - 1) [Q] times
1204 # times == [-- dip] cons [swap] infra [0 >] swap while pop
1205 (quote, (n, stack)) = stack
1207 return stack, expression, dictionary
1210 expression = n, (quote, (S_times, expression))
1211 expression = concat(quote, expression)
1212 return stack, expression, dictionary
1215 # The current definition above works like this:
1218 # --------------------------------------
1219 # [P] nullary [Q [P] nullary] loop
1221 # while == [pop i not] [popop] [dudipd] primrec
1223 #def while_(S, expression, dictionary):
1224 # '''[if] [body] while'''
1225 # (body, (if_, stack)) = S
1226 # while joy(stack, if_, dictionary)[0][0]:
1227 # stack = joy(stack, body, dictionary)[0]
1228 # return stack, expression, dictionary
1233 def loop(stack, expression, dictionary):
1235 Basic loop combinator.
1239 -----------------------
1243 ------------------------
1247 quote, (flag, stack) = stack
1249 expression = concat(quote, (quote, (S_loop, expression)))
1250 return stack, expression, dictionary
1255 def cmp_(stack, expression, dictionary):
1257 cmp takes two values and three quoted programs on the stack and runs
1258 one of the three depending on the results of comparing the two values:
1262 ------------------------- a > b
1266 ------------------------- a = b
1270 ------------------------- a < b
1273 L, (E, (G, (b, (a, stack)))) = stack
1274 expression = concat(G if a > b else L if a < b else E, expression)
1275 return stack, expression, dictionary
1278 # FunctionWrapper(cleave),
1279 # FunctionWrapper(while_),
1283 BinaryBuiltinWrapper(operator.add),
1284 BinaryBuiltinWrapper(operator.and_),
1285 BinaryBuiltinWrapper(operator.div),
1286 BinaryBuiltinWrapper(operator.eq),
1287 BinaryBuiltinWrapper(operator.floordiv),
1288 BinaryBuiltinWrapper(operator.ge),
1289 BinaryBuiltinWrapper(operator.gt),
1290 BinaryBuiltinWrapper(operator.le),
1291 BinaryBuiltinWrapper(operator.lshift),
1292 BinaryBuiltinWrapper(operator.lt),
1293 BinaryBuiltinWrapper(operator.mod),
1294 BinaryBuiltinWrapper(operator.mul),
1295 BinaryBuiltinWrapper(operator.ne),
1296 BinaryBuiltinWrapper(operator.or_),
1297 BinaryBuiltinWrapper(operator.pow),
1298 BinaryBuiltinWrapper(operator.rshift),
1299 BinaryBuiltinWrapper(operator.sub),
1300 BinaryBuiltinWrapper(operator.truediv),
1301 BinaryBuiltinWrapper(operator.xor),
1303 UnaryBuiltinWrapper(abs),
1304 UnaryBuiltinWrapper(bool),
1305 UnaryBuiltinWrapper(floor),
1306 UnaryBuiltinWrapper(operator.neg),
1307 UnaryBuiltinWrapper(operator.not_),
1308 UnaryBuiltinWrapper(sqrt),
1311 del F # Otherwise Sphinx autodoc will pick it up.
1314 add_aliases(_dictionary, ALIASES)
1317 DefinitionWrapper.add_definitions(definitions, _dictionary)