This repo is not current. Development has moved from Hg to Git. For the latest code use the "Source Code" tab above to go to the "Thun" git repo or navigate to:
https://osdn.net/projects/joypy/scm/git/Thun
リビジョン | 239cf1409d4f8aa8cdb3b88269464bdbe69a576e (tree) |
---|---|
日時 | 2020-05-20 07:34:20 |
作者 | Simon Forman <sforman@hush...> |
コミッター | Simon Forman |
Remove the Joy code.
It's now a dependency that you have to get from e.g. PyPI.
@@ -1,14 +1,14 @@ | ||
1 | 1 | # My make-fu style is old and tired. I just want to have a few helper commands. |
2 | 2 | |
3 | 3 | TESTDIR = ./test00 |
4 | -VERSION = 0.4.0 | |
4 | +VERSION = 0.1.0 | |
5 | 5 | WEBSERVER = sforman@shell.osdn.net |
6 | 6 | |
7 | 7 | .PHONY: clean sdist test |
8 | 8 | |
9 | 9 | |
10 | 10 | clean: |
11 | - $(RM) -r Thun.egg-info/ dist/ build/ __pycache__/ $(TESTDIR) | |
11 | + $(RM) -r Xerblin.egg-info/ dist/ build/ __pycache__/ $(TESTDIR) | |
12 | 12 | find . -name '*.pyc' | xargs $(RM) |
13 | 13 | |
14 | 14 | sdist: |
@@ -21,5 +21,6 @@ | ||
21 | 21 | $(RM) -r $(TESTDIR) |
22 | 22 | virtualenv --system-site-packages --never-download $(TESTDIR) |
23 | 23 | . $(TESTDIR)/bin/activate && \ |
24 | - pip install --no-cache-dir --no-index ./dist/Thun-$(VERSION).tar.gz | |
24 | + pip install setuptools && \ | |
25 | + pip install --no-cache-dir --no-index ./dist/Xerblin-$(VERSION).tar.gz | |
25 | 26 | echo "Type: source $(TESTDIR)/bin/activate" |
@@ -1,34 +0,0 @@ | ||
1 | -# -*- coding: utf-8 -*- | |
2 | -# | |
3 | -# Copyright © 2014, 2015, 2017 Simon Forman | |
4 | -# | |
5 | -# This file is part of Thun | |
6 | -# | |
7 | -# Thun is free software: you can redistribute it and/or modify | |
8 | -# it under the terms of the GNU General Public License as published by | |
9 | -# the Free Software Foundation, either version 3 of the License, or | |
10 | -# (at your option) any later version. | |
11 | -# | |
12 | -# Thun is distributed in the hope that it will be useful, | |
13 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | -# GNU General Public License for more details. | |
16 | -# | |
17 | -# You should have received a copy of the GNU General Public License | |
18 | -# along with Thun. If not see <http://www.gnu.org/licenses/>. | |
19 | -# | |
20 | -from __future__ import print_function | |
21 | -from .library import initialize | |
22 | -from .joy import repl | |
23 | -from .utils import pretty_print # Inscribe trace command. | |
24 | - | |
25 | - | |
26 | -print('''\ | |
27 | -Thun - Copyright © 2017 Simon Forman | |
28 | -This program comes with ABSOLUTELY NO WARRANTY; for details type "warranty". | |
29 | -This is free software, and you are welcome to redistribute it | |
30 | -under certain conditions; type "sharing" for details. | |
31 | -Type "words" to see a list of all words, and "[<name>] help" to print the | |
32 | -docs for a word. | |
33 | -''') | |
34 | -stack = repl(dictionary=initialize()) |
@@ -1,111 +0,0 @@ | ||
1 | -# -*- coding: utf-8 -*- | |
2 | -# | |
3 | -# Copyright © 2014, 2015, 2017, 2018 Simon Forman | |
4 | -# | |
5 | -# This file is part of Thun | |
6 | -# | |
7 | -# Thun is free software: you can redistribute it and/or modify | |
8 | -# it under the terms of the GNU General Public License as published by | |
9 | -# the Free Software Foundation, either version 3 of the License, or | |
10 | -# (at your option) any later version. | |
11 | -# | |
12 | -# Thun is distributed in the hope that it will be useful, | |
13 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | -# GNU General Public License for more details. | |
16 | -# | |
17 | -# You should have received a copy of the GNU General Public License | |
18 | -# along with Thun. If not see <http://www.gnu.org/licenses/>. | |
19 | -# | |
20 | -''' | |
21 | -This module implements an interpreter for a dialect of Joy that | |
22 | -attempts to stay very close to the spirit of Joy but does not precisely | |
23 | -match the behaviour of the original version(s) written in C. | |
24 | - | |
25 | -''' | |
26 | -from __future__ import print_function | |
27 | -from builtins import input | |
28 | -from traceback import print_exc, format_exc | |
29 | -from .parser import text_to_expression, ParseError, Symbol | |
30 | -from .utils.stack import stack_to_string | |
31 | - | |
32 | - | |
33 | -def joy(stack, expression, dictionary, viewer=None): | |
34 | - '''Evaluate a Joy expression on a stack. | |
35 | - | |
36 | - This function iterates through a sequence of terms which are either | |
37 | - literals (strings, numbers, sequences of terms) or function symbols. | |
38 | - Literals are put onto the stack and functions are looked up in the | |
39 | - disctionary and executed. | |
40 | - | |
41 | - The viewer is a function that is called with the stack and expression | |
42 | - on every iteration, its return value is ignored. | |
43 | - | |
44 | - :param stack stack: The stack. | |
45 | - :param stack expression: The expression to evaluate. | |
46 | - :param dict dictionary: A ``dict`` mapping names to Joy functions. | |
47 | - :param function viewer: Optional viewer function. | |
48 | - :rtype: (stack, (), dictionary) | |
49 | - | |
50 | - ''' | |
51 | - while expression: | |
52 | - | |
53 | - if viewer: viewer(stack, expression) | |
54 | - | |
55 | - term, expression = expression | |
56 | - if isinstance(term, Symbol): | |
57 | - term = dictionary[term] | |
58 | - stack, expression, dictionary = term(stack, expression, dictionary) | |
59 | - else: | |
60 | - stack = term, stack | |
61 | - | |
62 | - if viewer: viewer(stack, expression) | |
63 | - return stack, expression, dictionary | |
64 | - | |
65 | - | |
66 | -def run(text, stack, dictionary, viewer=None): | |
67 | - ''' | |
68 | - Return the stack resulting from running the Joy code text on the stack. | |
69 | - | |
70 | - :param str text: Joy code. | |
71 | - :param stack stack: The stack. | |
72 | - :param dict dictionary: A ``dict`` mapping names to Joy functions. | |
73 | - :param function viewer: Optional viewer function. | |
74 | - :rtype: (stack, (), dictionary) | |
75 | - | |
76 | - ''' | |
77 | - expression = text_to_expression(text) | |
78 | - return joy(stack, expression, dictionary, viewer) | |
79 | - | |
80 | - | |
81 | -def repl(stack=(), dictionary=None): | |
82 | - ''' | |
83 | - Read-Evaluate-Print Loop | |
84 | - | |
85 | - Accept input and run it on the stack, loop. | |
86 | - | |
87 | - :param stack stack: The stack. | |
88 | - :param dict dictionary: A ``dict`` mapping names to Joy functions. | |
89 | - :rtype: stack | |
90 | - | |
91 | - ''' | |
92 | - if dictionary is None: | |
93 | - dictionary = {} | |
94 | - try: | |
95 | - while True: | |
96 | - print() | |
97 | - print(stack_to_string(stack), '<-top') | |
98 | - print() | |
99 | - try: | |
100 | - text = input('joy? ') | |
101 | - except (EOFError, KeyboardInterrupt): | |
102 | - break | |
103 | - try: | |
104 | - stack, _, dictionary = run(text, stack, dictionary) | |
105 | - except: | |
106 | - exc = format_exc() # Capture the exception. | |
107 | - print(exc) # Print the original exception. | |
108 | - except: | |
109 | - print_exc() | |
110 | - print() | |
111 | - return stack |
@@ -1,1683 +0,0 @@ | ||
1 | -# -*- coding: utf-8 -*- | |
2 | -# | |
3 | -# Copyright © 2014, 2015, 2017, 2018 Simon Forman | |
4 | -# | |
5 | -# This file is part of Thun | |
6 | -# | |
7 | -# Thun is free software: you can redistribute it and/or modify | |
8 | -# it under the terms of the GNU General Public License as published by | |
9 | -# the Free Software Foundation, either version 3 of the License, or | |
10 | -# (at your option) any later version. | |
11 | -# | |
12 | -# Thun is distributed in the hope that it will be useful, | |
13 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | -# GNU General Public License for more details. | |
16 | -# | |
17 | -# You should have received a copy of the GNU General Public License | |
18 | -# along with Thun. If not see <http://www.gnu.org/licenses/>. | |
19 | -# | |
20 | -''' | |
21 | -This module contains the Joy function infrastructure and a library of | |
22 | -functions. Its main export is a Python function initialize() that | |
23 | -returns a dictionary of Joy functions suitable for use with the joy() | |
24 | -function. | |
25 | -''' | |
26 | -from __future__ import print_function | |
27 | -from builtins import map, object, range, zip | |
28 | -from logging import getLogger | |
29 | - | |
30 | -_log = getLogger(__name__) | |
31 | -_log.info('Loading library.') | |
32 | - | |
33 | -from inspect import getdoc | |
34 | -from functools import wraps | |
35 | -from itertools import count | |
36 | -from inspect import getmembers, isfunction | |
37 | -import operator, math | |
38 | - | |
39 | -from .parser import text_to_expression, Symbol | |
40 | -from .utils.stack import expression_to_string, list_to_stack, iter_stack, pick, concat | |
41 | -import sys | |
42 | -if sys.version_info.major < 3: | |
43 | - from .utils.brutal_hackery import rename_code_object | |
44 | -else: | |
45 | - rename_code_object = lambda _: lambda f: f | |
46 | - | |
47 | -from .utils import generated_library as genlib | |
48 | -from .utils.types import ( | |
49 | - compose, | |
50 | - ef, | |
51 | - stack_effect, | |
52 | - AnyJoyType, | |
53 | - AnyStarJoyType, | |
54 | - BooleanJoyType, | |
55 | - NumberJoyType, | |
56 | - NumberStarJoyType, | |
57 | - StackJoyType, | |
58 | - StackStarJoyType, | |
59 | - FloatJoyType, | |
60 | - IntJoyType, | |
61 | - SymbolJoyType, | |
62 | - CombinatorJoyType, | |
63 | - TextJoyType, | |
64 | - _functions, | |
65 | - FUNCTIONS, | |
66 | - infer, | |
67 | - infer_expression, | |
68 | - JoyTypeError, | |
69 | - combinator_effect, | |
70 | - poly_combinator_effect, | |
71 | - doc_from_stack_effect, | |
72 | - ) | |
73 | - | |
74 | - | |
75 | -HELP_TEMPLATE = '''\ | |
76 | - | |
77 | -==== Help on %s ==== | |
78 | - | |
79 | -%s | |
80 | - | |
81 | ----- end (%s) | |
82 | -''' | |
83 | - | |
84 | - | |
85 | -_SYM_NUMS = lambda c=count(): next(c) | |
86 | -_COMB_NUMS = lambda c=count(): next(c) | |
87 | - | |
88 | - | |
89 | -_R = list(range(10)) | |
90 | -A = a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = list(map(AnyJoyType, _R)) | |
91 | -B = b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = list(map(BooleanJoyType, _R)) | |
92 | -N = n0, n1, n2, n3, n4, n5, n6, n7, n8, n9 = list(map(NumberJoyType, _R)) | |
93 | -S = s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 = list(map(StackJoyType, _R)) | |
94 | -F = f0, f1, f2, f3, f4, f5, f6, f7, f8, f9 = list(map(FloatJoyType, _R)) | |
95 | -I = i0, i1, i2, i3, i4, i5, i6, i7, i8, i9 = list(map(IntJoyType, _R)) | |
96 | -T = t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 = list(map(TextJoyType, _R)) | |
97 | - | |
98 | - | |
99 | -_R = list(range(1, 11)) | |
100 | -As = list(map(AnyStarJoyType, _R)) | |
101 | -Ns = list(map(NumberStarJoyType, _R)) | |
102 | -Ss = list(map(StackStarJoyType, _R)) | |
103 | - | |
104 | - | |
105 | -# "sec": stack effect comment, like in Forth. | |
106 | -sec0 = stack_effect(t1)() | |
107 | -sec1 = stack_effect(s0, i1)(s1) | |
108 | -sec2 = stack_effect(s0, i1)(a1) | |
109 | -sec_binary_cmp = stack_effect(n1, n2)(b1) | |
110 | -sec_binary_ints = stack_effect(i1, i2)(i3) | |
111 | -sec_binary_logic = stack_effect(b1, b2)(b3) | |
112 | -sec_binary_math = stack_effect(n1, n2)(n3) | |
113 | -sec_unary_logic = stack_effect(a1)(b1) | |
114 | -sec_unary_math = stack_effect(n1)(n2) | |
115 | -sec_Ns_math = stack_effect((Ns[1], s1),)(n0) | |
116 | - | |
117 | -# This is the main dict we're building. | |
118 | -_dictionary = {} | |
119 | - | |
120 | - | |
121 | -def inscribe(function): | |
122 | - '''A decorator to inscribe functions into the default dictionary.''' | |
123 | - _dictionary[function.name] = function | |
124 | - return function | |
125 | - | |
126 | - | |
127 | -def initialize(): | |
128 | - '''Return a dictionary of Joy functions for use with joy().''' | |
129 | - return _dictionary.copy() | |
130 | - | |
131 | - | |
132 | -ALIASES = ( | |
133 | - ('add', ['+']), | |
134 | - ('and', ['&']), | |
135 | - ('bool', ['truthy']), | |
136 | - ('mul', ['*']), | |
137 | - ('floordiv', ['/floor', '//']), | |
138 | - ('floor', ['round']), | |
139 | - ('truediv', ['/', 'div']), | |
140 | - ('mod', ['%', 'rem', 'remainder', 'modulus']), | |
141 | - ('eq', ['=']), | |
142 | - ('ge', ['>=']), | |
143 | - ('getitem', ['pick', 'at']), | |
144 | - ('gt', ['>']), | |
145 | - ('le', ['<=']), | |
146 | - ('lshift', ['<<']), | |
147 | - ('lt', ['<']), | |
148 | - ('ne', ['<>', '!=']), | |
149 | - ('rshift', ['>>']), | |
150 | - ('sub', ['-']), | |
151 | - ('xor', ['^']), | |
152 | - ('succ', ['++']), | |
153 | - ('pred', ['--']), | |
154 | - ('rolldown', ['roll<']), | |
155 | - ('rollup', ['roll>']), | |
156 | - ('eh', ['?']), | |
157 | - ('id', [u'•']), | |
158 | - ) | |
159 | - | |
160 | - | |
161 | -def add_aliases(D, A): | |
162 | - ''' | |
163 | - Given a dict and a iterable of (name, [alias, ...]) pairs, create | |
164 | - additional entries in the dict mapping each alias to the named function | |
165 | - if it's in the dict. Aliases for functions not in the dict are ignored. | |
166 | - ''' | |
167 | - for name, aliases in A: | |
168 | - try: | |
169 | - F = D[name] | |
170 | - except KeyError: | |
171 | - continue | |
172 | - for alias in aliases: | |
173 | - D[alias] = F | |
174 | - | |
175 | - | |
176 | -def yin_functions(): | |
177 | - ''' | |
178 | - Return a dict of named stack effects. | |
179 | - | |
180 | - "Yin" functions are those that only rearrange items in stacks and | |
181 | - can be defined completely by their stack effects. This means they | |
182 | - can be auto-compiled. | |
183 | - ''' | |
184 | - # pylint: disable=unused-variable | |
185 | - cons = ef(a1, s0)((a1, s0)) | |
186 | - ccons = compose(cons, cons) | |
187 | - dup = ef(a1)(a1, a1) | |
188 | - dupd = ef(a2, a1)(a2, a2, a1) | |
189 | - dupdd = ef(a3, a2, a1)(a3, a3, a2, a1) | |
190 | - first = ef((a1, s1),)(a1,) | |
191 | - over = ef(a2, a1)(a2, a1, a2) | |
192 | - pop = ef(a1)() | |
193 | - popd = ef(a2, a1,)(a1) | |
194 | - popdd = ef(a3, a2, a1,)(a2, a1,) | |
195 | - popop = ef(a2, a1,)() | |
196 | - popopd = ef(a3, a2, a1,)(a1) | |
197 | - popopdd = ef(a4, a3, a2, a1,)(a2, a1) | |
198 | - rest = ef((a1, s0),)(s0,) | |
199 | - rolldown = ef(a1, a2, a3)(a2, a3, a1) | |
200 | - rollup = ef(a1, a2, a3)(a3, a1, a2) | |
201 | - rrest = compose(rest, rest) | |
202 | - second = compose(rest, first) | |
203 | - stack = s0, (s0, s0) | |
204 | - swaack = (s1, s0), (s0, s1) | |
205 | - swap = ef(a1, a2)(a2, a1) | |
206 | - swons = compose(swap, cons) | |
207 | - third = compose(rest, second) | |
208 | - tuck = ef(a2, a1)(a1, a2, a1) | |
209 | - uncons = ef((a1, s0),)(a1, s0) | |
210 | - unswons = compose(uncons, swap) | |
211 | - stuncons = compose(stack, uncons) | |
212 | - stununcons = compose(stack, uncons, uncons) | |
213 | - unit = ef(a1)((a1, ())) | |
214 | - | |
215 | - first_two = compose(uncons, uncons, pop) | |
216 | - fourth = compose(rest, third) | |
217 | - | |
218 | - _Tree_add_Ee = compose(pop, swap, rolldown, rrest, ccons) | |
219 | - _Tree_get_E = compose(popop, second) | |
220 | - _Tree_delete_clear_stuff = compose(rollup, popop, rest) | |
221 | - _Tree_delete_R0 = compose(over, first, swap, dup) | |
222 | - | |
223 | - return locals() | |
224 | - | |
225 | - | |
226 | -definitions = ('''\ | |
227 | -? dup truthy | |
228 | -*fraction [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons | |
229 | -*fraction0 concat [[swap] dip * [*] dip] infra | |
230 | -anamorphism [pop []] swap [dip swons] genrec | |
231 | -average [sum 1.0 *] [size] cleave / | |
232 | -binary nullary [popop] dip | |
233 | -cleave fork [popd] dip | |
234 | -codireco cons dip rest cons | |
235 | -dinfrirst dip infra first | |
236 | -unstack ? [uncons ?] loop pop | |
237 | -down_to_zero [0 >] [dup --] while | |
238 | -dupdipd dup dipd | |
239 | -enstacken stack [clear] dip | |
240 | -flatten [] swap [concat] step | |
241 | -fork [i] app2 | |
242 | -gcd 1 [tuck modulus dup 0 >] loop pop | |
243 | -ifte [nullary not] dipd branch | |
244 | -ii [dip] dupdip i | |
245 | -least_fraction dup [gcd] infra [div] concat map | |
246 | -make_generator [codireco] ccons | |
247 | -nullary [stack] dinfrirst | |
248 | -of swap at | |
249 | -pam [i] map | |
250 | -tailrec [i] genrec | |
251 | -product 1 swap [*] step | |
252 | -quoted [unit] dip | |
253 | -range [0 <=] [1 - dup] anamorphism | |
254 | -range_to_zero unit [down_to_zero] infra | |
255 | -run [] swap infra | |
256 | -size 0 swap [pop ++] step | |
257 | -sqr dup mul | |
258 | -step_zero 0 roll> step | |
259 | -swoncat swap concat | |
260 | -ternary unary [popop] dip | |
261 | -unary nullary popd | |
262 | -unquoted [i] dip | |
263 | -while swap [nullary] cons dup dipd concat loop | |
264 | -''' | |
265 | -# | |
266 | -# | |
267 | -# ifte == [nullary] dipd swap branch | |
268 | -# genrec == [[genrec] cons cons cons cons] nullary swons concat ifte | |
269 | - | |
270 | -# Another definition for while. FWIW | |
271 | -# while == over [[i] dip nullary] ccons [nullary] dip loop | |
272 | - | |
273 | -##ccons == cons cons | |
274 | -##unit == [] cons | |
275 | -##second == rest first | |
276 | -##third == rest rest first | |
277 | -##swons == swap cons | |
278 | - | |
279 | -##Zipper | |
280 | -##z-down == [] swap uncons swap | |
281 | -##z-up == swons swap shunt | |
282 | -##z-right == [swons] cons dip uncons swap | |
283 | -##z-left == swons [uncons swap] dip swap | |
284 | - | |
285 | -##Quadratic Formula | |
286 | -##divisor == popop 2 * | |
287 | -##minusb == pop neg | |
288 | -##radical == swap dup * rollup * 4 * - sqrt | |
289 | -##root1 == + swap / | |
290 | -##root2 == - swap / | |
291 | -##q0 == [[divisor] [minusb] [radical]] pam | |
292 | -##q1 == [[root1] [root2]] pam | |
293 | -##quadratic == [q0] ternary i [q1] ternary | |
294 | - | |
295 | -# Project Euler | |
296 | -##'''\ | |
297 | -##PE1.1 == + dup [+] dip | |
298 | -##PE1.2 == dup [3 & PE1.1] dip 2 >> | |
299 | -##PE1.3 == 14811 swap [PE1.2] times pop | |
300 | -##PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop | |
301 | -##''' | |
302 | -#PE1.2 == [PE1.1] step | |
303 | -#PE1 == 0 0 66 [[3 2 1 3 1 2 3] PE1.2] times [3 2 1 3] PE1.2 pop | |
304 | -) | |
305 | - | |
306 | - | |
307 | -def FunctionWrapper(f): | |
308 | - '''Set name attribute.''' | |
309 | - if not f.__doc__: | |
310 | - raise ValueError('Function %s must have doc string.' % f.__name__) | |
311 | - f.name = f.__name__.rstrip('_') # Don't shadow builtins. | |
312 | - return f | |
313 | - | |
314 | - | |
315 | -def SimpleFunctionWrapper(f): | |
316 | - ''' | |
317 | - Wrap functions that take and return just a stack. | |
318 | - ''' | |
319 | - @FunctionWrapper | |
320 | - @wraps(f) | |
321 | - @rename_code_object(f.__name__) | |
322 | - def inner(stack, expression, dictionary): | |
323 | - return f(stack), expression, dictionary | |
324 | - return inner | |
325 | - | |
326 | - | |
327 | -def BinaryBuiltinWrapper(f): | |
328 | - ''' | |
329 | - Wrap functions that take two arguments and return a single result. | |
330 | - ''' | |
331 | - @FunctionWrapper | |
332 | - @wraps(f) | |
333 | - @rename_code_object(f.__name__) | |
334 | - def inner(stack, expression, dictionary): | |
335 | - (a, (b, stack)) = stack | |
336 | - result = f(b, a) | |
337 | - return (result, stack), expression, dictionary | |
338 | - return inner | |
339 | - | |
340 | - | |
341 | -def UnaryBuiltinWrapper(f): | |
342 | - ''' | |
343 | - Wrap functions that take one argument and return a single result. | |
344 | - ''' | |
345 | - @FunctionWrapper | |
346 | - @wraps(f) | |
347 | - @rename_code_object(f.__name__) | |
348 | - def inner(stack, expression, dictionary): | |
349 | - (a, stack) = stack | |
350 | - result = f(a) | |
351 | - return (result, stack), expression, dictionary | |
352 | - return inner | |
353 | - | |
354 | - | |
355 | -class DefinitionWrapper(object): | |
356 | - ''' | |
357 | - Provide implementation of defined functions, and some helper methods. | |
358 | - ''' | |
359 | - | |
360 | - def __init__(self, name, body_text, doc=None): | |
361 | - self.name = self.__name__ = name | |
362 | - self.body = text_to_expression(body_text) | |
363 | - self._body = tuple(iter_stack(self.body)) | |
364 | - self.__doc__ = doc or body_text | |
365 | - self._compiled = None | |
366 | - | |
367 | - def __call__(self, stack, expression, dictionary): | |
368 | - if self._compiled: | |
369 | - return self._compiled(stack, expression, dictionary) # pylint: disable=E1102 | |
370 | - expression = list_to_stack(self._body, expression) | |
371 | - return stack, expression, dictionary | |
372 | - | |
373 | - @classmethod | |
374 | - def parse_definition(class_, defi): | |
375 | - ''' | |
376 | - Given some text describing a Joy function definition parse it and | |
377 | - return a DefinitionWrapper. | |
378 | - ''' | |
379 | - return class_(*(n.strip() for n in defi.split(None, 1))) | |
380 | - | |
381 | - @classmethod | |
382 | - def add_definitions(class_, defs, dictionary): | |
383 | - ''' | |
384 | - Scan multi-line string defs for definitions and add them to the | |
385 | - dictionary. | |
386 | - ''' | |
387 | - for definition in _text_to_defs(defs): | |
388 | - class_.add_def(definition, dictionary) | |
389 | - | |
390 | - @classmethod | |
391 | - def add_def(class_, definition, dictionary, fail_fails=False): | |
392 | - ''' | |
393 | - Add the definition to the dictionary. | |
394 | - ''' | |
395 | - F = class_.parse_definition(definition) | |
396 | - _log.info('Adding definition %s := %s', F.name, expression_to_string(F.body)) | |
397 | - dictionary[F.name] = F | |
398 | - | |
399 | - @classmethod | |
400 | - def load_definitions(class_, filename, dictionary): | |
401 | - with open(filename) as f: | |
402 | - lines = [line for line in f if '==' in line] | |
403 | - for line in lines: | |
404 | - class_.add_def(line, dictionary) | |
405 | - | |
406 | - | |
407 | -def _text_to_defs(text): | |
408 | - return ( | |
409 | - line.strip() | |
410 | - for line in text.splitlines() | |
411 | - if not line.startswith('#') | |
412 | - ) | |
413 | - | |
414 | - | |
415 | -# | |
416 | -# Functions | |
417 | -# | |
418 | - | |
419 | - | |
420 | -@inscribe | |
421 | -@sec0 | |
422 | -@FunctionWrapper | |
423 | -def inscribe_(stack, expression, dictionary): | |
424 | - ''' | |
425 | - Create a new Joy function definition in the Joy dictionary. A | |
426 | - definition is given as a string with a name followed by a double | |
427 | - equal sign then one or more Joy functions, the body. for example: | |
428 | - | |
429 | - sqr == dup mul | |
430 | - | |
431 | - If you want the definition to persist over restarts, enter it into | |
432 | - the definitions.txt resource. | |
433 | - ''' | |
434 | - definition, stack = stack | |
435 | - DefinitionWrapper.add_def(definition, dictionary, fail_fails=True) | |
436 | - return stack, expression, dictionary | |
437 | - | |
438 | - | |
439 | -@inscribe | |
440 | -@SimpleFunctionWrapper | |
441 | -def parse(stack): | |
442 | - '''Parse the string on the stack to a Joy expression.''' | |
443 | - text, stack = stack | |
444 | - expression = text_to_expression(text) | |
445 | - return expression, stack | |
446 | - | |
447 | - | |
448 | -@inscribe | |
449 | -@SimpleFunctionWrapper | |
450 | -def infer_(stack): | |
451 | - '''Attempt to infer the stack effect of a Joy expression.''' | |
452 | - E, stack = stack | |
453 | - effects = infer_expression(E) | |
454 | - e = list_to_stack([(fi, (fo, ())) for fi, fo in effects]) | |
455 | - return e, stack | |
456 | - | |
457 | - | |
458 | -@inscribe | |
459 | -@sec2 | |
460 | -@SimpleFunctionWrapper | |
461 | -def getitem(stack): | |
462 | - ''' | |
463 | - :: | |
464 | - | |
465 | - getitem == drop first | |
466 | - | |
467 | - Expects an integer and a quote on the stack and returns the item at the | |
468 | - nth position in the quote counting from 0. | |
469 | - :: | |
470 | - | |
471 | - [a b c d] 0 getitem | |
472 | - ------------------------- | |
473 | - a | |
474 | - | |
475 | - ''' | |
476 | - n, (Q, stack) = stack | |
477 | - return pick(Q, n), stack | |
478 | - | |
479 | - | |
480 | -@inscribe | |
481 | -@sec1 | |
482 | -@SimpleFunctionWrapper | |
483 | -def drop(stack): | |
484 | - ''' | |
485 | - :: | |
486 | - | |
487 | - drop == [rest] times | |
488 | - | |
489 | - Expects an integer and a quote on the stack and returns the quote with | |
490 | - n items removed off the top. | |
491 | - :: | |
492 | - | |
493 | - [a b c d] 2 drop | |
494 | - ---------------------- | |
495 | - [c d] | |
496 | - | |
497 | - ''' | |
498 | - n, (Q, stack) = stack | |
499 | - while n > 0: | |
500 | - try: | |
501 | - _, Q = Q | |
502 | - except ValueError: | |
503 | - raise IndexError | |
504 | - n -= 1 | |
505 | - return Q, stack | |
506 | - | |
507 | - | |
508 | -@inscribe | |
509 | -@sec1 | |
510 | -@SimpleFunctionWrapper | |
511 | -def take(stack): | |
512 | - ''' | |
513 | - Expects an integer and a quote on the stack and returns the quote with | |
514 | - just the top n items in reverse order (because that's easier and you can | |
515 | - use reverse if needed.) | |
516 | - :: | |
517 | - | |
518 | - [a b c d] 2 take | |
519 | - ---------------------- | |
520 | - [b a] | |
521 | - | |
522 | - ''' | |
523 | - n, (Q, stack) = stack | |
524 | - x = () | |
525 | - while n > 0: | |
526 | - try: | |
527 | - item, Q = Q | |
528 | - except ValueError: | |
529 | - raise IndexError | |
530 | - x = item, x | |
531 | - n -= 1 | |
532 | - return x, stack | |
533 | - | |
534 | - | |
535 | -@inscribe | |
536 | -@SimpleFunctionWrapper | |
537 | -def choice(stack): | |
538 | - ''' | |
539 | - Use a Boolean value to select one of two items. | |
540 | - :: | |
541 | - | |
542 | - A B False choice | |
543 | - ---------------------- | |
544 | - A | |
545 | - | |
546 | - | |
547 | - A B True choice | |
548 | - --------------------- | |
549 | - B | |
550 | - | |
551 | - Currently Python semantics are used to evaluate the "truthiness" of the | |
552 | - Boolean value (so empty string, zero, etc. are counted as false, etc.) | |
553 | - ''' | |
554 | - (if_, (then, (else_, stack))) = stack | |
555 | - return then if if_ else else_, stack | |
556 | - | |
557 | - | |
558 | -@inscribe | |
559 | -@SimpleFunctionWrapper | |
560 | -def select(stack): | |
561 | - ''' | |
562 | - Use a Boolean value to select one of two items from a sequence. | |
563 | - :: | |
564 | - | |
565 | - [A B] False select | |
566 | - ------------------------ | |
567 | - A | |
568 | - | |
569 | - | |
570 | - [A B] True select | |
571 | - ----------------------- | |
572 | - B | |
573 | - | |
574 | - The sequence can contain more than two items but not fewer. | |
575 | - Currently Python semantics are used to evaluate the "truthiness" of the | |
576 | - Boolean value (so empty string, zero, etc. are counted as false, etc.) | |
577 | - ''' | |
578 | - (flag, (choices, stack)) = stack | |
579 | - (else_, (then, _)) = choices | |
580 | - return then if flag else else_, stack | |
581 | - | |
582 | - | |
583 | -@inscribe | |
584 | -@sec_Ns_math | |
585 | -@SimpleFunctionWrapper | |
586 | -def max_(S): | |
587 | - '''Given a list find the maximum.''' | |
588 | - tos, stack = S | |
589 | - return max(iter_stack(tos)), stack | |
590 | - | |
591 | - | |
592 | -@inscribe | |
593 | -@sec_Ns_math | |
594 | -@SimpleFunctionWrapper | |
595 | -def min_(S): | |
596 | - '''Given a list find the minimum.''' | |
597 | - tos, stack = S | |
598 | - return min(iter_stack(tos)), stack | |
599 | - | |
600 | - | |
601 | -@inscribe | |
602 | -@sec_Ns_math | |
603 | -@SimpleFunctionWrapper | |
604 | -def sum_(S): | |
605 | - '''Given a quoted sequence of numbers return the sum. | |
606 | - | |
607 | - sum == 0 swap [+] step | |
608 | - ''' | |
609 | - tos, stack = S | |
610 | - return sum(iter_stack(tos)), stack | |
611 | - | |
612 | - | |
613 | -@inscribe | |
614 | -@SimpleFunctionWrapper | |
615 | -def remove(S): | |
616 | - ''' | |
617 | - Expects an item on the stack and a quote under it and removes that item | |
618 | - from the the quote. The item is only removed once. | |
619 | - :: | |
620 | - | |
621 | - [1 2 3 1] 1 remove | |
622 | - ------------------------ | |
623 | - [2 3 1] | |
624 | - | |
625 | - ''' | |
626 | - (tos, (second, stack)) = S | |
627 | - l = list(iter_stack(second)) | |
628 | - l.remove(tos) | |
629 | - return list_to_stack(l), stack | |
630 | - | |
631 | - | |
632 | -@inscribe | |
633 | -@SimpleFunctionWrapper | |
634 | -def unique(S): | |
635 | - '''Given a list remove duplicate items.''' | |
636 | - tos, stack = S | |
637 | - I = list(iter_stack(tos)) | |
638 | - return list_to_stack(sorted(set(I), key=I.index)), stack | |
639 | - | |
640 | - | |
641 | -@inscribe | |
642 | -@SimpleFunctionWrapper | |
643 | -def sort_(S): | |
644 | - '''Given a list return it sorted.''' | |
645 | - tos, stack = S | |
646 | - return list_to_stack(sorted(iter_stack(tos))), stack | |
647 | - | |
648 | - | |
649 | -_functions['clear'] = s0, s1 | |
650 | -@inscribe | |
651 | -@SimpleFunctionWrapper | |
652 | -def clear(stack): | |
653 | - '''Clear everything from the stack. | |
654 | - :: | |
655 | - | |
656 | - clear == stack [pop stack] loop | |
657 | - | |
658 | - ... clear | |
659 | - --------------- | |
660 | - | |
661 | - ''' | |
662 | - return () | |
663 | - | |
664 | - | |
665 | -@inscribe | |
666 | -@SimpleFunctionWrapper | |
667 | -def disenstacken(stack): | |
668 | - ''' | |
669 | - The disenstacken operator expects a list on top of the stack and makes that | |
670 | - the stack discarding the rest of the stack. | |
671 | - ''' | |
672 | - return stack[0] | |
673 | - | |
674 | - | |
675 | -@inscribe | |
676 | -@SimpleFunctionWrapper | |
677 | -def reverse(S): | |
678 | - '''Reverse the list on the top of the stack. | |
679 | - :: | |
680 | - | |
681 | - reverse == [] swap shunt | |
682 | - ''' | |
683 | - (tos, stack) = S | |
684 | - res = () | |
685 | - for term in iter_stack(tos): | |
686 | - res = term, res | |
687 | - return res, stack | |
688 | - | |
689 | - | |
690 | -@inscribe | |
691 | -@combinator_effect(_COMB_NUMS(), s7, s6) | |
692 | -@SimpleFunctionWrapper | |
693 | -def concat_(S): | |
694 | - '''Concatinate the two lists on the top of the stack. | |
695 | - :: | |
696 | - | |
697 | - [a b c] [d e f] concat | |
698 | - ---------------------------- | |
699 | - [a b c d e f] | |
700 | - | |
701 | -''' | |
702 | - (tos, (second, stack)) = S | |
703 | - return concat(second, tos), stack | |
704 | - | |
705 | - | |
706 | -@inscribe | |
707 | -@SimpleFunctionWrapper | |
708 | -def shunt(stack): | |
709 | - '''Like concat but reverses the top list into the second. | |
710 | - :: | |
711 | - | |
712 | - shunt == [swons] step == reverse swap concat | |
713 | - | |
714 | - [a b c] [d e f] shunt | |
715 | - --------------------------- | |
716 | - [f e d a b c] | |
717 | - | |
718 | - ''' | |
719 | - (tos, (second, stack)) = stack | |
720 | - while tos: | |
721 | - term, tos = tos | |
722 | - second = term, second | |
723 | - return second, stack | |
724 | - | |
725 | - | |
726 | -@inscribe | |
727 | -@SimpleFunctionWrapper | |
728 | -def zip_(S): | |
729 | - ''' | |
730 | - Replace the two lists on the top of the stack with a list of the pairs | |
731 | - from each list. The smallest list sets the length of the result list. | |
732 | - ''' | |
733 | - (tos, (second, stack)) = S | |
734 | - accumulator = [ | |
735 | - (a, (b, ())) | |
736 | - for a, b in zip(iter_stack(tos), iter_stack(second)) | |
737 | - ] | |
738 | - return list_to_stack(accumulator), stack | |
739 | - | |
740 | - | |
741 | -@inscribe | |
742 | -@sec_unary_math | |
743 | -@SimpleFunctionWrapper | |
744 | -def succ(S): | |
745 | - '''Increment TOS.''' | |
746 | - (tos, stack) = S | |
747 | - return tos + 1, stack | |
748 | - | |
749 | - | |
750 | -@inscribe | |
751 | -@sec_unary_math | |
752 | -@SimpleFunctionWrapper | |
753 | -def pred(S): | |
754 | - '''Decrement TOS.''' | |
755 | - (tos, stack) = S | |
756 | - return tos - 1, stack | |
757 | - | |
758 | - | |
759 | -@inscribe | |
760 | -@SimpleFunctionWrapper | |
761 | -def pm(stack): | |
762 | - ''' | |
763 | - Plus or minus | |
764 | - :: | |
765 | - | |
766 | - a b pm | |
767 | - ------------- | |
768 | - a+b a-b | |
769 | - | |
770 | - ''' | |
771 | - a, (b, stack) = stack | |
772 | - p, m, = b + a, b - a | |
773 | - return m, (p, stack) | |
774 | - | |
775 | - | |
776 | -def floor(n): | |
777 | - return int(math.floor(n)) | |
778 | - | |
779 | -floor.__doc__ = math.floor.__doc__ | |
780 | - | |
781 | - | |
782 | -@inscribe | |
783 | -@SimpleFunctionWrapper | |
784 | -def divmod_(S): | |
785 | - ''' | |
786 | - divmod(x, y) -> (quotient, remainder) | |
787 | - | |
788 | - Return the tuple (x//y, x%y). Invariant: div*y + mod == x. | |
789 | - ''' | |
790 | - a, (b, stack) = S | |
791 | - d, m = divmod(a, b) | |
792 | - return d, (m, stack) | |
793 | - | |
794 | - | |
795 | -def sqrt(a): | |
796 | - ''' | |
797 | - Return the square root of the number a. | |
798 | - Negative numbers return complex roots. | |
799 | - ''' | |
800 | - try: | |
801 | - r = math.sqrt(a) | |
802 | - except ValueError: | |
803 | - assert a < 0, repr(a) | |
804 | - r = math.sqrt(-a) * 1j | |
805 | - return r | |
806 | - | |
807 | - | |
808 | -#def execute(S): | |
809 | -# (text, stack) = S | |
810 | -# if isinstance(text, str): | |
811 | -# return run(text, stack) | |
812 | -# return stack | |
813 | - | |
814 | - | |
815 | -@inscribe | |
816 | -@SimpleFunctionWrapper | |
817 | -def id_(stack): | |
818 | - '''The identity function.''' | |
819 | - return stack | |
820 | - | |
821 | - | |
822 | -@inscribe | |
823 | -@SimpleFunctionWrapper | |
824 | -def void(stack): | |
825 | - '''True if the form on TOS is void otherwise False.''' | |
826 | - form, stack = stack | |
827 | - return _void(form), stack | |
828 | - | |
829 | - | |
830 | -def _void(form): | |
831 | - return any(not _void(i) for i in iter_stack(form)) | |
832 | - | |
833 | - | |
834 | - | |
835 | -## transpose | |
836 | -## sign | |
837 | -## take | |
838 | - | |
839 | - | |
840 | -@inscribe | |
841 | -@FunctionWrapper | |
842 | -def words(stack, expression, dictionary): | |
843 | - '''Print all the words in alphabetical order.''' | |
844 | - print(' '.join(sorted(dictionary))) | |
845 | - return stack, expression, dictionary | |
846 | - | |
847 | - | |
848 | -@inscribe | |
849 | -@FunctionWrapper | |
850 | -def sharing(stack, expression, dictionary): | |
851 | - '''Print redistribution information.''' | |
852 | - print("You may convey verbatim copies of the Program's source code as" | |
853 | - ' you receive it, in any medium, provided that you conspicuously' | |
854 | - ' and appropriately publish on each copy an appropriate copyright' | |
855 | - ' notice; keep intact all notices stating that this License and' | |
856 | - ' any non-permissive terms added in accord with section 7 apply' | |
857 | - ' to the code; keep intact all notices of the absence of any' | |
858 | - ' warranty; and give all recipients a copy of this License along' | |
859 | - ' with the Program.' | |
860 | - ' You should have received a copy of the GNU General Public License' | |
861 | - ' along with Thun. If not see <http://www.gnu.org/licenses/>.') | |
862 | - return stack, expression, dictionary | |
863 | - | |
864 | - | |
865 | -@inscribe | |
866 | -@FunctionWrapper | |
867 | -def warranty(stack, expression, dictionary): | |
868 | - '''Print warranty information.''' | |
869 | - print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY' | |
870 | - ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE' | |
871 | - ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM' | |
872 | - ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR' | |
873 | - ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES' | |
874 | - ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE' | |
875 | - ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS' | |
876 | - ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE' | |
877 | - ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.') | |
878 | - return stack, expression, dictionary | |
879 | - | |
880 | - | |
881 | -# def simple_manual(stack): | |
882 | -# ''' | |
883 | -# Print words and help for each word. | |
884 | -# ''' | |
885 | -# for name, f in sorted(FUNCTIONS.items()): | |
886 | -# d = getdoc(f) | |
887 | -# boxline = '+%s+' % ('-' * (len(name) + 2)) | |
888 | -# print('\n'.join(( | |
889 | -# boxline, | |
890 | -# '| %s |' % (name,), | |
891 | -# boxline, | |
892 | -# d if d else ' ...', | |
893 | -# '', | |
894 | -# '--' * 40, | |
895 | -# '', | |
896 | -# ))) | |
897 | -# return stack | |
898 | - | |
899 | - | |
900 | -@inscribe | |
901 | -@FunctionWrapper | |
902 | -def help_(S, expression, dictionary): | |
903 | - '''Accepts a quoted symbol on the top of the stack and prints its docs.''' | |
904 | - ((symbol, _), stack) = S | |
905 | - word = dictionary[symbol] | |
906 | - print(HELP_TEMPLATE % (symbol, getdoc(word), symbol)) | |
907 | - return stack, expression, dictionary | |
908 | - | |
909 | - | |
910 | -# | |
911 | -# § Combinators | |
912 | -# | |
913 | - | |
914 | - | |
915 | -# Several combinators depend on other words in their definitions, | |
916 | -# we use symbols to prevent hard-coding these, so in theory, you | |
917 | -# could change the word in the dictionary to use different semantics. | |
918 | -S_choice = Symbol('choice') | |
919 | -S_first = Symbol('first') | |
920 | -S_genrec = Symbol('genrec') | |
921 | -S_getitem = Symbol('getitem') | |
922 | -S_i = Symbol('i') | |
923 | -S_ifte = Symbol('ifte') | |
924 | -S_infra = Symbol('infra') | |
925 | -S_loop = Symbol('loop') | |
926 | -S_pop = Symbol('pop') | |
927 | -S_primrec = Symbol('primrec') | |
928 | -S_step = Symbol('step') | |
929 | -S_swaack = Symbol('swaack') | |
930 | -S_times = Symbol('times') | |
931 | - | |
932 | - | |
933 | -@inscribe | |
934 | -@combinator_effect(_COMB_NUMS(), s1) | |
935 | -@FunctionWrapper | |
936 | -def i(stack, expression, dictionary): | |
937 | - ''' | |
938 | - The i combinator expects a quoted program on the stack and unpacks it | |
939 | - onto the pending expression for evaluation. | |
940 | - :: | |
941 | - | |
942 | - [Q] i | |
943 | - ----------- | |
944 | - Q | |
945 | - | |
946 | - ''' | |
947 | - quote, stack = stack | |
948 | - return stack, concat(quote, expression), dictionary | |
949 | - | |
950 | - | |
951 | -@inscribe | |
952 | -@combinator_effect(_COMB_NUMS(), s1) | |
953 | -@FunctionWrapper | |
954 | -def x(stack, expression, dictionary): | |
955 | - ''' | |
956 | - :: | |
957 | - | |
958 | - x == dup i | |
959 | - | |
960 | - ... [Q] x = ... [Q] dup i | |
961 | - ... [Q] x = ... [Q] [Q] i | |
962 | - ... [Q] x = ... [Q] Q | |
963 | - | |
964 | - ''' | |
965 | - quote, _ = stack | |
966 | - return stack, concat(quote, expression), dictionary | |
967 | - | |
968 | - | |
969 | -@inscribe | |
970 | -@combinator_effect(_COMB_NUMS(), s7, s6) | |
971 | -@FunctionWrapper | |
972 | -def b(stack, expression, dictionary): | |
973 | - ''' | |
974 | - :: | |
975 | - | |
976 | - b == [i] dip i | |
977 | - | |
978 | - ... [P] [Q] b == ... [P] i [Q] i | |
979 | - ... [P] [Q] b == ... P Q | |
980 | - | |
981 | - ''' | |
982 | - q, (p, (stack)) = stack | |
983 | - return stack, concat(p, concat(q, expression)), dictionary | |
984 | - | |
985 | - | |
986 | -@inscribe | |
987 | -@combinator_effect(_COMB_NUMS(), a1, s1) | |
988 | -@FunctionWrapper | |
989 | -def dupdip(stack, expression, dictionary): | |
990 | - ''' | |
991 | - :: | |
992 | - | |
993 | - [F] dupdip == dup [F] dip | |
994 | - | |
995 | - ... a [F] dupdip | |
996 | - ... a dup [F] dip | |
997 | - ... a a [F] dip | |
998 | - ... a F a | |
999 | - | |
1000 | - ''' | |
1001 | - F, stack = stack | |
1002 | - a = stack[0] | |
1003 | - return stack, concat(F, (a, expression)), dictionary | |
1004 | - | |
1005 | - | |
1006 | -@inscribe | |
1007 | -@combinator_effect(_COMB_NUMS(), s7, s6) | |
1008 | -@FunctionWrapper | |
1009 | -def infra(stack, expression, dictionary): | |
1010 | - ''' | |
1011 | - Accept a quoted program and a list on the stack and run the program | |
1012 | - with the list as its stack. Does not affect the rest of the stack. | |
1013 | - :: | |
1014 | - | |
1015 | - ... [a b c] [Q] . infra | |
1016 | - ----------------------------- | |
1017 | - c b a . Q [...] swaack | |
1018 | - | |
1019 | - ''' | |
1020 | - (quote, (aggregate, stack)) = stack | |
1021 | - return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary | |
1022 | - | |
1023 | - | |
1024 | -@inscribe | |
1025 | -#@combinator_effect(_COMB_NUMS(), s7, s6, s5, s4) | |
1026 | -@FunctionWrapper | |
1027 | -def genrec(stack, expression, dictionary): | |
1028 | - ''' | |
1029 | - General Recursion Combinator. | |
1030 | - :: | |
1031 | - | |
1032 | - [if] [then] [rec1] [rec2] genrec | |
1033 | - --------------------------------------------------------------------- | |
1034 | - [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte | |
1035 | - | |
1036 | - From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun: | |
1037 | - "The genrec combinator takes four program parameters in addition to | |
1038 | - whatever data parameters it needs. Fourth from the top is an if-part, | |
1039 | - followed by a then-part. If the if-part yields true, then the then-part | |
1040 | - is executed and the combinator terminates. The other two parameters are | |
1041 | - the rec1-part and the rec2-part. If the if-part yields false, the | |
1042 | - rec1-part is executed. Following that the four program parameters and | |
1043 | - the combinator are again pushed onto the stack bundled up in a quoted | |
1044 | - form. Then the rec2-part is executed, where it will find the bundled | |
1045 | - form. Typically it will then execute the bundled form, either with i or | |
1046 | - with app2, or some other combinator." | |
1047 | - | |
1048 | - The way to design one of these is to fix your base case [then] and the | |
1049 | - test [if], and then treat rec1 and rec2 as an else-part "sandwiching" | |
1050 | - a quotation of the whole function. | |
1051 | - | |
1052 | - For example, given a (general recursive) function 'F': | |
1053 | - :: | |
1054 | - | |
1055 | - F == [I] [T] [R1] [R2] genrec | |
1056 | - | |
1057 | - If the [I] if-part fails you must derive R1 and R2 from: | |
1058 | - :: | |
1059 | - | |
1060 | - ... R1 [F] R2 | |
1061 | - | |
1062 | - Just set the stack arguments in front, and figure out what R1 and R2 | |
1063 | - have to do to apply the quoted [F] in the proper way. In effect, the | |
1064 | - genrec combinator turns into an ifte combinator with a quoted copy of | |
1065 | - the original definition in the else-part: | |
1066 | - :: | |
1067 | - | |
1068 | - F == [I] [T] [R1] [R2] genrec | |
1069 | - == [I] [T] [R1 [F] R2] ifte | |
1070 | - | |
1071 | - Primitive recursive functions are those where R2 == i. | |
1072 | - :: | |
1073 | - | |
1074 | - P == [I] [T] [R] tailrec | |
1075 | - == [I] [T] [R [P] i] ifte | |
1076 | - == [I] [T] [R P] ifte | |
1077 | - | |
1078 | - ''' | |
1079 | - (rec2, (rec1, stack)) = stack | |
1080 | - (then, (if_, _)) = stack | |
1081 | - F = (if_, (then, (rec1, (rec2, (S_genrec, ()))))) | |
1082 | - else_ = concat(rec1, (F, rec2)) | |
1083 | - return (else_, stack), (S_ifte, expression), dictionary | |
1084 | - | |
1085 | - | |
1086 | -@inscribe | |
1087 | -@combinator_effect(_COMB_NUMS(), s7, s6) | |
1088 | -@FunctionWrapper | |
1089 | -def map_(S, expression, dictionary): | |
1090 | - ''' | |
1091 | - Run the quoted program on TOS on the items in the list under it, push a | |
1092 | - new list with the results in place of the program and original list. | |
1093 | - ''' | |
1094 | -# (quote, (aggregate, stack)) = S | |
1095 | -# results = list_to_stack([ | |
1096 | -# joy((term, stack), quote, dictionary)[0][0] | |
1097 | -# for term in iter_stack(aggregate) | |
1098 | -# ]) | |
1099 | -# return (results, stack), expression, dictionary | |
1100 | - (quote, (aggregate, stack)) = S | |
1101 | - if not aggregate: | |
1102 | - return (aggregate, stack), expression, dictionary | |
1103 | - batch = () | |
1104 | - for term in iter_stack(aggregate): | |
1105 | - s = term, stack | |
1106 | - batch = (s, (quote, (S_infra, (S_first, batch)))) | |
1107 | - stack = (batch, ((), stack)) | |
1108 | - return stack, (S_infra, expression), dictionary | |
1109 | - | |
1110 | - | |
1111 | -@inscribe | |
1112 | -@FunctionWrapper | |
1113 | -def primrec(stack, expression, dictionary): | |
1114 | - ''' | |
1115 | - From the "Overview of the language JOY": | |
1116 | - | |
1117 | - > The primrec combinator expects two quoted programs in addition to a | |
1118 | - data parameter. For an integer data parameter it works like this: If | |
1119 | - the data parameter is zero, then the first quotation has to produce | |
1120 | - the value to be returned. If the data parameter is positive then the | |
1121 | - second has to combine the data parameter with the result of applying | |
1122 | - the function to its predecessor. | |
1123 | - | |
1124 | - 5 [1] [*] primrec | |
1125 | - | |
1126 | - > Then primrec tests whether the top element on the stack (initially | |
1127 | - the 5) is equal to zero. If it is, it pops it off and executes one of | |
1128 | - the quotations, the [1] which leaves 1 on the stack as the result. | |
1129 | - Otherwise it pushes a decremented copy of the top element and | |
1130 | - recurses. On the way back from the recursion it uses the other | |
1131 | - quotation, [*], to multiply what is now a factorial on top of the | |
1132 | - stack by the second element on the stack. | |
1133 | - | |
1134 | - n [Base] [Recur] primrec | |
1135 | - | |
1136 | - 0 [Base] [Recur] primrec | |
1137 | - ------------------------------ | |
1138 | - Base | |
1139 | - | |
1140 | - n [Base] [Recur] primrec | |
1141 | - ------------------------------------------ n > 0 | |
1142 | - n (n-1) [Base] [Recur] primrec Recur | |
1143 | - | |
1144 | - ''' | |
1145 | - recur, (base, (n, stack)) = stack | |
1146 | - if n <= 0: | |
1147 | - expression = concat(base, expression) | |
1148 | - else: | |
1149 | - expression = S_primrec, concat(recur, expression) | |
1150 | - stack = recur, (base, (n - 1, (n, stack))) | |
1151 | - return stack, expression, dictionary | |
1152 | - | |
1153 | - | |
1154 | -#def cleave(S, expression, dictionary): | |
1155 | -# ''' | |
1156 | -# The cleave combinator expects two quotations, and below that an item X. | |
1157 | -# It first executes [P], with X on top, and saves the top result element. | |
1158 | -# Then it executes [Q], again with X, and saves the top result. | |
1159 | -# Finally it restores the stack to what it was below X and pushes the two | |
1160 | -# results P(X) and Q(X). | |
1161 | -# ''' | |
1162 | -# (Q, (P, (x, stack))) = S | |
1163 | -# p = joy((x, stack), P, dictionary)[0][0] | |
1164 | -# q = joy((x, stack), Q, dictionary)[0][0] | |
1165 | -# return (q, (p, stack)), expression, dictionary | |
1166 | - | |
1167 | - | |
1168 | -def branch_true(stack, expression, dictionary): | |
1169 | - # pylint: disable=unused-variable | |
1170 | - (then, (else_, (flag, stack))) = stack | |
1171 | - return stack, concat(then, expression), dictionary | |
1172 | - | |
1173 | - | |
1174 | -def branch_false(stack, expression, dictionary): | |
1175 | - # pylint: disable=unused-variable | |
1176 | - (then, (else_, (flag, stack))) = stack | |
1177 | - return stack, concat(else_, expression), dictionary | |
1178 | - | |
1179 | - | |
1180 | -@inscribe | |
1181 | -@poly_combinator_effect(_COMB_NUMS(), [branch_true, branch_false], b1, s7, s6) | |
1182 | -@FunctionWrapper | |
1183 | -def branch(stack, expression, dictionary): | |
1184 | - ''' | |
1185 | - Use a Boolean value to select one of two quoted programs to run. | |
1186 | - | |
1187 | - :: | |
1188 | - | |
1189 | - branch == roll< choice i | |
1190 | - | |
1191 | - :: | |
1192 | - | |
1193 | - False [F] [T] branch | |
1194 | - -------------------------- | |
1195 | - F | |
1196 | - | |
1197 | - True [F] [T] branch | |
1198 | - ------------------------- | |
1199 | - T | |
1200 | - | |
1201 | - ''' | |
1202 | - (then, (else_, (flag, stack))) = stack | |
1203 | - return stack, concat(then if flag else else_, expression), dictionary | |
1204 | - | |
1205 | - | |
1206 | -#FUNCTIONS['branch'] = CombinatorJoyType('branch', [branch_true, branch_false], 100) | |
1207 | - | |
1208 | - | |
1209 | -##@inscribe | |
1210 | -##@FunctionWrapper | |
1211 | -##def ifte(stack, expression, dictionary): | |
1212 | -## ''' | |
1213 | -## If-Then-Else Combinator | |
1214 | -## :: | |
1215 | -## | |
1216 | -## ... [if] [then] [else] ifte | |
1217 | -## --------------------------------------------------- | |
1218 | -## ... [[else] [then]] [...] [if] infra select i | |
1219 | -## | |
1220 | -## | |
1221 | -## | |
1222 | -## | |
1223 | -## ... [if] [then] [else] ifte | |
1224 | -## ------------------------------------------------------- | |
1225 | -## ... [else] [then] [...] [if] infra first choice i | |
1226 | -## | |
1227 | -## | |
1228 | -## Has the effect of grabbing a copy of the stack on which to run the | |
1229 | -## if-part using infra. | |
1230 | -## ''' | |
1231 | -## (else_, (then, (if_, stack))) = stack | |
1232 | -## expression = (S_infra, (S_first, (S_choice, (S_i, expression)))) | |
1233 | -## stack = (if_, (stack, (then, (else_, stack)))) | |
1234 | -## return stack, expression, dictionary | |
1235 | - | |
1236 | - | |
1237 | -@inscribe | |
1238 | -@FunctionWrapper | |
1239 | -def cond(stack, expression, dictionary): | |
1240 | - ''' | |
1241 | - This combinator works like a case statement. It expects a single quote | |
1242 | - on the stack that must contain zero or more condition quotes and a | |
1243 | - default quote. Each condition clause should contain a quoted predicate | |
1244 | - followed by the function expression to run if that predicate returns | |
1245 | - true. If no predicates return true the default function runs. | |
1246 | - | |
1247 | - It works by rewriting into a chain of nested `ifte` expressions, e.g.:: | |
1248 | - | |
1249 | - [[[B0] T0] [[B1] T1] [D]] cond | |
1250 | - ----------------------------------------- | |
1251 | - [B0] [T0] [[B1] [T1] [D] ifte] ifte | |
1252 | - | |
1253 | - ''' | |
1254 | - conditions, stack = stack | |
1255 | - if conditions: | |
1256 | - expression = _cond(conditions, expression) | |
1257 | - try: | |
1258 | - # Attempt to preload the args to first ifte. | |
1259 | - (P, (T, (E, expression))) = expression | |
1260 | - except ValueError: | |
1261 | - # If, for any reason, the argument to cond should happen to contain | |
1262 | - # only the default clause then this optimization will fail. | |
1263 | - pass | |
1264 | - else: | |
1265 | - stack = (E, (T, (P, stack))) | |
1266 | - return stack, expression, dictionary | |
1267 | - | |
1268 | - | |
1269 | -def _cond(conditions, expression): | |
1270 | - (clause, rest) = conditions | |
1271 | - if not rest: # clause is [D] | |
1272 | - return clause | |
1273 | - P, T = clause | |
1274 | - return (P, (T, (_cond(rest, ()), (S_ifte, expression)))) | |
1275 | - | |
1276 | - | |
1277 | -@inscribe | |
1278 | -@combinator_effect(_COMB_NUMS(), a1, s1) | |
1279 | -@FunctionWrapper | |
1280 | -def dip(stack, expression, dictionary): | |
1281 | - ''' | |
1282 | - The dip combinator expects a quoted program on the stack and below it | |
1283 | - some item, it hoists the item into the expression and runs the program | |
1284 | - on the rest of the stack. | |
1285 | - :: | |
1286 | - | |
1287 | - ... x [Q] dip | |
1288 | - ------------------- | |
1289 | - ... Q x | |
1290 | - | |
1291 | - ''' | |
1292 | - (quote, (x, stack)) = stack | |
1293 | - expression = (x, expression) | |
1294 | - return stack, concat(quote, expression), dictionary | |
1295 | - | |
1296 | - | |
1297 | -@inscribe | |
1298 | -@combinator_effect(_COMB_NUMS(), a2, a1, s1) | |
1299 | -@FunctionWrapper | |
1300 | -def dipd(S, expression, dictionary): | |
1301 | - ''' | |
1302 | - Like dip but expects two items. | |
1303 | - :: | |
1304 | - | |
1305 | - ... y x [Q] dip | |
1306 | - --------------------- | |
1307 | - ... Q y x | |
1308 | - | |
1309 | - ''' | |
1310 | - (quote, (x, (y, stack))) = S | |
1311 | - expression = (y, (x, expression)) | |
1312 | - return stack, concat(quote, expression), dictionary | |
1313 | - | |
1314 | - | |
1315 | -@inscribe | |
1316 | -@combinator_effect(_COMB_NUMS(), a3, a2, a1, s1) | |
1317 | -@FunctionWrapper | |
1318 | -def dipdd(S, expression, dictionary): | |
1319 | - ''' | |
1320 | - Like dip but expects three items. | |
1321 | - :: | |
1322 | - | |
1323 | - ... z y x [Q] dip | |
1324 | - ----------------------- | |
1325 | - ... Q z y x | |
1326 | - | |
1327 | - ''' | |
1328 | - (quote, (x, (y, (z, stack)))) = S | |
1329 | - expression = (z, (y, (x, expression))) | |
1330 | - return stack, concat(quote, expression), dictionary | |
1331 | - | |
1332 | - | |
1333 | -@inscribe | |
1334 | -@combinator_effect(_COMB_NUMS(), a1, s1) | |
1335 | -@FunctionWrapper | |
1336 | -def app1(S, expression, dictionary): | |
1337 | - ''' | |
1338 | - Given a quoted program on TOS and anything as the second stack item run | |
1339 | - the program and replace the two args with the first result of the | |
1340 | - program. | |
1341 | - :: | |
1342 | - | |
1343 | - ... x [Q] . app1 | |
1344 | - ----------------------------------- | |
1345 | - ... [x ...] [Q] . infra first | |
1346 | - ''' | |
1347 | - (quote, (x, stack)) = S | |
1348 | - stack = (quote, ((x, stack), stack)) | |
1349 | - expression = (S_infra, (S_first, expression)) | |
1350 | - return stack, expression, dictionary | |
1351 | - | |
1352 | - | |
1353 | -@inscribe | |
1354 | -@combinator_effect(_COMB_NUMS(), a2, a1, s1) | |
1355 | -@FunctionWrapper | |
1356 | -def app2(S, expression, dictionary): | |
1357 | - '''Like app1 with two items. | |
1358 | - :: | |
1359 | - | |
1360 | - ... y x [Q] . app2 | |
1361 | - ----------------------------------- | |
1362 | - ... [y ...] [Q] . infra first | |
1363 | - [x ...] [Q] infra first | |
1364 | - | |
1365 | - ''' | |
1366 | - (quote, (x, (y, stack))) = S | |
1367 | - expression = (S_infra, (S_first, | |
1368 | - ((x, stack), (quote, (S_infra, (S_first, | |
1369 | - expression)))))) | |
1370 | - stack = (quote, ((y, stack), stack)) | |
1371 | - return stack, expression, dictionary | |
1372 | - | |
1373 | - | |
1374 | -@inscribe | |
1375 | -@combinator_effect(_COMB_NUMS(), a3, a2, a1, s1) | |
1376 | -@FunctionWrapper | |
1377 | -def app3(S, expression, dictionary): | |
1378 | - '''Like app1 with three items. | |
1379 | - :: | |
1380 | - | |
1381 | - ... z y x [Q] . app3 | |
1382 | - ----------------------------------- | |
1383 | - ... [z ...] [Q] . infra first | |
1384 | - [y ...] [Q] infra first | |
1385 | - [x ...] [Q] infra first | |
1386 | - | |
1387 | - ''' | |
1388 | - (quote, (x, (y, (z, stack)))) = S | |
1389 | - expression = (S_infra, (S_first, | |
1390 | - ((y, stack), (quote, (S_infra, (S_first, | |
1391 | - ((x, stack), (quote, (S_infra, (S_first, | |
1392 | - expression)))))))))) | |
1393 | - stack = (quote, ((z, stack), stack)) | |
1394 | - return stack, expression, dictionary | |
1395 | - | |
1396 | - | |
1397 | -@inscribe | |
1398 | -@combinator_effect(_COMB_NUMS(), s7, s6) | |
1399 | -@FunctionWrapper | |
1400 | -def step(S, expression, dictionary): | |
1401 | - ''' | |
1402 | - Run a quoted program on each item in a sequence. | |
1403 | - :: | |
1404 | - | |
1405 | - ... [] [Q] . step | |
1406 | - ----------------------- | |
1407 | - ... . | |
1408 | - | |
1409 | - | |
1410 | - ... [a] [Q] . step | |
1411 | - ------------------------ | |
1412 | - ... a . Q | |
1413 | - | |
1414 | - | |
1415 | - ... [a b c] [Q] . step | |
1416 | - ---------------------------------------- | |
1417 | - ... a . Q [b c] [Q] step | |
1418 | - | |
1419 | - The step combinator executes the quotation on each member of the list | |
1420 | - on top of the stack. | |
1421 | - ''' | |
1422 | - (quote, (aggregate, stack)) = S | |
1423 | - if not aggregate: | |
1424 | - return stack, expression, dictionary | |
1425 | - head, tail = aggregate | |
1426 | - stack = quote, (head, stack) | |
1427 | - if tail: | |
1428 | - expression = tail, (quote, (S_step, expression)) | |
1429 | - expression = S_i, expression | |
1430 | - return stack, expression, dictionary | |
1431 | - | |
1432 | - | |
1433 | -@inscribe | |
1434 | -@combinator_effect(_COMB_NUMS(), i1, s6) | |
1435 | -@FunctionWrapper | |
1436 | -def times(stack, expression, dictionary): | |
1437 | - ''' | |
1438 | - times == [-- dip] cons [swap] infra [0 >] swap while pop | |
1439 | - :: | |
1440 | - | |
1441 | - ... n [Q] . times | |
1442 | - --------------------- w/ n <= 0 | |
1443 | - ... . | |
1444 | - | |
1445 | - | |
1446 | - ... 1 [Q] . times | |
1447 | - --------------------------------- | |
1448 | - ... . Q | |
1449 | - | |
1450 | - | |
1451 | - ... n [Q] . times | |
1452 | - --------------------------------- w/ n > 1 | |
1453 | - ... . Q (n - 1) [Q] times | |
1454 | - | |
1455 | - ''' | |
1456 | - # times == [-- dip] cons [swap] infra [0 >] swap while pop | |
1457 | - (quote, (n, stack)) = stack | |
1458 | - if n <= 0: | |
1459 | - return stack, expression, dictionary | |
1460 | - n -= 1 | |
1461 | - if n: | |
1462 | - expression = n, (quote, (S_times, expression)) | |
1463 | - expression = concat(quote, expression) | |
1464 | - return stack, expression, dictionary | |
1465 | - | |
1466 | - | |
1467 | -# The current definition above works like this: | |
1468 | - | |
1469 | -# [P] [Q] while | |
1470 | -# -------------------------------------- | |
1471 | -# [P] nullary [Q [P] nullary] loop | |
1472 | - | |
1473 | -# while == [pop i not] [popop] [dudipd] tailrec | |
1474 | - | |
1475 | -#def while_(S, expression, dictionary): | |
1476 | -# '''[if] [body] while''' | |
1477 | -# (body, (if_, stack)) = S | |
1478 | -# while joy(stack, if_, dictionary)[0][0]: | |
1479 | -# stack = joy(stack, body, dictionary)[0] | |
1480 | -# return stack, expression, dictionary | |
1481 | - | |
1482 | - | |
1483 | -def loop_true(stack, expression, dictionary): | |
1484 | - quote, (flag, stack) = stack # pylint: disable=unused-variable | |
1485 | - return stack, concat(quote, (S_pop, expression)), dictionary | |
1486 | - | |
1487 | -def loop_two_true(stack, expression, dictionary): | |
1488 | - quote, (flag, stack) = stack # pylint: disable=unused-variable | |
1489 | - return stack, concat(quote, (S_pop, concat(quote, (S_pop, expression)))), dictionary | |
1490 | - | |
1491 | -def loop_false(stack, expression, dictionary): | |
1492 | - quote, (flag, stack) = stack # pylint: disable=unused-variable | |
1493 | - return stack, expression, dictionary | |
1494 | - | |
1495 | - | |
1496 | -@inscribe | |
1497 | -@poly_combinator_effect(_COMB_NUMS(), [loop_two_true, loop_true, loop_false], b1, s6) | |
1498 | -@FunctionWrapper | |
1499 | -def loop(stack, expression, dictionary): | |
1500 | - ''' | |
1501 | - Basic loop combinator. | |
1502 | - :: | |
1503 | - | |
1504 | - ... True [Q] loop | |
1505 | - ----------------------- | |
1506 | - ... Q [Q] loop | |
1507 | - | |
1508 | - ... False [Q] loop | |
1509 | - ------------------------ | |
1510 | - ... | |
1511 | - | |
1512 | - ''' | |
1513 | - quote, (flag, stack) = stack | |
1514 | - if flag: | |
1515 | - expression = concat(quote, (quote, (S_loop, expression))) | |
1516 | - return stack, expression, dictionary | |
1517 | - | |
1518 | - | |
1519 | -@inscribe | |
1520 | -@combinator_effect(_COMB_NUMS(), a1, a2, s6, s7, s8) | |
1521 | -@FunctionWrapper | |
1522 | -def cmp_(stack, expression, dictionary): | |
1523 | - ''' | |
1524 | - cmp takes two values and three quoted programs on the stack and runs | |
1525 | - one of the three depending on the results of comparing the two values: | |
1526 | - :: | |
1527 | - | |
1528 | - a b [G] [E] [L] cmp | |
1529 | - ------------------------- a > b | |
1530 | - G | |
1531 | - | |
1532 | - a b [G] [E] [L] cmp | |
1533 | - ------------------------- a = b | |
1534 | - E | |
1535 | - | |
1536 | - a b [G] [E] [L] cmp | |
1537 | - ------------------------- a < b | |
1538 | - L | |
1539 | - ''' | |
1540 | - L, (E, (G, (b, (a, stack)))) = stack | |
1541 | - expression = concat(G if a > b else L if a < b else E, expression) | |
1542 | - return stack, expression, dictionary | |
1543 | - | |
1544 | - | |
1545 | -# FunctionWrapper(cleave), | |
1546 | -# FunctionWrapper(while_), | |
1547 | - | |
1548 | - | |
1549 | -for F in ( | |
1550 | - | |
1551 | - #divmod_ = pm = __(n2, n1), __(n4, n3) | |
1552 | - | |
1553 | - sec_binary_cmp(BinaryBuiltinWrapper(operator.eq)), | |
1554 | - sec_binary_cmp(BinaryBuiltinWrapper(operator.ge)), | |
1555 | - sec_binary_cmp(BinaryBuiltinWrapper(operator.gt)), | |
1556 | - sec_binary_cmp(BinaryBuiltinWrapper(operator.le)), | |
1557 | - sec_binary_cmp(BinaryBuiltinWrapper(operator.lt)), | |
1558 | - sec_binary_cmp(BinaryBuiltinWrapper(operator.ne)), | |
1559 | - | |
1560 | - sec_binary_ints(BinaryBuiltinWrapper(operator.xor)), | |
1561 | - sec_binary_ints(BinaryBuiltinWrapper(operator.lshift)), | |
1562 | - sec_binary_ints(BinaryBuiltinWrapper(operator.rshift)), | |
1563 | - | |
1564 | - sec_binary_logic(BinaryBuiltinWrapper(operator.and_)), | |
1565 | - sec_binary_logic(BinaryBuiltinWrapper(operator.or_)), | |
1566 | - | |
1567 | - sec_binary_math(BinaryBuiltinWrapper(operator.add)), | |
1568 | - sec_binary_math(BinaryBuiltinWrapper(operator.floordiv)), | |
1569 | - sec_binary_math(BinaryBuiltinWrapper(operator.mod)), | |
1570 | - sec_binary_math(BinaryBuiltinWrapper(operator.mul)), | |
1571 | - sec_binary_math(BinaryBuiltinWrapper(operator.pow)), | |
1572 | - sec_binary_math(BinaryBuiltinWrapper(operator.sub)), | |
1573 | - sec_binary_math(BinaryBuiltinWrapper(operator.truediv)), | |
1574 | - | |
1575 | - sec_unary_logic(UnaryBuiltinWrapper(bool)), | |
1576 | - sec_unary_logic(UnaryBuiltinWrapper(operator.not_)), | |
1577 | - | |
1578 | - sec_unary_math(UnaryBuiltinWrapper(abs)), | |
1579 | - sec_unary_math(UnaryBuiltinWrapper(operator.neg)), | |
1580 | - sec_unary_math(UnaryBuiltinWrapper(sqrt)), | |
1581 | - | |
1582 | - stack_effect(n1)(i1)(UnaryBuiltinWrapper(floor)), | |
1583 | - ): | |
1584 | - inscribe(F) | |
1585 | -del F # Otherwise Sphinx autodoc will pick it up. | |
1586 | - | |
1587 | - | |
1588 | -YIN_STACK_EFFECTS = yin_functions() | |
1589 | -add_aliases(YIN_STACK_EFFECTS, ALIASES) | |
1590 | - | |
1591 | -# Load the auto-generated primitives into the dictionary. | |
1592 | -_functions.update(YIN_STACK_EFFECTS) | |
1593 | -# exec ''' | |
1594 | - | |
1595 | -# eh = compose(dup, bool) | |
1596 | -# sqr = compose(dup, mul) | |
1597 | -# of = compose(swap, at) | |
1598 | - | |
1599 | -# ''' in dict(compose=compose), _functions | |
1600 | -for name in sorted(_functions): | |
1601 | - sec = _functions[name] | |
1602 | - F = FUNCTIONS[name] = SymbolJoyType(name, [sec], _SYM_NUMS()) | |
1603 | - if name in YIN_STACK_EFFECTS: | |
1604 | - _log.info('Setting stack effect for Yin function %s := %s', F.name, doc_from_stack_effect(*sec)) | |
1605 | - | |
1606 | -for name, primitive in getmembers(genlib, isfunction): | |
1607 | - inscribe(SimpleFunctionWrapper(primitive)) | |
1608 | - | |
1609 | - | |
1610 | -add_aliases(_dictionary, ALIASES) | |
1611 | -add_aliases(_functions, ALIASES) | |
1612 | -add_aliases(FUNCTIONS, ALIASES) | |
1613 | - | |
1614 | - | |
1615 | -DefinitionWrapper.add_definitions(definitions, _dictionary) | |
1616 | - | |
1617 | - | |
1618 | -EXPECTATIONS = dict( | |
1619 | - ifte=(s7, (s6, (s5, s4))), | |
1620 | - nullary=(s7, s6), | |
1621 | - run=(s7, s6), | |
1622 | -) | |
1623 | -EXPECTATIONS['while'] = (s7, (s6, s5)) | |
1624 | - | |
1625 | - | |
1626 | -for name in ''' | |
1627 | - dinfrirst | |
1628 | - nullary | |
1629 | - ifte | |
1630 | - run | |
1631 | - dupdipd codireco | |
1632 | - while | |
1633 | - '''.split(): | |
1634 | - C = _dictionary[name] | |
1635 | - expect = EXPECTATIONS.get(name) | |
1636 | - if expect: | |
1637 | - sec = doc_from_stack_effect(expect) | |
1638 | - _log.info('Setting stack EXPECT for combinator %s := %s', C.name, sec) | |
1639 | - else: | |
1640 | - _log.info('combinator %s', C.name) | |
1641 | - FUNCTIONS[name] = CombinatorJoyType(name, [C], _COMB_NUMS(), expect) | |
1642 | - | |
1643 | - | |
1644 | -for name in (''' | |
1645 | - of quoted enstacken ? | |
1646 | - unary binary ternary | |
1647 | - sqr unquoted | |
1648 | - '''.split()): | |
1649 | - of_ = _dictionary[name] | |
1650 | - secs = infer_expression(of_.body) | |
1651 | - assert len(secs) == 1, repr(secs) | |
1652 | - _log.info( | |
1653 | - 'Setting stack effect for definition %s := %s', | |
1654 | - name, | |
1655 | - doc_from_stack_effect(*secs[0]), | |
1656 | - ) | |
1657 | - FUNCTIONS[name] = SymbolJoyType(name, infer_expression(of_.body), _SYM_NUMS()) | |
1658 | - | |
1659 | - | |
1660 | -#sec_Ns_math(_dictionary['product']) | |
1661 | - | |
1662 | -## product == 1 swap [*] step | |
1663 | -## flatten == [] swap [concat] step | |
1664 | -## pam == [i] map | |
1665 | -## size == 0 swap [pop ++] step | |
1666 | -## fork == [i] app2 | |
1667 | -## cleave == fork [popd] dip | |
1668 | -## average == [sum 1.0 *] [size] cleave / | |
1669 | -## gcd == 1 [tuck modulus dup 0 >] loop pop | |
1670 | -## least_fraction == dup [gcd] infra [div] concat map | |
1671 | -## *fraction == [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons | |
1672 | -## *fraction0 == concat [[swap] dip * [*] dip] infra | |
1673 | -## down_to_zero == [0 >] [dup --] while | |
1674 | -## range_to_zero == unit [down_to_zero] infra | |
1675 | -## anamorphism == [pop []] swap [dip swons] genrec | |
1676 | -## range == [0 <=] [1 - dup] anamorphism | |
1677 | -## while == swap [nullary] cons dup dipd concat loop | |
1678 | -## dupdipd == dup dipd | |
1679 | -## tailrec == [i] genrec | |
1680 | -## step_zero == 0 roll> step | |
1681 | -## codireco == cons dip rest cons | |
1682 | -## make_generator == [codireco] ccons | |
1683 | -## ifte == [nullary not] dipd branch |
@@ -1,124 +0,0 @@ | ||
1 | -# -*- coding: utf-8 -*- | |
2 | -# | |
3 | -# Copyright © 2014, 2015, 2016, 2017 Simon Forman | |
4 | -# | |
5 | -# This file is part of Thun. | |
6 | -# | |
7 | -# Thun is free software: you can redistribute it and/or modify | |
8 | -# it under the terms of the GNU General Public License as published by | |
9 | -# the Free Software Foundation, either version 3 of the License, or | |
10 | -# (at your option) any later version. | |
11 | -# | |
12 | -# Thun is distributed in the hope that it will be useful, | |
13 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | -# GNU General Public License for more details. | |
16 | -# | |
17 | -# You should have received a copy of the GNU General Public License | |
18 | -# along with Thun. If not see <http://www.gnu.org/licenses/>. | |
19 | -# | |
20 | -''' | |
21 | -This module exports a single function for converting text to a joy | |
22 | -expression as well as a single Symbol class and a single Exception type. | |
23 | - | |
24 | -The Symbol string class is used by the interpreter to recognize literals | |
25 | -by the fact that they are not Symbol objects. | |
26 | - | |
27 | -A crude grammar:: | |
28 | - | |
29 | - joy = term* | |
30 | - term = int | float | string | '[' joy ']' | symbol | |
31 | - | |
32 | -A Joy expression is a sequence of zero or more terms. A term is a | |
33 | -literal value (integer, float, string, or Joy expression) or a function | |
34 | -symbol. Function symbols are unquoted strings and cannot contain square | |
35 | -brackets. Terms must be separated by blanks, which can be omitted | |
36 | -around square brackets. | |
37 | - | |
38 | -''' | |
39 | -from re import Scanner | |
40 | -from .utils.stack import list_to_stack | |
41 | - | |
42 | - | |
43 | -#TODO: explain the details of float lits and strings. | |
44 | -FLOAT = r'-?\d+\.\d*(e(-|\+)\d+)+' | |
45 | -INT = r'-?\d+' | |
46 | -SYMBOL = r'[•\w!@$%^&*()_+<>?|\/;:`~,.=-]+' | |
47 | -BRACKETS = r'\[|\]' | |
48 | -STRING_DOUBLE_QUOTED = r'"(?:[^"\\]|\\.)*"' | |
49 | -STRING_SINGLE_QUOTED = r"'(?:[^'\\]|\\.)*'" | |
50 | -BLANKS = r'\s+' | |
51 | - | |
52 | - | |
53 | -class Symbol(str): | |
54 | - '''A string class that represents Joy function names.''' | |
55 | - __repr__ = str.__str__ | |
56 | - | |
57 | - | |
58 | -def text_to_expression(text): | |
59 | - '''Convert a string to a Joy expression. | |
60 | - | |
61 | - When supplied with a string this function returns a Python datastructure | |
62 | - that represents the Joy datastructure described by the text expression. | |
63 | - Any unbalanced square brackets will raise a ParseError. | |
64 | - | |
65 | - :param str text: Text to convert. | |
66 | - :rtype: stack | |
67 | - :raises ParseError: if the parse fails. | |
68 | - ''' | |
69 | - return _parse(_tokenize(text)) | |
70 | - | |
71 | - | |
72 | -class ParseError(ValueError): | |
73 | - '''Raised when there is a error while parsing text.''' | |
74 | - | |
75 | - | |
76 | -def _tokenize(text): | |
77 | - '''Convert a text into a stream of tokens. | |
78 | - | |
79 | - Converts function names to Symbols. | |
80 | - | |
81 | - Raise ParseError (with some of the failing text) if the scan fails. | |
82 | - ''' | |
83 | - tokens, rest = _scanner.scan(text) | |
84 | - if rest: | |
85 | - raise ParseError( | |
86 | - 'Scan failed at position %i, %r' | |
87 | - % (len(text) - len(rest), rest[:10]) | |
88 | - ) | |
89 | - return tokens | |
90 | - | |
91 | - | |
92 | -def _parse(tokens): | |
93 | - ''' | |
94 | - Return a stack/list expression of the tokens. | |
95 | - ''' | |
96 | - frame = [] | |
97 | - stack = [] | |
98 | - for tok in tokens: | |
99 | - if tok == '[': | |
100 | - stack.append(frame) | |
101 | - frame = [] | |
102 | - stack[-1].append(frame) | |
103 | - elif tok == ']': | |
104 | - try: | |
105 | - frame = stack.pop() | |
106 | - except IndexError: | |
107 | - raise ParseError('Extra closing bracket.') | |
108 | - frame[-1] = list_to_stack(frame[-1]) | |
109 | - else: | |
110 | - frame.append(tok) | |
111 | - if stack: | |
112 | - raise ParseError('Unclosed bracket.') | |
113 | - return list_to_stack(frame) | |
114 | - | |
115 | - | |
116 | -_scanner = Scanner([ | |
117 | - (FLOAT, lambda _, token: float(token)), | |
118 | - (INT, lambda _, token: int(token)), | |
119 | - (SYMBOL, lambda _, token: Symbol(token)), | |
120 | - (BRACKETS, lambda _, token: token), | |
121 | - (STRING_DOUBLE_QUOTED, lambda _, token: token[1:-1].replace('\\"', '"')), | |
122 | - (STRING_SINGLE_QUOTED, lambda _, token: token[1:-1].replace("\\'", "'")), | |
123 | - (BLANKS, None), | |
124 | - ]) |
@@ -1,96 +0,0 @@ | ||
1 | -# -*- coding: utf-8 -*- | |
2 | -# | |
3 | -# Copyright © 2018 Simon Forman | |
4 | -# | |
5 | -# This file is part of Thun | |
6 | -# | |
7 | -# Thun is free software: you can redistribute it and/or modify | |
8 | -# it under the terms of the GNU General Public License as published by | |
9 | -# the Free Software Foundation, either version 3 of the License, or | |
10 | -# (at your option) any later version. | |
11 | -# | |
12 | -# Thun is distributed in the hope that it will be useful, | |
13 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | -# GNU General Public License for more details. | |
16 | -# | |
17 | -# You should have received a copy of the GNU General Public License | |
18 | -# along with Thun. If not see <http://www.gnu.org/licenses/>. | |
19 | -# | |
20 | -''' | |
21 | -I really want tracebacks to show which function was being executed when | |
22 | -an error in the wrapper function happens. In order to do that, you have | |
23 | -to do this (the function in this module.) | |
24 | - | |
25 | -Here's what it looks like when you pass too few arguments to e.g. "mul". | |
26 | - | |
27 | - >>> from joy.library import _dictionary | |
28 | - >>> m = _dictionary['*'] | |
29 | - >>> m((), (), {}) | |
30 | - | |
31 | - Traceback (most recent call last): | |
32 | - File "<pyshell#49>", line 1, in <module> | |
33 | - m((), (), {}) | |
34 | - File "joy/library.py", line 185, in mul:inner | |
35 | - (a, (b, stack)) = stack | |
36 | - ValueError: need more than 0 values to unpack | |
37 | - >>> | |
38 | - | |
39 | - | |
40 | -Notice that line 185 in the library.py file is (as of this writing) in | |
41 | -the BinaryBuiltinWrapper's inner() function, but this hacky code has | |
42 | -managed to insert the name of the wrapped function ("mul") along with a | |
43 | -colon into the wrapper function's reported name. | |
44 | - | |
45 | -Normally I would frown on this sort of mad hackery, but... this is in | |
46 | -the service of ease-of-debugging! Very valuable. And note that all the | |
47 | -hideous patching is finished in the module-load-stage, it shouldn't cause | |
48 | -issues of its own at runtime. | |
49 | - | |
50 | -The main problem I see with this is that people coming to this code later | |
51 | -might be mystified if they just see a traceback with a ':' in the | |
52 | -function name! Hopefully they will discover this documentation. | |
53 | -''' | |
54 | - | |
55 | - | |
56 | -def rename_code_object(new_name): | |
57 | - ''' | |
58 | - If you want to wrap a function in another function and have the wrapped | |
59 | - function's name show up in the traceback when an exception occurs in | |
60 | - the wrapper function, you must do this brutal hackery to change the | |
61 | - func.__code__.co_name attribute. Just functools.wraps() is not enough. | |
62 | - | |
63 | - See: | |
64 | - | |
65 | - https://stackoverflow.com/questions/29919804/function-decorated-using-functools-wraps-raises-typeerror-with-the-name-of-the-w | |
66 | - | |
67 | - https://stackoverflow.com/questions/29488327/changing-the-name-of-a-generator/29488561#29488561 | |
68 | - | |
69 | - I'm just glad it's possible. | |
70 | - ''' | |
71 | - def inner(func): | |
72 | - name = new_name + ':' + func.__name__ | |
73 | - code_object = func.__code__ | |
74 | - return type(func)( | |
75 | - type(code_object)( | |
76 | - code_object.co_argcount, | |
77 | - code_object.co_nlocals, | |
78 | - code_object.co_stacksize, | |
79 | - code_object.co_flags, | |
80 | - code_object.co_code, | |
81 | - code_object.co_consts, | |
82 | - code_object.co_names, | |
83 | - code_object.co_varnames, | |
84 | - code_object.co_filename, | |
85 | - name, | |
86 | - code_object.co_firstlineno, | |
87 | - code_object.co_lnotab, | |
88 | - code_object.co_freevars, | |
89 | - code_object.co_cellvars | |
90 | - ), | |
91 | - func.__globals__, | |
92 | - name, | |
93 | - func.__defaults__, | |
94 | - func.__closure__ | |
95 | - ) | |
96 | - return inner |
@@ -1,241 +0,0 @@ | ||
1 | -''' | |
2 | -A crude compiler for a subset of Joy functions. | |
3 | - | |
4 | -I think I'm going about this the wrong way. | |
5 | - | |
6 | -The inference algorithm can "collapse" Yin function sequences into | |
7 | -single stack effects which can then be written out as Python functions. | |
8 | -Why not keep track of the new variables introduced as results of Yang | |
9 | -functions, during inference? Could I write out better code that way? | |
10 | - | |
11 | -In any event, I am proceeding with this sort of ad hoc way for now. | |
12 | -''' | |
13 | -from __future__ import print_function | |
14 | -from builtins import next | |
15 | -from builtins import str | |
16 | -from builtins import object | |
17 | -from joy.parser import text_to_expression, Symbol | |
18 | -from joy.utils.stack import concat, iter_stack, list_to_stack | |
19 | -from joy.library import SimpleFunctionWrapper, YIN_STACK_EFFECTS | |
20 | -from functools import reduce | |
21 | - | |
22 | - | |
23 | -def import_yin(): | |
24 | - from joy.utils.generated_library import * | |
25 | - return locals() | |
26 | - | |
27 | - | |
28 | -class InfiniteStack(tuple): | |
29 | - | |
30 | - def _names(): | |
31 | - n = 0 | |
32 | - while True: | |
33 | - m = yield Symbol('a' + str(n)) | |
34 | - n = n + 1 if m is None else m | |
35 | - | |
36 | - _NAMES = _names() | |
37 | - next(_NAMES) | |
38 | - | |
39 | - names = lambda: next(_NAMES) | |
40 | - reset = lambda _self, _n=_NAMES: _n.send(-1) | |
41 | - | |
42 | - def __init__(self, code): | |
43 | - self.reset() | |
44 | - self.code = code | |
45 | - | |
46 | - def __iter__(self): | |
47 | - if not self: | |
48 | - new_var = self.names() | |
49 | - self.code.append(('pop', new_var)) | |
50 | - return iter((new_var, self)) | |
51 | - | |
52 | - | |
53 | -def I(expression): | |
54 | - code = [] | |
55 | - stack = InfiniteStack(code) | |
56 | - | |
57 | - while expression: | |
58 | - term, expression = expression | |
59 | - if isinstance(term, Symbol): | |
60 | - func = D[term] | |
61 | - stack, expression, _ = func(stack, expression, code) | |
62 | - else: | |
63 | - stack = term, stack | |
64 | - | |
65 | - code.append(tuple(['ret'] + list(iter_stack(stack)))) | |
66 | - return code | |
67 | - | |
68 | - | |
69 | -strtup = lambda a, b: '(%s, %s)' % (b, a) | |
70 | -strstk = lambda rest: reduce(strtup, rest, 'stack') | |
71 | - | |
72 | - | |
73 | -def code_gen(code): | |
74 | - #for p in code: print p | |
75 | - coalesce_pops(code) | |
76 | - lines = [] | |
77 | - emit = lines.append | |
78 | - for t in code: | |
79 | - tag, rest = t[0], t[1:] | |
80 | - if tag == 'pop': emit(strstk(rest) + ' = stack') | |
81 | - elif tag == 'call': emit('%s = %s%s' % rest) | |
82 | - elif tag == 'ret': emit('return ' + strstk(rest[::-1])) | |
83 | - else: | |
84 | - raise ValueError(tag) | |
85 | - return '\n'.join(' ' + line for line in lines) | |
86 | - | |
87 | - | |
88 | -def coalesce_pops(code): | |
89 | - code.sort(key=lambda p: p[0] != 'pop') # All pops to the front. | |
90 | - try: index = next((i for i, t in enumerate(code) if t[0] != 'pop')) | |
91 | - except StopIteration: return | |
92 | - code[:index] = [tuple(['pop'] + [t for _, t in code[:index][::-1]])] | |
93 | - | |
94 | - | |
95 | -def compile_yinyang(name, text): | |
96 | - return ''' | |
97 | -def %s(stack): | |
98 | -%s | |
99 | -''' % (name, code_gen(I(text_to_expression(text)))) | |
100 | - | |
101 | - | |
102 | -def q(): | |
103 | - memo = {} | |
104 | - def bar(type_var): | |
105 | - try: | |
106 | - res = memo[type_var] | |
107 | - except KeyError: | |
108 | - res = memo[type_var] = InfiniteStack.names() | |
109 | - return res | |
110 | - return bar | |
111 | - | |
112 | - | |
113 | -def type_vars_to_labels(thing, map_): | |
114 | - if not thing: | |
115 | - return thing | |
116 | - if not isinstance(thing, tuple): | |
117 | - return map_(thing) | |
118 | - return tuple(type_vars_to_labels(inner, map_) for inner in thing) | |
119 | - | |
120 | - | |
121 | -def remap_inputs(in_, stack, code): | |
122 | - map_ = q() | |
123 | - while in_: | |
124 | - term, in_ = in_ | |
125 | - arg0, stack = stack | |
126 | - term = type_vars_to_labels(term, map_) | |
127 | - code.append(('call', term, '', arg0)) | |
128 | - return stack, map_ | |
129 | - | |
130 | - | |
131 | -class BinaryBuiltin(object): | |
132 | - | |
133 | - def __init__(self, name): | |
134 | - self.name = name | |
135 | - | |
136 | - def __call__(self, stack, expression, code): | |
137 | - in1, (in0, stack) = stack | |
138 | - out = InfiniteStack.names() | |
139 | - code.append(('call', out, self.name, (in0, in1))) | |
140 | - return (out, stack), expression, code | |
141 | - | |
142 | - | |
143 | -YIN = import_yin() | |
144 | - | |
145 | - | |
146 | -D = { | |
147 | - name: SimpleFunctionWrapper(YIN[name]) | |
148 | - for name in ''' | |
149 | - ccons | |
150 | - cons | |
151 | - dup | |
152 | - dupd | |
153 | - dupdd | |
154 | - over | |
155 | - pop | |
156 | - popd | |
157 | - popdd | |
158 | - popop | |
159 | - popopd | |
160 | - popopdd | |
161 | - rolldown | |
162 | - rollup | |
163 | - swap | |
164 | - swons | |
165 | - tuck | |
166 | - unit | |
167 | - '''.split() | |
168 | - } | |
169 | - | |
170 | - | |
171 | -for name in ''' | |
172 | - first | |
173 | - first_two | |
174 | - fourth | |
175 | - rest | |
176 | - rrest | |
177 | - second | |
178 | - third | |
179 | - uncons | |
180 | - unswons | |
181 | - '''.split(): | |
182 | - | |
183 | - def foo(stack, expression, code, name=name): | |
184 | - in_, out = YIN_STACK_EFFECTS[name] | |
185 | - stack, map_ = remap_inputs(in_, stack, code) | |
186 | - out = type_vars_to_labels(out, map_) | |
187 | - return concat(out, stack), expression, code | |
188 | - | |
189 | - foo.__name__ = name | |
190 | - D[name] = foo | |
191 | - | |
192 | - | |
193 | -for name in ''' | |
194 | - eq | |
195 | - ge | |
196 | - gt | |
197 | - le | |
198 | - lt | |
199 | - ne | |
200 | - xor | |
201 | - lshift | |
202 | - rshift | |
203 | - and_ | |
204 | - or_ | |
205 | - add | |
206 | - floordiv | |
207 | - mod | |
208 | - mul | |
209 | - pow | |
210 | - sub | |
211 | - truediv | |
212 | - '''.split(): | |
213 | - D[name.rstrip('-')] = BinaryBuiltin(name) | |
214 | - | |
215 | - | |
216 | -''' | |
217 | - stack | |
218 | - stuncons | |
219 | - stununcons | |
220 | - swaack | |
221 | -''' | |
222 | - | |
223 | -for name in sorted(D): | |
224 | - print(name, end=' ') | |
225 | -## print compile_yinyang(name, name) | |
226 | -print('-' * 100) | |
227 | - | |
228 | - | |
229 | -print(compile_yinyang('mul_', 'mul')) | |
230 | -print(compile_yinyang('pop', 'pop')) | |
231 | -print(compile_yinyang('ppm', 'popop mul')) | |
232 | -print(compile_yinyang('sqr', 'dup mul')) | |
233 | -print(compile_yinyang('foo', 'dup 23 sub mul')) | |
234 | -print(compile_yinyang('four_mul', 'mul mul mul mul')) | |
235 | -print(compile_yinyang('baz', 'mul dup sub dup')) | |
236 | -print(compile_yinyang('to_the_fifth_power', 'dup dup mul dup mul mul')) | |
237 | -print(compile_yinyang('dup3', 'dup dup dup')) | |
238 | -print(compile_yinyang('df2m', 'dup first_two mul')) | |
239 | -print(compile_yinyang('sqr_first', 'uncons swap dup mul swons')) | |
240 | -print(compile_yinyang('0BAD', 'uncons dup mul')) | |
241 | -print(compile_yinyang('uncons', 'uncons')) |
@@ -1,387 +0,0 @@ | ||
1 | -# GENERATED FILE. DO NOT EDIT. | |
2 | - | |
3 | - | |
4 | -def _Tree_add_Ee(stack): | |
5 | - """ | |
6 | - :: | |
7 | - | |
8 | - ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1]) | |
9 | - | |
10 | - """ | |
11 | - (a1, (a2, (a3, ((a4, (a5, s1)), s2)))) = stack | |
12 | - return ((a2, (a3, s1)), s2) | |
13 | - | |
14 | - | |
15 | -def _Tree_delete_R0(stack): | |
16 | - """ | |
17 | - :: | |
18 | - | |
19 | - ([a2 ...1] a1 -- [a2 ...1] a2 a1 a1) | |
20 | - | |
21 | - """ | |
22 | - (a1, ((a2, s1), s2)) = stack | |
23 | - return (a1, (a1, (a2, ((a2, s1), s2)))) | |
24 | - | |
25 | - | |
26 | -def _Tree_delete_clear_stuff(stack): | |
27 | - """ | |
28 | - :: | |
29 | - | |
30 | - (a3 a2 [a1 ...1] -- [...1]) | |
31 | - | |
32 | - """ | |
33 | - ((a1, s1), (a2, (a3, s2))) = stack | |
34 | - return (s1, s2) | |
35 | - | |
36 | - | |
37 | -def _Tree_get_E(stack): | |
38 | - """ | |
39 | - :: | |
40 | - | |
41 | - ([a3 a4 ...1] a2 a1 -- a4) | |
42 | - | |
43 | - """ | |
44 | - (a1, (a2, ((a3, (a4, s1)), s2))) = stack | |
45 | - return (a4, s2) | |
46 | - | |
47 | - | |
48 | -def ccons(stack): | |
49 | - """ | |
50 | - :: | |
51 | - | |
52 | - (a2 a1 [...1] -- [a2 a1 ...1]) | |
53 | - | |
54 | - """ | |
55 | - (s1, (a1, (a2, s2))) = stack | |
56 | - return ((a2, (a1, s1)), s2) | |
57 | - | |
58 | - | |
59 | -def cons(stack): | |
60 | - """ | |
61 | - :: | |
62 | - | |
63 | - (a1 [...0] -- [a1 ...0]) | |
64 | - | |
65 | - """ | |
66 | - (s0, (a1, s23)) = stack | |
67 | - return ((a1, s0), s23) | |
68 | - | |
69 | - | |
70 | -def dup(stack): | |
71 | - """ | |
72 | - :: | |
73 | - | |
74 | - (a1 -- a1 a1) | |
75 | - | |
76 | - """ | |
77 | - (a1, s23) = stack | |
78 | - return (a1, (a1, s23)) | |
79 | - | |
80 | - | |
81 | -def dupd(stack): | |
82 | - """ | |
83 | - :: | |
84 | - | |
85 | - (a2 a1 -- a2 a2 a1) | |
86 | - | |
87 | - """ | |
88 | - (a1, (a2, s23)) = stack | |
89 | - return (a1, (a2, (a2, s23))) | |
90 | - | |
91 | - | |
92 | -def dupdd(stack): | |
93 | - """ | |
94 | - :: | |
95 | - | |
96 | - (a3 a2 a1 -- a3 a3 a2 a1) | |
97 | - | |
98 | - """ | |
99 | - (a1, (a2, (a3, s23))) = stack | |
100 | - return (a1, (a2, (a3, (a3, s23)))) | |
101 | - | |
102 | - | |
103 | -def first(stack): | |
104 | - """ | |
105 | - :: | |
106 | - | |
107 | - ([a1 ...1] -- a1) | |
108 | - | |
109 | - """ | |
110 | - ((a1, s1), s23) = stack | |
111 | - return (a1, s23) | |
112 | - | |
113 | - | |
114 | -def first_two(stack): | |
115 | - """ | |
116 | - :: | |
117 | - | |
118 | - ([a1 a2 ...1] -- a1 a2) | |
119 | - | |
120 | - """ | |
121 | - ((a1, (a2, s1)), s2) = stack | |
122 | - return (a2, (a1, s2)) | |
123 | - | |
124 | - | |
125 | -def fourth(stack): | |
126 | - """ | |
127 | - :: | |
128 | - | |
129 | - ([a1 a2 a3 a4 ...1] -- a4) | |
130 | - | |
131 | - """ | |
132 | - ((a1, (a2, (a3, (a4, s1)))), s2) = stack | |
133 | - return (a4, s2) | |
134 | - | |
135 | - | |
136 | -def over(stack): | |
137 | - """ | |
138 | - :: | |
139 | - | |
140 | - (a2 a1 -- a2 a1 a2) | |
141 | - | |
142 | - """ | |
143 | - (a1, (a2, s23)) = stack | |
144 | - return (a2, (a1, (a2, s23))) | |
145 | - | |
146 | - | |
147 | -def pop(stack): | |
148 | - """ | |
149 | - :: | |
150 | - | |
151 | - (a1 --) | |
152 | - | |
153 | - """ | |
154 | - (a1, s23) = stack | |
155 | - return s23 | |
156 | - | |
157 | - | |
158 | -def popd(stack): | |
159 | - """ | |
160 | - :: | |
161 | - | |
162 | - (a2 a1 -- a1) | |
163 | - | |
164 | - """ | |
165 | - (a1, (a2, s23)) = stack | |
166 | - return (a1, s23) | |
167 | - | |
168 | - | |
169 | -def popdd(stack): | |
170 | - """ | |
171 | - :: | |
172 | - | |
173 | - (a3 a2 a1 -- a2 a1) | |
174 | - | |
175 | - """ | |
176 | - (a1, (a2, (a3, s23))) = stack | |
177 | - return (a1, (a2, s23)) | |
178 | - | |
179 | - | |
180 | -def popop(stack): | |
181 | - """ | |
182 | - :: | |
183 | - | |
184 | - (a2 a1 --) | |
185 | - | |
186 | - """ | |
187 | - (a1, (a2, s23)) = stack | |
188 | - return s23 | |
189 | - | |
190 | - | |
191 | -def popopd(stack): | |
192 | - """ | |
193 | - :: | |
194 | - | |
195 | - (a3 a2 a1 -- a1) | |
196 | - | |
197 | - """ | |
198 | - (a1, (a2, (a3, s23))) = stack | |
199 | - return (a1, s23) | |
200 | - | |
201 | - | |
202 | -def popopdd(stack): | |
203 | - """ | |
204 | - :: | |
205 | - | |
206 | - (a4 a3 a2 a1 -- a2 a1) | |
207 | - | |
208 | - """ | |
209 | - (a1, (a2, (a3, (a4, s23)))) = stack | |
210 | - return (a1, (a2, s23)) | |
211 | - | |
212 | - | |
213 | -def rest(stack): | |
214 | - """ | |
215 | - :: | |
216 | - | |
217 | - ([a1 ...0] -- [...0]) | |
218 | - | |
219 | - """ | |
220 | - ((a1, s0), s23) = stack | |
221 | - return (s0, s23) | |
222 | - | |
223 | - | |
224 | -def rolldown(stack): | |
225 | - """ | |
226 | - :: | |
227 | - | |
228 | - (a1 a2 a3 -- a2 a3 a1) | |
229 | - | |
230 | - """ | |
231 | - (a3, (a2, (a1, s23))) = stack | |
232 | - return (a1, (a3, (a2, s23))) | |
233 | - | |
234 | - | |
235 | -def rollup(stack): | |
236 | - """ | |
237 | - :: | |
238 | - | |
239 | - (a1 a2 a3 -- a3 a1 a2) | |
240 | - | |
241 | - """ | |
242 | - (a3, (a2, (a1, s23))) = stack | |
243 | - return (a2, (a1, (a3, s23))) | |
244 | - | |
245 | - | |
246 | -def rrest(stack): | |
247 | - """ | |
248 | - :: | |
249 | - | |
250 | - ([a1 a2 ...1] -- [...1]) | |
251 | - | |
252 | - """ | |
253 | - ((a1, (a2, s1)), s2) = stack | |
254 | - return (s1, s2) | |
255 | - | |
256 | - | |
257 | -def second(stack): | |
258 | - """ | |
259 | - :: | |
260 | - | |
261 | - ([a1 a2 ...1] -- a2) | |
262 | - | |
263 | - """ | |
264 | - ((a1, (a2, s1)), s2) = stack | |
265 | - return (a2, s2) | |
266 | - | |
267 | - | |
268 | -def stack(stack): | |
269 | - """ | |
270 | - :: | |
271 | - | |
272 | - (... -- ... [...]) | |
273 | - | |
274 | - """ | |
275 | - s0 = stack | |
276 | - return (s0, s0) | |
277 | - | |
278 | - | |
279 | -def stuncons(stack): | |
280 | - """ | |
281 | - :: | |
282 | - | |
283 | - (... a1 -- ... a1 a1 [...]) | |
284 | - | |
285 | - """ | |
286 | - (a1, s1) = stack | |
287 | - return (s1, (a1, (a1, s1))) | |
288 | - | |
289 | - | |
290 | -def stununcons(stack): | |
291 | - """ | |
292 | - :: | |
293 | - | |
294 | - (... a2 a1 -- ... a2 a1 a1 a2 [...]) | |
295 | - | |
296 | - """ | |
297 | - (a1, (a2, s1)) = stack | |
298 | - return (s1, (a2, (a1, (a1, (a2, s1))))) | |
299 | - | |
300 | - | |
301 | -def swaack(stack): | |
302 | - """ | |
303 | - :: | |
304 | - | |
305 | - ([...1] -- [...0]) | |
306 | - | |
307 | - """ | |
308 | - (s1, s0) = stack | |
309 | - return (s0, s1) | |
310 | - | |
311 | - | |
312 | -def swap(stack): | |
313 | - """ | |
314 | - :: | |
315 | - | |
316 | - (a1 a2 -- a2 a1) | |
317 | - | |
318 | - """ | |
319 | - (a2, (a1, s23)) = stack | |
320 | - return (a1, (a2, s23)) | |
321 | - | |
322 | - | |
323 | -def swons(stack): | |
324 | - """ | |
325 | - :: | |
326 | - | |
327 | - ([...1] a1 -- [a1 ...1]) | |
328 | - | |
329 | - """ | |
330 | - (a1, (s1, s2)) = stack | |
331 | - return ((a1, s1), s2) | |
332 | - | |
333 | - | |
334 | -def third(stack): | |
335 | - """ | |
336 | - :: | |
337 | - | |
338 | - ([a1 a2 a3 ...1] -- a3) | |
339 | - | |
340 | - """ | |
341 | - ((a1, (a2, (a3, s1))), s2) = stack | |
342 | - return (a3, s2) | |
343 | - | |
344 | - | |
345 | -def tuck(stack): | |
346 | - """ | |
347 | - :: | |
348 | - | |
349 | - (a2 a1 -- a1 a2 a1) | |
350 | - | |
351 | - """ | |
352 | - (a1, (a2, s23)) = stack | |
353 | - return (a1, (a2, (a1, s23))) | |
354 | - | |
355 | - | |
356 | -def uncons(stack): | |
357 | - """ | |
358 | - :: | |
359 | - | |
360 | - ([a1 ...0] -- a1 [...0]) | |
361 | - | |
362 | - """ | |
363 | - ((a1, s0), s23) = stack | |
364 | - return (s0, (a1, s23)) | |
365 | - | |
366 | - | |
367 | -def unit(stack): | |
368 | - """ | |
369 | - :: | |
370 | - | |
371 | - (a1 -- [a1 ]) | |
372 | - | |
373 | - """ | |
374 | - (a1, s23) = stack | |
375 | - return ((a1, ()), s23) | |
376 | - | |
377 | - | |
378 | -def unswons(stack): | |
379 | - """ | |
380 | - :: | |
381 | - | |
382 | - ([a1 ...1] -- [...1] a1) | |
383 | - | |
384 | - """ | |
385 | - ((a1, s1), s2) = stack | |
386 | - return (a1, (s1, s2)) | |
387 | - |
@@ -1,31 +0,0 @@ | ||
1 | -from builtins import str | |
2 | -from joy.parser import Symbol | |
3 | - | |
4 | - | |
5 | -def _names(): | |
6 | - n = 0 | |
7 | - while True: | |
8 | - yield Symbol('a' + str(n)) | |
9 | - n += 1 | |
10 | - | |
11 | - | |
12 | -class InfiniteStack(tuple): | |
13 | - | |
14 | - names = lambda n=_names(): next(n) | |
15 | - | |
16 | - def __iter__(self): | |
17 | - if not self: | |
18 | - return iter((self.names(), self)) | |
19 | - | |
20 | - | |
21 | -i = InfiniteStack() | |
22 | - | |
23 | -a, b = i | |
24 | - | |
25 | -lambda u: (lambda fu, u: fu * fu * u)( | |
26 | - (lambda u: (lambda fu, u: fu * fu)( | |
27 | - (lambda u: (lambda fu, u: fu * fu * u)( | |
28 | - (lambda u: 1)(u), u))(u), u))(u), | |
29 | - u) | |
30 | - | |
31 | -lambda u: (lambda fu, u: fu * fu * u)((lambda u: (lambda fu, u: fu * fu)((lambda u: (lambda fu, u: fu * fu * u)((lambda u: 1)(u), u))(u), u))(u), u) |
@@ -1,128 +0,0 @@ | ||
1 | -# -*- coding: utf-8 -*- | |
2 | -# | |
3 | -# Copyright © 2016 Simon Forman | |
4 | -# | |
5 | -# This file is part of Thun. | |
6 | -# | |
7 | -# Thun is free software: you can redistribute it and/or modify | |
8 | -# it under the terms of the GNU General Public License as published by | |
9 | -# the Free Software Foundation, either version 3 of the License, or | |
10 | -# (at your option) any later version. | |
11 | -# | |
12 | -# Thun is distributed in the hope that it will be useful, | |
13 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | -# GNU General Public License for more details. | |
16 | -# | |
17 | -# You should have received a copy of the GNU General Public License | |
18 | -# along with Thun. If not see <http://www.gnu.org/licenses/>. | |
19 | -# | |
20 | -''' | |
21 | -Pretty printing support, e.g.:: | |
22 | - | |
23 | - Joy? 23 18 * 99 + | |
24 | - . 23 18 mul 99 add | |
25 | - 23 . 18 mul 99 add | |
26 | - 23 18 . mul 99 add | |
27 | - 414 . 99 add | |
28 | - 414 99 . add | |
29 | - 513 . | |
30 | - | |
31 | - 513 <-top | |
32 | - | |
33 | - joy? | |
34 | - | |
35 | -On each line the stack is printed with the top to the right, then a ``.`` to | |
36 | -represent the current locus of processing, then the pending expression to the | |
37 | -left. | |
38 | - | |
39 | -''' | |
40 | -# (Kinda clunky and hacky. This should be swapped out in favor of much | |
41 | -# smarter stuff.) | |
42 | -from __future__ import print_function | |
43 | -from builtins import object | |
44 | -from traceback import print_exc | |
45 | -from .stack import expression_to_string, stack_to_string | |
46 | -from ..joy import joy | |
47 | -from ..library import inscribe, FunctionWrapper | |
48 | - | |
49 | - | |
50 | -@inscribe | |
51 | -@FunctionWrapper | |
52 | -def trace(stack, expression, dictionary): | |
53 | - '''Evaluate a Joy expression on a stack and print a trace. | |
54 | - | |
55 | - This function is just like the `i` combinator but it also prints a | |
56 | - trace of the evaluation | |
57 | - | |
58 | - :param stack stack: The stack. | |
59 | - :param stack expression: The expression to evaluate. | |
60 | - :param dict dictionary: A ``dict`` mapping names to Joy functions. | |
61 | - :rtype: (stack, (), dictionary) | |
62 | - | |
63 | - ''' | |
64 | - tp = TracePrinter() | |
65 | - quote, stack = stack | |
66 | - try: | |
67 | - s, _, d = joy(stack, quote, dictionary, tp.viewer) | |
68 | - except: | |
69 | - tp.print_() | |
70 | - print('-' * 73) | |
71 | - raise | |
72 | - else: | |
73 | - tp.print_() | |
74 | - return s, expression, d | |
75 | - | |
76 | - | |
77 | -class TracePrinter(object): | |
78 | - ''' | |
79 | - This is what does the formatting. You instantiate it and pass the ``viewer()`` | |
80 | - method to the :py:func:`joy.joy.joy` function, then print it to see the | |
81 | - trace. | |
82 | - ''' | |
83 | - | |
84 | - def __init__(self): | |
85 | - self.history = [] | |
86 | - | |
87 | - def viewer(self, stack, expression): | |
88 | - ''' | |
89 | - Record the current stack and expression in the TracePrinter's history. | |
90 | - Pass this method as the ``viewer`` argument to the :py:func:`joy.joy.joy` function. | |
91 | - | |
92 | - :param stack quote: A stack. | |
93 | - :param stack expression: A stack. | |
94 | - ''' | |
95 | - self.history.append((stack, expression)) | |
96 | - | |
97 | - def __str__(self): | |
98 | - return '\n'.join(self.go()) | |
99 | - | |
100 | - def go(self): | |
101 | - ''' | |
102 | - Return a list of strings, one for each entry in the history, prefixed | |
103 | - with enough spaces to align all the interpreter dots. | |
104 | - | |
105 | - This method is called internally by the ``__str__()`` method. | |
106 | - | |
107 | - :rtype: list(str) | |
108 | - ''' | |
109 | - max_stack_length = 0 | |
110 | - lines = [] | |
111 | - for stack, expression in self.history: | |
112 | - stack = stack_to_string(stack) | |
113 | - expression = expression_to_string(expression) | |
114 | - n = len(stack) | |
115 | - if n > max_stack_length: | |
116 | - max_stack_length = n | |
117 | - lines.append((n, '%s . %s' % (stack, expression))) | |
118 | - return [ # Prefix spaces to line up '.'s. | |
119 | - (' ' * (max_stack_length - length) + line) | |
120 | - for length, line in lines | |
121 | - ] | |
122 | - | |
123 | - def print_(self): | |
124 | - try: | |
125 | - print(self) | |
126 | - except: | |
127 | - print_exc() | |
128 | - print('Exception while printing viewer.') |
@@ -1,197 +0,0 @@ | ||
1 | -# -*- coding: utf-8 -*- | |
2 | -# | |
3 | -# Copyright © 2014, 2015, 2017 Simon Forman | |
4 | -# | |
5 | -# This file is part of Thun | |
6 | -# | |
7 | -# Thun is free software: you can redistribute it and/or modify | |
8 | -# it under the terms of the GNU General Public License as published by | |
9 | -# the Free Software Foundation, either version 3 of the License, or | |
10 | -# (at your option) any later version. | |
11 | -# | |
12 | -# Thun is distributed in the hope that it will be useful, | |
13 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | -# GNU General Public License for more details. | |
16 | -# | |
17 | -# You should have received a copy of the GNU General Public License | |
18 | -# along with Thun. If not see <http://www.gnu.org/licenses/>. | |
19 | -# | |
20 | -''' | |
21 | -When talking about Joy we use the terms "stack", "quote", "sequence", | |
22 | -"list", and others to mean the same thing: a simple linear datatype that | |
23 | -permits certain operations such as iterating and pushing and popping | |
24 | -values from (at least) one end. | |
25 | - | |
26 | -There is no "Stack" Python class, instead we use the `cons list`_, a | |
27 | -venerable two-tuple recursive sequence datastructure, where the | |
28 | -empty tuple ``()`` is the empty stack and ``(head, rest)`` gives the | |
29 | -recursive form of a stack with one or more items on it:: | |
30 | - | |
31 | - stack := () | (item, stack) | |
32 | - | |
33 | -Putting some numbers onto a stack:: | |
34 | - | |
35 | - () | |
36 | - (1, ()) | |
37 | - (2, (1, ())) | |
38 | - (3, (2, (1, ()))) | |
39 | - ... | |
40 | - | |
41 | -Python has very nice "tuple packing and unpacking" in its syntax which | |
42 | -means we can directly "unpack" the expected arguments to a Joy function. | |
43 | - | |
44 | -For example:: | |
45 | - | |
46 | - def dup((head, tail)): | |
47 | - return head, (head, tail) | |
48 | - | |
49 | -We replace the argument "stack" by the expected structure of the stack, | |
50 | -in this case "(head, tail)", and Python takes care of unpacking the | |
51 | -incoming tuple and assigning values to the names. (Note that Python | |
52 | -syntax doesn't require parentheses around tuples used in expressions | |
53 | -where they would be redundant.) | |
54 | - | |
55 | -Unfortunately, the Sphinx documentation generator, which is used to generate this | |
56 | -web page, doesn't handle tuples in the function parameters. And in Python 3, this | |
57 | -syntax was removed entirely. Instead you would have to write:: | |
58 | - | |
59 | - def dup(stack): | |
60 | - head, tail = stack | |
61 | - return head, (head, tail) | |
62 | - | |
63 | - | |
64 | -We have two very simple functions, one to build up a stack from a Python | |
65 | -iterable and another to iterate through a stack and yield its items | |
66 | -one-by-one in order. There are also two functions to generate string representations | |
67 | -of stacks. They only differ in that one prints the terms in stack from left-to-right while the other prints from right-to-left. In both functions *internal stacks* are | |
68 | -printed left-to-right. These functions are written to support :doc:`../pretty`. | |
69 | - | |
70 | -.. _cons list: https://en.wikipedia.org/wiki/Cons#Lists | |
71 | - | |
72 | -''' | |
73 | - | |
74 | - | |
75 | -from builtins import map | |
76 | -def list_to_stack(el, stack=()): | |
77 | - '''Convert a Python list (or other sequence) to a Joy stack:: | |
78 | - | |
79 | - [1, 2, 3] -> (1, (2, (3, ()))) | |
80 | - | |
81 | - :param list el: A Python list or other sequence (iterators and generators | |
82 | - won't work because ``reverse()`` is called on ``el``.) | |
83 | - :param stack stack: A stack, optional, defaults to the empty stack. | |
84 | - :rtype: stack | |
85 | - | |
86 | - ''' | |
87 | - for item in reversed(el): | |
88 | - stack = item, stack | |
89 | - return stack | |
90 | - | |
91 | - | |
92 | -def iter_stack(stack): | |
93 | - '''Iterate through the items on the stack. | |
94 | - | |
95 | - :param stack stack: A stack. | |
96 | - :rtype: iterator | |
97 | - ''' | |
98 | - while stack: | |
99 | - item, stack = stack | |
100 | - yield item | |
101 | - | |
102 | - | |
103 | -def stack_to_string(stack): | |
104 | - ''' | |
105 | - Return a "pretty print" string for a stack. | |
106 | - | |
107 | - The items are written right-to-left:: | |
108 | - | |
109 | - (top, (second, ...)) -> '... second top' | |
110 | - | |
111 | - :param stack stack: A stack. | |
112 | - :rtype: str | |
113 | - ''' | |
114 | - f = lambda stack: reversed(list(iter_stack(stack))) | |
115 | - return _to_string(stack, f) | |
116 | - | |
117 | - | |
118 | -def expression_to_string(expression): | |
119 | - ''' | |
120 | - Return a "pretty print" string for a expression. | |
121 | - | |
122 | - The items are written left-to-right:: | |
123 | - | |
124 | - (top, (second, ...)) -> 'top second ...' | |
125 | - | |
126 | - :param stack expression: A stack. | |
127 | - :rtype: str | |
128 | - ''' | |
129 | - return _to_string(expression, iter_stack) | |
130 | - | |
131 | - | |
132 | -def _to_string(stack, f): | |
133 | - if not isinstance(stack, tuple): return repr(stack) | |
134 | - if not stack: return '' # shortcut | |
135 | - return ' '.join(map(_s, f(stack))) | |
136 | - | |
137 | - | |
138 | -_s = lambda s: ( | |
139 | - '[%s]' % expression_to_string(s) if isinstance(s, tuple) | |
140 | - else repr(s) | |
141 | - ) | |
142 | - | |
143 | - | |
144 | -def concat(quote, expression): | |
145 | - '''Concatinate quote onto expression. | |
146 | - | |
147 | - In joy [1 2] [3 4] would become [1 2 3 4]. | |
148 | - | |
149 | - :param stack quote: A stack. | |
150 | - :param stack expression: A stack. | |
151 | - :raises RuntimeError: if quote is larger than sys.getrecursionlimit(). | |
152 | - :rtype: stack | |
153 | - ''' | |
154 | - # This is the fastest implementation, but will trigger | |
155 | - # RuntimeError: maximum recursion depth exceeded | |
156 | - # on quotes longer than sys.getrecursionlimit(). | |
157 | - | |
158 | - return (quote[0], concat(quote[1], expression)) if quote else expression | |
159 | - | |
160 | - # Original implementation. | |
161 | - | |
162 | -## return list_to_stack(list(iter_stack(quote)), expression) | |
163 | - | |
164 | - # In-lining is slightly faster (and won't break the | |
165 | - # recursion limit on long quotes.) | |
166 | - | |
167 | -## temp = [] | |
168 | -## while quote: | |
169 | -## item, quote = quote | |
170 | -## temp.append(item) | |
171 | -## for item in reversed(temp): | |
172 | -## expression = item, expression | |
173 | -## return expression | |
174 | - | |
175 | - | |
176 | - | |
177 | -def pick(stack, n): | |
178 | - ''' | |
179 | - Return the nth item on the stack. | |
180 | - | |
181 | - :param stack stack: A stack. | |
182 | - :param int n: An index into the stack. | |
183 | - :raises ValueError: if ``n`` is less than zero. | |
184 | - :raises IndexError: if ``n`` is equal to or greater than the length of ``stack``. | |
185 | - :rtype: whatever | |
186 | - ''' | |
187 | - if n < 0: | |
188 | - raise ValueError | |
189 | - while True: | |
190 | - try: | |
191 | - item, stack = stack | |
192 | - except ValueError: | |
193 | - raise IndexError | |
194 | - n -= 1 | |
195 | - if n < 0: | |
196 | - break | |
197 | - return item |
@@ -1,756 +0,0 @@ | ||
1 | -# -*- coding: utf_8 | |
2 | -# | |
3 | -# Copyright © 2018 Simon Forman | |
4 | -# | |
5 | -# This file is part of Thun | |
6 | -# | |
7 | -# Thun is free software: you can redistribute it and/or modify | |
8 | -# it under the terms of the GNU General Public License as published by | |
9 | -# the Free Software Foundation, either version 3 of the License, or | |
10 | -# (at your option) any later version. | |
11 | -# | |
12 | -# Thun is distributed in the hope that it will be useful, | |
13 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | -# GNU General Public License for more details. | |
16 | -# | |
17 | -# You should have received a copy of the GNU General Public License | |
18 | -# along with Thun. If not see <http://www.gnu.org/licenses/>. | |
19 | -# | |
20 | -from __future__ import print_function | |
21 | -from builtins import str | |
22 | -from builtins import map | |
23 | -from past.builtins import basestring | |
24 | -from builtins import object | |
25 | -from logging import getLogger, addLevelName | |
26 | -from functools import reduce | |
27 | - | |
28 | -_log = getLogger(__name__) | |
29 | -addLevelName(15, 'hmm') | |
30 | - | |
31 | -from collections import Counter | |
32 | -from itertools import chain, product | |
33 | -from inspect import stack as inspect_stack | |
34 | -from joy.utils.stack import ( | |
35 | - concat, | |
36 | - expression_to_string, | |
37 | - list_to_stack, | |
38 | - stack_to_string, | |
39 | - ) | |
40 | -from joy.parser import Symbol, text_to_expression | |
41 | - | |
42 | - | |
43 | -class AnyJoyType(object): | |
44 | - ''' | |
45 | - Joy type variable. Represents any Joy value. | |
46 | - ''' | |
47 | - | |
48 | - accept = tuple, int, float, int, complex, str, bool, Symbol | |
49 | - prefix = 'a' | |
50 | - | |
51 | - def __init__(self, number): | |
52 | - self.number = number | |
53 | - | |
54 | - def __repr__(self): | |
55 | - return self.prefix + str(self.number) | |
56 | - | |
57 | - def __eq__(self, other): | |
58 | - return ( | |
59 | - isinstance(other, self.__class__) | |
60 | - and other.prefix == self.prefix | |
61 | - and other.number == self.number | |
62 | - ) | |
63 | - | |
64 | - def __ge__(self, other): | |
65 | - return ( | |
66 | - issubclass(other.__class__, self.__class__) | |
67 | - or isinstance(other, self.accept) | |
68 | - ) | |
69 | - | |
70 | - def __le__(self, other): | |
71 | - # 'a string' >= AnyJoyType() should be False. | |
72 | - return issubclass(self.__class__, other.__class__) | |
73 | - | |
74 | - def __add__(self, other): | |
75 | - return self.__class__(self.number + other) | |
76 | - __radd__ = __add__ | |
77 | - | |
78 | - def __hash__(self): | |
79 | - return hash(repr(self)) | |
80 | - | |
81 | - | |
82 | -class BooleanJoyType(AnyJoyType): | |
83 | - accept = bool | |
84 | - prefix = 'b' | |
85 | - | |
86 | - | |
87 | -class NumberJoyType(AnyJoyType): | |
88 | - accept = bool, int, float, complex | |
89 | - prefix = 'n' | |
90 | - | |
91 | - | |
92 | -class FloatJoyType(NumberJoyType): | |
93 | - accept = float | |
94 | - prefix = 'f' | |
95 | - | |
96 | - | |
97 | -class IntJoyType(FloatJoyType): | |
98 | - accept = int | |
99 | - prefix = 'i' | |
100 | - | |
101 | - | |
102 | -class TextJoyType(AnyJoyType): | |
103 | - accept = basestring | |
104 | - prefix = 't' | |
105 | - | |
106 | - | |
107 | -class StackJoyType(AnyJoyType): | |
108 | - | |
109 | - accept = tuple | |
110 | - prefix = 's' | |
111 | - | |
112 | - def __bool__(self): | |
113 | - # Imitate () at the end of cons list. | |
114 | - return False | |
115 | - | |
116 | - | |
117 | -class KleeneStar(object): | |
118 | - u''' | |
119 | - A sequence of zero or more `AnyJoyType` variables would be: | |
120 | - | |
121 | - A* | |
122 | - | |
123 | - The `A*` works by splitting the universe into two alternate histories: | |
124 | - | |
125 | - A* → ∅ | |
126 | - | |
127 | - A* → A A* | |
128 | - | |
129 | - The Kleene star variable disappears in one universe, and in the other | |
130 | - it turns into an `AnyJoyType` variable followed by itself again. | |
131 | - | |
132 | - We have to return all universes (represented by their substitution | |
133 | - dicts, the "unifiers") that don't lead to type conflicts. | |
134 | - ''' | |
135 | - | |
136 | - kind = AnyJoyType | |
137 | - | |
138 | - def __init__(self, number): | |
139 | - assert number | |
140 | - self.number = number | |
141 | - self.count = 0 | |
142 | - self.prefix = repr(self) | |
143 | - | |
144 | - def __repr__(self): | |
145 | - return '%s%i*' % (self.kind.prefix, self.number) | |
146 | - | |
147 | - def another(self): | |
148 | - self.count += 1 | |
149 | - return self.kind(10000 * self.number + self.count) | |
150 | - | |
151 | - def __eq__(self, other): | |
152 | - return ( | |
153 | - isinstance(other, self.__class__) | |
154 | - and other.number == self.number | |
155 | - ) | |
156 | - | |
157 | - def __ge__(self, other): | |
158 | - return self.kind >= other.kind | |
159 | - | |
160 | - def __add__(self, other): | |
161 | - return self.__class__(self.number + other) | |
162 | - __radd__ = __add__ | |
163 | - | |
164 | - def __hash__(self): | |
165 | - return hash(repr(self)) | |
166 | - | |
167 | - | |
168 | -class AnyStarJoyType(KleeneStar): kind = AnyJoyType | |
169 | -class NumberStarJoyType(KleeneStar): kind = NumberJoyType | |
170 | -class FloatStarJoyType(KleeneStar): kind = FloatJoyType | |
171 | -class IntStarJoyType(KleeneStar): kind = IntJoyType | |
172 | -class StackStarJoyType(KleeneStar): kind = StackJoyType | |
173 | -class TextStarJoyType(KleeneStar): kind = TextJoyType | |
174 | - | |
175 | - | |
176 | -class FunctionJoyType(AnyJoyType): | |
177 | - | |
178 | - def __init__(self, name, sec, number): | |
179 | - self.name = name | |
180 | - self.stack_effects = sec | |
181 | - self.number = number | |
182 | - | |
183 | - def __add__(self, other): | |
184 | - return self | |
185 | - __radd__ = __add__ | |
186 | - | |
187 | - def __repr__(self): | |
188 | - return self.name | |
189 | - | |
190 | - | |
191 | -class SymbolJoyType(FunctionJoyType): | |
192 | - ''' | |
193 | - Represent non-combinator functions. | |
194 | - | |
195 | - These type variables carry the stack effect comments and can | |
196 | - appear in expressions (as in quoted programs.) | |
197 | - ''' | |
198 | - prefix = 'F' | |
199 | - | |
200 | - | |
201 | -class CombinatorJoyType(FunctionJoyType): | |
202 | - ''' | |
203 | - Represent combinators. | |
204 | - | |
205 | - These type variables carry Joy functions that implement the | |
206 | - behaviour of Joy combinators and they can appear in expressions. | |
207 | - For simple combinators the implementation functions can be the | |
208 | - combinators themselves. | |
209 | - | |
210 | - These types can also specify a stack effect (input side only) to | |
211 | - guard against being used on invalid types. | |
212 | - ''' | |
213 | - | |
214 | - prefix = 'C' | |
215 | - | |
216 | - def __init__(self, name, sec, number, expect=None): | |
217 | - super(CombinatorJoyType, self).__init__(name, sec, number) | |
218 | - self.expect = expect | |
219 | - | |
220 | - def enter_guard(self, f): | |
221 | - if self.expect is None: | |
222 | - return f | |
223 | - g = self.expect, self.expect | |
224 | - new_f = list(poly_compose(f, g, ())) | |
225 | - assert len(new_f) == 1, repr(new_f) | |
226 | - return new_f[0][1] | |
227 | - | |
228 | - | |
229 | -class JoyTypeError(Exception): pass | |
230 | - | |
231 | - | |
232 | -def reify(meaning, name, seen=None): | |
233 | - ''' | |
234 | - Apply substitution dict to term, returning new term. | |
235 | - ''' | |
236 | - if isinstance(name, tuple): | |
237 | - return tuple(reify(meaning, inner) for inner in name) | |
238 | - safety = 101 | |
239 | - while name in meaning and safety: | |
240 | - safety -= 1 | |
241 | - name = meaning[name] | |
242 | - if not safety: | |
243 | - raise ValueError('Cycle in substitution dict: %s' % (meaning,)) | |
244 | - return name | |
245 | - | |
246 | - | |
247 | -def relabel(left, right): | |
248 | - ''' | |
249 | - Re-number type variables to avoid collisions between stack effects. | |
250 | - ''' | |
251 | - return left, _1000(right) | |
252 | - | |
253 | - | |
254 | -def _1000(right): | |
255 | - if isinstance(right, Symbol): | |
256 | - return right | |
257 | - if not isinstance(right, tuple): | |
258 | - return 1000 + right | |
259 | - return tuple(_1000(n) for n in right) | |
260 | - | |
261 | - | |
262 | -def delabel(f, seen=None, c=None): | |
263 | - ''' | |
264 | - Fix up type variable numbers after relabel(). | |
265 | - ''' | |
266 | - if seen is None: | |
267 | - assert c is None | |
268 | - seen, c = {}, Counter() | |
269 | - | |
270 | - try: | |
271 | - return seen[f] | |
272 | - except KeyError: | |
273 | - pass | |
274 | - | |
275 | - if not isinstance(f, tuple): | |
276 | - try: | |
277 | - seen[f] = f.__class__(c[f.prefix] + 1) | |
278 | - except (TypeError, # FunctionJoyTypes break this. | |
279 | - AttributeError): # Symbol | |
280 | - seen[f] = f | |
281 | - else: | |
282 | - c[f.prefix] += 1 | |
283 | - return seen[f] | |
284 | - | |
285 | - return tuple(delabel(inner, seen, c) for inner in f) | |
286 | - | |
287 | - | |
288 | -def uni_unify(u, v, s=None): | |
289 | - ''' | |
290 | - Return a substitution dict representing a unifier for u and v. | |
291 | - ''' | |
292 | - if s is None: | |
293 | - s = {} | |
294 | - elif s: | |
295 | - u = reify(s, u) | |
296 | - v = reify(s, v) | |
297 | - | |
298 | - if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType): | |
299 | - if u >= v: | |
300 | - s[u] = v | |
301 | - elif v >= u: | |
302 | - s[v] = u | |
303 | - else: | |
304 | - raise JoyTypeError('Cannot unify %r and %r.' % (u, v)) | |
305 | - | |
306 | - elif isinstance(u, tuple) and isinstance(v, tuple): | |
307 | - if len(u) != len(v) != 2: | |
308 | - raise ValueError(repr((u, v))) # Bad input. | |
309 | - (a, b), (c, d) = u, v | |
310 | - s = uni_unify(b, d, uni_unify(a, c, s)) | |
311 | - | |
312 | - elif isinstance(v, tuple): | |
313 | - if not _stacky(u): | |
314 | - raise JoyTypeError('Cannot unify %r and %r.' % (u, v)) | |
315 | - s[u] = v | |
316 | - | |
317 | - elif isinstance(u, tuple): | |
318 | - if not _stacky(v): | |
319 | - raise JoyTypeError('Cannot unify %r and %r.' % (v, u)) | |
320 | - s[v] = u | |
321 | - | |
322 | - else: | |
323 | - raise JoyTypeError('Cannot unify %r and %r.' % (u, v)) | |
324 | - | |
325 | - return s | |
326 | - | |
327 | - | |
328 | -def _log_uni(U): | |
329 | - def inner(u, v, s=None): | |
330 | - _log.debug( | |
331 | - '%3i %s U %s w/ %s', | |
332 | - len(inspect_stack()), u, v, s, | |
333 | - ) | |
334 | - res = U(u, v, s) | |
335 | - _log.debug( | |
336 | - '%3i %s U %s w/ %s => %s', | |
337 | - len(inspect_stack()), u, v, s, res, | |
338 | - ) | |
339 | - return res | |
340 | - return inner | |
341 | - | |
342 | - | |
343 | -@_log_uni | |
344 | -def unify(u, v, s=None): | |
345 | - ''' | |
346 | - Return a tuple of substitution dicts representing unifiers for u and v. | |
347 | - ''' | |
348 | - if s is None: | |
349 | - s = {} | |
350 | - elif s: | |
351 | - u = reify(s, u) | |
352 | - v = reify(s, v) | |
353 | - | |
354 | - if u == v: | |
355 | - res = s, | |
356 | - | |
357 | - elif isinstance(u, tuple) and isinstance(v, tuple): | |
358 | - if len(u) != 2 or len(v) != 2: | |
359 | - if _that_one_special_case(u, v): | |
360 | - return s, | |
361 | - raise ValueError(repr((u, v))) # Bad input. | |
362 | - | |
363 | - | |
364 | - (a, b), (c, d) = v, u | |
365 | - if isinstance(a, KleeneStar): | |
366 | - if isinstance(c, KleeneStar): | |
367 | - s = _lil_uni(a, c, s) # Attempt to unify the two K-stars. | |
368 | - res = unify(d, b, s[0]) | |
369 | - | |
370 | - else: | |
371 | - # Two universes, in one the Kleene star disappears and | |
372 | - # unification continues without it... | |
373 | - s0 = unify(u, b) | |
374 | - | |
375 | - # In the other it spawns a new variable. | |
376 | - s1 = unify(u, (a.another(), v)) | |
377 | - | |
378 | - res = s0 + s1 | |
379 | - for sn in res: | |
380 | - sn.update(s) | |
381 | - | |
382 | - elif isinstance(c, KleeneStar): | |
383 | - res = unify(v, d) + unify(v, (c.another(), u)) | |
384 | - for sn in res: | |
385 | - sn.update(s) | |
386 | - | |
387 | - else: | |
388 | - res = tuple(flatten(unify(d, b, sn) for sn in unify(c, a, s))) | |
389 | - | |
390 | - elif isinstance(v, tuple): | |
391 | - if not _stacky(u): | |
392 | - raise JoyTypeError('Cannot unify %r and %r.' % (u, v)) | |
393 | - s[u] = v | |
394 | - res = s, | |
395 | - | |
396 | - elif isinstance(u, tuple): | |
397 | - if not _stacky(v): | |
398 | - raise JoyTypeError('Cannot unify %r and %r.' % (v, u)) | |
399 | - s[v] = u | |
400 | - res = s, | |
401 | - | |
402 | - else: | |
403 | - res = _lil_uni(u, v, s) | |
404 | - | |
405 | - return res | |
406 | - | |
407 | - | |
408 | -def _that_one_special_case(u, v): | |
409 | - ''' | |
410 | - Handle e.g. ((), (n1*, s1)) when type-checking sum, product, etc... | |
411 | - ''' | |
412 | - return ( | |
413 | - u == () | |
414 | - and len(v) == 2 | |
415 | - and isinstance(v[0], KleeneStar) | |
416 | - and isinstance(v[1], StackJoyType) | |
417 | - ) | |
418 | - | |
419 | - | |
420 | -def flatten(g): | |
421 | - return list(chain.from_iterable(g)) | |
422 | - | |
423 | - | |
424 | -def _lil_uni(u, v, s): | |
425 | - if u >= v: | |
426 | - s[u] = v | |
427 | - return s, | |
428 | - if v >= u: | |
429 | - s[v] = u | |
430 | - return s, | |
431 | - raise JoyTypeError('Cannot unify %r and %r.' % (u, v)) | |
432 | - | |
433 | - | |
434 | -def _stacky(thing): | |
435 | - return thing.__class__ in {AnyJoyType, StackJoyType} | |
436 | - | |
437 | - | |
438 | -def _compose(f, g): | |
439 | - ''' | |
440 | - Return the stack effect of the composition of two stack effects. | |
441 | - ''' | |
442 | - # Relabel, unify, update, delabel. | |
443 | - (f_in, f_out), (g_in, g_out) = relabel(f, g) | |
444 | - fg = reify(uni_unify(g_in, f_out), (f_in, g_out)) | |
445 | - return delabel(fg) | |
446 | - | |
447 | - | |
448 | -def compose(*functions): | |
449 | - ''' | |
450 | - Return the stack effect of the composition of some of stack effects. | |
451 | - ''' | |
452 | - return reduce(_compose, functions) | |
453 | - | |
454 | - | |
455 | -def compilable(f): | |
456 | - ''' | |
457 | - Return True if a stack effect represents a function that can be | |
458 | - automatically compiled (to Python), False otherwise. | |
459 | - ''' | |
460 | - return isinstance(f, tuple) and all(map(compilable, f)) or _stacky(f) | |
461 | - | |
462 | - | |
463 | -def doc_from_stack_effect(inputs, outputs=('??', ())): | |
464 | - ''' | |
465 | - Return a crude string representation of a stack effect. | |
466 | - ''' | |
467 | - switch = [False] # Do we need to display the '...' for the rest of the main stack? | |
468 | - i, o = _f(inputs, switch), _f(outputs, switch) | |
469 | - if switch[0]: | |
470 | - i.append('...') | |
471 | - o.append('...') | |
472 | - return '(%s--%s)' % ( | |
473 | - ' '.join(reversed([''] + i)), | |
474 | - ' '.join(reversed(o + [''])), | |
475 | - ) | |
476 | - | |
477 | - | |
478 | -def _f(term, switch): | |
479 | - a = [] | |
480 | - while term and isinstance(term, tuple): | |
481 | - item, term = term | |
482 | - a.append(item) | |
483 | - assert isinstance(term, (tuple, StackJoyType)), repr(term) | |
484 | - a = [_to_str(i, term, switch) for i in a] | |
485 | - return a | |
486 | - | |
487 | - | |
488 | -def _to_str(term, stack, switch): | |
489 | - if not isinstance(term, tuple): | |
490 | - if term == stack: | |
491 | - switch[0] = True | |
492 | - return '[...]' | |
493 | - return ( | |
494 | - '[...%i]' % term.number | |
495 | - if isinstance(term, StackJoyType) | |
496 | - else str(term) | |
497 | - ) | |
498 | - | |
499 | - a = [] | |
500 | - while term and isinstance(term, tuple): | |
501 | - item, term = term | |
502 | - a.append(_to_str(item, stack, switch)) | |
503 | - assert isinstance(term, (tuple, StackJoyType)), repr(term) | |
504 | - if term == stack: | |
505 | - switch[0] = True | |
506 | - end = '' if term == () else '...' | |
507 | - #end = '...' | |
508 | - else: | |
509 | - end = '' if term == () else '...%i' % term.number | |
510 | - a.append(end) | |
511 | - return '[%s]' % ' '.join(a) | |
512 | - | |
513 | - | |
514 | -def compile_(name, f, doc=None): | |
515 | - ''' | |
516 | - Return a string of Python code implementing the function described | |
517 | - by the stack effect. If no doc string is passed doc_from_stack_effect() | |
518 | - is used to generate one. | |
519 | - ''' | |
520 | - i, o = f | |
521 | - if doc is None: | |
522 | - doc = doc_from_stack_effect(i, o) | |
523 | - return '''def %s(stack): | |
524 | - """ | |
525 | - :: | |
526 | - | |
527 | - %s | |
528 | - | |
529 | - """ | |
530 | - %s = stack | |
531 | - return %s''' % (name, doc, i, o) | |
532 | - | |
533 | - | |
534 | -def _poly_compose(f, g, e): | |
535 | - (f_in, f_out), (g_in, g_out) = f, g | |
536 | - for s in unify(g_in, f_out): | |
537 | - yield reify(s, (e, (f_in, g_out))) | |
538 | - | |
539 | - | |
540 | -def poly_compose(f, g, e): | |
541 | - ''' | |
542 | - Yield the stack effects of the composition of two stack effects. An | |
543 | - expression is carried along and updated and yielded. | |
544 | - ''' | |
545 | - f, g = relabel(f, g) | |
546 | - for fg in _poly_compose(f, g, e): | |
547 | - yield delabel(fg) | |
548 | - | |
549 | - | |
550 | -def _meta_compose(F, G, e): | |
551 | - for f, g in product(F, G): | |
552 | - try: | |
553 | - for result in poly_compose(f, g, e): yield result | |
554 | - except JoyTypeError: | |
555 | - pass | |
556 | - | |
557 | - | |
558 | -def meta_compose(F, G, e): | |
559 | - ''' | |
560 | - Yield the stack effects of the composition of two lists of stack | |
561 | - effects. An expression is carried along and updated and yielded. | |
562 | - ''' | |
563 | - res = sorted(set(_meta_compose(F, G, e))) | |
564 | - if not res: | |
565 | - raise JoyTypeError('Cannot unify %r and %r.' % (F, G)) | |
566 | - return res | |
567 | - | |
568 | - | |
569 | -_S0 = StackJoyType(0) | |
570 | -ID = _S0, _S0 # Identity function. | |
571 | - | |
572 | - | |
573 | -def _infer(e, F=ID): | |
574 | - if __debug__: | |
575 | - _log_it(e, F) | |
576 | - if not e: | |
577 | - return [F] | |
578 | - | |
579 | - n, e = e | |
580 | - | |
581 | - if isinstance(n, SymbolJoyType): | |
582 | - eFG = meta_compose([F], n.stack_effects, e) | |
583 | - res = flatten(_infer(e, Fn) for e, Fn in eFG) | |
584 | - | |
585 | - elif isinstance(n, CombinatorJoyType): | |
586 | - fi, fo = n.enter_guard(F) | |
587 | - res = flatten(_interpret(f, fi, fo, e) for f in n.stack_effects) | |
588 | - | |
589 | - elif isinstance(n, Symbol): | |
590 | - if n in FUNCTIONS: | |
591 | - res =_infer((FUNCTIONS[n], e), F) | |
592 | - else: | |
593 | - raise JoyTypeError(n) | |
594 | - # print n | |
595 | - # func = joy.library._dictionary[n] | |
596 | - # res = _interpret(func, F[0], F[1], e) | |
597 | - | |
598 | - else: | |
599 | - fi, fo = F | |
600 | - res = _infer(e, (fi, (n, fo))) | |
601 | - | |
602 | - return res | |
603 | - | |
604 | - | |
605 | -def _interpret(f, fi, fo, e): | |
606 | - new_fo, ee, _ = f(fo, e, {}) | |
607 | - ee = reify(FUNCTIONS, ee) # Fix Symbols. | |
608 | - new_F = fi, new_fo | |
609 | - return _infer(ee, new_F) | |
610 | - | |
611 | - | |
612 | -def _log_it(e, F): | |
613 | - _log.log( | |
614 | - 15, | |
615 | - u'%3i %s ∘ %s', | |
616 | - len(inspect_stack()), | |
617 | - doc_from_stack_effect(*F), | |
618 | - expression_to_string(e), | |
619 | - ) | |
620 | - | |
621 | - | |
622 | -def infer(*expression): | |
623 | - ''' | |
624 | - Return a list of stack effects for a Joy expression. | |
625 | - | |
626 | - For example:: | |
627 | - | |
628 | - h = infer(pop, swap, rolldown, rest, rest, cons, cons) | |
629 | - for fi, fo in h: | |
630 | - print doc_from_stack_effect(fi, fo) | |
631 | - | |
632 | - Prints:: | |
633 | - | |
634 | - ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1]) | |
635 | - | |
636 | - ''' | |
637 | - return sorted(set(_infer(list_to_stack(expression)))) | |
638 | - | |
639 | - | |
640 | -def infer_string(string): | |
641 | - e = reify(FUNCTIONS, text_to_expression(string)) # Fix Symbols. | |
642 | - return sorted(set(_infer(e))) | |
643 | - | |
644 | - | |
645 | -def infer_expression(expression): | |
646 | - e = reify(FUNCTIONS, expression) # Fix Symbols. | |
647 | - return sorted(set(_infer(e))) | |
648 | - | |
649 | - | |
650 | -def type_check(name, stack): | |
651 | - ''' | |
652 | - Trinary predicate. True if named function type-checks, False if it | |
653 | - fails, None if it's indeterminate (because I haven't entered it into | |
654 | - the FUNCTIONS dict yet.) | |
655 | - ''' | |
656 | - try: | |
657 | - func = FUNCTIONS[name] | |
658 | - except KeyError: | |
659 | - return # None, indicating unknown | |
660 | - if isinstance(func, SymbolJoyType): | |
661 | - secs = func.stack_effects | |
662 | - elif isinstance(func, CombinatorJoyType): | |
663 | - if func.expect is None: | |
664 | - return # None, indicating unknown | |
665 | - secs = [(func.expect, ())] | |
666 | - else: | |
667 | - raise TypeError(repr(func)) # wtf? | |
668 | - for fi, fo in secs: | |
669 | - try: | |
670 | - unify(fi, stack) | |
671 | - except (JoyTypeError, ValueError): | |
672 | - continue | |
673 | - except: | |
674 | - _log.exception( | |
675 | - 'Type-checking %s %s against %s', | |
676 | - name, | |
677 | - doc_from_stack_effect(fi, fo), | |
678 | - stack_to_string(stack), | |
679 | - ) | |
680 | - continue | |
681 | - return True | |
682 | - return False | |
683 | - | |
684 | - | |
685 | -FUNCTIONS = {} # Polytypes (lists of stack effects.) | |
686 | -_functions = {} # plain ol' stack effects. | |
687 | - | |
688 | - | |
689 | -def __(*seq): | |
690 | - stack = StackJoyType(23) | |
691 | - for item in seq: stack = item, stack | |
692 | - return stack | |
693 | - | |
694 | - | |
695 | -def stack_effect(*inputs): | |
696 | - def _stack_effect(*outputs): | |
697 | - def _apply_to(function): | |
698 | - i, o = _functions[function.name] = __(*inputs), __(*outputs) | |
699 | - d = doc_from_stack_effect(i, o) | |
700 | - function.__doc__ += ( | |
701 | - '\nStack effect::\n\n ' # '::' for Sphinx docs. | |
702 | - + d | |
703 | - ) | |
704 | - _log.info('Setting stack effect for %s := %s', function.name, d) | |
705 | - return function | |
706 | - return _apply_to | |
707 | - return _stack_effect | |
708 | - | |
709 | - | |
710 | -def ef(*inputs): | |
711 | - def _ef(*outputs): | |
712 | - return __(*inputs), __(*outputs) | |
713 | - return _ef | |
714 | - | |
715 | - | |
716 | -def combinator_effect(number, *expect): | |
717 | - def _combinator_effect(c): | |
718 | - e = __(*expect) if expect else None | |
719 | - FUNCTIONS[c.name] = CombinatorJoyType(c.name, [c], number, e) | |
720 | - if e: | |
721 | - sec = doc_from_stack_effect(e) | |
722 | - _log.info('Setting stack EXPECT for combinator %s := %s', c.name, sec) | |
723 | - return c | |
724 | - return _combinator_effect | |
725 | - | |
726 | - | |
727 | -def show(DEFS): | |
728 | - for name, stack_effect_comment in sorted(DEFS.items()): | |
729 | - t = ' *'[compilable(stack_effect_comment)] | |
730 | - print(name, '=', doc_from_stack_effect(*stack_effect_comment), t) | |
731 | - | |
732 | - | |
733 | -def generate_library_code(DEFS, f=None): | |
734 | - if f is None: | |
735 | - import sys | |
736 | - f = sys.stdout | |
737 | - print('# GENERATED FILE. DO NOT EDIT.\n', file=f) | |
738 | - for name, stack_effect_comment in sorted(DEFS.items()): | |
739 | - if not compilable(stack_effect_comment): | |
740 | - continue | |
741 | - print(file=f) | |
742 | - print(compile_(name, stack_effect_comment), file=f) | |
743 | - print(file=f) | |
744 | - | |
745 | - | |
746 | -def poly_combinator_effect(number, effect_funcs, *expect): | |
747 | - def _poly_combinator_effect(c): | |
748 | - e = __(*expect) if expect else None | |
749 | - FUNCTIONS[c.name] = CombinatorJoyType(c.name, effect_funcs, number, e) | |
750 | - if e: | |
751 | - _log.info('Setting stack EXPECT for combinator %s := %s', c.name, e) | |
752 | - return c | |
753 | - return _poly_combinator_effect | |
754 | - | |
755 | -#FUNCTIONS['branch'].expect = s7, (s6, (b1, s5)) | |
756 | - |