[pypy-svn] r17743 - pypy/dist/pypy/module/__builtin__

tismer at codespeak.net tismer at codespeak.net
Wed Sep 21 20:14:21 CEST 2005


Author: tismer
Date: Wed Sep 21 20:14:19 2005
New Revision: 17743

Added:
   pypy/dist/pypy/module/__builtin__/functional.py   (contents, props changed)
Modified:
   pypy/dist/pypy/module/__builtin__/__init__.py
   pypy/dist/pypy/module/__builtin__/app_functional.py
Log:
added an interplevel implementation of range.
The original function will stay in app_functional;
it is used for non-integer and all exception cases.

The speed effect on pystone is just tremendous,
although I believe that this would be almost
achievable by specialization of geninterp output.
Not easily, of course, so I think this is worth the small effort.

Modified: pypy/dist/pypy/module/__builtin__/__init__.py
==============================================================================
--- pypy/dist/pypy/module/__builtin__/__init__.py	(original)
+++ pypy/dist/pypy/module/__builtin__/__init__.py	Wed Sep 21 20:14:19 2005
@@ -22,7 +22,9 @@
         'filter'        : 'app_functional.filter',
         'zip'           : 'app_functional.zip',
         'reduce'        : 'app_functional.reduce',
-        'range'         : 'app_functional.range',
+        #'range'         : 'app_functional.range',
+        # redirected to functional.py, applevel version
+        # is still needed and should stay where it is.
         'min'           : 'app_functional.min',
         'max'           : 'app_functional.max',
         'enumerate'     : 'app_functional.enumerate',
@@ -107,6 +109,8 @@
 
         '__import__'    : 'importing.importhook',
 
+        'range'         : 'functional.range_int',
+
         # float->string helper
         '_formatd'      : 'special._formatd'
     }

Modified: pypy/dist/pypy/module/__builtin__/app_functional.py
==============================================================================
--- pypy/dist/pypy/module/__builtin__/app_functional.py	(original)
+++ pypy/dist/pypy/module/__builtin__/app_functional.py	Wed Sep 21 20:14:19 2005
@@ -140,6 +140,16 @@
 
 # ____________________________________________________________
 
+"""
+The following is a nice example of collaboration between
+interp-level and app-level.
+range is primarily implemented in functional.py for the integer case.
+On every error or different data types, it redirects to the applevel
+implementation below. functional.py uses this source via the inspect
+module and uses gateway.applevel. This is also an alternative to
+writing longer functions in strings.
+"""
+
 def range(x, y=None, step=1):
     """ returns a list of integers in arithmetic position from start (defaults
         to zero) to stop - 1 by step (defaults to 1).  Use a negative step to
@@ -147,11 +157,11 @@
 
 
     if y is None: 
-            start = 0
-            stop = x
+        start = 0
+        stop = x
     else:
-            start = x
-            stop = y
+        start = x
+        stop = y
 
     if not isinstance(start, (int, long)):
         raise TypeError('range() integer start argument expected, got %s' % type(start))
@@ -302,7 +312,7 @@
         if not isinstance(index, int):
             raise TypeError, "sequence index must be integer"
         len = self.len 
-        if index<0:
+        if index < 0:
             index += len
         if 0 <= index < len:
             return self.start + index * self.step

Added: pypy/dist/pypy/module/__builtin__/functional.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/module/__builtin__/functional.py	Wed Sep 21 20:14:19 2005
@@ -0,0 +1,94 @@
+"""
+Interp-level definition of frequently used functionals.
+
+Candidates      implemented
+
+  range             yes
+  zip               no
+  min               no
+  max               no
+  enumerate         no
+  xrange            no
+
+"""
+
+from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import ObjSpace, W_Root, NoneNotWrapped, applevel
+from pypy.rpython.rarithmetic import r_uint, intmask
+from pypy.module.__builtin__.app_functional import range as app_range
+from inspect import getsource, getfile
+
+"""
+Implementation of the common integer case of range. Instead of handling
+all other cases here, too, we fall back to the applevel implementation
+for non-integer arguments.
+Ideally this implementation could be saved, if we were able to
+specialize the geninterp generated code. But I guess having this
+hand-optimized is a good idea.
+
+Note the fun of using range inside range :-)
+"""
+
+def get_len_of_range(lo, hi, step):
+    """
+    Return number of items in range/xrange (lo, hi, step).  step > 0
+    required.  Return a value < 0 if & only if the true value is too
+    large to fit in a signed long.
+    """
+
+    # If lo >= hi, the range is empty.
+    # Else if n values are in the range, the last one is
+    # lo + (n-1)*step, which must be <= hi-1.  Rearranging,
+    # n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives
+    # the proper value.  Since lo < hi in this case, hi-lo-1 >= 0, so
+    # the RHS is non-negative and so truncation is the same as the
+    # floor.  Letting M be the largest positive long, the worst case
+    # for the RHS numerator is hi=M, lo=-M-1, and then
+    # hi-lo-1 = M-(-M-1)-1 = 2*M.  Therefore unsigned long has enough
+    # precision to compute the RHS exactly.
+
+    # slight modification: we raise on everything bad and also adjust args
+    if step == 0:
+        raise ValueError
+    elif step < 0:
+        lo, hi, step = hi, lo, -step
+    if lo < hi:
+        uhi = r_uint(hi)
+        ulo = r_uint(lo)
+        diff = uhi - ulo - 1
+        n = intmask(diff // r_uint(step) + 1)
+        if n < 0:
+            raise OverflowError
+    else:
+        n = 0
+    return n
+
+def range(space, w_x, w_y=None, w_step=1):
+    """ returns a list of integers in arithmetic position from start (defaults
+        to zero) to stop - 1 by step (defaults to 1).  Use a negative step to
+        get a list in decending order."""
+
+    try:
+        # save duplication by redirecting every error to applevel
+        x = space.int_w(w_x)
+        if w_y is space.w_None:
+            start, stop = 0, x
+        else:
+            start, stop = x, space.int_w(w_y)
+        step = space.int_w(w_step)
+        howmany = get_len_of_range(start, stop, step)
+    except (OperationError, ValueError, OverflowError):
+        return range_fallback(space, w_x, w_y, w_step)
+
+    res_w = [None] * howmany
+    v = start
+    for idx in range(howmany):
+        res_w[idx] = space.wrap(v)
+        v += step
+    return space.newlist(res_w)
+range_int = range
+range_int.unwrap_spec = [ObjSpace, W_Root, W_Root, W_Root]
+del range # don't hide the builtin one
+
+range_fallback = applevel(getsource(app_range), getfile(app_range)
+                          ).interphook('range')



More information about the Pypy-commit mailing list