OSDN Git Service

Clean up Zipper notebook.
[joypy/Thun.git] / joy / library.py
index 5faf22f..800ff3b 100644 (file)
@@ -23,11 +23,14 @@ functions.  Its main export is a Python function initialize() that
 returns a dictionary of Joy functions suitable for use with the joy()
 function.
 '''
+from pkg_resources import resource_stream
+from io import TextIOWrapper
 from inspect import getdoc, getmembers, isfunction
 from functools import wraps
 from itertools import count
 import operator, math
 
+from . import __name__ as _joy_package_name
 from .parser import text_to_expression, Symbol
 from .utils import generated_library as genlib
 from .utils.errors import (
@@ -44,6 +47,14 @@ from .utils.stack import (
     )
 
 
+def default_defs(dictionary):
+    def_stream = TextIOWrapper(
+        resource_stream(_joy_package_name, 'defs.txt'),
+        encoding='UTF_8',
+        )
+    Def.load_definitions(def_stream, dictionary)
+
+
 HELP_TEMPLATE = '''\
 
 ==== Help on %s ====
@@ -58,9 +69,9 @@ HELP_TEMPLATE = '''\
 _dictionary = {}
 
 
-def inscribe(function):
+def inscribe(function, d=_dictionary):
     '''A decorator to inscribe functions into the default dictionary.'''
-    _dictionary[function.name] = function
+    d[function.name] = function
     return function
 
 
@@ -111,88 +122,6 @@ def add_aliases(D, A):
             D[alias] = F
 
 
-definitions = ('''\
-? == dup truthy
-*fraction == [uncons] dip uncons [swap] dip concat [*] infra [*] dip cons
-*fraction0 == concat [[swap] dip * [*] dip] infra
-anamorphism == [pop []] swap [dip swons] genrec
-average == [sum 1.0 *] [size] cleave /
-binary == nullary [popop] dip
-cleave == fork [popd] dip
-codireco == cons dip rest cons
-dinfrirst == dip infra first
-unstack == ? [uncons ?] loop pop
-down_to_zero == [0 >] [dup --] while
-dupdipd == dup dipd
-enstacken == stack [clear] dip
-flatten == [] swap [concat] step
-fork == [i] app2
-gcd == 1 [tuck modulus dup 0 >] loop pop
-ifte == [nullary not] dipd branch
-ii == [dip] dupdip i
-least_fraction == dup [gcd] infra [div] concat map
-make_generator == [codireco] ccons
-nullary == [stack] dinfrirst
-of == swap at
-pam == [i] map
-tailrec == [i] genrec
-product == 1 swap [*] step
-quoted == [unit] dip
-range == [0 <=] [1 - dup] anamorphism
-range_to_zero == unit [down_to_zero] infra
-run == [] swap infra
-size == 0 swap [pop ++] step
-sqr == dup mul
-step_zero == 0 roll> step
-swoncat == swap concat
-tailrec == [i] genrec
-ternary == unary [popop] dip
-unary == nullary popd
-unquoted == [i] dip
-while == swap [nullary] cons dup dipd concat loop
-'''
-#
-#
-# ifte == [nullary] dipd swap branch
-# genrec == [[genrec] cons cons cons cons] nullary swons concat ifte
-
-# Another definition for while. FWIW
-# while == over [[i] dip nullary] ccons [nullary] dip loop
-
-##ccons == cons cons
-##unit == [] cons
-##second == rest first
-##third == rest rest first
-##swons == swap cons
-
-##Zipper
-##z-down == [] swap uncons swap
-##z-up == swons swap shunt
-##z-right == [swons] cons dip uncons swap
-##z-left == swons [uncons swap] dip swap
-
-##Quadratic Formula
-##divisor == popop 2 *
-##minusb == pop neg
-##radical == swap dup * rollup * 4 * - sqrt
-##root1 == + swap /
-##root2 == - swap /
-##q0 == [[divisor] [minusb] [radical]] pam
-##q1 == [[root1] [root2]] pam
-##quadratic == [q0] ternary i [q1] ternary
-
-# Project Euler
-##'''\
-##PE1.1 == + dup [+] dip
-##PE1.2 == dup [3 & PE1.1] dip 2 >>
-##PE1.3 == 14811 swap [PE1.2] times pop
-##PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop
-##'''
-#PE1.2 == [PE1.1] step
-#PE1 == 0 0 66 [[3 2 1 3 1 2 3] PE1.2] times [3 2 1 3] PE1.2 pop
-)
-
-
 def FunctionWrapper(f):
     '''Set name attribute.'''
     if not f.__doc__:
@@ -223,12 +152,13 @@ def BinaryBuiltinWrapper(f):
             (a, (b, stack)) = stack
         except ValueError:
             raise StackUnderflowError('Not enough values on stack.')
-        if (   not isinstance(a, int)
-            or not isinstance(b, int)
-            or isinstance(a, bool)  # Because bools are ints in Python.
-            or isinstance(b, bool)
-            ):
-            raise NotAnIntError
+        # Boolean predicates like "or" fail here.  :(
+##        if (   not isinstance(a, int)
+##            or not isinstance(b, int)
+##            or isinstance(a, bool)  # Because bools are ints in Python.
+##            or isinstance(b, bool)
+##            ):
+##            raise NotAnIntError
         result = f(b, a)
         return (result, stack), expression, dictionary
     return inner
@@ -247,16 +177,16 @@ def UnaryBuiltinWrapper(f):
     return inner
 
 
-class DefinitionWrapper(object):
+class Def(object):
     '''
-    Provide implementation of defined functions, and some helper methods.
+    Definitions created by inscribe.
     '''
 
-    def __init__(self, name, body_text, doc=None):
-        self.name = self.__name__ = name
-        self.body = text_to_expression(body_text)
-        self._body = tuple(iter_stack(self.body))
-        self.__doc__ = doc or body_text
+    def __init__(self, name, body):
+        self.name = name
+        self.body = body
+        self._body = tuple(iter_stack(body))
+        self.__doc__ = expression_to_string(body)
         self._compiled = None
 
     def __call__(self, stack, expression, dictionary):
@@ -266,58 +196,14 @@ class DefinitionWrapper(object):
         return stack, expression, dictionary
 
     @classmethod
-    def parse_definition(class_, defi):
-        '''
-        Given some text describing a Joy function definition parse it and
-        return a DefinitionWrapper.
-        '''
-        # At some point I decided that the definitions file should NOT
-        # use '==' to separate the name from the body.  But somehow the
-        # xerblin\gui\default_joy_home\definitions.txt file didn't get
-        # the memo.  Nor did the load_definitions() method.
-        # So I think the simplest way forward at the moment will be to
-        # edit this function to expect '=='.
-
-        name, part, body = defi.partition('==')
-        if part:
-            return class_(name.strip(), body.strip())
-        raise ValueError("No '==' in definition text %r" % (defi,))
-
-        # return class_(*(n.strip() for n in defi.split(None, 1)))
-
-    @classmethod
-    def add_definitions(class_, defs, dictionary):
-        '''
-        Scan multi-line string defs for definitions and add them to the
-        dictionary.
-        '''
-        for definition in _text_to_defs(defs):
-            class_.add_def(definition, dictionary)
-
-    @classmethod
-    def add_def(class_, definition, dictionary, fail_fails=False):
-        '''
-        Add the definition to the dictionary.
-        '''
-        F = class_.parse_definition(definition)
-        dictionary[F.name] = F
-
-    @classmethod
-    def load_definitions(class_, filename, dictionary):
-        with open(filename) as f:
-            lines = [line for line in f if '==' in line]
-        for line in lines:
-            class_.add_def(line, dictionary)
-
-
-def _text_to_defs(text):
-    return (
-        line.strip()
-        for line in text.splitlines()
-        if line
-           and not line.startswith('#')
-           and '==' in line
-        )
+    def load_definitions(class_, stream, dictionary):
+        for line in stream:
+            if line.lstrip().startswith('#'):
+                continue
+            name, body = text_to_expression(line)
+            if name not in dictionary:
+                inscribe(class_(name, body), dictionary)
+##            inscribe(class_(name, body), dictionary)
 
 
 #
@@ -330,16 +216,14 @@ def _text_to_defs(text):
 def inscribe_(stack, expression, dictionary):
     '''
     Create a new Joy function definition in the Joy dictionary.  A
-    definition is given as a string with a name followed by a double
-    equal sign then one or more Joy functions, the body. for example:
+    definition is given as a quote with a name followed by a Joy
+    expression. for example:
 
-        sqr == dup mul
+        [sqr dup mul] inscribe
 
-    If you want the definition to persist over restarts, enter it into
-    the definitions.txt resource.
     '''
-    definition, stack = stack
-    DefinitionWrapper.add_def(definition, dictionary, fail_fails=True)
+    (name, body), stack = stack
+    inscribe(Def(name, body), dictionary)
     return stack, expression, dictionary
 
 
@@ -901,6 +785,22 @@ def b(stack, expression, dictionary):
 
 @inscribe
 @FunctionWrapper
+def ii(stack, expression, dictionary):
+    '''
+    ::
+
+           ... a [Q] ii
+        ------------------
+            ... Q a Q
+
+    '''
+    quote, (a, stack) = stack
+    expression = concat(quote, (a, concat(quote, expression)))
+    return stack, expression, dictionary
+
+
+@inscribe
+@FunctionWrapper
 def dupdip(stack, expression, dictionary):
     '''
     ::
@@ -1477,6 +1377,3 @@ for name, primitive in getmembers(genlib, isfunction):
 
 
 add_aliases(_dictionary, ALIASES)
-
-
-DefinitionWrapper.add_definitions(definitions, _dictionary)