• R/O
  • SSH

Joypy: コミット

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.

変更サマリ

  • modified: Makefile (diff)
  • delete: xerblin/__main__.py
  • delete: xerblin/joy.py
  • delete: xerblin/library.py
  • delete: xerblin/parser.py
  • delete: xerblin/utils/__init__.py
  • delete: xerblin/utils/brutal_hackery.py
  • delete: xerblin/utils/compiler.py
  • delete: xerblin/utils/generated_library.py
  • delete: xerblin/utils/infinite_stack.py
  • delete: xerblin/utils/pretty_print.py
  • delete: xerblin/utils/stack.py
  • delete: xerblin/utils/types.py

差分

diff -r 3b453f3f1a53 -r 239cf1409d4f Makefile
--- a/Makefile Tue May 19 15:13:38 2020 -0700
+++ b/Makefile Tue May 19 15:34:20 2020 -0700
@@ -1,14 +1,14 @@
11 # My make-fu style is old and tired. I just want to have a few helper commands.
22
33 TESTDIR = ./test00
4-VERSION = 0.4.0
4+VERSION = 0.1.0
55 WEBSERVER = sforman@shell.osdn.net
66
77 .PHONY: clean sdist test
88
99
1010 clean:
11- $(RM) -r Thun.egg-info/ dist/ build/ __pycache__/ $(TESTDIR)
11+ $(RM) -r Xerblin.egg-info/ dist/ build/ __pycache__/ $(TESTDIR)
1212 find . -name '*.pyc' | xargs $(RM)
1313
1414 sdist:
@@ -21,5 +21,6 @@
2121 $(RM) -r $(TESTDIR)
2222 virtualenv --system-site-packages --never-download $(TESTDIR)
2323 . $(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
2526 echo "Type: source $(TESTDIR)/bin/activate"
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/__main__.py
--- a/xerblin/__main__.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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())
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/joy.py
--- a/xerblin/joy.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/library.py
--- a/xerblin/library.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/parser.py
--- a/xerblin/parser.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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- ])
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/utils/brutal_hackery.py
--- a/xerblin/utils/brutal_hackery.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/utils/compiler.py
--- a/xerblin/utils/compiler.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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'))
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/utils/generated_library.py
--- a/xerblin/utils/generated_library.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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-
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/utils/infinite_stack.py
--- a/xerblin/utils/infinite_stack.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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)
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/utils/pretty_print.py
--- a/xerblin/utils/pretty_print.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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.')
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/utils/stack.py
--- a/xerblin/utils/stack.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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
diff -r 3b453f3f1a53 -r 239cf1409d4f xerblin/utils/types.py
--- a/xerblin/utils/types.py Tue May 19 15:13:38 2020 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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-
旧リポジトリブラウザで表示