[pypy-svn] r17484 - in pypy/dist/pypy: annotation doc rpython rpython/test translator/c/test
tismer at codespeak.net
tismer at codespeak.net
Mon Sep 12 02:36:45 CEST 2005
Author: tismer
Date: Mon Sep 12 02:36:42 2005
New Revision: 17484
Modified:
pypy/dist/pypy/annotation/builtin.py
pypy/dist/pypy/doc/coding-guide.txt
pypy/dist/pypy/rpython/rlist.py
pypy/dist/pypy/rpython/rrange.py
pypy/dist/pypy/rpython/test/test_rrange.py
pypy/dist/pypy/translator/c/test/test_typed.py
Log:
completed the implementation of rrange.
It is true that ranges with a variable step are seldom animals.
Anyway I tried to make RPython complete in this area, without adding
too much overhead or slowing down the common cases.
- augmented the annotator to allow for variable step in ranges. This is
flagged by setting range_step to zero, a disallowed value in the constant case.
- special-cased variable step in rrange.py. Only if it is variable, a three-element
object is allocated for range and its iterator, no overhead created otherwise.
- added a runtime-check for variable step. Range always issues ValueError if step
is zero. The case is rare enough to not add special cases here.
- tried to make the additional code as small as possible and did not influence
the speed of the const case in any way.
- added a couple of new tests that ensure correct dynamic handling of the variable step.
- updated the coding guide, mostly by removing dropped restrictions.
I honestly hope not to raise complaints by this, although it was mostly for my own pleasure.
considering if and how to map the range/xrange implementations of StdObjSpace to it.
Modified: pypy/dist/pypy/annotation/builtin.py
==============================================================================
--- pypy/dist/pypy/annotation/builtin.py (original)
+++ pypy/dist/pypy/annotation/builtin.py Mon Sep 12 02:36:42 2005
@@ -53,13 +53,15 @@
else:
raise Exception, "range() takes 1 to 3 arguments"
if not s_step.is_constant():
- raise Exception, "range() step argument should be a constant"
- step = s_step.const
- if step == 0:
- raise Exception, "range() with step zero"
- elif step > 0:
- nonneg = s_start.nonneg
+ step = 0 # this case signals a variable step
else:
+ step = s_step.const
+ if step == 0:
+ raise Exception, "range() with step zero"
+ nonneg = False # so far
+ if step > 0:
+ nonneg = s_start.nonneg
+ elif step < 0:
nonneg = s_stop.nonneg or (s_stop.is_constant() and s_stop.const >= -1)
return getbookkeeper().newlist(SomeInteger(nonneg=nonneg), range_step=step)
Modified: pypy/dist/pypy/doc/coding-guide.txt
==============================================================================
--- pypy/dist/pypy/doc/coding-guide.txt (original)
+++ pypy/dist/pypy/doc/coding-guide.txt Mon Sep 12 02:36:42 2005
@@ -109,12 +109,14 @@
**control structures**
- all allowed
+ all allowed but yield
**range**
- does not create an array. It is only allowed in for loops. The step argument
- must be a constant.
+ ``range`` and ``xrange`` are identical. ``range`` does not necessarily create an array,
+ only if the result is modified. It is allowed everywhere and completely
+ implemented. The only visible difference to CPython is the inaccessability
+ of the ``xrange`` fields start, stop and step.
**definitions**
@@ -149,20 +151,21 @@
lists are used as an allocated array; list.append() does naive resizing, so as
far as possible use list comprehensions (see below). list.extend() or the +=
- operator are allowed and efficient. Unless there is really a use case for it,
- repetition is limited to initialization purposes: '[single_value] * length'.
+ operator are allowed and efficient.
+ Repetition via `*` or `*=` is fully supported as well.
**dicts**
- dicts with string keys only (preferably the kind of strings that are usually
- interned in CPython, i.e. short strings that look like identifiers). The
- implementation could safely decide that all dict keys should be interned.
+ dicts with a unique key type only, provided it is hashable.
+ String keys have been the only allowed key types for a while, but this was generalized.
+ After some re-optimization,
+ the implementation could safely decide that all dict keys should be interned.
**list comprehensions**
- may be used to create allocated, initialized array. the array size must be
- computable in advance, which implies that we don't allow an if clause.
+ may be used to create allocated, initialized arrays.
+ After list over-allocation was introduced, there is no longer any restriction.
**functions**
Modified: pypy/dist/pypy/rpython/rlist.py
==============================================================================
--- pypy/dist/pypy/rpython/rlist.py (original)
+++ pypy/dist/pypy/rpython/rlist.py Mon Sep 12 02:36:42 2005
@@ -31,7 +31,7 @@
from pypy.rpython import rrange
listitem = self.listdef.listitem
s_value = listitem.s_value
- if listitem.range_step and not listitem.mutated:
+ if listitem.range_step is not None and not listitem.mutated:
return rrange.RangeRepr(listitem.range_step)
elif (s_value.__class__ is annmodel.SomeObject and s_value.knowntype == object):
return robject.pyobj_repr
Modified: pypy/dist/pypy/rpython/rrange.py
==============================================================================
--- pypy/dist/pypy/rpython/rrange.py (original)
+++ pypy/dist/pypy/rpython/rrange.py Mon Sep 12 02:36:42 2005
@@ -13,20 +13,35 @@
# struct range {
# Signed start, stop; // step is always constant
# }
+#
+# struct rangest {
+# Signed start, stop, step; // rare case, for completeness
+# }
RANGE = GcStruct("range", ("start", Signed), ("stop", Signed))
RANGEITER = GcStruct("range", ("next", Signed), ("stop", Signed))
+RANGEST = GcStruct("range", ("start", Signed), ("stop", Signed),("step", Signed))
+RANGESTITER = GcStruct("range", ("next", Signed), ("stop", Signed), ("step", Signed))
class RangeRepr(Repr):
- lowleveltype = Ptr(RANGE)
-
def __init__(self, step):
self.step = step
+ if step != 0:
+ self.lowleveltype = Ptr(RANGE)
+ else:
+ self.lowleveltype = Ptr(RANGEST)
+
+ def _getstep(self, v_rng, hop):
+ return hop.genop('getfield', [v_rng, hop.inputconst(Void, 'step')],
+ resulttype=Signed)
def rtype_len(self, hop):
v_rng, = hop.inputargs(self)
- cstep = hop.inputconst(Signed, self.step)
+ if self.step != 0:
+ cstep = hop.inputconst(Signed, self.step)
+ else:
+ cstep = self._getstep(v_rng, hop)
return hop.gendirectcall(ll_rangelen, v_rng, cstep)
def make_iterator_repr(self):
@@ -42,7 +57,10 @@
spec = dum_nocheck
v_func = hop.inputconst(Void, spec)
v_lst, v_index = hop.inputargs(r_rng, Signed)
- cstep = hop.inputconst(Signed, r_rng.step)
+ if r_rng.step != 0:
+ cstep = hop.inputconst(Signed, r_rng.step)
+ else:
+ cstep = r_rng._getstep(v_lst, hop)
if hop.args_s[1].nonneg:
llfn = ll_rangeitem_nonneg
else:
@@ -94,6 +112,15 @@
l.stop = stop
return l
+def ll_newrangest(start, stop, step):
+ if step == 0:
+ raise ValueError
+ l = malloc(RANGEST)
+ l.start = start
+ l.stop = stop
+ l.step = step
+ return l
+
def rtype_builtin_range(hop):
vstep = hop.inputconst(Signed, 1)
if hop.nb_args == 1:
@@ -103,10 +130,15 @@
vstart, vstop = hop.inputargs(Signed, Signed)
else:
vstart, vstop, vstep = hop.inputargs(Signed, Signed, Signed)
- assert isinstance(vstep, Constant)
-
+ const_step = isinstance(vstep, Constant)
+ if const_step and vstep.value == 0:
+ # not really needed, annotator catches it. Just in case...
+ raise TyperError("range cannot have a const step of zero")
if isinstance(hop.r_result, RangeRepr):
- return hop.gendirectcall(ll_newrange, vstart, vstop)
+ if const_step:
+ return hop.gendirectcall(ll_newrange, vstart, vstop)
+ else:
+ return hop.gendirectcall(ll_newrangest, vstart, vstop, vstep)
else:
# cannot build a RANGE object, needs a real list
r_list = hop.r_result
@@ -116,6 +148,8 @@
rtype_builtin_xrange = rtype_builtin_range
def ll_range2list(LISTPTR, start, stop, step):
+ if step == 0:
+ raise ValueError
length = _ll_rangelen(start, stop, step)
l = ll_newlist(LISTPTR, length)
idx = 0
@@ -131,10 +165,12 @@
# Iteration.
class RangeIteratorRepr(IteratorRepr):
- lowleveltype = Ptr(RANGEITER)
-
def __init__(self, r_rng):
self.r_rng = r_rng
+ if r_rng.step != 0:
+ self.lowleveltype = Ptr(RANGEITER)
+ else:
+ self.lowleveltype = Ptr(RANGESTITER)
def newiter(self, hop):
v_rng, = hop.inputargs(self.r_rng)
@@ -143,19 +179,24 @@
def rtype_next(self, hop):
v_iter, = hop.inputargs(self)
- cstep = hop.inputconst(Signed, self.r_rng.step)
+ args = hop.inputconst(Signed, self.r_rng.step),
if self.r_rng.step > 0:
llfn = ll_rangenext_up
- else:
+ elif self.r_rng.step < 0:
llfn = ll_rangenext_down
+ else:
+ llfn = ll_rangenext_updown
+ args = ()
hop.has_implicit_exception(StopIteration) # record that we know about it
hop.exception_is_here()
- return hop.gendirectcall(llfn, v_iter, cstep)
+ return hop.gendirectcall(llfn, v_iter, *args)
def ll_rangeiter(ITERPTR, rng):
iter = malloc(ITERPTR.TO)
iter.next = rng.start
iter.stop = rng.stop
+ if ITERPTR.TO is RANGESTITER:
+ iter.step = rng.step
return iter
def ll_rangenext_up(iter, step):
@@ -171,3 +212,10 @@
raise StopIteration
iter.next = next + step
return next
+
+def ll_rangenext_updown(iter):
+ step = iter.step
+ if step > 0:
+ return ll_rangenext_up(iter, step)
+ else:
+ return ll_rangenext_down(iter, step)
Modified: pypy/dist/pypy/rpython/test/test_rrange.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rrange.py (original)
+++ pypy/dist/pypy/rpython/test/test_rrange.py Mon Sep 12 02:36:42 2005
@@ -3,10 +3,14 @@
from pypy.rpython.test.test_llinterp import interpret
def test_rlist_range():
- def test1(start, stop, step):
+ def test1(start, stop, step, varstep):
expected = range(start, stop, step)
length = len(expected)
- l = ll_newrange(start, stop)
+ if varstep:
+ l = ll_newrangest(start, stop, step)
+ step = l.step
+ else:
+ l = ll_newrange(start,stop)
assert ll_rangelen(l, step) == length
lst = [ll_rangeitem(dum_nocheck, l, i, step) for i in range(length)]
assert lst == expected
@@ -18,7 +22,8 @@
for start in (-10, 0, 1, 10):
for stop in (-8, 0, 4, 8, 25):
for step in (1, 2, 3, -1, -2):
- test1(start, stop, step)
+ for varstep in False,True:
+ test1(start, stop, step, varstep)
# ____________________________________________________________
@@ -76,3 +81,26 @@
start, stop = 10, 17
res = interpret(dummyfn, [start, stop])
assert res == dummyfn(start, stop)
+
+def check_failed(func, *args):
+ try:
+ interpret(func, *args)
+ except:
+ return True
+ else:
+ return False
+
+def test_range_extra():
+ def failingfn_const():
+ r = range(10, 17, 0)
+ return r[-1]
+ assert check_failed(failingfn_const, [])
+
+ def failingfn_var(step):
+ r = range(10, 17, step)
+ return r[-1]
+ step = 3
+ res = interpret(failingfn_var, [step])
+ assert res == failingfn_var(step)
+ step = 0
+ assert check_failed(failingfn_var, [step])
Modified: pypy/dist/pypy/translator/c/test/test_typed.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_typed.py (original)
+++ pypy/dist/pypy/translator/c/test/test_typed.py Mon Sep 12 02:36:42 2005
@@ -11,7 +11,7 @@
class TestTypedTestCase(_TestAnnotatedTestCase):
- def getcompiled(self, func):
+ def getcompiled(self, func, view=False):
t = Translator(func, simplifying=True)
# builds starting-types from func_defs
argstypelist = []
@@ -24,6 +24,8 @@
a.simplify()
t.specialize()
t.checkgraphs()
+ if view:
+ t.view()
return skip_missing_compiler(t.ccompile)
def test_call_five(self):
@@ -367,4 +369,14 @@
f = self.getcompiled(fn)
assert f(0) == fn(0)
assert f(-1) == fn(-1)
- raises(IndexError, f, 42)
\ No newline at end of file
+ raises(IndexError, f, 42)
+
+ def test_range_step(self):
+ def fn(step=int):
+ r = range(10, 37, step)
+ # we always raise on step = 0
+ return r[-2]
+ f = self.getcompiled(fn)#, view=True)
+ assert f(1) == fn(1)
+ assert f(3) == fn(3)
+ raises(ValueError, f, 0)
More information about the Pypy-commit
mailing list