cvsuser 05/01/02 16:20:10
Modified: . MANIFEST
dynclasses pybuiltin.pmc pyclass.pmc pyconsts.h pygen.pmc
pyint.pmc pylist.pmc pyobject.pmc pyproxytype.pmc
Added: languages/python/t/pie b0.t
Log:
Progress towards t/pie/b0.t
Revision Changes Path
1.816 +1 -0 parrot/MANIFEST
Index: MANIFEST
===================================================================
RCS file: /cvs/public/parrot/MANIFEST,v
retrieving revision 1.815
retrieving revision 1.816
diff -u -r1.815 -r1.816
--- MANIFEST 28 Dec 2004 07:18:27 -0000 1.815
+++ MANIFEST 3 Jan 2005 00:20:07 -0000 1.816
@@ -2361,6 +2361,7 @@
languages/python/t/basic/oo_attr.t []
languages/python/t/basic/oo_class.t []
languages/python/t/basic/iter.t []
+languages/python/t/pie/b0.t []
languages/python/t/pie/b1.t []
languages/python/t/pie/b2.t []
languages/python/t/pie/b3.t []
1.39 +5 -1 parrot/dynclasses/pybuiltin.pmc
Index: pybuiltin.pmc
===================================================================
RCS file: /cvs/public/parrot/dynclasses/pybuiltin.pmc,v
retrieving revision 1.38
retrieving revision 1.39
diff -u -r1.38 -r1.39
--- pybuiltin.pmc 30 Dec 2004 11:46:38 -0000 1.38
+++ pybuiltin.pmc 3 Jan 2005 00:20:08 -0000 1.39
@@ -1,6 +1,6 @@
/*
Copyright: 2001-2003 The Perl Foundation. All Rights Reserved.
-$Id: pybuiltin.pmc,v 1.38 2004/12/30 11:46:38 rubys Exp $
+$Id: pybuiltin.pmc,v 1.39 2005/01/03 00:20:08 rubys Exp $
=head1 NAME
@@ -88,6 +88,7 @@
PyBuiltin_PyDict = Parrot_PMC_typenum(INTERP, "PyDict");
PyBuiltin_PyException = Parrot_PMC_typenum(INTERP,
"PyException");
PyBuiltin_PyFloat = Parrot_PMC_typenum(INTERP, "PyFloat");
+ PyBuiltin_PyGen = Parrot_PMC_typenum(INTERP, "PyGen");
PyBuiltin_PyInt = Parrot_PMC_typenum(INTERP, "PyInt");
PyBuiltin_PyIter = Parrot_PMC_typenum(INTERP, "PyIter");
PyBuiltin_PyList = Parrot_PMC_typenum(INTERP, "PyList");
@@ -108,6 +109,7 @@
PyString_call = const_string(INTERP, "__call__");
PyString_cmp = const_string(INTERP, "__cmp__");
PyString_hex = const_string(INTERP, "__hex__");
+ PyString_hash = const_string(INTERP, "__hash__");
PyString_init = const_string(INTERP, "__init__");
PyString_int = const_string(INTERP, "__int__");
PyString_iter = const_string(INTERP, "__iter__");
@@ -117,6 +119,8 @@
PyString_proxy = const_string(INTERP, "__proxy__");
PyString_repr = const_string(INTERP, "__repr__");
PyString_str = const_string(INTERP, "__str__");
+
+ PyString_next = const_string(INTERP, "next");
}
}
1.20 +36 -3 parrot/dynclasses/pyclass.pmc
Index: pyclass.pmc
===================================================================
RCS file: /cvs/public/parrot/dynclasses/pyclass.pmc,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- pyclass.pmc 23 Dec 2004 23:49:32 -0000 1.19
+++ pyclass.pmc 3 Jan 2005 00:20:08 -0000 1.20
@@ -1,6 +1,6 @@
/*
Copyright: 2001-2003 The Perl Foundation. All Rights Reserved.
-$Id: pyclass.pmc,v 1.19 2004/12/23 23:49:32 rubys Exp $
+$Id: pyclass.pmc,v 1.20 2005/01/03 00:20:08 rubys Exp $
=head1 NAME
@@ -256,12 +256,22 @@
*/
PMC* get_iter() {
- PMC *iter = VTABLE_find_method(INTERP, SELF, PyString_iter);
+ PMC *gen, *iter, *next;
+ iter = VTABLE_find_method(INTERP, SELF, PyString_iter);
if (!iter || !VTABLE_defined(INTERP, iter))
real_exception(INTERP, NULL, E_TypeError,
"TypeError: iteration over non-sequence");
- return Parrot_PyClass_run_meth_fromc_P(INTERP, iter, SELF,
+ iter = Parrot_PyClass_run_meth_fromc_P(INTERP, iter, SELF,
PyString_iter);
+ next = VTABLE_find_method(INTERP, iter, PyString_next);
+
+ if (PObj_get_FLAGS(next) & SUB_FLAG_GENERATOR)
+ return iter;
+ else {
+ gen = pmc_new(INTERP, PyBuiltin_PyGen);
+ VTABLE_set_pmc(INTERP, gen, iter);
+ return gen;
+ }
}
/*
@@ -327,6 +337,29 @@
/*
+=item C<INTVAL hash()>
+
+Returns a unique hash for this value
+
+=cut
+
+*/
+
+ INTVAL hash () {
+ PMC *hash_method = VTABLE_find_method(INTERP, SELF, PyString_hash);
+ if (hash_method) {
+ PMC *ret;
+ ret = Parrot_PyClass_run_meth_fromc_P(INTERP, hash_method, SELF,
+ PyString_hash);
+ return VTABLE_get_integer(INTERP, ret);
+ }
+ else {
+ return (INTVAL)SELF;
+ }
+ }
+
+/*
+
=item C<STRING *name()>
Returns the name of this class.
1.6 +5 -2 parrot/dynclasses/pyconsts.h
Index: pyconsts.h
===================================================================
RCS file: /cvs/public/parrot/dynclasses/pyconsts.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- pyconsts.h 30 Dec 2004 11:46:38 -0000 1.5
+++ pyconsts.h 3 Jan 2005 00:20:08 -0000 1.6
@@ -6,19 +6,20 @@
/* Python class types */
-PYVAR_SCOPE INTVAL PyBuiltin_PyObject;
PYVAR_SCOPE INTVAL PyBuiltin_PyBoolean;
PYVAR_SCOPE INTVAL PyBuiltin_PyClass;
PYVAR_SCOPE INTVAL PyBuiltin_PyComplex;
PYVAR_SCOPE INTVAL PyBuiltin_PyDict;
PYVAR_SCOPE INTVAL PyBuiltin_PyException;
+PYVAR_SCOPE INTVAL PyBuiltin_PyFloat;
+PYVAR_SCOPE INTVAL PyBuiltin_PyGen;
PYVAR_SCOPE INTVAL PyBuiltin_PyInt;
PYVAR_SCOPE INTVAL PyBuiltin_PyIter;
-PYVAR_SCOPE INTVAL PyBuiltin_PyFloat;
PYVAR_SCOPE INTVAL PyBuiltin_PyList;
PYVAR_SCOPE INTVAL PyBuiltin_PyLong;
PYVAR_SCOPE INTVAL PyBuiltin_PyNCI;
PYVAR_SCOPE INTVAL PyBuiltin_PyNone;
+PYVAR_SCOPE INTVAL PyBuiltin_PyObject;
PYVAR_SCOPE INTVAL PyBuiltin_PyProxyClass;
PYVAR_SCOPE INTVAL PyBuiltin_PyProxyType;
PYVAR_SCOPE INTVAL PyBuiltin_PySlice;
@@ -50,11 +51,13 @@
PYVAR_SCOPE STRING *PyString_class;
PYVAR_SCOPE STRING *PyString_cmp;
PYVAR_SCOPE STRING *PyString_hex;
+PYVAR_SCOPE STRING *PyString_hash;
PYVAR_SCOPE STRING *PyString_init;
PYVAR_SCOPE STRING *PyString_int;
PYVAR_SCOPE STRING *PyString_iter;
PYVAR_SCOPE STRING *PyString_name;
PYVAR_SCOPE STRING *PyString_new;
+PYVAR_SCOPE STRING *PyString_next;
PYVAR_SCOPE STRING *PyString_oct;
PYVAR_SCOPE STRING *PyString_proxy;
PYVAR_SCOPE STRING *PyString_repr;
1.5 +42 -36 parrot/dynclasses/pygen.pmc
Index: pygen.pmc
===================================================================
RCS file: /cvs/public/parrot/dynclasses/pygen.pmc,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- pygen.pmc 18 Dec 2004 15:08:20 -0000 1.4
+++ pygen.pmc 3 Jan 2005 00:20:08 -0000 1.5
@@ -1,6 +1,6 @@
/*
Copyright: 2001-2003 The Perl Foundation. All Rights Reserved.
-$Id: pygen.pmc,v 1.4 2004/12/18 15:08:20 rubys Exp $
+$Id: pygen.pmc,v 1.5 2005/01/03 00:20:08 rubys Exp $
=head1 NAME
@@ -19,11 +19,28 @@
*/
#include "parrot/parrot.h"
+#include "pyconsts.h"
-static STRING *NEXT;
static STRING *STOPITERATION;
-pmclass PyGen dynpmc group python_group {
+static PMC* PyGen_next(Parrot_Interp interpreter, PMC *self) {
+ PMC *ret;
+ PMC *iter = PMC_pmc_val(self);
+ PMC *next = VTABLE_find_method(interpreter, iter, PyString_next);
+ if (PObj_get_FLAGS(iter) & KEY_end_slice_FLAG) {
+ real_exception(interpreter, NULL, E_StopIteration, "StopIteration");
+ }
+
+ PObj_get_FLAGS(iter) |= KEY_end_slice_FLAG;
+ ret = Parrot_runops_fromc_args(interpreter, next, "PP", iter);
+ if (iter == ret)
+ /* XXX: runops ate my exception: presume StopIteration */
+ real_exception(interpreter, NULL, E_StopIteration, "StopIteration");
+ PObj_get_FLAGS(iter) &= ~KEY_end_slice_FLAG;
+ return ret;
+}
+
+pmclass PyGen dynpmc extends PyClass group python_group {
/*
@@ -37,7 +54,6 @@
void class_init() {
if (pass) {
- NEXT = const_string(INTERP, "next");
STOPITERATION = const_string(INTERP, "StopIteration");
}
}
@@ -55,7 +71,7 @@
void init () {
PObj_custom_mark_SET(SELF);
PMC_struct_val(SELF) = NULL;
- PMC_pmc_val(SELF) = NULL;
+ PMC_pmc_val(SELF) = SELF;
}
/*
@@ -77,29 +93,6 @@
/*
-=item C<PMC *find_method(STRING *method_name)>
-
-Looks up the method for C<*method_name> and returns it. If no method is
-found then lookup an attribute by this name, and return it. If all else
-fails, return null.
-
-=cut
-
-*/
-
- PMC* find_method(STRING* method_name) {
- PMC *method;
-
- if (0 == string_compare(INTERP, method_name, NEXT))
- return PMC_pmc_val(SELF);
-
- method = SUPER(method_name);
- if (!method) method = VTABLE_getprop(INTERP, SELF, method_name);
- return method;
- }
-
-/*
-
=item C<INTVAL get_bool()>
Returns true if there are more elements to return
@@ -116,13 +109,11 @@
new_internal_exception(INTERP);
push_new_c_exception_handler(INTERP, INTERP->exceptions);
if (!setjmp(INTERP->exceptions->destination)) {
- PMC_struct_val(SELF) =
- Parrot_runops_fromc_args(INTERP, PMC_pmc_val(SELF), "P");
+ PMC_struct_val(SELF) = PyGen_next(INTERP, SELF);
} else {
- PMC *exception = REG_PMC(5);
- INTVAL extype = VTABLE_get_integer_keyed_int(INTERP, exception,
1);
- if (extype != E_StopIteration) {
- rethrow_exception(INTERP, REG_PMC(5));
+ Parrot_exception *exception = INTERP->exceptions;
+ if (exception->error != E_StopIteration) {
+ rethrow_c_exception(INTERP);
}
}
@@ -148,6 +139,22 @@
/*
+=item C<void setprop(STRING *key, PMC *value)>
+
+Sets the property for C<*key> to C<*value>.
+
+=cut
+
+*/
+
+ void setprop(STRING* key, PMC* value) {
+ if (0 == string_compare(INTERP, key, PyString_next))
+ PObj_get_FLAGS(value) |= SUB_FLAG_GENERATOR;
+ SUPER(key, value);
+ }
+
+/*
+
=item C<void set_pmc(PMC *value)>
Sets the value of the NEXT function.
@@ -158,7 +165,6 @@
void set_pmc (PMC* value) {
PMC_pmc_val(SELF) = value;
- PObj_get_FLAGS(value) |= SUB_FLAG_GENERATOR;
}
/*
@@ -179,7 +185,7 @@
PMC_struct_val(SELF) = NULL;
}
else {
- ret = Parrot_runops_fromc_args(INTERP, PMC_pmc_val(SELF), "P");
+ ret = PyGen_next(INTERP, SELF);
}
return ret;
1.19 +17 -1 parrot/dynclasses/pyint.pmc
Index: pyint.pmc
===================================================================
RCS file: /cvs/public/parrot/dynclasses/pyint.pmc,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -r1.18 -r1.19
--- pyint.pmc 23 Dec 2004 23:49:32 -0000 1.18
+++ pyint.pmc 3 Jan 2005 00:20:08 -0000 1.19
@@ -1,6 +1,6 @@
/*
Copyright: 2001-2003 The Perl Foundation. All Rights Reserved.
-$Id: pyint.pmc,v 1.18 2004/12/23 23:49:32 rubys Exp $
+$Id: pyint.pmc,v 1.19 2005/01/03 00:20:08 rubys Exp $
=head1 NAME
@@ -53,6 +53,22 @@
/*
+=item C<PMC* clone()>
+
+Return a clone of the integer.
+
+=cut
+
+*/
+
+ PMC* clone () {
+ PMC* ret = pmc_new(INTERP, SELF->vtable->base_type);
+ PMC_int_val(ret) = PMC_int_val(SELF);
+ return ret;
+ }
+
+/*
+
=item C<PMC* "__hex__"(PMC *self)>
Returns the hex representation of C<self>.
1.19 +5 -5 parrot/dynclasses/pylist.pmc
Index: pylist.pmc
===================================================================
RCS file: /cvs/public/parrot/dynclasses/pylist.pmc,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -r1.18 -r1.19
--- pylist.pmc 30 Dec 2004 11:46:38 -0000 1.18
+++ pylist.pmc 3 Jan 2005 00:20:08 -0000 1.19
@@ -1,6 +1,6 @@
/*
Copyright: 2001-2003 The Perl Foundation. All Rights Reserved.
-$Id: pylist.pmc,v 1.18 2004/12/30 11:46:38 rubys Exp $
+$Id: pylist.pmc,v 1.19 2005/01/03 00:20:08 rubys Exp $
=head1 NAME
@@ -120,15 +120,15 @@
PMC * ret = pmc_new(INTERP, PyBuiltin_PyList);
if (argc > 1) {
- INTVAL n = VTABLE_elements(INTERP, value);
+ PMC *iter = VTABLE_get_iter(INTERP, value);
INTVAL j;
- for (j=0; j<n; j++) {
- PMC *item = VTABLE_get_pmc_keyed_int(INTERP, value, j);
+ for (j=0; VTABLE_get_bool(INTERP, iter); j++) {
+ PMC *item = VTABLE_shift_pmc(INTERP, iter);
VTABLE_set_pmc_keyed_int(INTERP, ret, j, item);
}
- VTABLE_set_integer_native(INTERP, ret, n);
+ VTABLE_set_integer_native(INTERP, ret, j);
}
return ret;
1.16 +18 -1 parrot/dynclasses/pyobject.pmc
Index: pyobject.pmc
===================================================================
RCS file: /cvs/public/parrot/dynclasses/pyobject.pmc,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- pyobject.pmc 24 Dec 2004 04:41:56 -0000 1.15
+++ pyobject.pmc 3 Jan 2005 00:20:08 -0000 1.16
@@ -1,6 +1,6 @@
/*
Copyright: 2001-2003 The Perl Foundation. All Rights Reserved.
-$Id: pyobject.pmc,v 1.15 2004/12/24 04:41:56 rubys Exp $
+$Id: pyobject.pmc,v 1.16 2005/01/03 00:20:08 rubys Exp $
=head1 NAME
@@ -205,6 +205,23 @@
/*
+=item C<PMC* "__hash__"(PMC *self)>
+
+Returns the hash value of C<self>.
+
+=cut
+
+*/
+
+ METHOD PMC* __hash__(PMC *self) {
+ PMC * ret = pmc_new(INTERP, PyBuiltin_PyInt);
+ INTVAL value = VTABLE_hash(INTERP, self);
+ VTABLE_set_integer_native(INTERP, ret, value);
+ return ret;
+ }
+
+/*
+
=item C<PMC* "__int__"(PMC *self)>
Returns the integer value of C<self>.
1.9 +6 -5 parrot/dynclasses/pyproxytype.pmc
Index: pyproxytype.pmc
===================================================================
RCS file: /cvs/public/parrot/dynclasses/pyproxytype.pmc,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- pyproxytype.pmc 23 Dec 2004 23:49:33 -0000 1.8
+++ pyproxytype.pmc 3 Jan 2005 00:20:08 -0000 1.9
@@ -1,6 +1,6 @@
/*
Copyright: 2001-2003 The Perl Foundation. All Rights Reserved.
-$Id: pyproxytype.pmc,v 1.8 2004/12/23 23:49:33 rubys Exp $
+$Id: pyproxytype.pmc,v 1.9 2005/01/03 00:20:08 rubys Exp $
=head1 NAME
@@ -108,10 +108,11 @@
object = pmc_new(INTERP, object_class->vtable->base_type);
}
- if (object_class == SELF)
- proxy = object;
- else {
- proxy = pmc_new(interpreter, PyBuiltin_PyClass);
+ proxy = object;
+
+ if (object_class != SELF) {
+ object = VTABLE_clone(interpreter, proxy);
+ pmc_reuse(INTERP, proxy, PyBuiltin_PyClass, 0);
VTABLE_setprop(INTERP, proxy, PyString_class, REG_PMC(0));
VTABLE_setprop(INTERP, proxy, PyString_proxy, object);
}
1.1 parrot/languages/python/t/pie/b0.t
Index: b0.t
===================================================================
# $Id: b0.t,v 1.1 2005/01/03 00:20:10 rubys Exp $
use strict;
use lib '../../lib';
use Parrot::Test tests => 3;
sub test {
language_output_is('python', $_[0], '', $_[1]);
}
test(<<'CODE', 'MetaToken');
def proto___new__(cls, name):
if name in cls.name2num:
return cls.name2num[name]
cls.name2num[name] = obj = int.__new__(cls, cls.next)
cls.num2name[obj] = name
cls.next += 1
return obj
def proto___repr__(self):
return self.__class__.num2name[self]
class MetaToken(type):
def __new__(metacls, name, bases, vars):
cls = type.__new__(metacls, name, bases, vars)
cls.__new__ = staticmethod(proto___new__)
cls.__repr__ = cls.__str__ = proto___repr__
cls.next = 0
cls.name2num = {}
cls.num2name = {}
return cls
Token = MetaToken('Token', (int,), {})
EOF = Token('EOF')
INDENT = Token('INDENT')
DEDENT = Token('DEDENT')
NEWLINE = Token('NEWLINE')
NAME = Token('NAME')
NUMBER = Token('NUMBER')
STRING = Token('STRING')
OPERATOR = Token('OPERATOR')
for t in [EOF, INDENT, DEDENT, NEWLINE, NAME, NUMBER, STRING, OPERATOR]:
print t, int(t)
CODE
test(<<'CODE', 'Clone iterator');
class Scanner(object):
def tokenize(self):
yield 5, '5'
yield 0, ''
class Link(object):
__slots__ = ['value', 'next']
def __init__(self):
self.value = None
self.next = None
class Clone(object):
__slots__ = ['link', 'itnext']
def __init__(self, it, link=None):
if isinstance(it, Clone):
self.itnext = it.itnext
self.link = it.link
else:
self.itnext = it.next
self.link = Link()
def __iter__(self):
return self
def next(self):
next = self.link.next
if next is None:
self.link.value = value = self.itnext()
self.link.next = next = Link()
else:
value = self.link.value
self.link = next
return value
def main():
s = Scanner()
it = Clone(s.tokenize())
it2 = Clone(it)
L = []
for pair in it:
L.append(pair)
print L
L2 = list(it2)
print L2
if __name__ == '__main__':
main()
CODE
SKIP: {
skip("todo", 1);
test(<<'CODE', 'b0.py');
def check(a, b):
if not a == b:
raise AssertionError("%.30r != %.30r" % (a, b))
def proto___new__(cls, name):
if name in cls.name2num:
return cls.name2num[name]
cls.name2num[name] = obj = int.__new__(cls, cls.next)
cls.num2name[obj] = name
cls.next += 1
return obj
def proto___repr__(self):
return self.__class__.num2name[self]
class MetaToken(type):
def __new__(metacls, name, bases, vars):
cls = type.__new__(metacls, name, bases, vars)
cls.__new__ = staticmethod(proto___new__)
cls.__repr__ = cls.__str__ = proto___repr__
cls.next = 0
cls.name2num = {}
cls.num2name = {}
return cls
Token = MetaToken('Token', (int,), {})
EOF = Token('EOF')
INDENT = Token('INDENT')
DEDENT = Token('DEDENT')
NEWLINE = Token('NEWLINE')
NAME = Token('NAME')
NUMBER = Token('NUMBER')
STRING = Token('STRING')
OPERATOR = Token('OPERATOR')
class Scanner(object):
# A vaguely Pythonic tokenizer
def __init__(self, getc):
self.getc = getc
self.indents = [0]
def tokenize(self,
eolchars = ('', '\r', '\n'),
quotes = ('"', '\''),
hspaces = (' ', '\t'),
longops = dict.fromkeys(
('<<', '>>', '<=', '>=', '==', '!=', '//', '**'))):
# A generator yielding successive tokens to the parser
# Each token is a (tokentype, value) pair
getc = self.getc
nextc = getc()
while True:
col = 0
while nextc.isspace():
if nextc == '\t':
col = ((col//8) + 1)*8
elif nextc == ' ':
col += 1
else:
col = 0 # \f, \v, \r, \n all reset the column
nextc = getc()
if nextc == '#':
while nextc not in eolchars:
nextc = getc()
continue
if col != self.indents[-1]:
while col < self.indents[-1]:
yield DEDENT, ''
del self.indents[-1]
# This will yield DEDENT + INDENT for inconsistent dedent
if col > self.indents[-1]:
yield INDENT, ''
self.indents.append(col)
while nextc not in eolchars:
if nextc.isalpha() or nextc == '_':
name = ''
while nextc.isalnum() or nextc == '_':
name += nextc
nextc = getc()
yield NAME, name
elif nextc.isdigit():
number = ''
while nextc.isdigit():
number += nextc
nextc = getc()
yield NUMBER, number
elif nextc in quotes:
quote = nextc
s = nextc
nextc = getc()
while nextc != quote:
if nextc in eolchars:
if not nextc:
raise SyntaxError("EOF in string")
raise SyntaxError("unescaped %r in string" %
nextc)
s += nextc
if nextc == '\\':
nextc = getc()
s += nextc
nextc = getc()
s += nextc
nextc = getc()
yield STRING, s
elif nextc == '#':
while nextc not in eolchars:
nextc = getc()
elif nextc in hspaces:
nextc = getc()
else:
c1 = nextc
nextc = getc()
if c1+nextc in longops:
c2 = nextc
nextc = getc()
yield OPERATOR, c1+c2
else:
yield OPERATOR, c1
yield NEWLINE, ''
nextc = getc()
if not nextc:
while self.indents[-1]:
yield DEDENT, ''
del self.indents[-1]
break
yield EOF, ''
class Link(object):
__slots__ = ['value', 'next']
def __init__(self):
self.value = None
self.next = None
class Clone(object):
__slots__ = ['link', 'itnext']
def __init__(self, it, link=None):
if isinstance(it, Clone):
self.itnext = it.itnext
self.link = it.link
else:
self.itnext = it.next
self.link = Link()
def __iter__(self):
return self
def next(self):
next = self.link.next
if next is None:
self.link.value = value = self.itnext()
self.link.next = next = Link()
else:
value = self.link.value
self.link = next
return value
def getcFromString(s):
for c in s:
yield c
while True:
yield ''
sample = '''
def pi():
# Compute digits of Pi. Algorithm due to LGLT Meertens.
k, a, b, a1, b1 = 2, 4, 1, 12, 4
while 1:
p, q, k = k*k, 2*k+1, k+1
a, b, a1, b1 = a1, b1, p*a+q*a1, p*b+q*b1
d, d1 = a//b, a1//b1
while d == d1:
yield d
a, a1 = 10*(a%b), 10*(a1%b1)
d, d1 = a//b, a1//b1
def strhash(s):
# Python 2.x string hash algorithm
if s == '':
return 0
x = ord(s[0])<<7
for c in s:
x = ((1000003*x) ^ ord(c)) & 4294967295 # 0xffffffff
return x^len(s)
'''
operators = {
'<': 1,
'<=': 1,
'==': 1,
'!=': 1,
'>': 1,
'>=': 1,
'|': 2,
'^': 3,
'&': 4,
'<<': 5,
'>>': 5,
'+': 6,
'-': 6,
'*': 7,
'/': 7,
'//': 7,
'%': 7,
'**': 8,
}
dispatch = {
'<': lambda x, y: x < y,
'<=': lambda x, y: x <= y,
'==': lambda x, y: x == y,
'!=': lambda x, y: x != y,
'>': lambda x, y: x > y,
'>=': lambda x, y: x >= y,
'|': lambda x, y: x | y,
'^': lambda x, y: x ^ y,
'&': lambda x, y: x & y,
'<<': lambda x, y: x << y,
'>>': lambda x, y: x >> y,
'+': lambda x, y: x + y,
'-': lambda x, y: x - y,
'*': lambda x, y: x * y,
'/': lambda x, y: x / y,
'%': lambda x, y: x % y,
'//': lambda x, y: x // y,
'**': lambda x, y: x ** y,
}
class DoReturn(Exception):
def __init__(self, value=None):
self.value = value
class DoBreak(Exception):
pass
def eval(body, globals, locals):
for stmt in body:
stmt.eval(globals, locals)
def geneval(body, globals, locals):
for stmt in body:
for value in stmt.geneval(globals, locals):
yield value
def isgenerator(body):
for stmt in body:
if stmt.isgenerator():
return True
return False
class Dict(dict):
def __repr__(self):
keys = self.keys()
keys.sort()
L = []
for key in keys:
L.append(repr(key) + ": " + repr(self[key]))
return "{" + ", ".join(L) + "}"
class Function(object):
makeLocals = Dict
def __init__(self, name, args, body, globals):
self.name = name
self.args = args
self.body = body
self.globals = globals
def __call__(self, *args):
check(len(args), len(self.args))
locals = self.makeLocals(zip(self.args, args))
try:
eval(self.body, self.globals, locals)
except DoReturn, exc:
return exc.value
class Generator(Function):
def __call__(self, *args):
check(len(args), len(self.args))
locals = self.makeLocals(zip(self.args, args))
try:
for value in geneval(self.body, self.globals, locals):
yield value
except DoReturn, exc:
if exc.value is not None:
raise RuntimeError("'return' with argument in generator")
return
class Node(object):
def isgenerator(self):
return False
def geneval(self, globals, locals):
self.eval(globals, locals)
if False:
yield None
class Define(Node):
def __init__(self, name, args, body):
self.name = name
self.args = args
self.body = body
def __repr__(self):
return "%s(%r, %r, ...)" % (self.__class__.__name__,
self.name, self.args)
def eval(self, globals, locals):
if isgenerator(self.body):
obj = Generator(self.name, self.args, self.body, globals)
else:
obj = Function(self.name, self.args, self.body, globals)
globals[self.name] = obj
class For(Node):
def __init__(self, var, seq, body):
self.var = var
self.seq = seq
self.body = body
def __repr__(self):
return "%s(%r, %r, ...)" % (self.__class__.__name__,
self.var, self.seq)
def eval(self, globals, locals):
for value in self.seq.eval(globals, locals):
self.var.assign(value, globals, locals)
try:
eval(self.body, globals, locals)
except DoBreak:
break
def geneval(self, globals, locals):
for value in self.seq.eval(globals, locals):
self.var.assign(value, globals, locals)
try:
for v in geneval(self.body, globals, locals):
yield v
except DoBreak:
break
class While(Node):
def __init__(self, test, body):
self.test = test
self.body = body
def __repr__(self):
return "%s(%r, ...)" % (self.__class__.__name__, self.test)
def isgenerator(self):
return isgenerator(self.body)
def eval(self, globals, locals):
while self.test.eval(globals, locals):
try:
eval(self.body, globals, locals)
except DoBreak:
break
def geneval(self, globals, locals):
while self.test.eval(globals, locals):
try:
for value in geneval(self.body, globals, locals):
yield value
except DoBreak:
break
class If(Node):
def __init__(self, test, body, elsebody=None):
self.test = test
self.body = body
self.elsebody = elsebody
def __repr__(self):
return "%s(%r, ...)" % (self.__class__.__name__, self.test)
def isgenerator(self):
return isgenerator(self.body) or (self.elsebody is not None and
isgenerator(self.elsebody))
def eval(self, globals, locals):
if self.test.eval(globals, locals):
eval(self.body, globals, locals)
elif self.elsebody is not None:
eval(self.elsebody, globals, locals)
def geneval(self, globals, locals):
if self.test.eval(globals, locals):
for value in geneval(self.body, globals, locals):
yield value
elif self.elsebody is not None:
for value in geneval(self.elsebody, globals, locals):
yield value
class Return(Node):
def __init__(self, expr=None):
self.expr = expr
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.expr)
def eval(self, globals, locals):
if self.expr is None:
value = None
else:
value = self.expr.eval(globals, locals)
raise DoReturn(value)
class Yield(Node):
def __init__(self, expr):
self.expr = expr
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.expr)
def isgenerator(self):
return True
def geneval(self, globals, locals):
if self.expr is None:
value = None
else:
value = self.expr.eval(globals, locals)
yield value
class Break(Node):
def __repr__(self):
return "%s()" % (self.__class__.__name__,)
def eval(self, globals, locals):
raise DoBreak()
class Print(Node):
def __init__(self, exprs):
if not isinstance(exprs, list):
exprs = [exprs]
self.exprs = exprs
def eval(self, globals, locals):
for e in self.exprs:
print e.eval(globals, locals),
print
class Assign(Node):
def __init__(self, lhs, expr):
self.lhs = lhs
self.expr = expr
def __repr__(self):
return "%s(%r, %r)" % (self.__class__.__name__, self.lhs, self.expr)
def eval(self, globals, locals):
value = self.expr.eval(globals, locals)
for v in self.lhs:
v.assign(value, globals, locals)
class Exprs(Node):
def __init__(self, exprs):
self.exprs = exprs
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.exprs)
def eval(self, globals, locals):
return tuple([e.eval(globals, locals) for e in self.exprs])
def assign(self, value, globals, locals):
if len(self.exprs) != len(value):
raise TypeError("multi-assign length mismatch")
for e, v in zip(self.exprs, value):
e.assign(v, globals, locals)
class Binop(Node):
def __init__(self, left, op, right):
self.left = left
self.op = op
self.right = right
self.opeval = dispatch[self.op]
def __repr__(self):
return "%s(%r, %r, %r)" % (self.__class__.__name__,
self.left, self.op, self.right)
def eval(self, globals, locals):
return self.opeval(self.left.eval(globals, locals),
self.right.eval(globals, locals))
class Attribute(Node):
def __init__(self, expr, name):
self.expr = expr
self.name = name
def __repr__(self):
return "%s(%r, %r)" % (self.__class__.__name__, self.expr, self.name)
def eval(self, globals, locals):
v = self.expr.eval(globals, locals)
return getattr(v, self.name)
class Index(Node):
def __init__(self, expr, index):
self.expr = expr
self.index = index
def __repr__(self):
return "%s(%r, %r)" % (self.__class__.__name__, self.expr, self.index)
def eval(self, globals, locals):
v = self.expr.eval(globals, locals)
return v[self.index.eval(globals, locals)]
def assign(self, value, globals, locals):
v = self.expr.eval(globals, locals)
v[self.index.eval(globals, locals)] = value
class Call(Node):
def __init__(self, expr, args):
if not isinstance(args, list):
args = [args]
self.expr = expr
self.args = args
def __repr__(self):
return "%s(%r, %r)" % (self.__class__.__name__, self.expr, self.args)
def eval(self, globals, locals):
f = self.expr.eval(globals, locals)
args = [a.eval(globals, locals) for a in self.args]
return f(*args)
class Literal(Node):
def __init__(self, literal):
self.literal = literal
self.value = self.evalit()
def eval(self, globals, locals):
return self.value
def __repr__(self):
return "%s(%r)" % (self.__class__.__name__, self.literal)
simple_escapes = {"a": "\a",
"b": "\b",
"f": "\f",
"n": "\n",
"r": "\r",
"t": "\t",
"v": "\v",
"'": "'",
'"': '"',
"\\": "\\"}
class String(Literal):
def evalit(self, octals='01234567'):
s = self.literal[1:-1]
if '\\' not in s:
return s
L = []
it = iter(range(len(s)))
for i in it:
c = s[i]
if c != '\\':
L.append(c)
else:
i = it.next()
c = s[i]
if c == 'x':
if i+2 >= len(s):
raise ValueError("incomplete \\x escape in string")
d1 = s[it.next()]
d2 = s[it.next()]
L.append(chr(int(d1+d2, 16)))
elif c in octals:
if i+1 < len(s) and s[i+1] in octals:
c += s[it.next()]
if i+2 < len(s) and s[i+2] in octals:
c += s[it.next()]
L.append(chr(int(c, 8)))
else:
L.append(simple_escapes.get(c, '\\' + c))
return "".join(L)
class Number(Literal):
def evalit(self):
return int(self.literal)
class Name(Node):
def __init__(self, name):
self.name = name
def __repr__(self):
return "Name(%r)" % self.name
def eval(self, globals, locals):
if self.name in locals:
return locals[self.name]
if self.name in globals:
return globals[self.name]
if self.name == 'ord':
return ord
if self.name == 'len':
return len
raise NameError(self.name)
def assign(self, value, globals, locals):
locals[self.name] = value
class Parser(object):
def __init__(self, scanner):
self.scanner = scanner
self.nexttoken()
def nexttoken(self):
self.token, self.value = rv = self.scanner.next()
##print rv
return rv
def expect(self, token, value=None):
if self.token != token:
raise SyntaxError
if value is not None and self.value != value:
raise SyntaxError
value = self.value
self.nexttoken()
return value
def parse(self):
stmts = []
while self.token != EOF:
stmts.append(self.parse_stmt())
return stmts
def parse_stmt(self,
keywords=('def', 'for', 'while', 'if',
'print', 'return', 'yield', 'break')):
if self.value in keywords:
return getattr(self, 'parse_' + self.value)()
else:
return self.parse_simple()
def parse_def(self):
self.expect(NAME, 'def')
name = self.expect(NAME)
self.expect(OPERATOR, '(')
args = []
if self.value != ')':
args.append(self.expect(NAME))
while self.value == ',':
self.nexttoken()
args.append(self.expect(NAME))
self.expect(OPERATOR, ')')
self.expect(OPERATOR, ':')
self.expect(NEWLINE)
body = self.parse_body()
return Define(name, args, body)
def parse_for(self):
self.expect(NAME, 'for')
var = self.parse_expr()
self.expect(NAME, 'in')
seq = self.parse_expr()
self.expect(OPERATOR, ':')
self.expect(NEWLINE)
body = self.parse_body()
return For(var, seq, body)
def parse_while(self):
self.expect(NAME, 'while')
test = self.parse_expr()
self.expect(OPERATOR, ':')
self.expect(NEWLINE)
body = self.parse_body()
return While(test, body)
def parse_if(self):
self.expect(NAME, 'if')
test = self.parse_expr()
self.expect(OPERATOR, ':')
self.expect(NEWLINE)
body = self.parse_body()
if self.value != 'else':
elsebody = None
else:
self.expect(NAME, 'else')
self.expect(OPERATOR, ':')
self.expect(NEWLINE)
elsebody = self.parse_body()
return If(test, body, elsebody)
def parse_body(self):
self.expect(INDENT)
body = [self.parse_stmt()]
while self.token != DEDENT:
body.append(self.parse_stmt())
self.expect(DEDENT)
return body
def parse_print(self):
self.expect(NAME, 'print')
exprs = self.parse_exprs()
self.expect(NEWLINE)
return Print(exprs)
def parse_return(self):
self.expect(NAME, 'return')
if self.token == NEWLINE:
e = None
else:
e = self.parse_exprs()
self.expect(NEWLINE)
return Return(e)
def parse_yield(self):
self.expect(NAME, 'yield')
e = self.parse_exprs()
self.expect(NEWLINE)
return Yield(e)
def parse_break(self):
self.expect(NAME, 'break')
self.expect(NEWLINE)
return Break()
def parse_simple(self):
e = self.parse_exprs()
if self.value == '=':
lhs = []
while self.value == '=':
self.nexttoken()
lhs.append(e)
e = self.parse_exprs()
self.expect(NEWLINE)
return Assign(lhs, e)
else:
self.expect(NEWLINE)
return e
def parse_exprs(self):
e = self.parse_expr()
if self.value != ',':
return e
exprs = [e]
while self.value == ',':
self.nexttoken()
exprs.append(self.parse_expr())
return Exprs(exprs)
def parse_expr(self, prio=0):
left = self.parse_term()
while self.value in operators and operators[self.value] > prio:
op = self.expect(OPERATOR)
right = self.parse_expr(operators[op])
left = Binop(left, op, right)
return left
def parse_term(self):
t = self.parse_atom()
while self.value in ('.', '[', '('):
if self.value == '.':
self.nexttoken()
name = self.expect(NAME)
t = Attribute(t, name)
elif self.value == '[':
self.nexttoken()
index = self.parse_exprs()
self.expect(OPERATOR, ']')
t = Index(t, index)
elif self.value == '(':
self.nexttoken()
if self.value == ')':
args = []
else:
args = self.parse_exprs()
if isinstance(args, Exprs):
args = args.exprs
self.expect(OPERATOR, ')')
t = Call(t, args)
else:
raise AssertionError("shouldn't get here")
return t
def parse_atom(self):
if self.value == '(':
self.nexttoken()
exprs = self.parse_exprs()
self.expect(OPERATOR, ')')
return exprs
if self.token is STRING:
return String(self.expect(STRING))
if self.token is NAME:
return Name(self.expect(NAME))
if self.token is NUMBER:
return Number(self.expect(NUMBER))
raise SyntaxError
class List(list):
__setitem__ = list.__setitem__
__getitem__ = list.__getitem__
class Str(str):
__getitem__ = str.__getitem__
class OutputFile(object):
def __init__(self):
self.data = []
self.softspace = True
reset = __init__
def write(self, s):
self.data.append(s)
def getvalue(self):
r = "".join(self.data)
self.data = List()
return r
output = OutputFile()
def cleanup(s):
s = str(s).replace('<__main__.', '<').replace('<b0.', '<')
i = s.find(' at 0x')
while i > 0:
j = s.find('>', i+6)
if j < 0:
break
digits = s[i+4:j]
try:
number = int(digits, 0)
except ValueError:
break
else:
s = s[:i+5] + s[j:]
i = s.find(' at 0x')
return s
def write(s):
s = cleanup(s)
if __debug__:
print s,
print >>output, s,
def writeln(s=''):
write(str(s) + '\n')
def myhash(s, ord=ord):
if not s:
return 0
x = ord(s[0])<<7
for c in s:
x = ((1000003*x) ^ ord(c)) & 0xffffffffL
return x^len(s)
def checkoutput(n=0):
outputtext = output.getvalue()
check(strhash(outputtext), n)
#!!! Added two helper functions
def check2(a, b1, b2):
if not a == b1 and not a == b2:
raise AssertionError("%.30r != %.30r or %.30r" % (a, b1, b2))
def checkoutput2(n=0, alt=0):
outputtext = output.getvalue()
check2(strhash(outputtext), n, alt)
strhash = myhash
indent = ""
def instrumentClass(cls):
cname = cls.__name__ + '.'
for name in cls.__dict__:
if name == '__dict__':
continue
descr = cls.__dict__[name]
if hasattr(descr, '__get__'):
setattr(cls, name, instrumentDescriptor(cname+name, descr))
def unInstrumentClass(cls):
for name in cls.__dict__:
descr = cls.__dict__[name]
if isinstance(descr, instrumentDescriptor):
setattr(cls, name, descr.obj)
class instrumentDescriptor(object):
def __init__(self, name, obj):
self.name = name
self.obj = obj
def __get__(self, *args):
result = self.obj.__get__(*args)
if not hasattr(result, '__call__'):
return result
return instrumentCall(self.name, result)
class instrumentCall(object):
def __init__(self, name, obj):
self.name = name
self.obj = obj
def __call__(self, *args):
global indent
oldindent = indent
try:
indent = indent + " "
argreprs = map(repr, args)
for i, s in enumerate(argreprs):
s = cleanup(s)
if len(s) >= 45:
s = s[:20] + "..." + s[-20:]
argreprs[i] = s
writeln(indent + "%s(%r)" % (self.name, ", ".join(argreprs)))
try:
result = self.obj(*args)
except Exception, exc:
writeln(indent + "raise %r" % (exc,))
raise
else:
if result is None:
writeln(indent + "return")
else:
writeln(indent + "return %r" % (result,))
return result
finally:
indent = oldindent
def instrumentTree(base):
instrumentClass(base)
for cls in base.__subclasses__():
instrumentTree(cls)
def unInstrumentTree(base):
unInstrumentClass(base)
for cls in base.__subclasses__():
unInstrumentTree(cls)
def main():
output.reset()
s = Scanner(getcFromString(sample).next)
it = Clone(s.tokenize())
it2 = Clone(it)
L = []
for pair in it:
L.append(pair)
L2 = list(it2)
check(L, L2)
scanner = Scanner(getcFromString(sample).next).tokenize()
parser = Parser(scanner)
instrumentClass(Parser)
root = parser.parse()
checkoutput(1413352820)
env = Dict()
eval(root, env, env)
g = env['pi']()
for i in range(1000):
write(g.next())
writeln('')
strhash = env['strhash']
for x in '', 'x', 'abc', 'abc'*100:
check(strhash(x), myhash(x))
strhash = myhash
checkoutput(3960406533)
it = Clone(getcFromString(unicode(sample, "utf8")))
it2 = Clone(it)
scanner = Scanner(it.next).tokenize()
parser = Parser(scanner)
root = parser.parse()
checkoutput2(1308798191, 2837404767) #!!! unicode repr
env = Dict()
eval(root, env, env)
g = env['pi']()
for i in range(1000):
write(g.next())
writeln()
checkoutput(3960406533)
instrumentTree(Node)
scanner = Clone(Scanner(it2.next).tokenize())
scanner2 = Clone(scanner)
parser = Parser(scanner)
root = parser.parse()
checkoutput2(3257889492, 1379369348) #!!! unicode repr
env = Dict()
eval(root, env, env)
g = env['pi']()
digits = []
for i in range(10):
digits.append(g.next())
checkoutput2(3259608361, 1529706333) #!!! unicode repr
print "".join(map(str, digits))
unInstrumentTree(Node)
unInstrumentClass(Parser)
parser = Parser(scanner2)
root = parser.parse()
checkoutput(0)
env = Dict()
eval(root, env, env)
g = env['pi']()
digits = []
for i in range(10):
digits.append(g.next())
print "".join(map(str, digits))
out2 = output.getvalue()
checkoutput(0)
class TrackingDict(Dict):
def __setitem__(self, *args):
writeln("%s = %.50r" % args)
super(TrackingDict, self).__setitem__(*args)
Function.makeLocals = TrackingDict
g = env['pi']()
digits = []
for i in range(100):
digits.append(g.next())
checkoutput(902386495)
Function.makeLocals = Dict
if __name__ == '__main__':
main()
CODE
}