From arigo at codespeak.net Thu Aug 12 17:46:30 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Thu, 12 Aug 2004 17:46:30 +0200 (MEST) Subject: [pypy-svn] r5937 - pypy/trunk/src/pypy/objspace/std/test Message-ID: <20040812154630.0305B5A4C8@thoth.codespeak.net> Author: arigo Date: Thu Aug 12 17:46:30 2004 New Revision: 5937 Modified: pypy/trunk/src/pypy/objspace/std/test/test_unicodestring.py Log: Skip a hopelessly failing test in the trivial object space. (Patch from Roeland Rengelink.) Modified: pypy/trunk/src/pypy/objspace/std/test/test_unicodestring.py ============================================================================== --- pypy/trunk/src/pypy/objspace/std/test/test_unicodestring.py (original) +++ pypy/trunk/src/pypy/objspace/std/test/test_unicodestring.py Thu Aug 12 17:46:30 2004 @@ -5,13 +5,17 @@ from pypy.tool import testit -class TestUnicodeString(testit.AppTestCase): +class TestUnicodeStringStdOnly(testit.AppTestCase): + def setUp(self): + self.space = testit.objspace('std') + def test_compares(self): self.assertEqual(u'a', 'a') self.assertEqual('a', u'a') self.assertNotEqual(u'a', 'b') self.assertNotEqual('a', u'b') +class TestUnicodeString(testit.AppTestCase): def test_addition(self): def check(a, b): self.assertEqual(a, b) From mwh at codespeak.net Fri Aug 13 12:30:15 2004 From: mwh at codespeak.net (mwh at codespeak.net) Date: Fri, 13 Aug 2004 12:30:15 +0200 (MEST) Subject: [pypy-svn] r5942 - pypy/trunk/src/pypy/tool Message-ID: <20040813103015.608B95A958@thoth.codespeak.net> Author: mwh Date: Fri Aug 13 12:30:14 2004 New Revision: 5942 Modified: pypy/trunk/src/pypy/tool/option.py Log: Make the StdObjSpace the default. Modified: pypy/trunk/src/pypy/tool/option.py ============================================================================== --- pypy/trunk/src/pypy/tool/option.py (original) +++ pypy/trunk/src/pypy/tool/option.py Fri Aug 13 12:30:14 2004 @@ -68,7 +68,7 @@ if Options.spaces: name = Options.spaces[-1] else: - name = os.environ.get('OBJSPACE', 'trivial') + name = os.environ.get('OBJSPACE', 'std') try: return _spacecache[name] From arigo at codespeak.net Fri Aug 13 14:46:46 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 13 Aug 2004 14:46:46 +0200 (MEST) Subject: [pypy-svn] r5943 - in pypy/trunk/src: goal/translate_pypy pypy/annotation pypy/objspace/flow pypy/translator Message-ID: <20040813124646.AD1695A958@thoth.codespeak.net> Author: arigo Date: Fri Aug 13 14:46:45 2004 New Revision: 5943 Modified: pypy/trunk/src/goal/translate_pypy/graphserver.py pypy/trunk/src/goal/translate_pypy/httpserver.py pypy/trunk/src/goal/translate_pypy/translate_pypy.py pypy/trunk/src/pypy/annotation/model.py pypy/trunk/src/pypy/objspace/flow/model.py pypy/trunk/src/pypy/translator/annrpython.py Log: Small progress for translate_pypy.py. Modified: pypy/trunk/src/goal/translate_pypy/graphserver.py ============================================================================== --- pypy/trunk/src/goal/translate_pypy/graphserver.py (original) +++ pypy/trunk/src/goal/translate_pypy/graphserver.py Fri Aug 13 14:46:45 2004 @@ -59,12 +59,12 @@ data = '=== %s ===\n\n%s' % (n, data) return StringIO(data), 'text/plain' - def serve(self): + def serve(self, port=8000): httpserver.register('', self.indexloader) httpserver.register('func', self.funcloader) httpserver.register('img', self.imgloader) httpserver.register('var', self.varloader) - BaseHTTPServer.test(HandlerClass=httpserver.MiniHandler) + httpserver.serve(port) # ____________________________________________________________ Modified: pypy/trunk/src/goal/translate_pypy/httpserver.py ============================================================================== --- pypy/trunk/src/goal/translate_pypy/httpserver.py (original) +++ pypy/trunk/src/goal/translate_pypy/httpserver.py Fri Aug 13 14:46:45 2004 @@ -169,7 +169,7 @@ f.close() -def my_host(): - import gamesrv - port = gamesrv.socketports[gamesrv.openhttpsocket()] - return '127.0.0.1:%d' % port +def serve(port=8000): + server_address = ('', port) + httpd = BaseHTTPServer.HTTPServer(server_address, MiniHandler) + httpd.serve_forever() Modified: pypy/trunk/src/goal/translate_pypy/translate_pypy.py ============================================================================== --- pypy/trunk/src/goal/translate_pypy/translate_pypy.py (original) +++ pypy/trunk/src/goal/translate_pypy/translate_pypy.py Fri Aug 13 14:46:45 2004 @@ -6,6 +6,7 @@ from pypy.objspace.std.objspace import StdObjSpace, W_Object from pypy.objspace.std.intobject import W_IntObject from pypy.translator.translator import Translator +from pypy.translator.annrpython import AnnotatorError # __________ Entry point __________ @@ -44,9 +45,15 @@ return print "don't know about", x - import graphserver - def run_server(): - graphserver.Server(t).serve() + def run_server(background=False, port=8000): + import graphserver + server = graphserver.Server(t) + print >> sys.stderr, '* View the blocks at http://127.0.0.1:%d/' % port + if background: + import thread + thread.start_new_thread(server.serve, ()) + else: + server.serve() t = Translator(entry_point, verbose=True, simplifying=True) try: @@ -58,7 +65,14 @@ print >> sys.stderr traceback.print_exception(exc, val, tb) print >> sys.stderr - thread.start_new_thread(run_server, ()) + + if isinstance(val, AnnotatorError) and hasattr(val, 'block'): + print '-'*60 + about(val.block) + print '-'*60 + + run_server(background=True) + print >> sys.stderr import pdb pdb.post_mortem(tb) else: Modified: pypy/trunk/src/pypy/annotation/model.py ============================================================================== --- pypy/trunk/src/pypy/annotation/model.py (original) +++ pypy/trunk/src/pypy/annotation/model.py Fri Aug 13 14:46:45 2004 @@ -142,6 +142,14 @@ s1 = pair(s1, s2).union() return s1 +def ishashable(x): + try: + hash(x) + except TypeError: + return False + else: + return True + def immutablevalue(x): "The most precise SomeValue instance that contains the immutable value x." if isinstance(bool, type) and isinstance(x, bool): @@ -152,7 +160,7 @@ result = SomeString() elif isinstance(x, tuple): result = SomeTuple(items = [immutablevalue(e) for e in x]) - elif x in BUILTIN_FUNCTIONS: + elif ishashable(x) and x in BUILTIN_FUNCTIONS: result = SomeBuiltin(BUILTIN_FUNCTIONS[x]) elif isinstance(x, (type, ClassType)) and x.__module__ != '__builtin__': result = SomeClass(x) Modified: pypy/trunk/src/pypy/objspace/flow/model.py ============================================================================== --- pypy/trunk/src/pypy/objspace/flow/model.py (original) +++ pypy/trunk/src/pypy/objspace/flow/model.py Fri Aug 13 14:46:45 2004 @@ -99,7 +99,14 @@ return hash(self.value) def __repr__(self): - return '(%r)' % (self.value,) + # try to limit the size of the repr to make it more readable + r = repr(self.value) + if (r.startswith('<') and r.endswith('>') and + hasattr(self.value, '__name__')): + r = '%s %s' % (type(self.value).__name__, self.value.__name__) + elif len(r) > 30: + r = r[:20] + '...' + r[-8:] + return '(%s)' % r # hack! it is useful to have UNDEFINED be an instance of Constant too. # PyFrame then automatically uses this Constant as a marker for Modified: pypy/trunk/src/pypy/translator/annrpython.py ============================================================================== --- pypy/trunk/src/pypy/translator/annrpython.py (original) +++ pypy/trunk/src/pypy/translator/annrpython.py Fri Aug 13 14:46:45 2004 @@ -132,8 +132,16 @@ # add default arguments if necessary missingargs = len(block.inputargs) - len(inputcells) if missingargs: - assert missingargs >= 0 - assert missingargs <= len(func.func_defaults or ()) + nbdefaults = len(func.func_defaults or ()) + if not (0 <= missingargs <= nbdefaults): + if nbdefaults: + msg = "%d to %d" % (len(block.inputargs)-nbdefaults, + len(block.inputargs)) + else: + msg = "%d" % len(block.inputargs) + raise AnnotatorError, ( + "got %d inputcells in call to %r; expected %s" % ( + len(inputcells), func, msg)) for extra in func.func_defaults[-missingargs:]: inputcells.append(annmodel.immutablevalue(extra)) self.addpendingblock(block, inputcells) @@ -209,6 +217,10 @@ #import traceback, sys #traceback.print_tb(sys.exc_info()[2]) self.annotated[block] = False # failed, hopefully temporarily + except AnnotatorError, e: + if not hasattr(e, 'block'): + e.block = block + raise def reflowpendingblock(self, block): self.pendingblocks.append((block, None)) From hpk at codespeak.net Fri Aug 13 15:50:17 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 13 Aug 2004 15:50:17 +0200 (MEST) Subject: [pypy-svn] r5946 - pypy/trunk/doc Message-ID: <20040813135017.2F0BA5A958@thoth.codespeak.net> Author: hpk Date: Fri Aug 13 15:50:16 2004 New Revision: 5946 Modified: pypy/trunk/doc/index.txt Log: fix link according to lauras suggestion. Modified: pypy/trunk/doc/index.txt ============================================================================== --- pypy/trunk/doc/index.txt (original) +++ pypy/trunk/doc/index.txt Fri Aug 13 15:50:16 2004 @@ -107,5 +107,5 @@ .. _xhelix: http://codespeak.net/pipermail/pypy-dev/2003q4/002437.html .. _PyPy-IronPython: http://codespeak.net/pipermail/pypy-dev/2003q4/002474.html .. _IronPython: http://www.python.org/pycon/dc2004/papers/9/ -.. _pliant: http://codespeak.net/pipermail/pypy-dev/2003q4/002395.html +.. _pliant: http://pliant.cx .. _recently-modified: http://codespeak.net/pypy/index.cgi?doc/recent From arigo at codespeak.net Fri Aug 13 16:02:01 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 13 Aug 2004 16:02:01 +0200 (MEST) Subject: [pypy-svn] r5947 - in pypy/trunk/src: goal/translate_pypy pypy/objspace/std pypy/translator Message-ID: <20040813140201.0B8F75A958@thoth.codespeak.net> Author: arigo Date: Fri Aug 13 16:02:00 2004 New Revision: 5947 Modified: pypy/trunk/src/goal/translate_pypy/translate_pypy.py pypy/trunk/src/pypy/objspace/std/fake.py pypy/trunk/src/pypy/objspace/std/objspace.py pypy/trunk/src/pypy/translator/annrpython.py Log: * shuffled imports and usages of fake.py to be RPython-compliant. * allow *args in the signature of RPython functions (currently all functions must be *called* with a known number of arguments, so this shouldn't be difficult to translate). * translate_pypy apparently completes, but probably still manages to miss large parts of the source base. Modified: pypy/trunk/src/goal/translate_pypy/translate_pypy.py ============================================================================== --- pypy/trunk/src/goal/translate_pypy/translate_pypy.py (original) +++ pypy/trunk/src/goal/translate_pypy/translate_pypy.py Fri Aug 13 16:02:00 2004 @@ -1,7 +1,7 @@ # # # -import autopath +import autopath, sys from pypy.objspace.std.objspace import StdObjSpace, W_Object from pypy.objspace.std.intobject import W_IntObject @@ -60,15 +60,16 @@ a = t.annotate([]) a.simplify() except: - import sys, traceback, thread + import traceback exc, val, tb = sys.exc_info() print >> sys.stderr traceback.print_exception(exc, val, tb) print >> sys.stderr - if isinstance(val, AnnotatorError) and hasattr(val, 'block'): + block = getattr(val, '__annotator_block', None) + if block: print '-'*60 - about(val.block) + about(block) print '-'*60 run_server(background=True) Modified: pypy/trunk/src/pypy/objspace/std/fake.py ============================================================================== --- pypy/trunk/src/pypy/objspace/std/fake.py (original) +++ pypy/trunk/src/pypy/objspace/std/fake.py Fri Aug 13 16:02:00 2004 @@ -29,7 +29,7 @@ w_value = space.wrap(value) raise OperationError, OperationError(w_exc, w_value), tb -def fake_type(cpy_type): +def fake_type(cpy_type, ignored=None): assert type(cpy_type) is type if cpy_type in _fake_type_cache: return _fake_type_cache[cpy_type] Modified: pypy/trunk/src/pypy/objspace/std/objspace.py ============================================================================== --- pypy/trunk/src/pypy/objspace/std/objspace.py (original) +++ pypy/trunk/src/pypy/objspace/std/objspace.py Fri Aug 13 16:02:00 2004 @@ -153,11 +153,12 @@ from pypy.objspace.std import noneobject from pypy.objspace.std import iterobject from pypy.objspace.std import unicodeobject + from pypy.objspace.std import fake # hack to avoid imports in the time-critical functions below global W_ObjectObject, W_BoolObject, W_IntObject, W_FloatObject global W_TupleObject, W_ListObject, W_DictObject, W_StringObject global W_TypeObject, W_SliceObject, W_LongObject, W_NoneObject - global W_SeqIterObject, W_UnicodeObject + global W_SeqIterObject, W_UnicodeObject, fake_type W_ObjectObject = objectobject.W_ObjectObject W_BoolObject = boolobject.W_BoolObject W_IntObject = intobject.W_IntObject @@ -172,6 +173,7 @@ W_NoneObject = noneobject.W_NoneObject W_SeqIterObject = iterobject.W_SeqIterObject W_UnicodeObject = unicodeobject.W_UnicodeObject + fake_type = fake.fake_type # end of hacks # singletons self.w_None = W_NoneObject(self) @@ -248,10 +250,11 @@ if isinstance(x, type(Exception)) and issubclass(x, Exception): if hasattr(self, 'w_' + x.__name__): return getattr(self, 'w_' + x.__name__) - from pypy.objspace.std import fake if isinstance(x, type): - return self.gettypeobject(fake.fake_type(x).typedef) - return fake.fake_type(type(x))(self, x) + ft = self.loadfromcache(x, fake_type) + return self.gettypeobject(ft.typedef) + ft = self.loadfromcache(type(x), fake_type) + return ft(self, x) def newint(self, intval): return W_IntObject(self, intval) Modified: pypy/trunk/src/pypy/translator/annrpython.py ============================================================================== --- pypy/trunk/src/pypy/translator/annrpython.py (original) +++ pypy/trunk/src/pypy/translator/annrpython.py Fri Aug 13 16:02:00 2004 @@ -7,6 +7,7 @@ from pypy.annotation.factory import BlockedInference, Bookkeeper from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant from pypy.objspace.flow.model import SpaceOperation, FunctionGraph +from pypy.interpreter.pycode import CO_VARARGS, CO_VARKEYWORDS class AnnotatorError(Exception): @@ -129,21 +130,35 @@ # generalize the function's input arguments block = graph.startblock inputcells = list(args) + # process *varargs in the called function + expectedargs = len(block.inputargs) + if func.func_code.co_flags & CO_VARARGS: + expectedargs -= 1 + if func.func_code.co_flags & CO_VARKEYWORDS: + expectedargs -= 1 + extracells = [] + if func.func_code.co_flags & CO_VARARGS: + s_varargs = annmodel.SomeTuple(inputcells[expectedargs:]) + extracells = [s_varargs] + del inputcells[expectedargs:] + if func.func_code.co_flags & CO_VARKEYWORDS: + raise AnnotatorError, "** argument of %r unsupported" % (func,) # add default arguments if necessary - missingargs = len(block.inputargs) - len(inputcells) - if missingargs: + if len(inputcells) != expectedargs: + missingargs = expectedargs - len(inputcells) nbdefaults = len(func.func_defaults or ()) if not (0 <= missingargs <= nbdefaults): if nbdefaults: - msg = "%d to %d" % (len(block.inputargs)-nbdefaults, - len(block.inputargs)) + msg = "%d to %d" % (expectedargs-nbdefaults, + expectedargs) else: - msg = "%d" % len(block.inputargs) + msg = "%d" % expectedargs raise AnnotatorError, ( "got %d inputcells in call to %r; expected %s" % ( len(inputcells), func, msg)) for extra in func.func_defaults[-missingargs:]: inputcells.append(annmodel.immutablevalue(extra)) + inputcells.extend(extracells) self.addpendingblock(block, inputcells) # get the (current) return value v = graph.getreturnvar() @@ -217,9 +232,10 @@ #import traceback, sys #traceback.print_tb(sys.exc_info()[2]) self.annotated[block] = False # failed, hopefully temporarily - except AnnotatorError, e: - if not hasattr(e, 'block'): - e.block = block + except Exception, e: + # hack for debug tools only + if not hasattr(e, '__annotator_block'): + setattr(e, '__annotator_block', block) raise def reflowpendingblock(self, block): From arigo at codespeak.net Fri Aug 13 16:27:57 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 13 Aug 2004 16:27:57 +0200 (MEST) Subject: [pypy-svn] r5948 - in pypy/trunk/src: goal/translate_pypy pypy/translator Message-ID: <20040813142757.CCE315A958@thoth.codespeak.net> Author: arigo Date: Fri Aug 13 16:27:57 2004 New Revision: 5948 Modified: pypy/trunk/src/goal/translate_pypy/graphserver.py pypy/trunk/src/pypy/translator/annrpython.py Log: Record and display the whole history of the bindings of each variable. Modified: pypy/trunk/src/goal/translate_pypy/graphserver.py ============================================================================== --- pypy/trunk/src/goal/translate_pypy/graphserver.py (original) +++ pypy/trunk/src/goal/translate_pypy/graphserver.py Fri Aug 13 16:27:57 2004 @@ -1,6 +1,7 @@ import BaseHTTPServer from cStringIO import StringIO import httpserver +from httpserver import htmlquote from pypy.translator.tool.pygame.graphviewer import GraphViewer from pypy.translator.tool.make_dot import make_dot_graphs @@ -11,8 +12,11 @@ self.translator = translator self.viewercache = {} self.binding_by_name = {} + self.binding_history = {} for var, s_value in self.translator.annotator.bindings.items(): self.binding_by_name[var.name] = '%s : %r' % (var.name, s_value) + for var, history in self.translator.annotator.bindingshistory.items(): + self.binding_history[var.name] = history def getviewer(self, i): t = self.translator @@ -54,10 +58,22 @@ def varloader(self, n, **options): n = n[0] - import textwrap - data = textwrap.fill(self.binding_by_name[n]) - data = '=== %s ===\n\n%s' % (n, data) - return StringIO(data), 'text/plain' + s = self.binding_by_name[n] + assert s.startswith('%s :' % n) + s = s[len('%s :' % n):] + data = '%s' % n + data += '

%s

' % n + data += '

%s

' % htmlquote(s) + if n in self.binding_history: + lines = ['
  • %s' % htmlquote(repr(s)) + for s in self.binding_history[n]] + lines.reverse() + data += '

    Previous values

    ' + data += '' + data += '' + return StringIO(data), 'text/html' def serve(self, port=8000): httpserver.register('', self.indexloader) Modified: pypy/trunk/src/pypy/translator/annrpython.py ============================================================================== --- pypy/trunk/src/pypy/translator/annrpython.py (original) +++ pypy/trunk/src/pypy/translator/annrpython.py Fri Aug 13 16:27:57 2004 @@ -24,6 +24,7 @@ self.bindings = {} # map Variables to SomeValues self.annotated = {} # set of blocks already seen self.notify = {} # {block: {factory-to-invalidate-when-done}} + self.bindingshistory = {}# map Variables to lists of SomeValues self.bookkeeper = Bookkeeper(self) #___ convenience high-level interface __________________ @@ -117,6 +118,14 @@ else: raise TypeError, 'Variable or Constant expected, got %r' % (arg,) + def setbinding(self, arg, s_value): + if arg in self.bindings: + # for debugging purposes, record the history of bindings that + # have been given to this variable + history = self.bindingshistory.setdefault(arg, []) + history.append(self.bindings[arg]) + self.bindings[arg] = s_value + #___ interface for annotator.factory _______ @@ -246,7 +255,7 @@ def bindinputargs(self, block, inputcells): # Create the initial bindings for the input args of a block. for a, cell in zip(block.inputargs, inputcells): - self.bindings[a] = cell + self.setbinding(a, cell) self.annotated[block] = False # must flowin. def mergeinputargs(self, block, inputcells): @@ -288,7 +297,7 @@ raise BlockedInference # the operation cannot succeed assert isinstance(resultcell, annmodel.SomeObject) assert isinstance(op.result, Variable) - self.bindings[op.result] = resultcell # bind resultcell to op.result + self.setbinding(op.result, resultcell) # bind resultcell to op.result def default_consider_op(self, *args): return annmodel.SomeObject() From lac at codespeak.net Fri Aug 13 17:40:45 2004 From: lac at codespeak.net (lac at codespeak.net) Date: Fri, 13 Aug 2004 17:40:45 +0200 (MEST) Subject: [pypy-svn] r5949 - in pypy/trunk/doc: . devel sprintinfo Message-ID: <20040813154045.AA61D5A958@thoth.codespeak.net> Author: lac Date: Fri Aug 13 17:40:44 2004 New Revision: 5949 Modified: pypy/trunk/doc/devel/testdesign.txt pypy/trunk/doc/index.txt pypy/trunk/doc/sprintinfo/LouvainLaNeuvePlan.txt pypy/trunk/doc/sprintinfo/LouvainLaNeuveReport.txt Log: Changed references to pypy-dev archives so that they work. Stated that Standard, and not Trivial is the default Object Space in testdesign.txt. Fixed one reference in sprintinfo/LouvainLaNeuvePlan.txt which was simply wrong. Added missing link in sprintinfo/LouvainLaNeuveReport.txt (these are the same links in both files, and don't appear to be used in SprintPlanning now, perhaps I should have deleted them instead? Capitalised Micheal's name. Modified: pypy/trunk/doc/devel/testdesign.txt ============================================================================== --- pypy/trunk/doc/devel/testdesign.txt (original) +++ pypy/trunk/doc/devel/testdesign.txt Fri Aug 13 17:40:44 2004 @@ -33,7 +33,7 @@ some preliminary information about a new test module that was recently developed at the Amsterdam Sprint (Dec. 2003) and still is under development. -Note that we are using a unittest2 framework +Note that we are using a unittest2 framework (but not for long) Command line tool test_all -------------------------- @@ -42,13 +42,17 @@ python test_all.py -which will run all tests against the Trivial Object Space. If you want -to test against the StandardObjectSpace then issue:: +which will run all tests against the Standard Object Space. If you want +to test against the TrivialObjectSpace then issue:: - python test_all.py -S + python test_all.py -T For more switches invoke "python test_all.py -h". +This used to be reversed -- the TrivialObjectSpace was the default, and +you had to specify the StandardObjectSpace. You may find some documentation +that has not been changed to reflect the new reality. + ------------------------------------------------------------------------------- .. _mail: http://codespeak.net/pipermail/pypy-dev/2003q2/000789.html Modified: pypy/trunk/doc/index.txt ============================================================================== --- pypy/trunk/doc/index.txt (original) +++ pypy/trunk/doc/index.txt Fri Aug 13 17:40:44 2004 @@ -80,7 +80,7 @@ * A thread discussing the xhelix_ python C extension implementing Helix encryption and authentication, which may be interesting to use as a pypy performance test at some point. -* A thread discussing comparison of PyPy-IronPython_ , and a paper for PyCon 2004: "IronPython_ is a new implementation of the Python language targeting the Common Language Runtime (CLR). It compiles python programs into bytecode (IL) that will run on either Microsoft's .NET or the Open Source Mono platform. IronPython includes an interactive interpreter and transparent on-the-fly compilation of source files just like standard Python. In addition, IronPython supports static compilation of Python code to produce static executables (.exe's) that can be run directly or static libraries (.dll's) that can be called from other CLR languages." +* A paper for PyCon 2004: "IronPython_ is a new implementation of the Python language targeting the Common Language Runtime (CLR). It compiles python programs into bytecode (IL) that will run on either Microsoft's .NET or the Open Source Mono platform. IronPython includes an interactive interpreter and transparent on-the-fly compilation of source files just like standard Python. In addition, IronPython supports static compilation of Python code to produce static executables (.exe's) that can be run directly or static libraries (.dll's) that can be called from other CLR languages." * A comparison of Python and Pliant_ , an OS written in a python-like language. @@ -94,18 +94,17 @@ .. _howtosvn: http://codespeak.net/pypy/index.cgi?doc/devel/howtosvn.html .. _optionaltool: http://codespeak.net/pypy/index.cgi?doc/devel/optionaltool.html .. _testdesign: http://codespeak.net/pypy/index.cgi?doc/devel/testdesign.html -.. _feasible: http://codespeak.net/pipermail/pypy-dev/2004q2/002619.html -.. _rock: http://codespeak.net/pipermail/pypy-dev/2004q1/002585.html -.. _Scheme: http://codespeak.net/pipermail/pypy-dev/2004q1/002586.html +.. _feasible: http://codespeak.net/pipermail/pypy-dev/2004q2/001289.html +.. _rock: http://codespeak.net/pipermail/pypy-dev/2004q1/001255.html +.. _Scheme: http://codespeak.net/pipermail/pypy-dev/2004q1/001256.html .. _FlashMob: http://www.flashmobcomputing.org/ -.. _lisp: http://codespeak.net/pipermail/pypy-dev/2003q4/002318.html +.. _lisp: http://codespeak.net/pipermail/pypy-dev/2003q4/001048.html .. _repository: http://www2.parc.com/csl/groups/sda/publications.shtml -.. _lightning: http://codespeak.net/pipermail/pypy-dev/2003q4/002321.html +.. _lightning: http://codespeak.net/pipermail/pypy-dev/2003q4/001051.html .. _LLVM: http://llvm.cs.uiuc.edu/ -.. _PyPy-LLVM: http://codespeak.net/pipermail/pypy-dev/2003q4/002398.html -.. _conversation: http://codespeak.net/pipermail/pypy-dev/2003q4/002411.html -.. _xhelix: http://codespeak.net/pipermail/pypy-dev/2003q4/002437.html -.. _PyPy-IronPython: http://codespeak.net/pipermail/pypy-dev/2003q4/002474.html +.. _PyPy-LLVM: http://codespeak.net/pipermail/pypy-dev/2003q4/001115.html +.. _conversation: http://codespeak.net/pipermail/pypy-dev/2003q4/001119.html +.. _xhelix: http://codespeak.net/pipermail/pypy-dev/2003q4/001129.html .. _IronPython: http://www.python.org/pycon/dc2004/papers/9/ .. _pliant: http://pliant.cx .. _recently-modified: http://codespeak.net/pypy/index.cgi?doc/recent Modified: pypy/trunk/doc/sprintinfo/LouvainLaNeuvePlan.txt ============================================================================== --- pypy/trunk/doc/sprintinfo/LouvainLaNeuvePlan.txt (original) +++ pypy/trunk/doc/sprintinfo/LouvainLaNeuvePlan.txt Fri Aug 13 17:40:44 2004 @@ -39,5 +39,4 @@ .. _dictobject: http://codespeak.net/pipermail/pypy-svn/2003-June/000515.html .. _intobject: http://codespeak.net/pipermail/pypy-svn/2003-June/000443.html .. _instmethod: http://codespeak.net/pipermail/pypy-svn/2003-June/000389.html -.. _iterobject: http://codespeak.net/pipermail/pypy-svn/2003-June/000999.html - +.. _iterobject: http://codespeak.net/pipermail/pypy-svn/2003-June/000529.html Modified: pypy/trunk/doc/sprintinfo/LouvainLaNeuveReport.txt ============================================================================== --- pypy/trunk/doc/sprintinfo/LouvainLaNeuveReport.txt (original) +++ pypy/trunk/doc/sprintinfo/LouvainLaNeuveReport.txt Fri Aug 13 17:40:44 2004 @@ -42,7 +42,7 @@ - refactoring of argument-parsing for tool/test.py and introduction of the "py.py" tool that unifies executing commands, files and going interactive. - (michael) + (Michael) - refactoring, improvements of multimethods (Armin, Samuele) @@ -51,7 +51,7 @@ Also improvements and crossreferences between the documents. (Anna) a trigger was implemented that generates the new html-pages after - each checkin in the pypy/trunk/doc directory. (holger) + each checkin in the pypy/trunk/doc directory. (Holger) - OSCON-2003 paper was beeing worked on and enhanced! (Laura, Jacob, Guido, Armin, ...) @@ -74,5 +74,6 @@ .. _userobject: http://codespeak.net/pipermail/pypy-svn/2003-June/000449.html .. _dictobject: http://codespeak.net/pipermail/pypy-svn/2003-June/000515.html .. _intobject: http://codespeak.net/pipermail/pypy-svn/2003-June/000443.html +.. _instmethod: http://codespeak.net/pipermail/pypy-svn/2003-June/000389.html .. _iterobject: http://codespeak.net/pipermail/pypy-svn/2003-June/000529.html From lac at codespeak.net Fri Aug 13 17:56:35 2004 From: lac at codespeak.net (lac at codespeak.net) Date: Fri, 13 Aug 2004 17:56:35 +0200 (MEST) Subject: [pypy-svn] r5950 - pypy/trunk/doc Message-ID: <20040813155635.088485A958@thoth.codespeak.net> Author: lac Date: Fri Aug 13 17:56:35 2004 New Revision: 5950 Added: pypy/trunk/doc/cmodules.txt Modified: pypy/trunk/doc/goals.txt Log: Add Anthony's helpful list of the Cmodules as its own doc, and refer to it in goals.txt Added: pypy/trunk/doc/cmodules.txt ============================================================================== --- (empty file) +++ pypy/trunk/doc/cmodules.txt Fri Aug 13 17:56:35 2004 @@ -0,0 +1,116 @@ +Anthony Baxter made us this wonderful list of C modules that we will want +to make Python versions of. Thank you Anthony. + +Probably doable +--------------- + +These ones are probably achievable, although you might think +otherwise. + +_bisectmodule - already exists +_codecsmodule +_csv - already exists? +_heapqmodule - already exists +_hotshot +_localemodule +_randommodule +_sre - already exists? +_weakref +arraymodule +audioop +binascii +cPickle - already exists +cStringIO - already exists +cgensupport +cmathmodule +collectionsmodule +cryptmodule +datetimemodule - already exists +gcmodule +imageop +imgfile +itertoolsmodule +mathmodule +md5module +operator +parsermodule +rgbimgmodule +sgimodule +shamodule +stropmodule +structmodule +symtablemodule +syslogmodule +timingmodule +unicodedata +yuvconvert +zipimport + +Deprecated +---------- + +These modules are deprecated + +regexmodule +regexpr +rotormodule +mpzmodule + +Links in an external library +---------------------------- + +These are all wrappers around external C libraries. Rewriting them +from scratch is really not practical. + +_bsddb +_curses_panel +_cursesmodule +_ssl +_tkinter +almodule +bsddbmodule +bz2module +cdmodule +clmodule +dbmmodule +flmodule +fmmodule +gdbmmodule +glmodule +nismodule +puremodule +pyexpat +readline (replace with pyrepl?) +svmodule +termios +tkappinit +zlibmodule + +System specific +--------------- + +These are modules that are wrappers around system calls +and the like. Rewriting them in pure Python will still +need a way to invoke the underlying calls - and will probably +be different for each platform (pypy, pycore, ironpython, &c). +A lot of these are also a mass of #ifdefs for various broken +operating systems. + +dlmodule +errnomodule +fcntlmodule +fpectlmodule +fpetestmodule +grpmodule +linuxaudiodev +mmapmodule +ossaudiodev +posixmodule +pwdmodule +resource +selectmodule +signalmodule +socketmodule +sunaudiodev +threadmodule +timemodule Modified: pypy/trunk/doc/goals.txt ============================================================================== --- pypy/trunk/doc/goals.txt (original) +++ pypy/trunk/doc/goals.txt Fri Aug 13 17:56:35 2004 @@ -246,7 +246,8 @@ - Port the built-in Python library to PyPy (functions, types and modules currently implemented in C) -- types needs refactoring, - the rest is mostly done. + the rest is mostly done. Anthony Baxter has given us a very useful + list of the CModules_ we need to consider porting. - Decide on a case-by-case basis which features are to be implemented using RPython or just general Python. -- moving target. @@ -324,7 +325,7 @@ - Design and implement a back-end component for dynamically emitting machine code for multiple processor architectures. Enable dynamic foreign function calls. - +n - Research optimisation heuristics for the Just-In-Time compiler. @@ -373,3 +374,4 @@ versions suitable for specialized environments such as small or very large computing devices. + _CModules: /pypy/doc/cmodules.txt \ No newline at end of file From lac at codespeak.net Fri Aug 13 18:05:32 2004 From: lac at codespeak.net (lac at codespeak.net) Date: Fri, 13 Aug 2004 18:05:32 +0200 (MEST) Subject: [pypy-svn] r5952 - pypy/trunk/doc Message-ID: <20040813160532.C82AA5A958@thoth.codespeak.net> Author: lac Date: Fri Aug 13 18:05:31 2004 New Revision: 5952 Modified: pypy/trunk/doc/goals.txt Log: The CamelCase for WikiWords mixes badly with how I spell Cmodules. See if this way goes better. Modified: pypy/trunk/doc/goals.txt ============================================================================== --- pypy/trunk/doc/goals.txt (original) +++ pypy/trunk/doc/goals.txt Fri Aug 13 18:05:31 2004 @@ -247,7 +247,7 @@ - Port the built-in Python library to PyPy (functions, types and modules currently implemented in C) -- types needs refactoring, the rest is mostly done. Anthony Baxter has given us a very useful - list of the CModules_ we need to consider porting. + list of the cmodules_ we need to consider porting. - Decide on a case-by-case basis which features are to be implemented using RPython or just general Python. -- moving target. @@ -325,9 +325,8 @@ - Design and implement a back-end component for dynamically emitting machine code for multiple processor architectures. Enable dynamic foreign function calls. -n -- Research optimisation heuristics for the Just-In-Time compiler. +- Research optimisation heuristics for the Just-In-Time compiler. - A Just-In-Time compiler for PyPy !!! Outperform the state-of-the art (Psyco, Stackless). @@ -374,4 +373,4 @@ versions suitable for specialized environments such as small or very large computing devices. - _CModules: /pypy/doc/cmodules.txt \ No newline at end of file + _cmodules: /pypy/doc/cmodules.txt \ No newline at end of file From lac at codespeak.net Fri Aug 13 18:07:11 2004 From: lac at codespeak.net (lac at codespeak.net) Date: Fri, 13 Aug 2004 18:07:11 +0200 (MEST) Subject: [pypy-svn] r5953 - pypy/trunk/doc Message-ID: <20040813160711.7D78D5A958@thoth.codespeak.net> Author: lac Date: Fri Aug 13 18:07:10 2004 New Revision: 5953 Modified: pypy/trunk/doc/goals.txt Log: Fix stupid error. Modified: pypy/trunk/doc/goals.txt ============================================================================== --- pypy/trunk/doc/goals.txt (original) +++ pypy/trunk/doc/goals.txt Fri Aug 13 18:07:10 2004 @@ -373,4 +373,4 @@ versions suitable for specialized environments such as small or very large computing devices. - _cmodules: /pypy/doc/cmodules.txt \ No newline at end of file +.. _cmodules: /pypy/doc/cmodules.txt \ No newline at end of file From lac at codespeak.net Fri Aug 13 18:10:29 2004 From: lac at codespeak.net (lac at codespeak.net) Date: Fri, 13 Aug 2004 18:10:29 +0200 (MEST) Subject: [pypy-svn] r5954 - pypy/trunk/doc Message-ID: <20040813161029.66BE55A958@thoth.codespeak.net> Author: lac Date: Fri Aug 13 18:10:28 2004 New Revision: 5954 Modified: pypy/trunk/doc/goals.txt Log: Ok, perhaps this is the way you are supposed to refer to another file. Modified: pypy/trunk/doc/goals.txt ============================================================================== --- pypy/trunk/doc/goals.txt (original) +++ pypy/trunk/doc/goals.txt Fri Aug 13 18:10:28 2004 @@ -373,4 +373,4 @@ versions suitable for specialized environments such as small or very large computing devices. -.. _cmodules: /pypy/doc/cmodules.txt \ No newline at end of file +.. _cmodules: http://codespeak.net/pypy/index.cgi?doc/cmodules.html \ No newline at end of file From lac at codespeak.net Fri Aug 13 18:19:59 2004 From: lac at codespeak.net (lac at codespeak.net) Date: Fri, 13 Aug 2004 18:19:59 +0200 (MEST) Subject: [pypy-svn] r5955 - pypy/trunk/doc Message-ID: <20040813161959.32C035A958@thoth.codespeak.net> Author: lac Date: Fri Aug 13 18:19:59 2004 New Revision: 5955 Modified: pypy/trunk/doc/cmodules.txt Log: see if we get this to format nicely. will probably take another round ... Modified: pypy/trunk/doc/cmodules.txt ============================================================================== --- pypy/trunk/doc/cmodules.txt (original) +++ pypy/trunk/doc/cmodules.txt Fri Aug 13 18:19:59 2004 @@ -8,42 +8,79 @@ otherwise. _bisectmodule - already exists + _codecsmodule + _csv - already exists? + _heapqmodule - already exists + _hotshot + _localemodule + _randommodule + _sre - already exists? + _weakref + arraymodule + audioop + binascii + cPickle - already exists + cStringIO - already exists + cgensupport + cmathmodule + collectionsmodule + cryptmodule + datetimemodule - already exists + gcmodule + imageop + imgfile + itertoolsmodule + mathmodule + md5module + operator + parsermodule + rgbimgmodule + sgimodule + shamodule + stropmodule + structmodule + symtablemodule + syslogmodule + timingmodule + unicodedata + yuvconvert + zipimport Deprecated @@ -52,8 +89,11 @@ These modules are deprecated regexmodule + regexpr + rotormodule + mpzmodule Links in an external library @@ -63,27 +103,49 @@ from scratch is really not practical. _bsddb + _curses_panel + _cursesmodule + _ssl + _tkinter + almodule + bsddbmodule + bz2module + cdmodule + clmodule + dbmmodule + flmodule + fmmodule + gdbmmodule + glmodule + nismodule + puremodule + pyexpat + readline (replace with pyrepl?) + svmodule + termios + tkappinit + zlibmodule System specific @@ -97,20 +159,37 @@ operating systems. dlmodule + errnomodule + fcntlmodule + fpectlmodule + fpetestmodule + grpmodule + linuxaudiodev + mmapmodule + ossaudiodev + posixmodule + pwdmodule + resource + selectmodule + signalmodule + socketmodule + sunaudiodev + threadmodule + timemodule From arigo at codespeak.net Fri Aug 13 18:30:53 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 13 Aug 2004 18:30:53 +0200 (MEST) Subject: [pypy-svn] r5956 - pypy/trunk/src/goal/translate_pypy Message-ID: <20040813163053.61E4B5A958@thoth.codespeak.net> Author: arigo Date: Fri Aug 13 18:30:52 2004 New Revision: 5956 Added: pypy/trunk/src/goal/translate_pypy/cdef.html Modified: pypy/trunk/src/goal/translate_pypy/graphserver.py pypy/trunk/src/goal/translate_pypy/index.html Log: Showing the Classdefs after annotation in the HTTP server. Added: pypy/trunk/src/goal/translate_pypy/cdef.html ============================================================================== --- (empty file) +++ pypy/trunk/src/goal/translate_pypy/cdef.html Fri Aug 13 18:30:52 2004 @@ -0,0 +1,32 @@ + + + + + +

    %(classdef.cls.__module__)s.%(classdef.cls.__name__)s

    + +%( +if classdef.basedef in t.annotator.getuserclassdefinitions(): + print '

    Parent class %s.

    ' % ( + t.annotator.getuserclassdefinitions().index(classdef.basedef), + classdef.basedef.cls.__name__) +)s + +

    Revision %(classdef.revision)d.

    + +

    Attributes and methods

    + +%( +attrs = classdef.attrs.items() +attrs.sort() +for name, s_value in attrs: + print "" +)s +
     " + print "%s" % htmlquote(name) + print "  " + print "%s" % htmlquote(repr(s_value)) + print " 
    + + + Modified: pypy/trunk/src/goal/translate_pypy/graphserver.py ============================================================================== --- pypy/trunk/src/goal/translate_pypy/graphserver.py (original) +++ pypy/trunk/src/goal/translate_pypy/graphserver.py Fri Aug 13 18:30:52 2004 @@ -75,11 +75,21 @@ data += '' return StringIO(data), 'text/html' + def cdefloader(self, i, **options): + i = int(i[0]) + t = self.translator + classdef = t.annotator.getuserclassdefinitions()[i] + return httpserver.load('cdef.html', 'text/html', + {'t': t, + 'classdef': classdef, + }) + def serve(self, port=8000): httpserver.register('', self.indexloader) httpserver.register('func', self.funcloader) httpserver.register('img', self.imgloader) httpserver.register('var', self.varloader) + httpserver.register('cdef', self.cdefloader) httpserver.serve(port) # ____________________________________________________________ Modified: pypy/trunk/src/goal/translate_pypy/index.html ============================================================================== --- pypy/trunk/src/goal/translate_pypy/index.html (original) +++ pypy/trunk/src/goal/translate_pypy/index.html Fri Aug 13 18:30:52 2004 @@ -13,5 +13,18 @@ print " " )s +

    Classdefs

    + +%( +lst = t.annotator.getuserclassdefinitions() +for i, classdef in zip(range(len(lst)), lst): + print "" +)s +
     " + print "%s" % htmlquote(classdef.cls.__module__) + print "  " + print "%s" % (i, + htmlquote(classdef.cls.__name__)) + print " 
    From arigo at codespeak.net Sat Aug 14 14:41:58 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 14 Aug 2004 14:41:58 +0200 (MEST) Subject: [pypy-svn] r5958 - pypy/trunk/src/pypy/translator/tool/pygame Message-ID: <20040814124158.7845D5A11C@thoth.codespeak.net> Author: arigo Date: Sat Aug 14 14:41:57 2004 New Revision: 5958 Added: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Log: Finished a rewrite started some time ago: the dot graphs are now rendered entierely by a Python script from a description produced by 'dot' in its so-called 'plain' format, which includes text, coordinates, and bezier control points for the edges. This allows much more flexibility. Try graphviewer.py. Variable names are highlighted; the mouse position can be matched to the text more precisely than was possible with the previous hack; the rendering's scale can be changed easily (try dragging the right mouse button). The goal is to display very large graphs with a reasonable amount of RAM (before, large graphs were large bitmap images). Also, the zoom feature will be convenient for that. Added: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py ============================================================================== --- (empty file) +++ pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py Sat Aug 14 14:41:57 2004 @@ -0,0 +1,392 @@ +""" +A custom graphic renderer for the '.plain' files produced by dot. + +""" + +from __future__ import generators +import autopath +import re, os, math +import pygame +from pygame.locals import * + + +FONT = os.path.join(autopath.this_dir, 'cyrvetic.ttf') +COLOR = { + 'black': (0,0,0), + 'white': (255,255,255), + 'red': (255,0,0), + 'green': (0,255,0), + } +re_nonword=re.compile(r'(\W+)') + + +class GraphLayout: + + def __init__(self, filename): + # parse the layout file (.plain format) + lines = open(filename, 'r').readlines() + for i in range(len(lines)-2, -1, -1): + if lines[i].endswith('\\\n'): # line ending in '\' + lines[i] = lines[i][:-2] + lines[i+1] + del lines[i+1] + header = splitline(lines.pop(0)) + assert header[0] == 'graph' + self.scale = float(header[1]) + self.boundingbox = float(header[2]), float(header[3]) + self.nodes = {} + self.edges = [] + for line in lines: + line = splitline(line) + if line[0] == 'node': + n = Node(*line[1:]) + self.nodes[n.name] = n + if line[0] == 'edge': + self.edges.append(Edge(self.nodes, *line[1:])) + if line[0] == 'stop': + break + +class Node: + def __init__(self, name, x, y, w, h, label, style, shape, color, fillcolor): + self.name = name + self.x = float(x) + self.y = float(y) + self.w = float(w) + self.h = float(h) + self.label = label + self.style = style + self.shape = shape + self.color = color + self.fillcolor = fillcolor + +class Edge: + label = None + + def __init__(self, nodes, tail, head, cnt, *rest): + self.tail = nodes[tail] + self.head = nodes[head] + cnt = int(cnt) + self.points = [(float(rest[i]), float(rest[i+1])) + for i in range(0, cnt*2, 2)] + rest = rest[cnt*2:] + if len(rest) > 2: + self.label, xl, yl = rest[:3] + self.xl = float(xl) + self.yl = float(yl) + rest = rest[3:] + self.style, self.color = rest + + def bezierpoints(self, resolution=8): + result = [] + pts = self.points + for i in range(0, len(pts)-3, 3): + result += beziercurve(pts[i], pts[i+1], + pts[i+2], pts[i+3], resolution) + return result + + def arrowhead(self): + bottom_up = self.points[0][1] > self.points[-1][1] + if (self.tail.y > self.head.y) != bottom_up: # reversed edge + x0, y0 = self.points[0] + x1, y1 = self.points[1] + else: + x0, y0 = self.points[-1] + x1, y1 = self.points[-2] + vx = x0-x1 + vy = y0-y1 + f = 0.12 / math.sqrt(vx*vx + vy*vy) + vx *= f + vy *= f + return [(x0 + 0.9*vx, y0 + 0.9*vy), + (x0 + 0.4*vy, y0 - 0.4*vx), + (x0 - 0.4*vy, y0 + 0.4*vx)] + +def beziercurve((x0,y0), (x1,y1), (x2,y2), (x3,y3), resolution=8): + result = [] + f = 1.0/(resolution-1) + for i in range(resolution): + t = f*i + t0 = (1-t)*(1-t)*(1-t) + t1 = t *(1-t)*(1-t) * 3.0 + t2 = t * t *(1-t) * 3.0 + t3 = t * t * t + result.append((x0*t0 + x1*t1 + x2*t2 + x3*t3, + y0*t0 + y1*t1 + y2*t2 + y3*t3)) + return result + +def splitline(line, re_word = re.compile(r'[^\s"]\S*|["]["]|["].*?[^\\]["]')): + result = [] + for word in re_word.findall(line): + if word.startswith('"'): + word = eval(word) + result.append(word) + return result + + +class GraphRenderer: + MARGIN = 0.2 + SCALEMIN = 25 + SCALEMAX = 90 + FONTCACHE = {} + + def __init__(self, screen, graphlayout, scale=75): + self.graphlayout = graphlayout + self.setscale(scale) + self.setoffset(0, 0) + self.screen = screen + self.textzones = [] + self.highlightwords = {} + + def setscale(self, scale): + scale = max(min(scale, self.SCALEMAX), self.SCALEMIN) + self.scale = float(scale) + w, h = self.graphlayout.boundingbox + self.margin = int(self.MARGIN*scale) + self.width = int((w + 2*self.MARGIN)*scale) + self.height = int((h + 2*self.MARGIN)*scale) + self.bboxh = h + size = int(14 * scale / 75) + if size in self.FONTCACHE: + self.font = self.FONTCACHE[size] + else: + self.font = self.FONTCACHE[size] = pygame.font.Font(FONT, size) + + def setoffset(self, offsetx, offsety): + "Set the (x,y) origin of the rectangle where the graph will be rendered." + self.ofsx = offsetx - self.margin + self.ofsy = offsety - self.margin + + def shiftoffset(self, dx, dy): + self.ofsx += dx + self.ofsy += dy + + def shiftscale(self, factor, fix=None): + if fix is None: + fixx, fixy = self.screen.get_size() + fixx //= 2 + fixy //= 2 + else: + fixx, fixy = fix + x, y = self.revmap(fixx, fixy) + self.setscale(self.scale * factor) + newx, newy = self.map(x, y) + self.shiftoffset(newx - fixx, newy - fixy) + + def getboundingbox(self): + "Get the rectangle where the graph will be rendered." + offsetx = - self.margin - self.ofsx + offsety = - self.margin - self.ofsy + return (offsetx, offsety, self.width, self.height) + + def map(self, x, y): + return (int(x*self.scale) - self.ofsx, + int((self.bboxh-y)*self.scale) - self.ofsy) + + def revmap(self, px, py): + return ((px + self.ofsx) / self.scale, + self.bboxh - (py + self.ofsy) / self.scale) + + def draw_node_commands(self, node): + xcenter, ycenter = self.map(node.x, node.y) + boxwidth = int(node.w * self.scale) + boxheight = int(node.h * self.scale) + fgcolor = COLOR.get(node.color, (0,0,0)) + bgcolor = COLOR.get(node.fillcolor, (255,255,255)) + + text = node.label + lines = text.replace('\l','\l\n').replace('\r','\r\n').split('\n') + # ignore a final newline + if not lines[-1]: + del lines[-1] + wmax = 0 + hmax = 0 + commands = [] + bkgndcommands = [] + + for line in lines: + raw_line = line.replace('\l','').replace('\r','') or ' ' + img = TextSnippet(self, raw_line, (0, 0, 0), bgcolor) + w, h = img.get_size() + if w>wmax: wmax = w + if raw_line.strip(): + if line.endswith('\l'): + def cmd(img=img, y=hmax): + img.draw(xleft, ytop+y) + elif line.endswith('\r'): + def cmd(img=img, y=hmax, w=w): + img.draw(xright-w, ytop+y) + else: + def cmd(img=img, y=hmax, w=w): + img.draw(xcenter-w//2, ytop+y) + commands.append(cmd) + hmax += h + #hmax += 8 + + # we know the bounding box only now; setting these variables will + # have an effect on the values seen inside the cmd() functions above + xleft = xcenter - wmax//2 + xright = xcenter + wmax//2 + ytop = ycenter - hmax//2 + x = xcenter-boxwidth//2 + y = ycenter-boxheight//2 + + if node.shape == 'box': + rect = (x-1, y-1, boxwidth+2, boxheight+2) + def cmd(): + self.screen.fill(bgcolor, rect) + bkgndcommands.append(cmd) + def cmd(): + pygame.draw.rect(self.screen, fgcolor, rect, 1) + commands.append(cmd) + elif node.shape == 'octagon': + step = 1-math.sqrt(2)/2 + points = [(int(x+boxwidth*fx), int(y+boxheight*fy)) + for fx, fy in [(step,0), (1-step,0), + (1,step), (1,1-step), + (1-step,1), (step,1), + (0,1-step), (0,step)]] + def cmd(): + pygame.draw.polygon(self.screen, bgcolor, points, 0) + bkgndcommands.append(cmd) + def cmd(): + pygame.draw.polygon(self.screen, fgcolor, points, 1) + commands.append(cmd) + return bkgndcommands, commands + + def draw_commands(self): + nodebkgndcmd = [] + nodecmd = [] + for node in self.graphlayout.nodes.values(): + cmd1, cmd2 = self.draw_node_commands(node) + nodebkgndcmd += cmd1 + nodecmd += cmd2 + + edgebodycmd = [] + edgeheadcmd = [] + for edge in self.graphlayout.edges: + fgcolor = COLOR.get(edge.color, (0,0,0)) + points = [self.map(*xy) for xy in edge.bezierpoints()] + + def drawedgebody(points=points, fgcolor=fgcolor): + pygame.draw.lines(self.screen, fgcolor, False, points) + edgebodycmd.append(drawedgebody) + + points = [self.map(*xy) for xy in edge.arrowhead()] + def drawedgehead(points=points, fgcolor=fgcolor): + pygame.draw.polygon(self.screen, fgcolor, points, 0) + edgeheadcmd.append(drawedgehead) + + if edge.label: + x, y = self.map(edge.xl, edge.yl) + img = TextSnippet(self, edge.label, (0, 0, 0)) + w, h = img.get_size() + def drawedgelabel(img=img, x1=x-w//2, y1=y-h//2): + img.draw(x1, y1) + edgeheadcmd.append(drawedgelabel) + + return edgebodycmd + nodebkgndcmd + edgeheadcmd + nodecmd + + def render(self): + bbox = self.getboundingbox() + self.screen.fill((224, 255, 224), bbox) + + # gray off-bkgnd areas + ox, oy, width, height = bbox + dpy_width, dpy_height = self.screen.get_size() + gray = (128, 128, 128) + if ox > 0: + self.screen.fill(gray, (0, 0, ox, dpy_height)) + if oy > 0: + self.screen.fill(gray, (0, 0, dpy_width, oy)) + w = dpy_width - (ox + width) + if w > 0: + self.screen.fill(gray, (dpy_width-w, 0, w, dpy_height)) + h = dpy_height - (oy + height) + if h > 0: + self.screen.fill(gray, (0, dpy_height-h, dpy_width, h)) + + # draw the graph and record the position of texts + del self.textzones[:] + for cmd in self.draw_commands(): + cmd() + + def at_position(self, (x, y)): + """Figure out the word under the cursor.""" + for rx, ry, rw, rh, word in self.textzones: + if rx <= x < rx+rw and ry <= y < ry+rh: + return word + return None + +class TextSnippet: + + def __init__(self, renderer, text, fgcolor, bgcolor=None): + self.renderer = renderer + parts = [] + for word in re_nonword.split(text): + if not word: + continue + if word in renderer.highlightwords: + fg, bg = renderer.highlightwords[word] + bg = bg or bgcolor + else: + fg, bg = fgcolor, bgcolor + parts.append((word, fg, bg)) + # consolidate sequences of words with the same color + for i in range(len(parts)-2, -1, -1): + if parts[i][1:] == parts[i+1][1:]: + word, fg, bg = parts[i] + parts[i] = word + parts[i+1][0], fg, bg + del parts[i+1] + # delete None backgrounds + for i in range(len(parts)): + if parts[i][2] is None: + parts[i] = parts[i][:2] + # render parts + self.imgs = [] + i = 0 + while i < len(parts): + part = parts[i] + word = part[0] + antialias = not re_nonword.match(word) # SDL bug with anti-aliasing + try: + img = renderer.font.render(word, antialias, *part[1:]) + except pygame.error: + del parts[i] # Text has zero width + else: + self.imgs.append(img) + i += 1 + self.parts = parts + + def get_size(self): + if self.imgs: + sizes = [img.get_size() for img in self.imgs] + return sum([w for w,h in sizes]), max([h for w,h in sizes]) + else: + return 0, 0 + + def draw(self, x, y): + for part, img in zip(self.parts, self.imgs): + word = part[0] + self.renderer.screen.blit(img, (x, y)) + w, h = img.get_size() + self.renderer.textzones.append((x, y, w, h, word)) + x += w + + +try: + sum # 2.3 only +except NameError: + def sum(lst): + total = 0 + for item in lst: + total += lst + return total + + +def build_layout(graphs, name=None): + """ Build a GraphLayout from a list of control flow graphs. + """ + from pypy.translator.tool.make_dot import make_dot_graphs + name = name or graphs[0].name + gs = [(graph.name, graph) for graph in graphs] + fn = make_dot_graphs(name, gs, target='plain') + return GraphLayout(str(fn)) Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Sat Aug 14 14:41:57 2004 @@ -3,7 +3,7 @@ import sys, os, re import pygame from pygame.locals import * -from pypy.translator.tool.make_dot import make_dot_graphs +from drawgraph import GraphRenderer, build_layout class Display(object): @@ -17,159 +17,10 @@ self.height = h self.screen = pygame.display.set_mode((w, h), HWSURFACE|RESIZABLE) -class GraphViewer(object): - FONT = os.path.join(autopath.this_dir, 'cyrvetic.ttf') - xscale = 1 - yscale = 1 - offsetx = 0 - offsety = 0 - - def __init__(self, xdotfile, pngfile): - pygame.init() - g = open(str(pngfile), 'rb') - try: - self.bkgnd = pygame.image.load(pngfile) - except Exception, e: - print >> sys.stderr, '* Pygame cannot load "%s":' % pngfile - print >> sys.stderr, '* %s: %s' % (e.__class__.__name__, e) - print >> sys.stderr, '* Trying with pngtopnm.' - import os - g = os.popen("pngtopnm '%s'" % pngfile, 'r') - w, h, data = decodepixmap(g) - g.close() - self.bkgnd = pygame.image.fromstring(data, (w, h), "RGB") - self.width, self.height = self.bkgnd.get_size() - self.font = pygame.font.Font(self.FONT, 18) - - # compute a list of (rect, originalw, text, name) - # where text is some text from the graph, - # rect is its position on the screen, - # originalw is its real (dot-computed) size on the screen, - # and name is XXX - self.positions = [] - g = open(xdotfile, 'rb') - lines = g.readlines() - g.close() - self.parse_xdot_output(lines) - - def render(self, dpy): - ox = -self.offsetx - oy = -self.offsety - dpy.screen.blit(self.bkgnd, (ox, oy)) - # gray off-bkgnd areas - gray = (128, 128, 128) - if ox > 0: - dpy.screen.fill(gray, (0, 0, ox, dpy.height)) - if oy > 0: - dpy.screen.fill(gray, (0, 0, dpy.width, oy)) - w = dpy.width - (ox + self.width) - if w > 0: - dpy.screen.fill(gray, (dpy.width-w, 0, w, dpy.height)) - h = dpy.height - (oy + self.height) - if h > 0: - dpy.screen.fill(gray, (0, dpy.height-h, dpy.width, h)) - - def at_position(self, (x, y), re_nonword=re.compile(r'(\W+)')): - """Compute (word, text, name) where word is the word under the cursor, - text is the complete line, and name is XXX. All three are None - if no text is under the cursor.""" - x += self.offsetx - y += self.offsety - for (rx,ry,rw,rh), originalw, text, name in self.positions: - if rx <= x < rx+originalw and ry <= y < ry+rh: - dx = x - rx - # scale dx to account for small font mismatches - dx = int(float(dx) * rw / originalw) - words = [s for s in re_nonword.split(text) if s] - segment = '' - word = '' - for word in words: - segment += word - img = self.font.render(segment, 1, (255, 0, 0)) - w, h = img.get_size() - if dx < w: - break - return word, text, name - return None, None, None - - def getzones(self, re_nonword=re.compile(r'(\W+)')): - for (rx,ry,rw,rh), originalw, text, name in self.positions: - words = [s for s in re_nonword.split(text) if s] - segment = '' - dx1 = 0 - for word in words: - segment += word - img = self.font.render(segment, 1, (255, 0, 0)) - w, h = img.get_size() - dx2 = int(float(w) * originalw / rw) - if word.strip(): - yield (rx+dx1, ry, dx2-dx1, rh), word - dx1 = dx2 - - def parse_xdot_output(self, lines): - for i in range(len(lines)): - if lines[i].endswith('\\\n'): - lines[i+1] = lines[i][:-2] + lines[i+1] - lines[i] = '' - for line in lines: - self.parse_xdot_line(line) - - def parse_xdot_line(self, line, - re_bb = re.compile(r'\s*graph\s+[[]bb=["]0,0,(\d+),(\d+)["][]]'), - re_text = re.compile(r"\s*T" + 5*r"\s+(-?\d+)" + r"\s+-"), - matchtext = ' _ldraw_="'): - match = re_bb.match(line) - if match: - self.xscale = float(self.width-12) / int(match.group(1)) - self.yscale = float(self.height-12) / int(match.group(2)) - return - p = line.find(matchtext) - if p < 0: - return - p += len(matchtext) - line = line[p:] - while 1: - match = re_text.match(line) - if not match: - break - x = 10+int(float(match.group(1)) * self.xscale) - y = self.height-2-int(float(match.group(2)) * self.yscale) - n = int(match.group(5)) - end = len(match.group()) - text = line[end:end+n] - line = line[end+n:] - if text: - img = self.font.render(text, 1, (255, 0, 0)) - w, h = img.get_size() - align = int(match.group(3)) - if align == 0: - x -= w//2 - elif align > 0: - x -= w - rect = x, y-h, w, h - originalw = int(float(match.group(4)) * self.xscale) - self.positions.append((rect, originalw, text, 'XXX')) - - - -def decodepixmap(f): - sig = f.readline().strip() - assert sig == "P6" - while 1: - line = f.readline().strip() - if not line.startswith('#'): - break - wh = line.split() - w, h = map(int, wh) - sig = f.readline().strip() - assert sig == "255" - data = f.read() - f.close() - return w, h, data - class GraphDisplay(Display): STATUSBARFONT = os.path.join(autopath.this_dir, 'VeraMoBd.ttf') + SCALE = 60 def __init__(self, translator, functions=None): super(GraphDisplay, self).__init__() @@ -182,15 +33,13 @@ for var in self.annotator.bindings: self.variables_by_name[var.name] = var - graphs = [] functions = functions or self.translator.functions - for func in functions: - graph = self.translator.getflowgraph(func) - graphs.append((graph.name, graph)) - xdotfile = make_dot_graphs(functions[0].__name__, graphs, target='xdot') - pngfile = make_dot_graphs(functions[0].__name__, graphs, target='png') - self.viewer = GraphViewer(str(xdotfile), str(pngfile)) - self.viewer.offsetx = (self.viewer.width - self.width) // 2 + graphs = [self.translator.getflowgraph(func) for func in functions] + layout = build_layout(graphs) + self.viewer = GraphRenderer(self.screen, layout, self.SCALE) + # center horizonally + self.viewer.setoffset((self.viewer.width - self.width) // 2, 0) + self.sethighlight() self.statusbarinfo = None self.must_redraw = True @@ -229,18 +78,26 @@ y += h def notifymousepos(self, pos): - word, text, name = self.viewer.at_position(pos) + word = self.viewer.at_position(pos) if word in self.variables_by_name: var = self.variables_by_name[word] s_value = self.annotator.binding(var) info = '%s: %s' % (var.name, s_value) self.setstatusbar(info) + self.sethighlight(word) + + def sethighlight(self, word=None): + self.viewer.highlightwords = {} + for name in self.variables_by_name: + self.viewer.highlightwords[name] = ((128,0,0), None) + if word: + self.viewer.highlightwords[word] = ((255,255,80), (128,0,0)) def run(self): dragging = None while 1: if self.must_redraw: - self.viewer.render(self) + self.viewer.render() if self.statusbarinfo: self.drawstatusbar() pygame.display.flip() @@ -252,8 +109,12 @@ if pygame.event.peek([MOUSEMOTION]): continue if dragging: - self.viewer.offsetx -= (event.pos[0] - dragging[0]) - self.viewer.offsety -= (event.pos[1] - dragging[1]) + dx = event.pos[0] - dragging[0] + dy = event.pos[1] - dragging[1] + if event.buttons[2]: # right mouse button + self.viewer.shiftscale(1.003 ** dy) + else: + self.viewer.shiftoffset(-2*dx, -2*dy) dragging = event.pos self.must_redraw = True else: From arigo at codespeak.net Sat Aug 14 15:02:35 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 14 Aug 2004 15:02:35 +0200 (MEST) Subject: [pypy-svn] r5959 - pypy/trunk/src/pypy/translator/tool/pygame Message-ID: <20040814130235.B65655A11C@thoth.codespeak.net> Author: arigo Date: Sat Aug 14 15:02:35 2004 New Revision: 5959 Modified: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Log: Minor tweaks. Modified: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py Sat Aug 14 15:02:35 2004 @@ -124,8 +124,8 @@ class GraphRenderer: MARGIN = 0.2 - SCALEMIN = 25 - SCALEMAX = 90 + SCALEMIN = 30 + SCALEMAX = 100 FONTCACHE = {} def __init__(self, screen, graphlayout, scale=75): @@ -144,7 +144,7 @@ self.width = int((w + 2*self.MARGIN)*scale) self.height = int((h + 2*self.MARGIN)*scale) self.bboxh = h - size = int(14 * scale / 75) + size = max(4, int(15 * (scale-10) / 75)) if size in self.FONTCACHE: self.font = self.FONTCACHE[size] else: Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Sat Aug 14 15:02:35 2004 @@ -37,11 +37,18 @@ graphs = [self.translator.getflowgraph(func) for func in functions] layout = build_layout(graphs) self.viewer = GraphRenderer(self.screen, layout, self.SCALE) - # center horizonally - self.viewer.setoffset((self.viewer.width - self.width) // 2, 0) + # center and scale to view the whole graph + self.viewer.setoffset((self.viewer.width - self.width) // 2, + (self.viewer.height - self.height) // 2) + f = min(float(self.width-40) / self.viewer.width, + float(self.height-40) / self.viewer.height) + if f < 1.0: + self.viewer.shiftscale(f) self.sethighlight() self.statusbarinfo = None self.must_redraw = True + self.setstatusbar('Drag left mouse button to scroll; ' + 'drag right mouse button to zoom') def setstatusbar(self, text, fgcolor=(255,255,80), bgcolor=(128,0,0)): info = (text, fgcolor, bgcolor) @@ -141,7 +148,7 @@ from pypy.translator.translator import Translator from pypy.translator.test import snippet - t = Translator(snippet.poor_man_range) + t = Translator(snippet.powerset) t.simplify() a = t.annotate([int]) a.simplify() From arigo at codespeak.net Sun Aug 15 12:08:28 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 15 Aug 2004 12:08:28 +0200 (MEST) Subject: [pypy-svn] r5961 - pypy/trunk/src/pypy/translator/tool/pygame Message-ID: <20040815100828.4F1D25AA86@thoth.codespeak.net> Author: arigo Date: Sun Aug 15 12:08:27 2004 New Revision: 5961 Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Log: Cannot safely close and re-open a Pygame window, depending on version and platform. Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Sun Aug 15 12:08:27 2004 @@ -141,7 +141,9 @@ self.must_redraw = True if event.type == QUIT: break - pygame.display.quit() + # cannot safely close and re-open the display, depending on + # Pygame version and platform. + pygame.display.set_mode((self.width,1)) if __name__ == '__main__': From arigo at codespeak.net Sun Aug 15 13:10:06 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 15 Aug 2004 13:10:06 +0200 (MEST) Subject: [pypy-svn] r5963 - pypy/trunk/src/pypy/translator/tool/pygame Message-ID: <20040815111006.349795AA86@thoth.codespeak.net> Author: arigo Date: Sun Aug 15 13:10:05 2004 New Revision: 5963 Modified: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Log: Clicking on nodes and edges to move around is definitely more convenient than doing mouse drags all the time (particularly on laptop's touchpads!). Modified: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py Sun Aug 15 13:10:05 2004 @@ -113,6 +113,24 @@ y0*t0 + y1*t1 + y2*t2 + y3*t3)) return result +def segmentdistance((x0,y0), (x1,y1), (x,y)): + "Distance between the point (x,y) and the segment (x0,y0)-(x1,y1)." + vx = x1-x0 + vy = y1-y0 + l = math.sqrt(vx*vx+vy*vy) + if l < 0.00001: + dlong = -1 + else: + vx /= l + vy /= l + dlong = vx*(x-x0) + vy*(y-y0) + if dlong < 0.0: + return math.sqrt((x-x0)*(x-x0) + (y-y0)*(y-y0)) + elif dlong > l: + return math.sqrt((x-x1)*(x-x1) + (y-y1)*(y-y1)) + else: + return abs(vy*(x-x0) - vx*(y-y0)) + def splitline(line, re_word = re.compile(r'[^\s"]\S*|["]["]|["].*?[^\\]["]')): result = [] for word in re_word.findall(line): @@ -159,6 +177,15 @@ self.ofsx += dx self.ofsy += dy + def getcenter(self): + w, h = self.screen.get_size() + return self.revmap(w//2, h//2) + + def setcenter(self, x, y): + w, h = self.screen.get_size() + x, y = self.map(x, y) + self.shiftoffset(x-w//2, y-h//2) + def shiftscale(self, factor, fix=None): if fix is None: fixx, fixy = self.screen.get_size() @@ -316,6 +343,29 @@ return word return None + def node_at_position(self, (x, y)): + """Return the Node under the cursor.""" + x, y = self.revmap(x, y) + for node in self.graphlayout.nodes.itervalues(): + if 2.0*abs(x-node.x) <= node.w and 2.0*abs(y-node.y) <= node.h: + return node + return None + + def edge_at_position(self, (x, y), distmax=14): + """Return the Edge near the cursor.""" + distmax /= self.scale + xy = self.revmap(x, y) + closest_edge = None + for edge in self.graphlayout.edges: + pts = edge.bezierpoints() + for i in range(1, len(pts)): + d = segmentdistance(pts[i-1], pts[i], xy) + if d < distmax: + distmax = d + closest_edge = edge + return closest_edge + + class TextSnippet: def __init__(self, renderer, text, fgcolor, bgcolor=None): Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Sun Aug 15 13:10:05 2004 @@ -1,6 +1,6 @@ from __future__ import generators import autopath -import sys, os, re +import sys, os, re, time import pygame from pygame.locals import * from drawgraph import GraphRenderer, build_layout @@ -21,6 +21,7 @@ class GraphDisplay(Display): STATUSBARFONT = os.path.join(autopath.this_dir, 'VeraMoBd.ttf') SCALE = 60 + ANIM_STEP = 0.07 def __init__(self, translator, functions=None): super(GraphDisplay, self).__init__() @@ -47,8 +48,7 @@ self.sethighlight() self.statusbarinfo = None self.must_redraw = True - self.setstatusbar('Drag left mouse button to scroll; ' - 'drag right mouse button to zoom') + self.setstatusbar('Click to move around, or drag mouse buttons (left to zoom, right to scroll)') def setstatusbar(self, text, fgcolor=(255,255,80), bgcolor=(128,0,0)): info = (text, fgcolor, bgcolor) @@ -100,8 +100,43 @@ if word: self.viewer.highlightwords[word] = ((255,255,80), (128,0,0)) + def animation(self, expectedtime=0.6): + start = time.time() + step = 0.0 + n = 0 + while True: + step += self.ANIM_STEP + if step >= expectedtime: + break + yield step / expectedtime + n += 1 + now = time.time() + frametime = (now-start) / n + self.ANIM_STEP = self.ANIM_STEP * 0.9 + frametime * 0.1 + yield 1.0 + + def look_at_node(self, node): + """Shift the node in view.""" + endscale = min(float(self.width-40) / node.w, + float(self.height-40) / node.h, + 75) + startscale = self.viewer.scale + cx1, cy1 = self.viewer.getcenter() + cx2, cy2 = node.x, node.y + moving = (abs(startscale-endscale) + abs(cx1-cx2) + abs(cy1-cy2) + > 2.0) + if moving: + self.statusbarinfo = None + self.sethighlight(None) + for t in self.animation(): + self.viewer.setscale(startscale*(1-t) + endscale*t) + self.viewer.setcenter(cx1*(1-t) + cx2*t, cy1*(1-t) + cy2*t) + self.viewer.render() + pygame.display.flip() + return moving + def run(self): - dragging = None + dragging = click_origin = click_time = None while 1: if self.must_redraw: self.viewer.render() @@ -116,9 +151,12 @@ if pygame.event.peek([MOUSEMOTION]): continue if dragging: + if (abs(event.pos[0] - click_origin[0]) + + abs(event.pos[1] - click_origin[1])) > 12: + click_time = None dx = event.pos[0] - dragging[0] dy = event.pos[1] - dragging[1] - if event.buttons[2]: # right mouse button + if event.buttons[0]: # left mouse button self.viewer.shiftscale(1.003 ** dy) else: self.viewer.shiftoffset(-2*dx, -2*dy) @@ -127,11 +165,23 @@ else: self.notifymousepos(event.pos) if event.type == MOUSEBUTTONDOWN: - dragging = event.pos + dragging = click_origin = event.pos + click_time = time.time() pygame.event.set_grab(True) if event.type == MOUSEBUTTONUP: dragging = None pygame.event.set_grab(False) + if click_time is not None and abs(time.time() - click_time) < 1: + # click (no significant dragging) + node = self.viewer.node_at_position(click_origin) + if node: + self.look_at_node(node) + else: + edge = self.viewer.edge_at_position(click_origin) + if edge: + if not self.look_at_node(edge.head): + self.look_at_node(edge.tail) + click_time = None self.notifymousepos(event.pos) if event.type == VIDEORESIZE: # short-circuit if there are more resize events pending From arigo at codespeak.net Sun Aug 15 13:38:33 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 15 Aug 2004 13:38:33 +0200 (MEST) Subject: [pypy-svn] r5964 - pypy/trunk/doc Message-ID: <20040815113833.350295AA86@thoth.codespeak.net> Author: arigo Date: Sun Aug 15 13:38:32 2004 New Revision: 5964 Modified: pypy/trunk/doc/cmodules.txt pypy/trunk/doc/howtopypy.txt Log: M cmodules.txt Updates to existing Python versions of some modules. M howtopypy.txt Extended the introduction to show the translator. Added a section "where to start looking in the source". Modified: pypy/trunk/doc/cmodules.txt ============================================================================== --- pypy/trunk/doc/cmodules.txt (original) +++ pypy/trunk/doc/cmodules.txt Sun Aug 15 13:38:32 2004 @@ -19,7 +19,7 @@ _localemodule -_randommodule +_randommodule - already exists but with a lesser generator _sre - already exists? @@ -51,15 +51,15 @@ imgfile -itertoolsmodule +itertoolsmodule - already exists? mathmodule md5module -operator +operator - done in PyPy -parsermodule +parsermodule - already exists? rgbimgmodule Modified: pypy/trunk/doc/howtopypy.txt ============================================================================== --- pypy/trunk/doc/howtopypy.txt (original) +++ pypy/trunk/doc/howtopypy.txt Sun Aug 15 13:38:32 2004 @@ -30,16 +30,11 @@ 3. To start interpreting Python with PyPy, use Python 2.3 or greater:: cd src/pypy/interpreter - python py.py -S + python py.py After a few seconds, you should be at the PyPy prompt, which is the same as the Python prompt, but with an extra ">". - The "-S" given on the command line instructs the PyPy interpreter - to use the `Standard object space`_. PyPy has the concept of different - object spaces_, but the standard space is the one which contains - the "real" Python-in-Python interpreter. - 4. Now you are ready to start running Python code. Some real Python modules will not run yet, and others will run too slowly to be @@ -61,12 +56,12 @@ As an example of using PyPy from the command line, you could type:: - python py.py -S -c "import pystone; pystone.main(10)" + python py.py -c "import pystone; pystone.main(10)" Alternatively, as with regular Python, you can simply give a script name on the command line:: - python py.py -S ../appspace/pystone.py + python py.py ../appspace/pystone.py (Note that this will run "forever" -- actually, "just" for many hours, with the current implementation of PyPy.) @@ -77,34 +72,132 @@ To run all the unit tests:: cd src/pypy - python test_all.py -S + python test_all.py Alternatively, you may run subtests by going to the correct subdirectory and running them individually:: cd src/pypy/module/test - python test_builtin.py -S + python test_builtin.py Finally, there are some more advanced tests (which are derived from some of the standard CPython tests). These are not part of the unit tests, because they take longer than the standard unit tests:: cd src/pypy/interpreter - python py.py -S -c "import builtin_types_test" + python py.py ../appspace/builtin_types_test.py + + +Trying out the translator +========================= + +The translator is a tool based on the PyPy interpreter which can translate +sufficiently static Python programs into low-level code. + +1. Download and install `Dot Graphviz`_. + +2. Download and install Pygame_ if you do not already have it. + +3. Type:: + + cd src/pypy/translator + python -i translator.py + + Test snippets of translatable code are provided in the file + ``test/snippet.py``. For example:: + + >>> t = Translator(test.is_perfect_number) + >>> t.simplify() + >>> t.view() + +4. We have a type annotator that can completely infer types for functions like + ``is_perfect_number``:: + + >>> t.annotate([int]) + >>> t.view() + + Move the mouse over variable names (in red) to see their inferred types. + +5. The graph can be turned into Pyrex code, with types if ``annotate()`` was + called:: + + >>> print t.pyrex() + >>> f = t.compile() + >>> f(28) + 1 + + Note how the strange-looking Pyrex code is unrelated to the original + Python source code. This is because the Pyrex code is generated from the + graph only, without reference to the original source. + + +Where to start reading the sources +================================== + +PyPy is made from parts that are relatively independent from each other. +You should start looking at the part that attracts you most: + +* `src/pypy/interpreter`_ contains the basic interpreter: bytecode dispatcher + in pyopcode.py_, frame and code objects in eval.py_ and pyframe.py_, + function objects and argument passing in function.py_ and argument.py_, + the object space interface definition in baseobjspace.py_, modules in + module.py_ and extmodule.py_. Core types supporting the interpreter are + defined in typedef.py_. + +* `src/pypy/objspace/std`_ contains the `Standard object space`_. The main file + is objspace.py_. For each type, the files ``xxxtype.py`` and + ``xxxobject.py`` contain respectively the definition of the type and its + (default) implementation. + +* `src/pypy/objspace`_ contains a few other object spaces: the trivial one + (but let's forget about it), the trace_ one, the flow_ one. The latter + is a relatively short piece of code that builds the control flow graphs + when the interpreter runs in it. + +* `src/pypy/translator`_ contains the code analysis and generation stuff. + Start reading from translator.py_, from which it should be easy to follow + the pieces of code involved in the various translation phases. + +* `src/pypy/annotation`_ contains the data model for the type annotation that + can be inferred about a graph. The graph "walker" that uses this is in + `src/pypy/translator/annrpython.py`_. + +To learn more +============= -7. To learn more about PyPy and its development process, read the documentation_ +* To learn more about PyPy and its development process, read the documentation_ and the wiki_, and consider subscribing to the `mailing lists`_ (or simply read the archives online) or communicating via irc.freenode.net:6667, channel #pypy. -8. To help PyPy become Python-the-next-generation, write some `unit tests`_ and +* To help PyPy become Python-the-next-generation, write some `unit tests`_ and file some `bug reports`_! -------------------------------------------------------------------------------- .. _subversion: http://codespeak.net/pypy/index.cgi?doc/devel/howtosvn.html +.. _Dot Graphviz: http://www.research.att.com/sw/tools/graphviz/ +.. _Pygame: http://www.pygame.org/ +.. _src/pypy/interpreter: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/ +.. _pyopcode.py: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/pyopcode.py +.. _eval.py: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/eval.py +.. _pyframe.py: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/pyframe.py +.. _function.py: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/function.py +.. _argument.py: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/argument.py +.. _baseobjspace.py: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/baseobjspace.py +.. _module.py: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/module.py +.. _extmodule.py: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/extmodule.py +.. _typedef.py: http://codespeak.net/svn/pypy/trunk/src/pypy/interpreter/typedef.py +.. _src/pypy/objspace/std: http://codespeak.net/svn/pypy/trunk/src/pypy/objspace/std/ .. _Standard object space: http://codespeak.net/pypy/index.cgi?doc/objspace/stdobjspace.html -.. _spaces: http://codespeak.net/pypy/index.cgi?doc/objspace/objspace.html +.. _objspace.py: http://codespeak.net/svn/pypy/trunk/src/pypy/objspace/std/objspace.py +.. _src/pypy/objspace: http://codespeak.net/svn/pypy/trunk/src/pypy/objspace/ +.. _trace: http://codespeak.net/svn/pypy/trunk/src/pypy/objspace/trace.py +.. _flow: http://codespeak.net/svn/pypy/trunk/src/pypy/objspace/flow/ +.. _src/pypy/translator: http://codespeak.net/svn/pypy/trunk/src/pypy/translator/ +.. _translator.py: http://codespeak.net/svn/pypy/trunk/src/pypy/translator/translator.py +.. _src/pypy/annotation: http://codespeak.net/svn/pypy/trunk/src/pypy/annotation/ +.. _src/pypy/translator/annrpython.py: http://codespeak.net/svn/pypy/trunk/src/pypy/translator/annrpython.py .. _mailing lists: http://codespeak.net/pypy/index.cgi?lists .. _documentation: http://codespeak.net/pypy/index.cgi?doc .. _wiki: http://codespeak.net/moin/pypy/moin.cgi/FrontPage?action=show From arigo at codespeak.net Sun Aug 15 13:58:11 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 15 Aug 2004 13:58:11 +0200 (MEST) Subject: [pypy-svn] r5966 - pypy/trunk/src/pypy/translator/tool/pygame Message-ID: <20040815115811.A8C545AA86@thoth.codespeak.net> Author: arigo Date: Sun Aug 15 13:58:11 2004 New Revision: 5966 Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Log: Another nice-and-easy animation effect to avoid getting lost in large graphs. Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Sun Aug 15 13:58:11 2004 @@ -124,12 +124,21 @@ cx1, cy1 = self.viewer.getcenter() cx2, cy2 = node.x, node.y moving = (abs(startscale-endscale) + abs(cx1-cx2) + abs(cy1-cy2) - > 2.0) + > 0.4) if moving: + # if the target is far off the window, reduce scale along the way + tx, ty = self.viewer.map(cx2, cy2) + offview = max(-tx, -ty, tx-self.width, ty-self.height) + middlescale = endscale - 0.06 * offview + if offview > 150 and middlescale < startscale: + bumpscale = 4.0 * (middlescale - 0.5*(startscale+endscale)) + else: + bumpscale = 0.0 self.statusbarinfo = None self.sethighlight(None) for t in self.animation(): - self.viewer.setscale(startscale*(1-t) + endscale*t) + self.viewer.setscale(startscale*(1-t) + endscale*t + + bumpscale*t*(1-t)) self.viewer.setcenter(cx1*(1-t) + cx2*t, cy1*(1-t) + cy2*t) self.viewer.render() pygame.display.flip() @@ -201,7 +210,7 @@ from pypy.translator.test import snippet t = Translator(snippet.powerset) - t.simplify() + #t.simplify() a = t.annotate([int]) a.simplify() GraphDisplay(t).run() From arigo at codespeak.net Sun Aug 15 19:49:06 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 15 Aug 2004 19:49:06 +0200 (MEST) Subject: [pypy-svn] r5969 - pypy/trunk/src/pypy/translator/tool/pygame Message-ID: <20040815174906.951F55AA86@thoth.codespeak.net> Author: arigo Date: Sun Aug 15 19:49:06 2004 New Revision: 5969 Modified: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py Log: Allow very small scales to get an overview of very large graphs. Modified: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py Sun Aug 15 19:49:06 2004 @@ -86,19 +86,26 @@ def arrowhead(self): bottom_up = self.points[0][1] > self.points[-1][1] if (self.tail.y > self.head.y) != bottom_up: # reversed edge - x0, y0 = self.points[0] - x1, y1 = self.points[1] + head = 0 + dir = 1 else: - x0, y0 = self.points[-1] - x1, y1 = self.points[-2] - vx = x0-x1 - vy = y0-y1 - f = 0.12 / math.sqrt(vx*vx + vy*vy) - vx *= f - vy *= f - return [(x0 + 0.9*vx, y0 + 0.9*vy), - (x0 + 0.4*vy, y0 - 0.4*vx), - (x0 - 0.4*vy, y0 + 0.4*vx)] + head = -1 + dir = -1 + n = 1 + while True: + x0, y0 = self.points[head] + x1, y1 = self.points[head+n*dir] + vx = x0-x1 + vy = y0-y1 + try: + f = 0.12 / math.sqrt(vx*vx + vy*vy) + vx *= f + vy *= f + return [(x0 + 0.9*vx, y0 + 0.9*vy), + (x0 + 0.4*vy, y0 - 0.4*vx), + (x0 - 0.4*vy, y0 + 0.4*vx)] + except (ZeroDivisionError, ValueError): + n += 1 def beziercurve((x0,y0), (x1,y1), (x2,y2), (x3,y3), resolution=8): result = [] @@ -117,13 +124,13 @@ "Distance between the point (x,y) and the segment (x0,y0)-(x1,y1)." vx = x1-x0 vy = y1-y0 - l = math.sqrt(vx*vx+vy*vy) - if l < 0.00001: - dlong = -1 - else: + try: + l = math.sqrt(vx*vx+vy*vy) vx /= l vy /= l dlong = vx*(x-x0) + vy*(y-y0) + except (ZeroDivisionError, ValueError): + dlong = -1 if dlong < 0.0: return math.sqrt((x-x0)*(x-x0) + (y-y0)*(y-y0)) elif dlong > l: @@ -142,7 +149,7 @@ class GraphRenderer: MARGIN = 0.2 - SCALEMIN = 30 + SCALEMIN = 3 SCALEMAX = 100 FONTCACHE = {} @@ -162,9 +169,11 @@ self.width = int((w + 2*self.MARGIN)*scale) self.height = int((h + 2*self.MARGIN)*scale) self.bboxh = h - size = max(4, int(15 * (scale-10) / 75)) + size = int(15 * (scale-10) / 75) if size in self.FONTCACHE: self.font = self.FONTCACHE[size] + elif size < 4: + self.font = None else: self.font = self.FONTCACHE[size] = pygame.font.Font(FONT, size) @@ -370,7 +379,11 @@ def __init__(self, renderer, text, fgcolor, bgcolor=None): self.renderer = renderer - parts = [] + self.imgs = [] + self.parts = [] + if renderer.font is None: + return + parts = self.parts for word in re_nonword.split(text): if not word: continue @@ -391,7 +404,6 @@ if parts[i][2] is None: parts[i] = parts[i][:2] # render parts - self.imgs = [] i = 0 while i < len(parts): part = parts[i] @@ -404,7 +416,6 @@ else: self.imgs.append(img) i += 1 - self.parts = parts def get_size(self): if self.imgs: From arigo at codespeak.net Sun Aug 15 23:27:26 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 15 Aug 2004 23:27:26 +0200 (MEST) Subject: [pypy-svn] r5973 - in pypy/trunk/src: goal goal/translate_pypy pypy/translator pypy/translator/tool pypy/translator/tool/pygame Message-ID: <20040815212726.383145AA86@thoth.codespeak.net> Author: arigo Date: Sun Aug 15 23:27:22 2004 New Revision: 5973 Added: pypy/trunk/src/goal/translate_pypy.py - copied, changed from r5957, pypy/trunk/src/goal/translate_pypy/translate_pypy.py pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py (contents, props changed) pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py - copied, changed from r5966, pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Removed: pypy/trunk/src/goal/translate_pypy/ pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Modified: pypy/trunk/src/pypy/translator/annrpython.py pypy/trunk/src/pypy/translator/tool/make_dot.py pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py (contents, props changed) pypy/trunk/src/pypy/translator/translator.py Log: Got rid of the HTML code generation, to use the Pygame-based graph viewer instead. All data is now viewed as dot graphs. This has the interesting effect that it is more natural to view the list of all functions as a call graph. M pypy/translator/translator.py M pypy/translator/annrpython.py Records who-calls-who information. AM pypy/translator/tool/pygame/flowviewer.py Generates graphs from a Translator instance: call graphs, class hierarchy, and (using make_dot) individual flow graphs. MM pypy/translator/tool/pygame/drawgraph.py D pypy/translator/tool/pygame/graphviewer.py A pypy/translator/tool/pygame/graphdisplay.py Hyperlinks between graphs: clicking on a highlighted word can point the graph displayer to another graph. The Left arrow key goes back. M pypy/translator/tool/make_dot.py Divided the class DotGen into a general-purpose class DotGen and a walker class FlowGraphDotGen for flow graphs specifically. A goal/translate_pypy.py D goal/translate_pypy Deleted the whole HTML/HTTP server code. (It's still in the history if needed.) Copied: pypy/trunk/src/goal/translate_pypy.py (from r5957, pypy/trunk/src/goal/translate_pypy/translate_pypy.py) ============================================================================== --- pypy/trunk/src/goal/translate_pypy/translate_pypy.py (original) +++ pypy/trunk/src/goal/translate_pypy.py Sun Aug 15 23:27:22 2004 @@ -6,7 +6,6 @@ from pypy.objspace.std.objspace import StdObjSpace, W_Object from pypy.objspace.std.intobject import W_IntObject from pypy.translator.translator import Translator -from pypy.translator.annrpython import AnnotatorError # __________ Entry point __________ @@ -45,15 +44,15 @@ return print "don't know about", x - def run_server(background=False, port=8000): - import graphserver - server = graphserver.Server(t) - print >> sys.stderr, '* View the blocks at http://127.0.0.1:%d/' % port + def run_server(background=False): + from pypy.translator.tool.pygame.flowviewer import TranslatorLayout + from pypy.translator.tool.pygame.graphdisplay import GraphDisplay + display = GraphDisplay(TranslatorLayout(t)) if background: import thread - thread.start_new_thread(server.serve, ()) + thread.start_new_thread(display.run, ()) else: - server.serve() + display.run() t = Translator(entry_point, verbose=True, simplifying=True) try: @@ -77,4 +76,10 @@ import pdb pdb.post_mortem(tb) else: +## print '-'*60 +## src = t.pyrex() +## g = open('code.pyx', 'w') +## g.write(src) +## print 'Wrote', g.name +## g.close() run_server() Modified: pypy/trunk/src/pypy/translator/annrpython.py ============================================================================== --- pypy/trunk/src/pypy/translator/annrpython.py (original) +++ pypy/trunk/src/pypy/translator/annrpython.py Sun Aug 15 23:27:22 2004 @@ -20,7 +20,7 @@ def __init__(self, translator=None): self.translator = translator - self.pendingblocks = [] # list of (block, list-of-SomeValues-args) + self.pendingblocks = [] # list of (fn, block, list-of-SomeValues-args) self.bindings = {} # map Variables to SomeValues self.annotated = {} # set of blocks already seen self.notify = {} # {block: {factory-to-invalidate-when-done}} @@ -29,16 +29,17 @@ #___ convenience high-level interface __________________ - def build_types(self, func_or_flowgraph, input_arg_types): + def build_types(self, func_or_flowgraph, input_arg_types, func=None): """Recursively build annotations about the specific entry point.""" if isinstance(func_or_flowgraph, FunctionGraph): flowgraph = func_or_flowgraph else: + func = func_or_flowgraph if self.translator is None: from pypy.translator.translator import Translator - self.translator = Translator(func_or_flowgraph) + self.translator = Translator(func) self.translator.annotator = self - flowgraph = self.translator.getflowgraph(func_or_flowgraph) + flowgraph = self.translator.getflowgraph(func) # make input arguments and set their type input_arg_types = list(input_arg_types) nbarg = len(flowgraph.getargs()) @@ -51,7 +52,7 @@ inputcells.append(t) # register the entry point - self.addpendingblock(flowgraph.startblock, inputcells) + self.addpendingblock(func, flowgraph.startblock, inputcells) # recursively proceed until no more pending block is left self.complete() return self.binding(flowgraph.getreturnvar()) @@ -90,19 +91,20 @@ #___ medium-level interface ____________________________ - def addpendingblock(self, block, cells): + def addpendingblock(self, fn, block, cells): """Register an entry point into block with the given input cells.""" + assert fn in self.translator.flowgraphs for a in cells: assert isinstance(a, annmodel.SomeObject) - self.pendingblocks.append((block, cells)) + self.pendingblocks.append((fn, block, cells)) def complete(self): """Process pending blocks until none is left.""" while self.pendingblocks: # XXX don't know if it is better to pop from the head or the tail. # but suspect from the tail is better in the new Factory model. - block, cells = self.pendingblocks.pop() - self.processblock(block, cells) + fn, block, cells = self.pendingblocks.pop() + self.processblock(fn, block, cells) if False in self.annotated.values(): raise AnnotatorError('%d blocks are still blocked' % self.annotated.values().count(False)) @@ -130,7 +132,9 @@ #___ interface for annotator.factory _______ def recursivecall(self, func, factory, *args): - graph = self.translator.getflowgraph(func) + parent_fn, parent_block, parent_index = factory.position_key + graph = self.translator.getflowgraph(func, parent_fn, + factory.position_key) # self.notify[graph.returnblock] is a dictionary of # FuncCallFactories (call points to this func) which triggers a # reflow whenever the return block of this graph has been analysed. @@ -168,14 +172,14 @@ for extra in func.func_defaults[-missingargs:]: inputcells.append(annmodel.immutablevalue(extra)) inputcells.extend(extracells) - self.addpendingblock(block, inputcells) + self.addpendingblock(func, block, inputcells) # get the (current) return value v = graph.getreturnvar() return self.bindings.get(v, annmodel.SomeImpossibleValue()) def reflowfromposition(self, position_key): - block, index = position_key - self.reflowpendingblock(block) + fn, block, index = position_key + self.reflowpendingblock(fn, block) #___ simplification (should be moved elsewhere?) _______ @@ -213,7 +217,7 @@ #___ flowing annotations in blocks _____________________ - def processblock(self, block, cells): + def processblock(self, fn, block, cells): # Important: this is not called recursively. # self.flowin() can only issue calls to self.addpendingblock(). # The analysis of a block can be in three states: @@ -234,7 +238,7 @@ if not self.annotated[block]: self.annotated[block] = True try: - self.flowin(block) + self.flowin(fn, block) except BlockedInference, e: #print '_'*60 #print 'Blocked at %r:' % (e.break_at,) @@ -247,8 +251,8 @@ setattr(e, '__annotator_block', block) raise - def reflowpendingblock(self, block): - self.pendingblocks.append((block, None)) + def reflowpendingblock(self, fn, block): + self.pendingblocks.append((fn, block, None)) assert block in self.annotated self.annotated[block] = False # must re-flow @@ -267,17 +271,17 @@ if unions != oldcells: self.bindinputargs(block, unions) - def flowin(self, block): + def flowin(self, fn, block): #print 'Flowing', block, [self.binding(a) for a in block.inputargs] for i in range(len(block.operations)): try: - self.bookkeeper.enter((block, i)) + self.bookkeeper.enter((fn, block, i)) self.consider_op(block.operations[i]) finally: self.bookkeeper.leave() for link in block.exits: cells = [self.binding(a) for a in link.args] - self.addpendingblock(link.target, cells) + self.addpendingblock(fn, link.target, cells) if block in self.notify: # invalidate some factories when this block is done for factory in self.notify[block]: Modified: pypy/trunk/src/pypy/translator/tool/make_dot.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/make_dot.py (original) +++ pypy/trunk/src/pypy/translator/tool/make_dot.py Sun Aug 15 23:27:22 2004 @@ -9,49 +9,40 @@ from pypy.tool.udir import udir from std.process import cmdexec -debug = 0 - class DotGen: - def get_source(self, funcgraph): - content = "\n".join(self.lines) - return """ -digraph test { -node [fontname=Times]; -edge [fontname=Times]; -%(content)s -}""" % locals() - - def getsubgraph(self, name, node): - self.blocks = {} + + def __init__(self, graphname): + self.graphname = graphname self.lines = [] - self.prefix = name - traverse(self, node) - content = "\n".join(self.lines) - return "subgraph %s {\n%s}" % (name, content) - - def getdigraph(self, name, node): - self.blocks = {} - self.lines = [] - self.prefix = name - traverse(self, node) - content = "\n".join(self.lines) - return "digraph %s {\n%s}" % (name, content) - - def getgraph(self, name, subgraphlist): - content = "\n".join(subgraphlist) - return "digraph %s {\n%s}" % (name, content) + self.source = None + self.emit("digraph %s {" % graphname) - def blockname(self, block): - i = id(block) - try: - return self.blocks[i] - except KeyError: - self.blocks[i] = name = "%s_%d" % (self.prefix, len(self.blocks)) - return name + def generate(self, storedir=None, target='ps'): + source = self.get_source() + if storedir is None: + storedir = udir + pdot = storedir.join('%s.dot' % self.graphname) + pdot.write(source) + ptarget = pdot.new(ext=target) + cmdexec('dot -T%s %s>%s' % (target, str(pdot),str(ptarget))) + return ptarget + + def get_source(self): + if self.source is None: + self.emit("}") + self.source = '\n'.join(self.lines) + del self.lines + return self.source def emit(self, line): self.lines.append(line) + def enter_subgraph(self, name): + self.emit("subgraph %s {" % (name,)) + + def leave_subgraph(self): + self.emit("}") + def emit_edge(self, name1, name2, label="", style="dashed", color="black", @@ -59,7 +50,8 @@ weight="5", ): d = locals() - attrs = [('%s="%s"' % (x, d[x])) for x in d if isinstance(x, str)] + attrs = [('%s="%s"' % (x, d[x])) + for x in ['label', 'style', 'color', 'dir', 'weight']] self.emit('edge [%s];' % ", ".join(attrs)) self.emit('%s -> %s' % (name1, name2)) @@ -71,9 +63,28 @@ style="filled", ): d = locals() - attrs = [('%s="%s"' % (x, d[x])) for x in d if isinstance(x, str)] + attrs = [('%s="%s"' % (x, d[x])) + for x in ['shape', 'label', 'color', 'fillcolor', 'style']] self.emit('%s [%s];' % (name, ", ".join(attrs))) + +class FlowGraphDotGen(DotGen): + + def emit_subgraph(self, name, node): + self.blocks = {} + self.prefix = name + self.enter_subgraph(name) + traverse(self, node) + self.leave_subgraph() + + def blockname(self, block): + i = id(block) + try: + return self.blocks[i] + except KeyError: + self.blocks[i] = name = "%s_%d" % (self.prefix, len(self.blocks)) + return name + def visit(self, obj): # ignore for now return @@ -138,14 +149,7 @@ os.system('gv %s' % fn) def make_dot_graphs(basefilename, graphs, storedir=None, target='ps'): - if storedir is None: - storedir = udir - - dotgen = DotGen() - dest = storedir.join('%s.dot' % basefilename) - #dest = storedir.dirname().join('%s.dot' % name) - subgraphs = [] - basefilename = '_'+basefilename + dotgen = FlowGraphDotGen(basefilename) names = {basefilename: True} for graphname, graph in graphs: if graphname in names: @@ -154,17 +158,8 @@ i += 1 graphname = graphname + str(i) names[graphname] = True - subgraphs.append(dotgen.getsubgraph(graphname, graph)) - source = dotgen.getgraph(basefilename, subgraphs) - #print source - dest.write(source) - psdest = dest.new(ext=target) - out = cmdexec('dot -T%s %s>%s' % (target, str(dest),str(psdest))) -## This is the old code, which doesnt work on binary files on windows -## out = cmdexec('dot -T%s %s' % (target, str(dest))) -## psdest.write(out) - #print "wrote", psdest - return psdest + dotgen.emit_subgraph(graphname, graph) + return dotgen.generate(storedir, target) if __name__ == '__main__': Modified: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py Sun Aug 15 23:27:22 2004 @@ -24,7 +24,7 @@ def __init__(self, filename): # parse the layout file (.plain format) - lines = open(filename, 'r').readlines() + lines = open(str(filename), 'r').readlines() for i in range(len(lines)-2, -1, -1): if lines[i].endswith('\\\n'): # line ending in '\' lines[i] = lines[i][:-2] + lines[i+1] @@ -44,6 +44,7 @@ self.edges.append(Edge(self.nodes, *line[1:])) if line[0] == 'stop': break + self.links = {} class Node: def __init__(self, name, x, y, w, h, label, style, shape, color, fillcolor): @@ -441,13 +442,3 @@ for item in lst: total += lst return total - - -def build_layout(graphs, name=None): - """ Build a GraphLayout from a list of control flow graphs. - """ - from pypy.translator.tool.make_dot import make_dot_graphs - name = name or graphs[0].name - gs = [(graph.name, graph) for graph in graphs] - fn = make_dot_graphs(name, gs, target='plain') - return GraphLayout(str(fn)) Added: pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py ============================================================================== --- (empty file) +++ pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py Sun Aug 15 23:27:22 2004 @@ -0,0 +1,176 @@ +import autopath +import inspect +from drawgraph import GraphLayout +from pypy.translator.tool.make_dot import DotGen +from pypy.interpreter.pycode import CO_VARARGS, CO_VARKEYWORDS +from pypy.annotation import model, factory + + +class FlowGraphLayout(GraphLayout): + """ A GraphLayout showing a Flow Graph (or a few flow graphs). + """ + def __init__(self, translator, functions=None): + from pypy.translator.tool.make_dot import make_dot_graphs + self.translator = translator + self.annotator = translator.annotator + functions = functions or translator.functions + graphs = [translator.getflowgraph(func) for func in functions] + gs = [(graph.name, graph) for graph in graphs] + fn = make_dot_graphs(graphs[0].name, gs, target='plain') + GraphLayout.__init__(self, fn) + # make the dictionary of links -- one per annotated variable + self.binding_history = {} + if self.annotator: + for var in self.annotator.bindings: + s_value = self.annotator.binding(var) + info = '%s: %s' % (var.name, s_value) + self.links[var.name] = info + for var, history in self.annotator.bindingshistory.items(): + self.binding_history[var.name] = history + + def followlink(self, varname): + # clicking on a variable name shows its binding history + dotgen = DotGen('binding') + data = "Most recent binding\\n\\n%s" % nottoowide(self.links[varname]) + dotgen.emit_node('0', shape="box", color="red", label=data) + history = list(self.binding_history.get(varname, [])) + history.reverse() + for n, data in zip(range(len(history)), history): + dotgen.emit_node(str(n+1), shape="box", label=nottoowide(data)) + dotgen.emit_edge(str(n+1), str(n)) + return GraphLayout(dotgen.generate(target='plain')) + + +def nottoowide(text, width=72): + parts = str(text).split(' ') + lines = [] + line = parts.pop(0) + for s in parts: + if len(line)+len(s) < width: + line = line + ' ' + s + else: + lines.append(line) + line = s + lines.append(line) + return '\\n'.join(lines) + + +class ClassDefLayout(GraphLayout): + """A GraphLayout showing the attributes of a class. + """ + def __init__(self, translator, cdef): + self.translator = translator + dotgen = DotGen(cdef.cls.__name__) + + def writecdef(cdef): + dotgen.emit_node(nameof(cdef), color="red", shape="octagon", + label=repr(cdef.cls)) + attrs = cdef.attrs.items() + attrs.sort() + for name, s_value in attrs: + dotgen.emit_node(name, shape="box", label=nottoowide(s_value)) + dotgen.emit_edge(nameof(cdef), name, label=name) + + prevcdef = None + while cdef is not None: + writecdef(cdef) + if prevcdef: + dotgen.emit_edge(nameof(cdef), nameof(prevcdef), color="red") + prevcdef = cdef + cdef = cdef.basedef + + GraphLayout.__init__(self, dotgen.generate(target='plain')) + + +class TranslatorLayout(GraphLayout): + """A GraphLayout showing a the call graph between functions + as well as the class hierarchy.""" + + def __init__(self, translator): + self.translator = translator + self.object_by_name = {} + dotgen = DotGen('translator') + + # show the call graph + functions = translator.functions + dotgen.emit_node('entry', fillcolor="green", shape="octagon", + label="Translator\\nEntry Point") + for func in functions: + data = self.labelof(func, func.func_name) + dotgen.emit_node(nameof(func), label=data, shape="box") + dotgen.emit_edge('entry', nameof(functions[0]), color="green") + for f1, f2 in translator.callgraph.itervalues(): + dotgen.emit_edge(nameof(f1), nameof(f2)) + + # show the class hierarchy + if self.translator.annotator: + dotgen.emit_node(nameof(None), color="red", shape="octagon", + label="Root Class\\nobject") + for classdef in self.translator.annotator.getuserclassdefinitions(): + data = self.labelof(classdef, classdef.cls.__name__) + dotgen.emit_node(nameof(classdef), label=data, shape="box") + dotgen.emit_edge(nameof(classdef.basedef), nameof(classdef)) + + GraphLayout.__init__(self, dotgen.generate(target='plain')) + + # link the function names to the individual flow graphs + for name, obj in self.object_by_name.iteritems(): + if isinstance(obj, factory.ClassDef): + #data = '%s.%s' % (obj.cls.__module__, obj.cls.__name__) + data = repr(obj.cls) + else: + func = obj + try: + source = inspect.getsource(func) + except IOError: # e.g. when func is defined interactively + source = func.func_name + data = '%s:%d %s' % (func.func_globals.get('__name__', '?'), + func.func_code.co_firstlineno, + source.split('\n')[0]) + self.links[name] = data + + def labelof(self, obj, objname): + name = objname + i = 1 + while name in self.object_by_name: + i += 1 + name = '%s__%d' % (objname, i) + self.object_by_name[name] = obj + return name + + def followlink(self, name): + obj = self.object_by_name[name] + if isinstance(obj, factory.ClassDef): + return ClassDefLayout(self.translator, obj) + else: + return FlowGraphLayout(self.translator, [obj]) + + +def nameof(obj, cache={}): + # NB. the purpose of the cache is not performance, but to ensure that + # two objects that compare equal get the same name + try: + return cache[obj] + except KeyError: + result = '%s__0x%x' % (getattr(obj, '__name__', ''), id(obj)) + cache[obj] = result + return result + +# ____________________________________________________________ + +if __name__ == '__main__': + from pypy.translator.translator import Translator + from pypy.translator.test import snippet + from graphdisplay import GraphDisplay + + t = Translator(snippet.powerset) + #t.simplify() + a = t.annotate([int]) + a.simplify() + GraphDisplay(FlowGraphLayout(t)).run() + +## t = Translator(snippet._methodcall1) +## t.simplify() +## a = t.annotate([int]) +## a.simplify() +## GraphDisplay(TranslatorLayout(t)).run() Copied: pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py (from r5966, pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py) ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py Sun Aug 15 23:27:22 2004 @@ -1,9 +1,9 @@ from __future__ import generators import autopath -import sys, os, re, time +import os, time import pygame from pygame.locals import * -from drawgraph import GraphRenderer, build_layout +from drawgraph import GraphRenderer class Display(object): @@ -20,24 +20,20 @@ class GraphDisplay(Display): STATUSBARFONT = os.path.join(autopath.this_dir, 'VeraMoBd.ttf') - SCALE = 60 ANIM_STEP = 0.07 - def __init__(self, translator, functions=None): + def __init__(self, layout): super(GraphDisplay, self).__init__() - self.translator = translator - self.annotator = translator.annotator self.font = pygame.font.Font(self.STATUSBARFONT, 16) - - self.variables_by_name = {} - if self.annotator: - for var in self.annotator.bindings: - self.variables_by_name[var.name] = var - - functions = functions or self.translator.functions - graphs = [self.translator.getflowgraph(func) for func in functions] - layout = build_layout(graphs) - self.viewer = GraphRenderer(self.screen, layout, self.SCALE) + self.viewers_history = [] + self.viewer = None + self.setlayout(layout) + + def setlayout(self, layout): + if self.viewer: + self.viewers_history.append(self.viewer) + self.layout = layout + self.viewer = GraphRenderer(self.screen, layout) # center and scale to view the whole graph self.viewer.setoffset((self.viewer.width - self.width) // 2, (self.viewer.height - self.height) // 2) @@ -45,10 +41,24 @@ float(self.height-40) / self.viewer.height) if f < 1.0: self.viewer.shiftscale(f) + self.updated_viewer() + + def updated_viewer(self): self.sethighlight() self.statusbarinfo = None self.must_redraw = True - self.setstatusbar('Click to move around, or drag mouse buttons (left to zoom, right to scroll)') + if self.viewers_history: + info = 'Press Left Arrow to go back to previous screen' + else: + info = ('Click to move around, or drag mouse buttons ' + '(left to zoom, right to scroll)') + self.setstatusbar(info) + + def layout_back(self): + if self.viewers_history: + self.viewer = self.viewers_history.pop() + self.layout = self.viewer.graphlayout + self.updated_viewer() def setstatusbar(self, text, fgcolor=(255,255,80), bgcolor=(128,0,0)): info = (text, fgcolor, bgcolor) @@ -86,16 +96,30 @@ def notifymousepos(self, pos): word = self.viewer.at_position(pos) - if word in self.variables_by_name: - var = self.variables_by_name[word] - s_value = self.annotator.binding(var) - info = '%s: %s' % (var.name, s_value) + if word in self.layout.links: + info = self.layout.links[word] self.setstatusbar(info) self.sethighlight(word) + def notifyclick(self, pos): + word = self.viewer.at_position(pos) + if word in self.layout.links: + newlayout = self.layout.followlink(word) + if newlayout is not None: + self.setlayout(newlayout) + return + node = self.viewer.node_at_position(pos) + if node: + self.look_at_node(node) + else: + edge = self.viewer.edge_at_position(pos) + if edge: + if not self.look_at_node(edge.head): + self.look_at_node(edge.tail) + def sethighlight(self, word=None): self.viewer.highlightwords = {} - for name in self.variables_by_name: + for name in self.layout.links: self.viewer.highlightwords[name] = ((128,0,0), None) if word: self.viewer.highlightwords[word] = ((255,255,80), (128,0,0)) @@ -129,7 +153,7 @@ # if the target is far off the window, reduce scale along the way tx, ty = self.viewer.map(cx2, cy2) offview = max(-tx, -ty, tx-self.width, ty-self.height) - middlescale = endscale - 0.06 * offview + middlescale = endscale * (0.999 ** offview) if offview > 150 and middlescale < startscale: bumpscale = 4.0 * (middlescale - 0.5*(startscale+endscale)) else: @@ -182,16 +206,12 @@ pygame.event.set_grab(False) if click_time is not None and abs(time.time() - click_time) < 1: # click (no significant dragging) - node = self.viewer.node_at_position(click_origin) - if node: - self.look_at_node(node) - else: - edge = self.viewer.edge_at_position(click_origin) - if edge: - if not self.look_at_node(edge.head): - self.look_at_node(edge.tail) + self.notifyclick(click_origin) click_time = None self.notifymousepos(event.pos) + if event.type == KEYDOWN: + if event.key in [K_p, K_LEFT, K_BACKSPACE]: + self.layout_back() if event.type == VIDEORESIZE: # short-circuit if there are more resize events pending if pygame.event.peek([VIDEORESIZE]): @@ -203,14 +223,3 @@ # cannot safely close and re-open the display, depending on # Pygame version and platform. pygame.display.set_mode((self.width,1)) - - -if __name__ == '__main__': - from pypy.translator.translator import Translator - from pypy.translator.test import snippet - - t = Translator(snippet.powerset) - #t.simplify() - a = t.annotate([int]) - a.simplify() - GraphDisplay(t).run() Deleted: /pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py ============================================================================== --- /pypy/trunk/src/pypy/translator/tool/pygame/graphviewer.py Sun Aug 15 23:27:22 2004 +++ (empty file) @@ -1,216 +0,0 @@ -from __future__ import generators -import autopath -import sys, os, re, time -import pygame -from pygame.locals import * -from drawgraph import GraphRenderer, build_layout - - -class Display(object): - - def __init__(self, (w,h)=(800,740)): - pygame.init() - self.resize((w,h)) - - def resize(self, (w,h)): - self.width = w - self.height = h - self.screen = pygame.display.set_mode((w, h), HWSURFACE|RESIZABLE) - - -class GraphDisplay(Display): - STATUSBARFONT = os.path.join(autopath.this_dir, 'VeraMoBd.ttf') - SCALE = 60 - ANIM_STEP = 0.07 - - def __init__(self, translator, functions=None): - super(GraphDisplay, self).__init__() - self.translator = translator - self.annotator = translator.annotator - self.font = pygame.font.Font(self.STATUSBARFONT, 16) - - self.variables_by_name = {} - if self.annotator: - for var in self.annotator.bindings: - self.variables_by_name[var.name] = var - - functions = functions or self.translator.functions - graphs = [self.translator.getflowgraph(func) for func in functions] - layout = build_layout(graphs) - self.viewer = GraphRenderer(self.screen, layout, self.SCALE) - # center and scale to view the whole graph - self.viewer.setoffset((self.viewer.width - self.width) // 2, - (self.viewer.height - self.height) // 2) - f = min(float(self.width-40) / self.viewer.width, - float(self.height-40) / self.viewer.height) - if f < 1.0: - self.viewer.shiftscale(f) - self.sethighlight() - self.statusbarinfo = None - self.must_redraw = True - self.setstatusbar('Click to move around, or drag mouse buttons (left to zoom, right to scroll)') - - def setstatusbar(self, text, fgcolor=(255,255,80), bgcolor=(128,0,0)): - info = (text, fgcolor, bgcolor) - if info != self.statusbarinfo: - self.statusbarinfo = info - self.must_redraw = True - - def drawstatusbar(self): - text, fgcolor, bgcolor = self.statusbarinfo - words = text.split(' ') - lines = [] - totalh = 0 - while words: - line = words.pop(0) - img = self.font.render(line, 1, fgcolor) - while words: - longerline = line + ' ' + words[0] - longerimg = self.font.render(longerline, 1, fgcolor) - w, h = longerimg.get_size() - if w > self.width: - break - words.pop(0) - line = longerline - img = longerimg - lines.append(img) - w, h = img.get_size() - totalh += h - - y = self.height - totalh - self.screen.fill(bgcolor, (0, y-16, self.width, totalh+16)) - for img in lines: - w, h = img.get_size() - self.screen.blit(img, ((self.width-w)//2, y-8)) - y += h - - def notifymousepos(self, pos): - word = self.viewer.at_position(pos) - if word in self.variables_by_name: - var = self.variables_by_name[word] - s_value = self.annotator.binding(var) - info = '%s: %s' % (var.name, s_value) - self.setstatusbar(info) - self.sethighlight(word) - - def sethighlight(self, word=None): - self.viewer.highlightwords = {} - for name in self.variables_by_name: - self.viewer.highlightwords[name] = ((128,0,0), None) - if word: - self.viewer.highlightwords[word] = ((255,255,80), (128,0,0)) - - def animation(self, expectedtime=0.6): - start = time.time() - step = 0.0 - n = 0 - while True: - step += self.ANIM_STEP - if step >= expectedtime: - break - yield step / expectedtime - n += 1 - now = time.time() - frametime = (now-start) / n - self.ANIM_STEP = self.ANIM_STEP * 0.9 + frametime * 0.1 - yield 1.0 - - def look_at_node(self, node): - """Shift the node in view.""" - endscale = min(float(self.width-40) / node.w, - float(self.height-40) / node.h, - 75) - startscale = self.viewer.scale - cx1, cy1 = self.viewer.getcenter() - cx2, cy2 = node.x, node.y - moving = (abs(startscale-endscale) + abs(cx1-cx2) + abs(cy1-cy2) - > 0.4) - if moving: - # if the target is far off the window, reduce scale along the way - tx, ty = self.viewer.map(cx2, cy2) - offview = max(-tx, -ty, tx-self.width, ty-self.height) - middlescale = endscale - 0.06 * offview - if offview > 150 and middlescale < startscale: - bumpscale = 4.0 * (middlescale - 0.5*(startscale+endscale)) - else: - bumpscale = 0.0 - self.statusbarinfo = None - self.sethighlight(None) - for t in self.animation(): - self.viewer.setscale(startscale*(1-t) + endscale*t + - bumpscale*t*(1-t)) - self.viewer.setcenter(cx1*(1-t) + cx2*t, cy1*(1-t) + cy2*t) - self.viewer.render() - pygame.display.flip() - return moving - - def run(self): - dragging = click_origin = click_time = None - while 1: - if self.must_redraw: - self.viewer.render() - if self.statusbarinfo: - self.drawstatusbar() - pygame.display.flip() - self.must_redraw = False - - event = pygame.event.wait() - if event.type == MOUSEMOTION: - # short-circuit if there are more motion events pending - if pygame.event.peek([MOUSEMOTION]): - continue - if dragging: - if (abs(event.pos[0] - click_origin[0]) + - abs(event.pos[1] - click_origin[1])) > 12: - click_time = None - dx = event.pos[0] - dragging[0] - dy = event.pos[1] - dragging[1] - if event.buttons[0]: # left mouse button - self.viewer.shiftscale(1.003 ** dy) - else: - self.viewer.shiftoffset(-2*dx, -2*dy) - dragging = event.pos - self.must_redraw = True - else: - self.notifymousepos(event.pos) - if event.type == MOUSEBUTTONDOWN: - dragging = click_origin = event.pos - click_time = time.time() - pygame.event.set_grab(True) - if event.type == MOUSEBUTTONUP: - dragging = None - pygame.event.set_grab(False) - if click_time is not None and abs(time.time() - click_time) < 1: - # click (no significant dragging) - node = self.viewer.node_at_position(click_origin) - if node: - self.look_at_node(node) - else: - edge = self.viewer.edge_at_position(click_origin) - if edge: - if not self.look_at_node(edge.head): - self.look_at_node(edge.tail) - click_time = None - self.notifymousepos(event.pos) - if event.type == VIDEORESIZE: - # short-circuit if there are more resize events pending - if pygame.event.peek([VIDEORESIZE]): - continue - self.resize(event.size) - self.must_redraw = True - if event.type == QUIT: - break - # cannot safely close and re-open the display, depending on - # Pygame version and platform. - pygame.display.set_mode((self.width,1)) - - -if __name__ == '__main__': - from pypy.translator.translator import Translator - from pypy.translator.test import snippet - - t = Translator(snippet.powerset) - #t.simplify() - a = t.annotate([int]) - a.simplify() - GraphDisplay(t).run() Modified: pypy/trunk/src/pypy/translator/translator.py ============================================================================== --- pypy/trunk/src/pypy/translator/translator.py (original) +++ pypy/trunk/src/pypy/translator/translator.py Sun Aug 15 23:27:22 2004 @@ -54,9 +54,10 @@ self.annotator = None self.flowgraphs = {} # {function: graph} self.functions = [] # the keys of self.flowgraphs, in creation order + self.callgraph = {} # {opaque_tag: (caller, callee)} self.getflowgraph() - def getflowgraph(self, func=None): + def getflowgraph(self, func=None, called_by=None, call_tag=None): """Get the flow graph for a function (default: the entry point).""" func = func or self.entrypoint try: @@ -84,6 +85,8 @@ graph.source = inspect.getsource(func) except IOError: pass # e.g. when func is defined interactively + if called_by: + self.callgraph[called_by, func, call_tag] = called_by, func return graph def gv(self, func=None): @@ -127,7 +130,7 @@ if self.annotator is None: self.annotator = RPythonAnnotator(self) graph = self.getflowgraph(func) - self.annotator.build_types(graph, input_args_types) + self.annotator.build_types(graph, input_args_types, func) return self.annotator def source(self, func=None): @@ -182,7 +185,7 @@ g = gencls(graph) g.by_the_way_the_function_was = func # XXX if input_arg_types is not None: - ann.build_types(graph, input_arg_types) + ann.build_types(graph, input_arg_types, func) if ann is not None: g.setannotator(ann) return g.emitcode() From arigo at codespeak.net Sun Aug 15 23:35:00 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 15 Aug 2004 23:35:00 +0200 (MEST) Subject: [pypy-svn] r5974 - pypy/trunk/src/pypy/translator Message-ID: <20040815213500.E2E655AA86@thoth.codespeak.net> Author: arigo Date: Sun Aug 15 23:35:00 2004 New Revision: 5974 Modified: pypy/trunk/src/pypy/translator/annrpython.py Log: Always run the test suite before checking in. Modified: pypy/trunk/src/pypy/translator/annrpython.py ============================================================================== --- pypy/trunk/src/pypy/translator/annrpython.py (original) +++ pypy/trunk/src/pypy/translator/annrpython.py Sun Aug 15 23:35:00 2004 @@ -93,7 +93,7 @@ def addpendingblock(self, fn, block, cells): """Register an entry point into block with the given input cells.""" - assert fn in self.translator.flowgraphs + assert self.translator is None or fn in self.translator.flowgraphs for a in cells: assert isinstance(a, annmodel.SomeObject) self.pendingblocks.append((fn, block, cells)) From alastair at codespeak.net Sun Aug 15 23:52:58 2004 From: alastair at codespeak.net (alastair at codespeak.net) Date: Sun, 15 Aug 2004 23:52:58 +0200 (MEST) Subject: [pypy-svn] r5975 - pypy/trunk/doc/funding Message-ID: <20040815215258.C2C385AA86@thoth.codespeak.net> Author: alastair Date: Sun Aug 15 23:52:58 2004 New Revision: 5975 Added: pypy/trunk/doc/funding/IST-2-004779-STP-CPF.tgz (contents, props changed) Log: Printable version of the CPFs as a tarball of PDF files. Added: pypy/trunk/doc/funding/IST-2-004779-STP-CPF.tgz ============================================================================== Binary file. No diff available. From alastair at codespeak.net Sun Aug 15 23:54:30 2004 From: alastair at codespeak.net (alastair at codespeak.net) Date: Sun, 15 Aug 2004 23:54:30 +0200 (MEST) Subject: [pypy-svn] r5976 - pypy/trunk/doc/funding/negotiations Message-ID: <20040815215430.F26115AA86@thoth.codespeak.net> Author: alastair Date: Sun Aug 15 23:54:30 2004 New Revision: 5976 Added: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF.tgz - copied unchanged from r5975, pypy/trunk/doc/funding/IST-2-004779-STP-CPF.tgz Log: Moved to the negotiations directory. From alastair at codespeak.net Mon Aug 16 00:10:48 2004 From: alastair at codespeak.net (alastair at codespeak.net) Date: Mon, 16 Aug 2004 00:10:48 +0200 (MEST) Subject: [pypy-svn] r5977 - pypy/trunk/doc/funding/negotiations Message-ID: <20040815221048.291CE5AA86@thoth.codespeak.net> Author: alastair Date: Mon Aug 16 00:10:47 2004 New Revision: 5977 Added: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-CM.pdf (contents, props changed) pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-DFKI.pdf (contents, props changed) pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-Logilab.pdf (contents, props changed) pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-Strakt.pdf (contents, props changed) pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-USH.pdf (contents, props changed) Log: Individual version of A2c of the CPFs for each partner to sign. Added: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-CM.pdf ============================================================================== Binary file. No diff available. Added: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-DFKI.pdf ============================================================================== Binary file. No diff available. Added: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-Logilab.pdf ============================================================================== Binary file. No diff available. Added: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-Strakt.pdf ============================================================================== Binary file. No diff available. Added: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP-CPF-A2c-USH.pdf ============================================================================== Binary file. No diff available. From arigo at codespeak.net Mon Aug 16 15:33:00 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 16 Aug 2004 15:33:00 +0200 (MEST) Subject: [pypy-svn] r5986 - pypy/trunk/src/pypy/translator Message-ID: <20040816133300.49A435A07A@thoth.codespeak.net> Author: arigo Date: Mon Aug 16 15:32:59 2004 New Revision: 5986 Modified: pypy/trunk/src/pypy/translator/annrpython.py Log: Detect unbound method calls with constant self, e.g. calls to a parent method from a space method. Modified: pypy/trunk/src/pypy/translator/annrpython.py ============================================================================== --- pypy/trunk/src/pypy/translator/annrpython.py (original) +++ pypy/trunk/src/pypy/translator/annrpython.py Mon Aug 16 15:32:59 2004 @@ -133,6 +133,16 @@ def recursivecall(self, func, factory, *args): parent_fn, parent_block, parent_index = factory.position_key + # detect unbound methods with a constant first argument + # and turn them into bound methods + if (hasattr(func, 'im_self') and func.im_self is None and + len(args) > 0 and args[0].is_constant()): + try: + func = func.__get__(args[0].const, type(args[0].const)) + except TypeError: + pass + else: + args = args[1:] graph = self.translator.getflowgraph(func, parent_fn, factory.position_key) # self.notify[graph.returnblock] is a dictionary of From arigo at codespeak.net Mon Aug 16 16:03:30 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 16 Aug 2004 16:03:30 +0200 (MEST) Subject: [pypy-svn] r5987 - in pypy/trunk/src: goal pypy/annotation pypy/translator Message-ID: <20040816140330.1B05E5A07A@thoth.codespeak.net> Author: arigo Date: Mon Aug 16 16:03:29 2004 New Revision: 5987 Modified: pypy/trunk/src/goal/translate_pypy.py pypy/trunk/src/pypy/annotation/unaryop.py pypy/trunk/src/pypy/translator/annrpython.py pypy/trunk/src/pypy/translator/translator.py Log: Got rid of the increasingly confusing constargs hacks and just build one control flow graph per function, even for methods with constant im_self. Instead, allow reading attributes from any constant object in the annotation phase. Modified: pypy/trunk/src/goal/translate_pypy.py ============================================================================== --- pypy/trunk/src/goal/translate_pypy.py (original) +++ pypy/trunk/src/goal/translate_pypy.py Mon Aug 16 16:03:29 2004 @@ -6,13 +6,12 @@ from pypy.objspace.std.objspace import StdObjSpace, W_Object from pypy.objspace.std.intobject import W_IntObject from pypy.translator.translator import Translator +from pypy.annotation import model as annmodel # __________ Entry point __________ -space = StdObjSpace() - -def entry_point(): +def entry_point(space): w_a = W_IntObject(space, -6) w_b = W_IntObject(space, -7) return space.mul(w_a, w_b) @@ -55,8 +54,9 @@ display.run() t = Translator(entry_point, verbose=True, simplifying=True) + space = StdObjSpace() try: - a = t.annotate([]) + a = t.annotate([annmodel.immutablevalue(space)]) a.simplify() except: import traceback Modified: pypy/trunk/src/pypy/annotation/unaryop.py ============================================================================== --- pypy/trunk/src/pypy/annotation/unaryop.py (original) +++ pypy/trunk/src/pypy/annotation/unaryop.py Mon Aug 16 16:03:29 2004 @@ -38,6 +38,9 @@ analyser = getattr(obj.__class__, 'method_' + attr, None) if analyser is not None: return SomeBuiltin(analyser, obj) + # if the SomeObject is itself a constant, allow reading its attrs + if obj.is_constant() and hasattr(obj.const, attr): + return immutablevalue(getattr(obj.const, attr)) return SomeObject() def classattribute(obj, classdef): Modified: pypy/trunk/src/pypy/translator/annrpython.py ============================================================================== --- pypy/trunk/src/pypy/translator/annrpython.py (original) +++ pypy/trunk/src/pypy/translator/annrpython.py Mon Aug 16 16:03:29 2004 @@ -132,17 +132,13 @@ #___ interface for annotator.factory _______ def recursivecall(self, func, factory, *args): + # calls to methods: expand the argument + if hasattr(func, 'im_func'): + if func.im_self is not None: + s_self = annmodel.immutablevalue(func.im_self) + args = [s_self] + list(args) + func = func.im_func parent_fn, parent_block, parent_index = factory.position_key - # detect unbound methods with a constant first argument - # and turn them into bound methods - if (hasattr(func, 'im_self') and func.im_self is None and - len(args) > 0 and args[0].is_constant()): - try: - func = func.__get__(args[0].const, type(args[0].const)) - except TypeError: - pass - else: - args = args[1:] graph = self.translator.getflowgraph(func, parent_fn, factory.position_key) # self.notify[graph.returnblock] is a dictionary of Modified: pypy/trunk/src/pypy/translator/translator.py ============================================================================== --- pypy/trunk/src/pypy/translator/translator.py (original) +++ pypy/trunk/src/pypy/translator/translator.py Mon Aug 16 16:03:29 2004 @@ -68,14 +68,8 @@ func.func_globals.get('__name__', '?'), func.func_code.co_firstlineno, func.__name__) - im_func = getattr(func, 'im_func', func) - im_self = getattr(func, 'im_self', None) - if im_self is not None: # bound method? - constargs = {0: im_self} - else: - constargs = {} space = FlowObjSpace() - graph = space.build_flow(im_func, constargs) + graph = space.build_flow(func) if self.simplifying: graph = simplify_graph(graph) self.flowgraphs[func] = graph From arigo at codespeak.net Mon Aug 16 18:05:13 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 16 Aug 2004 18:05:13 +0200 (MEST) Subject: [pypy-svn] r5992 - pypy/trunk/src/pypy/translator/tool/pygame Message-ID: <20040816160513.96E6A5A0B2@thoth.codespeak.net> Author: arigo Date: Mon Aug 16 18:05:13 2004 New Revision: 5992 Modified: pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py Log: Trying to make classdefs more readable. Better but still not perfect. Modified: pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py Mon Aug 16 18:05:13 2004 @@ -60,7 +60,7 @@ """ def __init__(self, translator, cdef): self.translator = translator - dotgen = DotGen(cdef.cls.__name__) + dotgen = DotGen(cdef.cls.__name__, rankdir="LR") def writecdef(cdef): dotgen.emit_node(nameof(cdef), color="red", shape="octagon", From arigo at codespeak.net Mon Aug 16 18:19:37 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 16 Aug 2004 18:19:37 +0200 (MEST) Subject: [pypy-svn] r5993 - in pypy/trunk/src/pypy/translator/tool: . pygame Message-ID: <20040816161937.4C4A95A0B2@thoth.codespeak.net> Author: arigo Date: Mon Aug 16 18:19:36 2004 New Revision: 5993 Modified: pypy/trunk/src/pypy/translator/tool/make_dot.py pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py Log: Forgot to check this in with the previous check-in. Modified: pypy/trunk/src/pypy/translator/tool/make_dot.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/make_dot.py (original) +++ pypy/trunk/src/pypy/translator/tool/make_dot.py Mon Aug 16 18:19:36 2004 @@ -11,11 +11,13 @@ class DotGen: - def __init__(self, graphname): + def __init__(self, graphname, rankdir=None): self.graphname = graphname self.lines = [] self.source = None self.emit("digraph %s {" % graphname) + if rankdir: + self.emit('rankdir="%s"' % rankdir) def generate(self, storedir=None, target='ps'): source = self.get_source() Modified: pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/drawgraph.py Mon Aug 16 18:19:36 2004 @@ -94,8 +94,11 @@ dir = -1 n = 1 while True: - x0, y0 = self.points[head] - x1, y1 = self.points[head+n*dir] + try: + x0, y0 = self.points[head] + x1, y1 = self.points[head+n*dir] + except IndexError: + return [] vx = x0-x1 vy = y0-y1 try: @@ -308,9 +311,10 @@ edgebodycmd.append(drawedgebody) points = [self.map(*xy) for xy in edge.arrowhead()] - def drawedgehead(points=points, fgcolor=fgcolor): - pygame.draw.polygon(self.screen, fgcolor, points, 0) - edgeheadcmd.append(drawedgehead) + if points: + def drawedgehead(points=points, fgcolor=fgcolor): + pygame.draw.polygon(self.screen, fgcolor, points, 0) + edgeheadcmd.append(drawedgehead) if edge.label: x, y = self.map(edge.xl, edge.yl) From arigo at codespeak.net Tue Aug 17 16:52:22 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 17 Aug 2004 16:52:22 +0200 (MEST) Subject: [pypy-svn] r6004 - in pypy/trunk/src: goal pypy/translator Message-ID: <20040817145222.BAB5C5A16A@thoth.codespeak.net> Author: arigo Date: Tue Aug 17 16:52:22 2004 New Revision: 6004 Modified: pypy/trunk/src/goal/translate_pypy.py pypy/trunk/src/pypy/translator/genpyrex.py Log: some clean-up in translate_pypy. genpyrex support for calling unbound methods. Modified: pypy/trunk/src/goal/translate_pypy.py ============================================================================== --- pypy/trunk/src/goal/translate_pypy.py (original) +++ pypy/trunk/src/goal/translate_pypy.py Tue Aug 17 16:52:22 2004 @@ -19,6 +19,14 @@ # __________ Main __________ +def analyse(entry_point=entry_point): + t = Translator(entry_point, verbose=True, simplifying=True) + space = StdObjSpace() + a = t.annotate([annmodel.immutablevalue(space)]) + a.simplify() + return t + + if __name__ == '__main__': def about(x): @@ -53,12 +61,7 @@ else: display.run() - t = Translator(entry_point, verbose=True, simplifying=True) - space = StdObjSpace() - try: - a = t.annotate([annmodel.immutablevalue(space)]) - a.simplify() - except: + def debug(): import traceback exc, val, tb = sys.exc_info() print >> sys.stderr @@ -75,11 +78,12 @@ print >> sys.stderr import pdb pdb.post_mortem(tb) + + try: + t = analyse() + print '-'*60 + t.compile() + except: + debug() else: -## print '-'*60 -## src = t.pyrex() -## g = open('code.pyx', 'w') -## g.write(src) -## print 'Wrote', g.name -## g.close() run_server() Modified: pypy/trunk/src/pypy/translator/genpyrex.py ============================================================================== --- pypy/trunk/src/pypy/translator/genpyrex.py (original) +++ pypy/trunk/src/pypy/translator/genpyrex.py Tue Aug 17 16:52:22 2004 @@ -162,6 +162,7 @@ self.ops = ops self.oparity = oparity self.annotator = None + self.namecache = {} def annotate(self, input_arg_types): a = RPythonAnnotator() @@ -280,8 +281,17 @@ return '%s__%x' % (name, id(cls))#self._hackname(cls) def getfunctionname(self,func): - assert inspect.isfunction(func) or inspect.ismethod(func) - return '%s__%x' % (func.__name__, id(func))#self._hackname(func) + # NB. the purpose of the cache is not performance, but to ensure that + # two methods that compare equal get the same name. + if inspect.ismethod(func) and func.im_self is None: + func = func.im_func # consider unbound methods as plain functions + try: + return self.namecache[func] + except KeyError: + assert inspect.isfunction(func) or inspect.ismethod(func) + name = '%s__%x' % (func.__name__, id(func))#self._hackname(func) + self.namecache[func] = name + return name def getvarname(self,var): assert inspect.isclass(var) @@ -295,7 +305,9 @@ import types if isinstance(obj.value,(types.ClassType,type)): fff=self.getclassname(obj.value) - elif isinstance(obj.value,(types.FunctionType,type)): + elif isinstance(obj.value,(types.FunctionType, + types.MethodType, + type)): fff=self.getfunctionname(obj.value) elif isinstance(obj.value, types.BuiltinFunctionType): fff=str(obj.value.__name__) @@ -389,7 +401,10 @@ list_methods=delay_methods.get(cls,[]) for py_fun in list_methods: # XXX! - fun = self.annotator.translator.flowgraphs[py_fun] + try: + fun = self.annotator.translator.flowgraphs[py_fun] + except KeyError: + continue # method present in class but never called hackedargs = ', '.join([var.name for var in fun.getargs()]) self.putline("def %s(%s):" % (py_fun.__name__, hackedargs)) self.indent += 1 From arigo at codespeak.net Tue Aug 17 17:35:05 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 17 Aug 2004 17:35:05 +0200 (MEST) Subject: [pypy-svn] r6005 - in pypy/trunk/src/pypy/objspace: . std Message-ID: <20040817153505.BA9535A6E7@thoth.codespeak.net> Author: arigo Date: Tue Aug 17 17:35:05 2004 New Revision: 6005 Modified: pypy/trunk/src/pypy/objspace/descroperation.py pypy/trunk/src/pypy/objspace/std/fake.py Log: Removed two lines in descroperation.py about the type of type(None).__repr__. To do that, fake.py now makes wrap() return a normal Function object when asked to wrap a built-in function, method, or slot_wrapper instance. The Function object's code is a fake.CPythonFakeCode instance, which just calls the CPython callable. Modified: pypy/trunk/src/pypy/objspace/descroperation.py ============================================================================== --- pypy/trunk/src/pypy/objspace/descroperation.py (original) +++ pypy/trunk/src/pypy/objspace/descroperation.py Tue Aug 17 17:35:05 2004 @@ -73,8 +73,6 @@ if w_result is not None: return w_result return descr.call_args(args.prepend(w_obj)) - elif type(descr) is type(space.wrap(SlotWrapper)): - return descr.call_args(args.prepend(w_obj)) else: w_impl = space.get(w_descr, w_obj) return space.call_args(w_impl, args) Modified: pypy/trunk/src/pypy/objspace/std/fake.py ============================================================================== --- pypy/trunk/src/pypy/objspace/std/fake.py (original) +++ pypy/trunk/src/pypy/objspace/std/fake.py Tue Aug 17 17:35:05 2004 @@ -1,4 +1,6 @@ from pypy.interpreter.error import OperationError +from pypy.interpreter import eval +from pypy.interpreter.function import Function from pypy.objspace.std.stdtypedef import * from pypy.objspace.std.objspace import W_Object, StdObjSpace from pypy.objspace.std.default import UnwrapError @@ -63,24 +65,41 @@ StdObjSpace.unwrap.register(fake_unwrap, W_Fake) W_Fake.__name__ = 'W_Fake%s'%(cpy_type.__name__.capitalize()) W_Fake.typedef.fakedcpytype = cpy_type - # XXX obviously this entire file is something of a hack, but it - # manages to get worse here: - if cpy_type is type(type(None).__repr__): - def call_args(self, args): - try: - unwrappedargs = [self.space.unwrap(w_arg) for w_arg in args.args_w] - unwrappedkwds = dict([(key, self.space.unwrap(w_value)) - for key, w_value in args.kwds_w.items()]) - except UnwrapError, e: - raise UnwrapError('calling %s: %s' % (cpy_type, e)) - try: - assert callable(self.val), self.val - result = apply(self.val, unwrappedargs, unwrappedkwds) - except: - wrap_exception(self.space) - return self.space.wrap(result) - - setattr(W_Fake, "call_args", call_args) _fake_type_cache[cpy_type] = W_Fake return W_Fake +# ____________________________________________________________ +# +# Special case for built-in functions, methods, and slot wrappers. + +class CPythonFakeCode(eval.Code): + def __init__(self, cpy_callable): + eval.Code.__init__(self, getattr(cpy_callable, '__name__', '?')) + self.cpy_callable = cpy_callable + assert callable(cpy_callable), cpy_callable + def create_frame(self, space, w_globals, closure=None): + return CPythonFakeFrame(space, self, w_globals) + def signature(self): + return [], 'args', 'kwds' + +class CPythonFakeFrame(eval.Frame): + def run(self): + fn = self.code.cpy_callable + w_args, w_kwds = self.fastlocals_w + try: + unwrappedargs = self.space.unwrap(w_args) + unwrappedkwds = self.space.unwrap(w_kwds) + except UnwrapError, e: + raise UnwrapError('calling %s: %s' % (cpy_type, e)) + try: + result = apply(fn, unwrappedargs, unwrappedkwds) + except: + wrap_exception(self.space) + return self.space.wrap(result) + +def fake_builtin_callable(space, val): + return Function(space, CPythonFakeCode(val)) + +_fake_type_cache[type(len)] = fake_builtin_callable +_fake_type_cache[type(list.append)] = fake_builtin_callable +_fake_type_cache[type(type(None).__repr__)] = fake_builtin_callable From arigo at codespeak.net Tue Aug 17 17:44:21 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 17 Aug 2004 17:44:21 +0200 (MEST) Subject: [pypy-svn] r6006 - pypy/trunk/src/pypy/objspace Message-ID: <20040817154421.A21F15A6E7@thoth.codespeak.net> Author: arigo Date: Tue Aug 17 17:44:21 2004 New Revision: 6006 Modified: pypy/trunk/src/pypy/objspace/descroperation.py Log: Die, last reference to type(type(None).__repr__), die! Modified: pypy/trunk/src/pypy/objspace/descroperation.py ============================================================================== --- pypy/trunk/src/pypy/objspace/descroperation.py (original) +++ pypy/trunk/src/pypy/objspace/descroperation.py Tue Aug 17 17:44:21 2004 @@ -52,8 +52,6 @@ def descr__init__(space, w_obj, __args__): pass # XXX some strange checking maybe -SlotWrapper = type(None).__repr__ - class DescrOperation: def getdict(space, w_obj): From arigo at codespeak.net Tue Aug 17 18:15:40 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 17 Aug 2004 18:15:40 +0200 (MEST) Subject: [pypy-svn] r6007 - pypy/trunk/src/pypy/translator/tool/pygame Message-ID: <20040817161540.9D8525A6E7@thoth.codespeak.net> Author: arigo Date: Tue Aug 17 18:15:40 2004 New Revision: 6007 Modified: pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py Log: The flow graph looks slightly more organized with this parameter. Modified: pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py Tue Aug 17 18:15:40 2004 @@ -90,6 +90,7 @@ self.translator = translator self.object_by_name = {} dotgen = DotGen('translator') + dotgen.emit('mclimit=15.0') # show the call graph functions = translator.functions From arigo at codespeak.net Tue Aug 17 18:40:17 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 17 Aug 2004 18:40:17 +0200 (MEST) Subject: [pypy-svn] r6008 - pypy/trunk/src/pypy/translator/tool/pygame Message-ID: <20040817164017.3EA875A6E7@thoth.codespeak.net> Author: arigo Date: Tue Aug 17 18:40:16 2004 New Revision: 6008 Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py Log: Left button movements left and right also zoom out and in. Modified: pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py ============================================================================== --- pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py (original) +++ pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py Tue Aug 17 18:40:16 2004 @@ -190,7 +190,7 @@ dx = event.pos[0] - dragging[0] dy = event.pos[1] - dragging[1] if event.buttons[0]: # left mouse button - self.viewer.shiftscale(1.003 ** dy) + self.viewer.shiftscale(1.003 ** (dx+dy)) else: self.viewer.shiftoffset(-2*dx, -2*dy) dragging = event.pos From arigo at codespeak.net Tue Aug 17 18:53:15 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 17 Aug 2004 18:53:15 +0200 (MEST) Subject: [pypy-svn] r6009 - pypy/trunk/src/pypy/translator Message-ID: <20040817165315.A63365A6E7@thoth.codespeak.net> Author: arigo Date: Tue Aug 17 18:53:15 2004 New Revision: 6009 Modified: pypy/trunk/src/pypy/translator/genpyrex.py pypy/trunk/src/pypy/translator/translator.py Log: A few fixes to make genpyrex write code that Pyrex accepts. Also added the return value's type in function declarations. Modified: pypy/trunk/src/pypy/translator/genpyrex.py ============================================================================== --- pypy/trunk/src/pypy/translator/genpyrex.py (original) +++ pypy/trunk/src/pypy/translator/genpyrex.py Tue Aug 17 18:53:15 2004 @@ -172,19 +172,19 @@ def setannotator(self, annotator): self.annotator = annotator - def emitcode(self): + def emitcode(self, public=True): self.blockids = {} #self.variablelocations = {} self.lines = [] self.indent = 0 - self.gen_graph() + self.gen_graph(public) return "\n".join(self.lines) def putline(self, line): for l in line.split('\n'): self.lines.append(" " * self.indent + l) - def gen_graph(self): + def gen_graph(self, public=True): fun = self.functiongraph self.entrymap = mkentrymap(fun) currentlines = self.lines @@ -204,14 +204,20 @@ function_object = self.by_the_way_the_function_was # XXX! except AttributeError: def function_object(): pass # XXX!!! - # make the function visible from the outside under its original name - args = ', '.join([var.name for var in fun.getargs()]) - self.putline("def %s(%s):" % (fun.name, args)) - self.indent += 1 - self.putline("return %s(%s)" % (self.getfunctionname(function_object), args)) - self.indent -= 1 + if public: + # make the function visible from the outside + # under its original name + args = ', '.join([var.name for var in fun.getargs()]) + self.putline("def %s(%s):" % (fun.name, args)) + self.indent += 1 + self.putline("return %s(%s)" % ( + self.getfunctionname(function_object), args)) + self.indent -= 1 # go ahead with the mandled header and body of the function - self.putline("def %s(%s):" % (self.getfunctionname(function_object), params)) + self.putline("cdef %s %s(%s):" % ( + returntypename, + self.getfunctionname(function_object), + params)) self.indent += 1 #self.putline("# %r" % self.annotations) decllines = [] @@ -408,8 +414,14 @@ hackedargs = ', '.join([var.name for var in fun.getargs()]) self.putline("def %s(%s):" % (py_fun.__name__, hackedargs)) self.indent += 1 - self.putline("return %s(%s)" % (self.getfunctionname(py_fun), - hackedargs)) + # XXX special case hack: cannot use 'return' in __init__ + if py_fun.__name__ == "__init__": + statement = "" + else: + statement = "return " + self.putline("%s%s(%s)" % (statement, + self.getfunctionname(py_fun), + hackedargs)) self.indent -= 1 empty = False if empty: Modified: pypy/trunk/src/pypy/translator/translator.py ============================================================================== --- pypy/trunk/src/pypy/translator/translator.py (original) +++ pypy/trunk/src/pypy/translator/translator.py Tue Aug 17 18:53:15 2004 @@ -165,7 +165,8 @@ self.entrypoint, ann)] for func in self.functions: if func is not self.entrypoint: - code = self.generatecode1(gencls, None, func, ann) + code = self.generatecode1(gencls, None, func, ann, + public=False) codes.append(code) else: codes = [self.generatecode1(gencls, input_arg_types, func, ann)] @@ -174,7 +175,7 @@ codes.insert(0, code) return '\n\n#_________________\n\n'.join(codes) - def generatecode1(self, gencls, input_arg_types, func, ann): + def generatecode1(self, gencls, input_arg_types, func, ann, public=True): graph = self.getflowgraph(func) g = gencls(graph) g.by_the_way_the_function_was = func # XXX @@ -182,7 +183,7 @@ ann.build_types(graph, input_arg_types, func) if ann is not None: g.setannotator(ann) - return g.emitcode() + return g.emitcode(public) def generateglobaldecl(self, gencls, func, ann): graph = self.getflowgraph(func) From arigo at codespeak.net Tue Aug 17 21:25:22 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 17 Aug 2004 21:25:22 +0200 (MEST) Subject: [pypy-svn] r6010 - in pypy/branch/pypy-genc: . objspace objspace/std translator translator/tool/pygame Message-ID: <20040817192522.1F6DC5A6E7@thoth.codespeak.net> Author: arigo Date: Tue Aug 17 21:25:17 2004 New Revision: 6010 Added: pypy/branch/pypy-genc/ - copied from r6002, pypy/trunk/src/pypy/ pypy/branch/pypy-genc/objspace/descroperation.py - copied unchanged from r6006, pypy/trunk/src/pypy/objspace/descroperation.py pypy/branch/pypy-genc/objspace/std/fake.py - copied unchanged from r6005, pypy/trunk/src/pypy/objspace/std/fake.py pypy/branch/pypy-genc/translator/autopath.py - copied unchanged from r6002, pypy/trunk/src/pypy/translator/test/autopath.py pypy/branch/pypy-genc/translator/genc.h (contents, props changed) pypy/branch/pypy-genc/translator/genc.py (contents, props changed) pypy/branch/pypy-genc/translator/genpyrex.py - copied unchanged from r6009, pypy/trunk/src/pypy/translator/genpyrex.py pypy/branch/pypy-genc/translator/tool/pygame/flowviewer.py - copied unchanged from r6007, pypy/trunk/src/pypy/translator/tool/pygame/flowviewer.py pypy/branch/pypy-genc/translator/tool/pygame/graphdisplay.py - copied unchanged from r6008, pypy/trunk/src/pypy/translator/tool/pygame/graphdisplay.py pypy/branch/pypy-genc/translator/translator.py - copied, changed from r6009, pypy/trunk/src/pypy/translator/translator.py Log: Perhaps generating Pyrex code isn't such a great idea. The great idea was to use CPython as an intermediate target. But writing C extension modules in C instead of in Pyrex is quite possibly much cleaner, because: * very basic C code is sufficient; * removing yet another intermediate step might not be a bad idea for clarity; * reference counting is easy because we know exactly when variables get out of scope: when they reach the end of a basic block and are not sent to the next block; * we don't have to work around various Pyrex restrictions (or hack Pyrex to lift them); * most importantly, the whole mess with the "class Op" in genpyrex.py can probably be completely omitted, because we can use C macros and generate just a succession of OP_XXX(a,b,c) where XXX is the operation name and a,b,c are the arguments. Mapping the operation to actual C code is done once and for all in a C header file defining these macros. The resulting C code looks much like assembler code. It's quite readable and easy to map to the original flow graph, too. Added: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- (empty file) +++ pypy/branch/pypy-genc/translator/genc.h Tue Aug 17 21:25:17 2004 @@ -0,0 +1,11 @@ + +/************************************************************/ + /*** Generic C header section ***/ + +#include + +#define FREE(x) Py_DECREF(x) + + +/************************************************************/ + /*** The rest is produced by genc.py ***/ Added: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- (empty file) +++ pypy/branch/pypy-genc/translator/genc.py Tue Aug 17 21:25:17 2004 @@ -0,0 +1,193 @@ +""" +Generate a C source file from the flowmodel. + +""" +import autopath, os +from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant +from pypy.objspace.flow.model import Block, traverse +import inspect + + +class GenC: + + def __init__(self, f, translator, modname=None): + self.f = f + self.translator = translator + self.modname = modname or translator.functions[0].__name__ + self.annotator = translator.annotator + self.namecache = {} + self.gen_source() + + def gen_source(self): + f = self.f + info = { + 'modname': self.modname, + 'exported': self.translator.functions[0].__name__, + } + # header + print >> f, C_HEADER % info + + # forward declarations + print >> f, '/* forward declarations */' + for func in self.translator.functions: + print >> f, 'static %s;' % self.cfunction_header(func) + print >> f + + # function implementation + print >> f, C_SEP + print >> f + for func in self.translator.functions: + self.gen_cfunction(func) + print >> f + + # entry point + print >> f, C_SEP + print >> f, C_ENTRYPOINT_HEADER % info + self.gen_entrypoint(self.translator.functions[0]) + print >> f, C_ENTRYPOINT_FOOTER % info + + # footer + print >> f, C_METHOD_TABLE % info + print >> f, C_FOOTER % info + + + def cfunction_name(self, func): + # NB. the purpose of the cache is not performance, but to ensure that + # two methods that compare equal get the same name. + if inspect.ismethod(func) and func.im_self is None: + func = func.im_func # consider unbound methods as plain functions + try: + return self.namecache[func] + except KeyError: + assert inspect.isfunction(func) or inspect.ismethod(func) + name = '%s__%x' % (func.__name__, id(func))#self._hackname(func) + self.namecache[func] = name + return name + + def cfunction_header(self, func): + graph = self.translator.flowgraphs[func] + args = graph.getargs() + returntypename = 'PyObject*' + l = ['PyObject* %s' % a for a in args] + return '%s %s(%s)' % (returntypename, + self.cfunction_name(func), + ', '.join(l)) + + def gen_entrypoint(self, func): + f = self.f + graph = self.translator.flowgraphs[func] + args = graph.getargs() + l = ['"' + 'O'*len(args) + '"'] + l2 = [] + for i in range(len(args)): + print >> f, '\tPyObject* a%d;' % i + l.append('&a%d' % i) + l2.append('a%d' % i) + print >> f, '\tif (!PyArg_ParseTuple(args, %s))' % ', '.join(l) + print >> f, '\t\treturn NULL;' + print >> f, '\treturn %s(%s);' % (self.cfunction_name(func), + ', '.join(l2)) + + + def gen_cfunction(self, func): + f = self.f + graph = self.translator.flowgraphs[func] + allblocks = [] + blockname = {} + def visit(n): + if isinstance(n, Block): + allblocks.append(n) + blockname[n] = 'block%d' % len(blockname) + traverse(visit, graph) + + # header + print >> f, self.cfunction_header(func) + print >> f, '{' + + # local variables + for block in allblocks: + for var in block.getvariables(): + print >> f, '\tPyObject* %s;' % var + print >> f + + # body + for block in allblocks: + print >> f, ' %s:' % blockname[block] + to_release = block.getvariables() + + # basic block operations + for op in block.operations: + l = [str(a) for a in op.args] + print >> f, '\tOP_%s(%s, %s)' % (op.opname.upper(), + ', '.join(l), + op.result) + + # exits + if block.exits: + macros = ['CASE'] * (len(block.exits)-1) + ['ELSE'] + for macro, exit in zip(macros, block.exits): + if len(block.exits) == 1: + indent = '\t' + close = '' + else: + print >> f, '\t%s(%s, %s) {' % (macro, + block.exitswitch, + exit.exitcase) + indent = '\t\t' + close = '\t}' + sourceargs = exit.args + targetargs = exit.target.inputargs + assert len(sourceargs) == len(targetargs) + # get rid of assignments of UndefinedConstant + sargs, targs = [], [] + for s,t in zip(sourceargs, targetargs): + if not isinstance(s, UndefinedConstant): + sargs.append(s) + targs.append(t) + for var in to_release: + if var not in sargs: + print >> f, '%sFREE(%s)' % (indent, var) + for s,t in zip(sargs, targs): + print >> f, '%s%s = %s;' % (indent, t, s) + print >> f, '%sgoto %s;' % (indent, + blockname[exit.target]) + print >> f, close + elif hasattr(block, 'exc_type'): + xxx_raise + else: + print >> f, '\treturn %s;' % block.inputargs[0] + print >> f + + print >> f, '}' + + +# ____________________________________________________________ + +C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read() + +C_SEP = "/************************************************************/" + +C_ENTRYPOINT_HEADER = ''' +static PyObject* c_%(exported)s(PyObject* self, PyObject* args) +{ +''' + +C_ENTRYPOINT_FOOTER = ''' +} +''' + +C_METHOD_TABLE = ''' +static PyMethodDef %(modname)sMethods[] = { +\t{"%(exported)s", (PyCFunction)c_%(exported)s, METH_VARARGS}, +\t{NULL, NULL} +}; +''' + +C_FOOTER = ''' +void init%(modname)s(void) +{ +\tPy_InitModule("%(modname)s", %(modname)sMethods); +} +''' + +# ____________________________________________________________ Copied: pypy/branch/pypy-genc/translator/translator.py (from r6009, pypy/trunk/src/pypy/translator/translator.py) ============================================================================== --- pypy/trunk/src/pypy/translator/translator.py (original) +++ pypy/branch/pypy-genc/translator/translator.py Tue Aug 17 21:25:17 2004 @@ -29,7 +29,7 @@ Try dir(test) for list of current snippets. """ -import test.autopath +import autopath from pypy.objspace.flow.model import * from pypy.annotation.model import * From arigo at codespeak.net Wed Aug 18 00:34:36 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Wed, 18 Aug 2004 00:34:36 +0200 (MEST) Subject: [pypy-svn] r6016 - pypy/trunk/src/pypy/translator Message-ID: <20040817223436.95A415A6E7@thoth.codespeak.net> Author: arigo Date: Wed Aug 18 00:34:36 2004 New Revision: 6016 Modified: pypy/trunk/src/pypy/translator/translator.py Log: Forgot to fix this for the new code in tool.pygame. Modified: pypy/trunk/src/pypy/translator/translator.py ============================================================================== --- pypy/trunk/src/pypy/translator/translator.py (original) +++ pypy/trunk/src/pypy/translator/translator.py Wed Aug 18 00:34:36 2004 @@ -103,8 +103,9 @@ def view(self, *functions): """Shows the control flow graph with annotations if computed. Requires 'dot' and pygame.""" - from pypy.translator.tool.pygame.graphviewer import GraphDisplay - GraphDisplay(self, functions).run() + from pypy.translator.tool.pygame.flowviewer import FlowGraphLayout + from pypy.translator.tool.pygame.graphdisplay import GraphDisplay + GraphDisplay(FlowGraphLayout(self, list(functions))).run() def simplify(self, func=None): """Simplifies the control flow graph (default: for all functions).""" From arigo at codespeak.net Wed Aug 18 00:59:24 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Wed, 18 Aug 2004 00:59:24 +0200 (MEST) Subject: [pypy-svn] r6017 - in pypy/branch/pypy-genc: objspace/flow translator Message-ID: <20040817225924.A34825A6E7@thoth.codespeak.net> Author: arigo Date: Wed Aug 18 00:59:24 2004 New Revision: 6017 Modified: pypy/branch/pypy-genc/objspace/flow/model.py pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py Log: Basically compiles and runs is_perfect_number(). Doesn't use annotations at this point. Modified: pypy/branch/pypy-genc/objspace/flow/model.py ============================================================================== --- pypy/branch/pypy-genc/objspace/flow/model.py (original) +++ pypy/branch/pypy-genc/objspace/flow/model.py Wed Aug 18 00:59:24 2004 @@ -57,6 +57,13 @@ result.append(op.result) return uniqueitems([w for w in result if isinstance(w, Variable)]) + def getconstants(self): + "Return all constants mentionned in this Block." + result = self.inputargs[:] + for op in self.operations: + result += op.args + return uniqueitems([w for w in result if isinstance(w, Constant)]) + def renamevariables(self, mapping): self.inputargs = [mapping.get(a, a) for a in self.inputargs] for op in self.operations: Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Wed Aug 18 00:59:24 2004 @@ -4,7 +4,26 @@ #include -#define FREE(x) Py_DECREF(x) +#define FREE(x) Py_DECREF(x); + +#define CASE(x,y) if (PyObject_RichCompareBool(x,y,Py_EQ)) +#define ELSE(x,y) assert(PyObject_RichCompareBool(x,y,Py_EQ)); + +#define OP_LT(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_LT))) goto err; +#define OP_LE(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_LT))) goto err; +#define OP_EQ(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_EQ))) goto err; +#define OP_NE(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_NE))) goto err; +#define OP_GT(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_GT))) goto err; +#define OP_GE(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_GE))) goto err; + +#define OP_IS_TRUE(x,r,err) switch (PyObject_IsTrue(x)) { \ + case 0: r=Py_False; Py_INCREF(r); break; \ + case -1: goto err; \ + default: r=Py_True; Py_INCREF(r); } + +#define OP_ADD(x,y,r,err) if (!(r=PyNumber_Add(x,y))) goto err; +#define OP_MOD(x,y,r,err) if (!(r=PyNumber_Remainder(x,y))) goto err; +#define OP_INPLACE_ADD(x,y,r,err) if (!(r=PyNumber_InPlaceAdd(x,y))) goto err; /************************************************************/ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Wed Aug 18 00:59:24 2004 @@ -4,7 +4,7 @@ """ import autopath, os from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant -from pypy.objspace.flow.model import Block, traverse +from pypy.objspace.flow.model import Block, Link, traverse import inspect @@ -15,7 +15,8 @@ self.translator = translator self.modname = modname or translator.functions[0].__name__ self.annotator = translator.annotator - self.namecache = {} + self.funcname = self.choose_func_names() + self.constname = self.choose_const_names() self.gen_source() def gen_source(self): @@ -33,6 +34,14 @@ print >> f, 'static %s;' % self.cfunction_header(func) print >> f + # constants + print >> f, '/* constants */' + consts = self.constname.values() + consts.sort() + for name in consts: + print >> f, 'static PyObject* %s;' % name + print >> f + # function implementation print >> f, C_SEP print >> f @@ -46,31 +55,59 @@ self.gen_entrypoint(self.translator.functions[0]) print >> f, C_ENTRYPOINT_FOOTER % info + # initialization of constants + print >> f, 'static void build_constants(void)' + print >> f, '{' + self.gen_constants() + print >> f, '}' + # footer print >> f, C_METHOD_TABLE % info print >> f, C_FOOTER % info - def cfunction_name(self, func): - # NB. the purpose of the cache is not performance, but to ensure that - # two methods that compare equal get the same name. - if inspect.ismethod(func) and func.im_self is None: - func = func.im_func # consider unbound methods as plain functions - try: - return self.namecache[func] - except KeyError: - assert inspect.isfunction(func) or inspect.ismethod(func) - name = '%s__%x' % (func.__name__, id(func))#self._hackname(func) - self.namecache[func] = name - return name + def choose_func_names(self): + result = {} + n = 0 + for func in self.translator.functions: + assert func not in result, '%r duplicate' % (func,) + result[func] = '%s__%d' % (func.func_name, n) + n += 1 + return result + + def choose_const_names(self): + result = {} + def see(value): + if value not in result: + if isinstance(value, int): + name = ('kint_%d' % value).replace('-','minus') + else: + name = 'k%04d' % len(result) + result[value] = name + def visit(node): + if isinstance(node, Block): + for c in node.getconstants(): + see(c.value) + elif isinstance(node, Link): + see(node.exitcase) + for graph in self.translator.flowgraphs.itervalues(): + traverse(visit, graph) + return result + + def var_or_const_name(self, v): + if isinstance(v, Variable): + return v.name + else: + return self.constname[v.value] def cfunction_header(self, func): graph = self.translator.flowgraphs[func] args = graph.getargs() + _eval = self.var_or_const_name returntypename = 'PyObject*' - l = ['PyObject* %s' % a for a in args] + l = ['PyObject* %s' % _eval(a) for a in args] return '%s %s(%s)' % (returntypename, - self.cfunction_name(func), + self.funcname[func], ', '.join(l)) def gen_entrypoint(self, func): @@ -85,13 +122,25 @@ l2.append('a%d' % i) print >> f, '\tif (!PyArg_ParseTuple(args, %s))' % ', '.join(l) print >> f, '\t\treturn NULL;' - print >> f, '\treturn %s(%s);' % (self.cfunction_name(func), + print >> f, '\treturn %s(%s);' % (self.funcname[func], ', '.join(l2)) + def gen_constants(self): + f = self.f + consts = [(name, value) for value, name in self.constname.items()] + consts.sort() + for name, value in consts: + if isinstance(value, int): + print >> f, '\t%s = PyInt_FromLong(%d);' % (name, value) + elif value is None: + print >> f, '\t%s = Py_None;' % name + else: + raise ValueError, value def gen_cfunction(self, func): f = self.f graph = self.translator.flowgraphs[func] + _eval = self.var_or_const_name allblocks = [] blockname = {} def visit(n): @@ -105,22 +154,32 @@ print >> f, '{' # local variables + input_args = {} + for var in graph.getargs(): + input_args[var] = True for block in allblocks: for var in block.getvariables(): - print >> f, '\tPyObject* %s;' % var + if var not in input_args: + print >> f, '\tPyObject* %s;' % var print >> f # body for block in allblocks: print >> f, ' %s:' % blockname[block] - to_release = block.getvariables() + to_release = block.inputargs[:] + errlabels = {} # basic block operations for op in block.operations: - l = [str(a) for a in op.args] - print >> f, '\tOP_%s(%s, %s)' % (op.opname.upper(), - ', '.join(l), - op.result) + l = [_eval(a) for a in op.args] + n = len(to_release) + errlabel = '%s_err%d' % (blockname[block], n) + errlabels[n] = errlabel + to_release.append(op.result) + print >> f, '\tOP_%s(%s, %s, %s)' % (op.opname.upper(), + ', '.join(l), + op.result, + errlabel) # exits if block.exits: @@ -130,9 +189,9 @@ indent = '\t' close = '' else: - print >> f, '\t%s(%s, %s) {' % (macro, - block.exitswitch, - exit.exitcase) + print >> f, '\t%s(%s, %s) {' % ( + macro, _eval(block.exitswitch), + self.constname[exit.exitcase]) indent = '\t\t' close = '\t}' sourceargs = exit.args @@ -144,19 +203,32 @@ if not isinstance(s, UndefinedConstant): sargs.append(s) targs.append(t) - for var in to_release: + for n in range(len(to_release)-1, -1, -1): + var = to_release[n] if var not in sargs: print >> f, '%sFREE(%s)' % (indent, var) for s,t in zip(sargs, targs): - print >> f, '%s%s = %s;' % (indent, t, s) + print >> f, '%s%s = %s;' % (indent, t, _eval(s)) print >> f, '%sgoto %s;' % (indent, blockname[exit.target]) - print >> f, close + if close: + print >> f, close elif hasattr(block, 'exc_type'): xxx_raise else: - print >> f, '\treturn %s;' % block.inputargs[0] - print >> f + print >> f, '\treturn %s;' % _eval(block.inputargs[0]) + + # error paths + reachable = False + for n in range(len(to_release)-1, -1, -1): + if reachable: + print >> f, '\tFREE(%s)' % to_release[n] + if n in errlabels: + print >> f, ' %s:' % errlabels[n] + reachable = True + if reachable: + print >> f, '\treturn NULL;' + print >> f print >> f, '}' @@ -187,6 +259,7 @@ void init%(modname)s(void) { \tPy_InitModule("%(modname)s", %(modname)sMethods); +\tbuild_constants(); } ''' From rxe at codespeak.net Wed Aug 18 01:20:50 2004 From: rxe at codespeak.net (rxe at codespeak.net) Date: Wed, 18 Aug 2004 01:20:50 +0200 (MEST) Subject: [pypy-svn] r6018 - in pypy/branch/pypy-genc/translator: . tool/pygame Message-ID: <20040817232050.92C585A6E7@thoth.codespeak.net> Author: rxe Date: Wed Aug 18 01:20:50 2004 New Revision: 6018 Modified: pypy/branch/pypy-genc/translator/tool/pygame/graphdisplay.py pypy/branch/pypy-genc/translator/translator.py Log: Some minor tweaks for translator.py for new modules graphdisplay and genc. Added escape key to graphdisplay. Modified: pypy/branch/pypy-genc/translator/tool/pygame/graphdisplay.py ============================================================================== --- pypy/branch/pypy-genc/translator/tool/pygame/graphdisplay.py (original) +++ pypy/branch/pypy-genc/translator/tool/pygame/graphdisplay.py Wed Aug 18 01:20:50 2004 @@ -212,6 +212,8 @@ if event.type == KEYDOWN: if event.key in [K_p, K_LEFT, K_BACKSPACE]: self.layout_back() + if event.key == K_ESCAPE: + break if event.type == VIDEORESIZE: # short-circuit if there are more resize events pending if pygame.event.peek([VIDEORESIZE]): Modified: pypy/branch/pypy-genc/translator/translator.py ============================================================================== --- pypy/branch/pypy-genc/translator/translator.py (original) +++ pypy/branch/pypy-genc/translator/translator.py Wed Aug 18 01:20:50 2004 @@ -29,7 +29,7 @@ Try dir(test) for list of current snippets. """ -import autopath +import test.autopath from pypy.objspace.flow.model import * from pypy.annotation.model import * @@ -37,6 +37,7 @@ from pypy.translator.simplify import simplify_graph from pypy.translator.genpyrex import GenPyrex from pypy.translator.gencl import GenCL +from pypy.translator.genc import GenC from pypy.translator.tool.buildpyxmodule import make_module_from_pyxstring from pypy.objspace.flow import FlowObjSpace @@ -103,8 +104,9 @@ def view(self, *functions): """Shows the control flow graph with annotations if computed. Requires 'dot' and pygame.""" - from pypy.translator.tool.pygame.graphviewer import GraphDisplay - GraphDisplay(self, functions).run() + from pypy.translator.tool.pygame.graphdisplay import GraphDisplay + from pypy.translator.tool.pygame.flowviewer import FlowGraphLayout + GraphDisplay(FlowGraphLayout(self)).run() def simplify(self, func=None): """Simplifies the control flow graph (default: for all functions).""" @@ -155,6 +157,18 @@ """ return self.generatecode(GenCL, input_arg_types, func) + def c(self, input_arg_types=None, func=None): + """c(self[, input_arg_types][, func]) -> C (CPython) translation + + Returns C (CPython) translation. If input_arg_types is provided, + returns type annotated translation. Subsequent calls are + not affected by this. + """ + from StringIO import StringIO + out = StringIO() + genc = GenC(out, self) + return out.getvalue() + def generatecode(self, gencls, input_arg_types, func): if input_arg_types is None: ann = self.annotator From arigo at codespeak.net Wed Aug 18 11:01:39 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Wed, 18 Aug 2004 11:01:39 +0200 (MEST) Subject: [pypy-svn] r6029 - pypy/branch/pypy-genc/translator Message-ID: <20040818090139.788A75A92A@thoth.codespeak.net> Author: arigo Date: Wed Aug 18 11:01:38 2004 New Revision: 6029 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py Log: Fix refcounting bugs. Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Wed Aug 18 11:01:38 2004 @@ -4,7 +4,8 @@ #include -#define FREE(x) Py_DECREF(x); +#define INCREF(x) Py_INCREF(x); +#define DECREF(x) Py_DECREF(x); #define CASE(x,y) if (PyObject_RichCompareBool(x,y,Py_EQ)) #define ELSE(x,y) assert(PyObject_RichCompareBool(x,y,Py_EQ)); Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Wed Aug 18 11:01:38 2004 @@ -105,7 +105,7 @@ args = graph.getargs() _eval = self.var_or_const_name returntypename = 'PyObject*' - l = ['PyObject* %s' % _eval(a) for a in args] + l = ['PyObject* %s' % a for a in args] return '%s %s(%s)' % (returntypename, self.funcname[func], ', '.join(l)) @@ -197,18 +197,15 @@ sourceargs = exit.args targetargs = exit.target.inputargs assert len(sourceargs) == len(targetargs) - # get rid of assignments of UndefinedConstant - sargs, targs = [], [] - for s,t in zip(sourceargs, targetargs): - if not isinstance(s, UndefinedConstant): - sargs.append(s) - targs.append(t) for n in range(len(to_release)-1, -1, -1): var = to_release[n] - if var not in sargs: - print >> f, '%sFREE(%s)' % (indent, var) - for s,t in zip(sargs, targs): - print >> f, '%s%s = %s;' % (indent, t, _eval(s)) + if var not in sourceargs: + print >> f, '%sDECREF(%s)' % (indent, var) + for s,t in zip(sourceargs, targetargs): + sname = _eval(s) + if isinstance(s, Constant): + print >> f, '%sINCREF(%s)' % (indent, sname) + print >> f, '%s%s = %s;' % (indent, t, sname) print >> f, '%sgoto %s;' % (indent, blockname[exit.target]) if close: @@ -222,7 +219,7 @@ reachable = False for n in range(len(to_release)-1, -1, -1): if reachable: - print >> f, '\tFREE(%s)' % to_release[n] + print >> f, '\tDECREF(%s)' % to_release[n] if n in errlabels: print >> f, ' %s:' % errlabels[n] reachable = True From alastair at codespeak.net Fri Aug 20 11:41:49 2004 From: alastair at codespeak.net (alastair at codespeak.net) Date: Fri, 20 Aug 2004 11:41:49 +0200 (MEST) Subject: [pypy-svn] r6067 - pypy/trunk/doc/funding/negotiations Message-ID: <20040820094149.6DC4B5A277@thoth.codespeak.net> Author: alastair Date: Fri Aug 20 11:41:48 2004 New Revision: 6067 Modified: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP.cpf Log: Fixed Strakt's VAT number. Modified: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP.cpf ============================================================================== --- pypy/trunk/doc/funding/negotiations/IST-2-004779-STP.cpf (original) +++ pypy/trunk/doc/funding/negotiations/IST-2-004779-STP.cpf Fri Aug 20 11:41:48 2004 @@ -416,7 +416,7 @@ PYPY 3 AB STRAKT - SE556609-2473 + 556609-2473 STRAKT Norra ?gatan 10 @@ -433,7 +433,7 @@ - 556609-2473 + SE556609247301 1100000 480000 2003 From lac at codespeak.net Fri Aug 20 20:25:38 2004 From: lac at codespeak.net (lac at codespeak.net) Date: Fri, 20 Aug 2004 20:25:38 +0200 (MEST) Subject: [pypy-svn] r6085 - pypy/trunk/doc Message-ID: <20040820182538.D9B825A26A@thoth.codespeak.net> Author: lac Date: Fri Aug 20 20:25:38 2004 New Revision: 6085 Modified: pypy/trunk/doc/cmodules.txt Log: add more infor from Raymon Hettinger about modules that already have python versions Modified: pypy/trunk/doc/cmodules.txt ============================================================================== --- pypy/trunk/doc/cmodules.txt (original) +++ pypy/trunk/doc/cmodules.txt Fri Aug 20 20:25:38 2004 @@ -39,7 +39,8 @@ cmathmodule -collectionsmodule +collectionsmodule - Rymond Hettinger has an ASPN recipe. He will update it +soon to match the final API for collections.deque(). cryptmodule @@ -51,7 +52,8 @@ imgfile -itertoolsmodule - already exists? +itertoolsmodule - the docs include pure python equivalents in a form +suitable for cut and pasting mathmodule From lac at codespeak.net Fri Aug 20 20:29:08 2004 From: lac at codespeak.net (lac at codespeak.net) Date: Fri, 20 Aug 2004 20:29:08 +0200 (MEST) Subject: [pypy-svn] r6087 - pypy/trunk/doc Message-ID: <20040820182908.AF2B55A26A@thoth.codespeak.net> Author: lac Date: Fri Aug 20 20:29:07 2004 New Revision: 6087 Modified: pypy/trunk/doc/cmodules.txt Log: Learn how to spell Raymond Modified: pypy/trunk/doc/cmodules.txt ============================================================================== --- pypy/trunk/doc/cmodules.txt (original) +++ pypy/trunk/doc/cmodules.txt Fri Aug 20 20:29:07 2004 @@ -39,7 +39,7 @@ cmathmodule -collectionsmodule - Rymond Hettinger has an ASPN recipe. He will update it +collectionsmodule - Rqymond Hettinger has an ASPN recipe. He will update it soon to match the final API for collections.deque(). cryptmodule From arigo at codespeak.net Sat Aug 21 14:09:38 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 21 Aug 2004 14:09:38 +0200 (MEST) Subject: [pypy-svn] r6099 - pypy/trunk/doc Message-ID: <20040821120938.E19905ABF4@thoth.codespeak.net> Author: arigo Date: Sat Aug 21 14:09:38 2004 New Revision: 6099 Modified: pypy/trunk/doc/cmodules.txt Log: Poor Raymond Modified: pypy/trunk/doc/cmodules.txt ============================================================================== --- pypy/trunk/doc/cmodules.txt (original) +++ pypy/trunk/doc/cmodules.txt Sat Aug 21 14:09:38 2004 @@ -39,7 +39,7 @@ cmathmodule -collectionsmodule - Rqymond Hettinger has an ASPN recipe. He will update it +collectionsmodule - Raymond Hettinger has an ASPN recipe. He will update it soon to match the final API for collections.deque(). cryptmodule From alastair at codespeak.net Fri Aug 20 10:59:56 2004 From: alastair at codespeak.net (alastair at codespeak.net) Date: Fri, 20 Aug 2004 10:59:56 +0200 (MEST) Subject: [pypy-svn] r6065 - pypy/trunk/doc/funding/negotiations Message-ID: <20040820085956.8B1A55A26A@thoth.codespeak.net> Author: alastair Date: Fri Aug 20 10:59:53 2004 New Revision: 6065 Modified: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP.cpf Log: Fixes from Strakt on their financial details. Modified: pypy/trunk/doc/funding/negotiations/IST-2-004779-STP.cpf ============================================================================== --- pypy/trunk/doc/funding/negotiations/IST-2-004779-STP.cpf (original) +++ pypy/trunk/doc/funding/negotiations/IST-2-004779-STP.cpf Fri Aug 20 10:59:53 2004 @@ -2,94 +2,94 @@ - @@ -125,198 +125,198 @@ - @@ -416,7 +416,7 @@ PYPY 3 AB STRAKT - 556609-2473 + SE556609-2473 STRAKT Norra ?gatan 10 @@ -435,7 +435,7 @@ 556609-2473 1100000 - 4800000 + 480000 2003 800000 17 @@ -561,209 +561,209 @@ - @@ -1038,134 +1038,134 @@ - @@ -1245,162 +1245,162 @@ - @@ -1615,77 +1615,77 @@ - @@ -1782,208 +1782,208 @@ - @@ -2046,103 +2046,103 @@ - @@ -2232,324 +2232,324 @@ - From rxe at codespeak.net Tue Aug 24 01:00:06 2004 From: rxe at codespeak.net (rxe at codespeak.net) Date: Tue, 24 Aug 2004 01:00:06 +0200 (MEST) Subject: [pypy-svn] r6143 - pypy/trunk/src/pypy/translator/tool Message-ID: <20040823230006.90FF65A922@thoth.codespeak.net> Author: rxe Date: Tue Aug 24 01:00:06 2004 New Revision: 6143 Added: pypy/trunk/src/pypy/translator/tool/flowtrace.py Log: See the __doc__ string. :-) Where best to check in hacks like these? Added: pypy/trunk/src/pypy/translator/tool/flowtrace.py ============================================================================== --- (empty file) +++ pypy/trunk/src/pypy/translator/tool/flowtrace.py Tue Aug 24 01:00:06 2004 @@ -0,0 +1,391 @@ +""" + +This is an experimental hack - use it at your peril! I (rxe) needed some sanity +check when (attempting) to write gencpp.py and this ended up being a useful +exercise. I doubt it much use to anyone else or the rest of the project, but it +might have some interesting possiblities later on once we have full translation. +:-) + +The idea is simple - take the basic blocks output via the flow object space and +evaluate them against the standard object space. It is effectively the same as +creating a C extension to the CPython interpreter to use the CPython API instead +of interpreting bytecodes - except in this case the we don't need to compile +anything and all space operations are emitted on the fly. One might call this a +flow interpreter - I wouldn't go that far! + +""" +import autopath +from pypy.objspace.flow import FlowObjSpace +from pypy.objspace.flow.model import traverse, Constant, Variable, Block, Link +from pypy.translator.simplify import simplify_graph +from pypy.interpreter.baseobjspace import OperationError + + +class FlowTracer(object): + + def __init__(self, flow_space, space, debug = False, trace = True): + self.space = space + self.flow_space = flow_space + self.trace = trace + self.debug = debug + + def pprint(self, v): + s = self.space.unwrap(self.space.repr(v.e_value)) + if len(s) > 30: + s = s[:27] + "..." + + if isinstance(v, Constant): + s = "Const(%s)" % s + + elif isinstance(v, Variable): + s = "%s(%s)" % (v, s) + + else: + assert False, "really?" + return s + + + def get_blocknames(self, graph): + blocknames = {} + def visit(n): + if isinstance(n, Block): + blocknames[n] = 'block%d' % len(blocknames) + + traverse(visit, graph) + return blocknames + + + def wrap_constants(self, graph): + all = [] + + def visit(n): + # Note - be careful with uniqueitems and constants, as there can + # multiple constant of the same value (XXX why?) + if isinstance(n, Block): + values = [] + values += n.inputargs + for op in n.operations: + values += op.args + + for ii in values: + all.append(ii) + + all.append(n.exitswitch) + + if isinstance(n, Link): + values = n.args + for ii in values: + all.append(ii) + + traverse(visit, graph) + for ii in all: + + if isinstance(ii, Constant) : + ii.e_value = self.space.wrap(ii.value) + + else: + assert (isinstance(ii, Variable) or ii is None) + + def wrap_linkexits(self, graph): + all = [] + + def visit(n): + if isinstance(n, Link): + all.append(n) + + traverse(visit, graph) + + for l in all: + l.exitcase = self.space.wrap(l.exitcase) + + + def execute_function(self, graph, *args_w): + + curblock = graph.startblock + assert len(curblock.inputargs) == len(args_w) + + # We add value as evaluated values during interpretation + # to variables, constants and exit switches. + # Variables are done on assignment (ie lazily) + for input, arg in zip(curblock.inputargs, args_w): + input.e_value = arg + + # Here we add value attribute with wrap + self.wrap_constants(graph) + self.wrap_linkexits(graph) + + blocknames = self.get_blocknames(graph) + last_exception = None + while True: + + if self.trace: + print 'Entering %s:' % blocknames[curblock] + print ' Input args :- %s' % (", ".join(map(self.pprint, curblock.inputargs))) + print ' Operations :-' + + for op in curblock.operations: + + # Why does op.args have a list in it? + opargs = [a.e_value for a in op.args] + if self.trace: + print ' %s = space.%s(%s)' % (op.result, + op.opname, + ", ".join(map(self.pprint, op.args))) + + if op.opname == "exception": + assert (len(opargs) == 1) + # XXX What we suppose to do with argument??? + if last_exception is not None: + res = last_exception + last_exception = None + else: + res = self.space.w_None + + elif op.opname == "simple_call": + assert (len(opargs) >= 1) + res = self.simple_call(opargs[0], *opargs[1:]) + + else: + # More special cases + spaceop = getattr(self.space, op.opname) + + if op.opname in ("newlist", "newdict", "newtuple"): + # These expect a list, not a *args + res = spaceop(opargs) + + else: + try: + res = spaceop(*opargs) + + # More special case + if op.opname == "is_true": + # Rewrap it! + res = self.space.wrap(res) + + except OperationError, exc: + last_exception = exc.w_type + res = self.space.w_None + + op.result.e_value = res + + if self.trace: + # Cases will likely not be a space object + if curblock.exits and curblock.exitswitch == op.result: + print ' %s := exit(%s)' % (op.result, op.result.e_value) + else: + print ' %s := %s' % (op.result, self.pprint(op.result)) + + # Switch to next block + if curblock.exits: + + # exits (safe code) + exit_link = None + if len(curblock.exits) == 1: + exit_link = curblock.exits[0] + + else: + exit_res = curblock.exitswitch.e_value + for link in curblock.exits: + if self.space.is_true(self.space.eq(exit_res, link.exitcase)): + exit_link = link + break + + assert exit_link is not None + + if self.trace: + print ' Exit to %s :- ' % blocknames[exit_link.target] + + sourceargs = exit_link.args + targetargs = exit_link.target.inputargs + assert len(sourceargs) == len(targetargs) + + for s, t in zip(sourceargs, targetargs): + if self.trace: + print " %s = %s" % (t, s) + + t.e_value = s.e_value + + curblock = exit_link.target + + if self.trace: + print + + elif hasattr(curblock, 'exc_type'): + raise curblock.exc_type + + else: + result = curblock.inputargs[0] + if self.trace: + print "Returning -", self.pprint(result) + + return result.e_value + + + def simple_call(self, w_func, *args_w): + + func = self.space.unwrap(w_func) + if hasattr(func, "func_code"): + graph = self.flow_space.build_flow(func) + graph = simplify_graph(graph) + if self.debug: + debug(func) + return self.execute_function(graph, *args_w) + + else: + # XXX We could try creating the flow graph by runnning another + # flow objspace under self.space. Hmmm - if only I had + # bigger computer. + + # Instead we cheat (this is great fun when it is a fake type :-)) + if self.trace: + print "WOA! Cheating!", w_func + + return self.space.call_function(w_func, *args_w) + + + def call(self, f, *args): + w = self.space.wrap + args_w = [w(ii) for ii in args] + w_func = w(f) + + res = self.simple_call(w_func, *args_w) + return self.space.unwrap(res) + + +def debug(func): + """Shows the control flow graph with annotations if computed. + Requires 'dot' and pygame.""" + from pypy.translator.tool.pygame.graphdisplay import GraphDisplay + from pypy.translator.tool.pygame.flowviewer import FlowGraphLayout + from pypy.translator.translator import Translator + t = Translator(func) + t.simplify() + #t.annotate([int]) + GraphDisplay(FlowGraphLayout(t)).run() + +def timeit(num, func, *args): + from time import time as now + start = now() + for i in xrange(num): + print func(*args) + return now() - start + +if __name__ == '__main__': + from earthenware.common import chunk + from earthenware.utils.stacktrace import init_module + init_module(True) + + from pypy.objspace.std import Space + space = Space() + + def create_std_func(app_func): + + import new + from pypy.interpreter.gateway import app2interp + from pypy.interpreter.argument import Arguments + + # Horrible hack (ame needs to start with "app_") + app_func = new.function(app_func.func_code, + app_func.func_globals, + "app_" + app_func.__name__) + + # Create our function + func_gw = app2interp(app_func) + func = func_gw.get_function(space) + w_func = space.wrap(func) + + def f(*args): + args_w = [space.wrap(ii) for ii in args] + args_ = Arguments(space, args_w) + w_result = space.call_args(w_func, args_) + return space.unwrap(w_result) + return f + + def create_flow_func(f): + flow_space = FlowObjSpace() + interpreter = FlowTracer(flow_space, space) + def func(*args): + return interpreter.call(f, *args) + return func + + def do(f, *args): + print "doing %s(%s)" % (f.__name__, ", ".join(map(str, args))) + f_flow = create_flow_func(f) + res = f_flow(*args) + f_norm = create_std_func(f) + res_norm = f_norm(*args) + assert res == res_norm + return res + + def do_flow_only(f, *args): + print "doing %s(%s)" % (f.__name__, ", ".join(map(str, args))) + f_flow = create_flow_func(f) + res = f_flow(*args) + return res + + #///////////////////////////////////////////////////////////////////////////// + + def tests(): + from pypy.translator.test import snippet + + tests = [ + (snippet.if_then_else, 1, 2, 3), + (snippet.if_then_else, 0, 2, 3), + (snippet.my_gcd, 256, 192), + (snippet.is_perfect_number, 81), + (snippet.my_bool, 1), + (snippet.my_bool, 0), + (snippet.two_plus_two,), + #(snippet.sieve_of_eratosthenes,), + (snippet.simple_func, 10), + (snippet.nested_whiles, 1, 10), + (snippet.simple_func, 10), + (snippet.builtinusage,), + (snippet.poor_man_range, 10), + (snippet.poor_man_rev_range, 10), + (snippet.simple_id, 2) , + (snippet.branch_id, 1, "k", 1.0) , + (snippet.branch_id, False, "k", 1.0) , + (snippet.builtinusage,), + (snippet.yast, [1,2,3,4,5]), + (snippet.time_waster, 5), + (snippet.half_of_n, 20), + (snippet.int_id, 20), + (snippet.greet, "world"), + (snippet.choose_last,), + #(snippet.choose_last,), XXX Why does repeating this break? + (snippet.poly_branch, 1), + (snippet.s_and, 1, 1), + (snippet.s_and, 0, 1), + (snippet.s_and, 1, 0), + (snippet.s_and, 0, 0), + (snippet.break_continue, 15), + (snippet.reverse_3, ("k", 1, 1.0)), + (snippet.finallys, ("k", 1, 1.0)), + (snippet.finallys, ("k",)), + (snippet.finallys, []), + (snippet._append_five, []), + (snippet._append_five, [1,2,3]), + ] + for ii in tests: + print do(*ii) + + tests = [ + (snippet.factorial, 4), + (snippet.factorial2, 4), + (snippet.call_five,), + (snippet.build_instance,), + (snippet.set_attr,), + (snippet.merge_setattr, 0), + (snippet.merge_setattr, 1), + # XXX These don't work from test.snippet (haven't tried anymore) + #(snippet.inheritance1,), + #(snippet.inheritance2,), + + ] + + for ii in tests: + print do_flow_only(*ii) + + tests() + From hpk at codespeak.net Wed Aug 25 20:32:02 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 25 Aug 2004 20:32:02 +0200 (MEST) Subject: [pypy-svn] r6179 - pypy/trunk/doc Message-ID: <20040825183202.0FE235A4B7@thoth.codespeak.net> Author: hpk Date: Wed Aug 25 20:32:02 2004 New Revision: 6179 Modified: pypy/trunk/doc/cmodules.txt Log: explained the context/purpose of the list of c modules some more. Modified: pypy/trunk/doc/cmodules.txt ============================================================================== --- pypy/trunk/doc/cmodules.txt (original) +++ pypy/trunk/doc/cmodules.txt Wed Aug 25 20:32:02 2004 @@ -1,5 +1,12 @@ -Anthony Baxter made us this wonderful list of C modules that we will want -to make Python versions of. Thank you Anthony. +PyPy shares a common need with other python reimplementations: +reimplementing c-coded modules in Python. The more plain +python code there is the less code needs to be ported either +Python or to the implementation language (e.g. C# for IronPython_). + +In the course of an email discussion_ Anthony Baxter came up with +a annotated list of modules and some rough categorization of +the to-be-or-has-been ported modules. The list doesn't claim +to be exhaustive and should certainly be worked on. Probably doable --------------- @@ -195,3 +202,7 @@ threadmodule timemodule + + +.. _IronPython: http://ironpython.com/ +.. _discussion: http://codespeak.net/pipermail/pypy-dev/2004q3/001509.html From lac at codespeak.net Sat Aug 28 00:25:49 2004 From: lac at codespeak.net (lac at codespeak.net) Date: Sat, 28 Aug 2004 00:25:49 +0200 (MEST) Subject: [pypy-svn] r6214 - pypy/trunk/doc Message-ID: <20040827222549.4F0295C2E8@thoth.codespeak.net> Author: lac Date: Sat Aug 28 00:25:48 2004 New Revision: 6214 Modified: pypy/trunk/doc/cmodules.txt Log: Raymond Hettinger has updated the pure python implementation of collections deque on ASPN. Modified: pypy/trunk/doc/cmodules.txt ============================================================================== --- pypy/trunk/doc/cmodules.txt (original) +++ pypy/trunk/doc/cmodules.txt Sat Aug 28 00:25:48 2004 @@ -46,8 +46,9 @@ cmathmodule -collectionsmodule - Raymond Hettinger has an ASPN recipe. He will update it -soon to match the final API for collections.deque(). +collectionsmodule - Raymond Hettinger has an ASPN recipe: + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/259179 +It is now a drop-in replacement. cryptmodule From hpk at codespeak.net Sat Aug 28 01:28:42 2004 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 28 Aug 2004 01:28:42 +0200 (MEST) Subject: [pypy-svn] r6215 - pypy/trunk/doc Message-ID: <20040827232842.2E9325C2E8@thoth.codespeak.net> Author: hpk Date: Sat Aug 28 01:28:41 2004 New Revision: 6215 Modified: pypy/trunk/doc/cmodules.txt Log: ReST/content fix Modified: pypy/trunk/doc/cmodules.txt ============================================================================== --- pypy/trunk/doc/cmodules.txt (original) +++ pypy/trunk/doc/cmodules.txt Sat Aug 28 01:28:41 2004 @@ -46,9 +46,8 @@ cmathmodule -collectionsmodule - Raymond Hettinger has an ASPN recipe: +collectionsmodule - Raymond Hettinger has an ASPN recipe (which has a 'deque' drop-in replacement): http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/259179 -It is now a drop-in replacement. cryptmodule From arigo at codespeak.net Mon Aug 30 12:55:25 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 30 Aug 2004 12:55:25 +0200 (MEST) Subject: [pypy-svn] r6218 - pypy/branch/pypy-genc/translator Message-ID: <20040830105525.DFEAD5C347@thoth.codespeak.net> Author: arigo Date: Mon Aug 30 12:55:25 2004 New Revision: 6218 Added: pypy/branch/pypy-genc/translator/typer.py Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/simplify.py Log: Generate typed C code. Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Mon Aug 30 12:55:25 2004 @@ -4,27 +4,64 @@ #include -#define INCREF(x) Py_INCREF(x); -#define DECREF(x) Py_DECREF(x); -#define CASE(x,y) if (PyObject_RichCompareBool(x,y,Py_EQ)) -#define ELSE(x,y) assert(PyObject_RichCompareBool(x,y,Py_EQ)); +/*** operations on ints ***/ -#define OP_LT(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_LT))) goto err; -#define OP_LE(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_LT))) goto err; -#define OP_EQ(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_EQ))) goto err; -#define OP_NE(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_NE))) goto err; -#define OP_GT(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_GT))) goto err; -#define OP_GE(x,y,r,err) if (!(r=PyObject_RichCompare(x,y,Py_GE))) goto err; - -#define OP_IS_TRUE(x,r,err) switch (PyObject_IsTrue(x)) { \ - case 0: r=Py_False; Py_INCREF(r); break; \ - case -1: goto err; \ - default: r=Py_True; Py_INCREF(r); } - -#define OP_ADD(x,y,r,err) if (!(r=PyNumber_Add(x,y))) goto err; -#define OP_MOD(x,y,r,err) if (!(r=PyNumber_Remainder(x,y))) goto err; -#define OP_INPLACE_ADD(x,y,r,err) if (!(r=PyNumber_InPlaceAdd(x,y))) goto err; +#define OP_LT_iii(x,y,r) r = x < y; +#define OP_LE_iii(x,y,r) r = x <= y; +#define OP_EQ_iii(x,y,r) r = x == y; +#define OP_NE_iii(x,y,r) r = x != y; +#define OP_GT_iii(x,y,r) r = x > y; +#define OP_GE_iii(x,y,r) r = x >= y; + +#define OP_IS_TRUE_ii(x,r) r = !(!x); + +#define OP_ADD_iii(x,y,r) r = x + y; +#define OP_MOD_iii(x,y,r) r = x % y; +#define OP_INPLACE_ADD_iii(x,y,r) r = x + y; + + +/*** generic operations on PyObjects ***/ + +#define op_richcmp_ooi(x,y,r,err,dir) \ + if ((r=PyObject_RichCompareBool(x,y,dir))<0) goto err; +#define OP_LT_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_LT) +#define OP_LE_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_LE) +#define OP_EQ_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_EQ) +#define OP_NE_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_NE) +#define OP_GT_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_GT) +#define OP_GE_ooi(x,y,r,err) op_richcmp_ooi(x,y,r,err, Py_GE) + +#define OP_IS_TRUE_oi(x,r,err) if ((r=PyObject_IsTrue(x))<0) goto err; +#define OP_ADD_ooo(x,y,r,err) if (!(r=PyNumber_Add(x,y))) goto err; +#define OP_MOD_ooo(x,y,r,err) if (!(r=PyNumber_Remainder(x,y))) goto err; +#define OP_INPLACE_ADD_ooo(x,y,r,err) if(!(r=PyNumber_InPlaceAdd(x,y)))goto err; + + +/*** conversions ***/ + +#define convert_io(x,r,err) if (!(r=PyInt_FromLong(x))) goto err; + + +/*** tests ***/ + +#define caseFalse_i(n, err) if (n) goto err; +#define caseTrue_i(n, err) if (!n) goto err; +#define case0_i(n, err) if (n != 0) goto err; +#define case1_i(n, err) if (n != 1) goto err; + +#define caseFalse_o(o, err) if (!(PyInt_Check(o) && !PyInt_AS_LONG(o))) goto err; +#define caseTrue_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o))) goto err; +#define case0_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o)==0)) goto err; +#define case1_o(o, err) if (!(PyInt_Check(o) && PyInt_AS_LONG(o)==1)) goto err; + + +/*** misc ***/ + +#define return_i(n) return n; +#define return_o(o) return o; +#define incref_o(o) Py_INCREF(o); +#define decref_o(o) Py_DECREF(o); /************************************************************/ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Aug 30 12:55:25 2004 @@ -2,10 +2,112 @@ Generate a C source file from the flowmodel. """ -import autopath, os +import autopath, os, re from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant from pypy.objspace.flow.model import Block, Link, traverse -import inspect +from pypy.translator.typer import LLFunction, LLOp +from pypy.annotation import model as annmodel + + +class CRepr: + "A possible representation of a flow-graph variable as C-level variables." + + def __init__(self, impl, error_retval, parse_code=None): + self.impl = impl # list [(C type, prefix for variable name)] + self.error_retval = error_retval # return value signalling an error + self.parse_code = parse_code # character(s) for PyArg_ParseTuple() + + def __repr__(self): + if hasattr(self, 'const'): + return '' % (self.const,) + else: + return '' % ' '.join(self.impl) + + +class CTypeSet: + "A (small) set of C types that typer.LLFunction can manipulate." + + R_VOID = CRepr([], error_retval='-1') + R_INT = CRepr(['int'], error_retval='-1', parse_code='i') + R_OBJECT = CRepr(['PyObject*'], error_retval='NULL', parse_code='O') + + REPR_BY_CODE = { + 'i': R_INT, + 'o': R_OBJECT, + } + + def __init__(self, bindings): + self.bindings = bindings + self.r_constants = {} + self.lloperations = {'convert': {}, 'release': {}} + self.parse_operation_templates() + + # __________ methods required by LLFunction __________ + # + # Here, we assume that every high-level type has a canonical representation + # so a high-level type is just a CRepr. + + def gethltype(self, var): + if isinstance(var, Variable): + s_value = self.bindings.get(var) or annmodel.SomeObject() + if s_value.is_constant(): + return self.constant_representation(s_value.const) + if issubclass(s_value.knowntype, int): + return self.R_INT + # fall-back + return self.R_OBJECT + if isinstance(var, Constant): + return self.constant_representation(var.value) + raise TypeError, var + + def represent(self, hltype): + return hltype.impl + + # ____________________________________________________________ + + def constant_representation(self, value): + key = type(value), value # to avoid mixing for example 0 and 0.0 + try: + return self.r_constants[key] + except KeyError: + # a constant doesn't need any C variable to be encoded + r = self.r_constants[key] = CRepr([], '-1') + # but to convert it to something more general like an int or + # a PyObject* we need to revive its value, which is done by + # new conversion operations that we define now + conv = self.lloperations['convert'] + if isinstance(value, int): + # can convert the constant to a C int + r.const = str(int(value)) + llname = '%s = ' + r.const + ';' + conv[r, self.R_INT] = llname, False + # can convert the constant to a PyObject* + llname = 'convert_io(' + r.const + ', %s, %s)' + conv[r, self.R_OBJECT] = llname, True + else: + pass # XXX not implemented + return r + + def parse_operation_templates(self): + # parse the genc.h header to figure out which macros are implemented + codes = ''.join(self.REPR_BY_CODE.keys()) + pattern = r"#define ([A-Za-z_][0-9A-Za-z_]*)_([%s]*)[(](.*?)[)]" % codes + rexp = re.compile(pattern) + for line in C_HEADER.split('\n'): + match = rexp.match(line) + if match: + opname, typecodes, formalargs = match.groups() + llname = '%s_%s' % (opname, typecodes) + sig = tuple([self.REPR_BY_CODE[code] for code in typecodes]) + can_fail = formalargs.replace(' ','').endswith(',err') + ops = self.lloperations.setdefault(opname, {}) + assert sig not in ops, llname + l = ['%s'] * (len(typecodes) + can_fail) + llname = llname + '(' + ', '.join(l) + ')' + ops.setdefault(sig, (llname, can_fail)) + + +# ____________________________________________________________ class GenC: @@ -14,9 +116,19 @@ self.f = f self.translator = translator self.modname = modname or translator.functions[0].__name__ - self.annotator = translator.annotator - self.funcname = self.choose_func_names() - self.constname = self.choose_const_names() + if translator.annotator: + bindings = translator.annotator.bindings.copy() + # force the entry point's return type to be 'PyObject*' + try: + entrypoint = translator.functions[0] + del bindings[translator.flowgraphs[entrypoint].getreturnvar()] + except KeyError: + pass + else: + bindings = {} + self.typeset = CTypeSet(bindings) + self.llfunctions = {} + self.build_llfunctions() self.gen_source() def gen_source(self): @@ -26,7 +138,7 @@ 'exported': self.translator.functions[0].__name__, } # header - print >> f, C_HEADER % info + print >> f, C_HEADER # forward declarations print >> f, '/* forward declarations */' @@ -34,14 +146,6 @@ print >> f, 'static %s;' % self.cfunction_header(func) print >> f - # constants - print >> f, '/* constants */' - consts = self.constname.values() - consts.sort() - for name in consts: - print >> f, 'static PyObject* %s;' % name - print >> f - # function implementation print >> f, C_SEP print >> f @@ -55,178 +159,98 @@ self.gen_entrypoint(self.translator.functions[0]) print >> f, C_ENTRYPOINT_FOOTER % info - # initialization of constants - print >> f, 'static void build_constants(void)' - print >> f, '{' - self.gen_constants() - print >> f, '}' - # footer print >> f, C_METHOD_TABLE % info print >> f, C_FOOTER % info - def choose_func_names(self): - result = {} + def build_llfunctions(self): n = 0 for func in self.translator.functions: - assert func not in result, '%r duplicate' % (func,) - result[func] = '%s__%d' % (func.func_name, n) + assert func not in self.llfunctions, '%r duplicate' % (func,) + self.llfunctions[func] = LLFunction( + typeset = self.typeset, + name = '%s__%d' % (func.func_name, n), + graph = self.translator.flowgraphs[func]) n += 1 - return result - - def choose_const_names(self): - result = {} - def see(value): - if value not in result: - if isinstance(value, int): - name = ('kint_%d' % value).replace('-','minus') - else: - name = 'k%04d' % len(result) - result[value] = name - def visit(node): - if isinstance(node, Block): - for c in node.getconstants(): - see(c.value) - elif isinstance(node, Link): - see(node.exitcase) - for graph in self.translator.flowgraphs.itervalues(): - traverse(visit, graph) - return result - - def var_or_const_name(self, v): - if isinstance(v, Variable): - return v.name - else: - return self.constname[v.value] def cfunction_header(self, func): - graph = self.translator.flowgraphs[func] - args = graph.getargs() - _eval = self.var_or_const_name - returntypename = 'PyObject*' - l = ['PyObject* %s' % a for a in args] - return '%s %s(%s)' % (returntypename, - self.funcname[func], + llfunc = self.llfunctions[func] + llargs, rettype = llfunc.ll_header() + l = ['%s %s' % (a.type, a.name) for a in llargs] + return '%s %s(%s)' % (rettype, + llfunc.name, ', '.join(l)) def gen_entrypoint(self, func): f = self.f - graph = self.translator.flowgraphs[func] - args = graph.getargs() - l = ['"' + 'O'*len(args) + '"'] + llfunc = self.llfunctions[func] + llargs, rettype = llfunc.ll_header() + assert rettype == 'PyObject*', rettype + l = [] l2 = [] - for i in range(len(args)): - print >> f, '\tPyObject* a%d;' % i - l.append('&a%d' % i) - l2.append('a%d' % i) + for a in llargs: + print >> f, '\t%s %s;' % (a.type, a.name) + l.append('&' + a.name) + l2.append(a.name) + formatstr = [] + for v in llfunc.graph.getargs(): + hltype = self.typeset.gethltype(v) + assert hltype.parse_code, ( + "entry point arg %s has unsupported type %s" % (v, hltype)) + formatstr.append(hltype.parse_code) + l.insert(0, '"' + ''.join(formatstr) + '"') print >> f, '\tif (!PyArg_ParseTuple(args, %s))' % ', '.join(l) print >> f, '\t\treturn NULL;' - print >> f, '\treturn %s(%s);' % (self.funcname[func], - ', '.join(l2)) - - def gen_constants(self): - f = self.f - consts = [(name, value) for value, name in self.constname.items()] - consts.sort() - for name, value in consts: - if isinstance(value, int): - print >> f, '\t%s = PyInt_FromLong(%d);' % (name, value) - elif value is None: - print >> f, '\t%s = Py_None;' % name - else: - raise ValueError, value + print >> f, '\treturn %s(%s);' % (llfunc.name, ', '.join(l2)) def gen_cfunction(self, func): f = self.f - graph = self.translator.flowgraphs[func] - _eval = self.var_or_const_name - allblocks = [] - blockname = {} - def visit(n): - if isinstance(n, Block): - allblocks.append(n) - blockname[n] = 'block%d' % len(blockname) - traverse(visit, graph) - - # header + llfunc = self.llfunctions[func] + + # print header print >> f, self.cfunction_header(func) print >> f, '{' - # local variables - input_args = {} - for var in graph.getargs(): - input_args[var] = True - for block in allblocks: - for var in block.getvariables(): - if var not in input_args: - print >> f, '\tPyObject* %s;' % var - print >> f - - # body - for block in allblocks: - print >> f, ' %s:' % blockname[block] - to_release = block.inputargs[:] - errlabels = {} - - # basic block operations - for op in block.operations: - l = [_eval(a) for a in op.args] - n = len(to_release) - errlabel = '%s_err%d' % (blockname[block], n) - errlabels[n] = errlabel - to_release.append(op.result) - print >> f, '\tOP_%s(%s, %s, %s)' % (op.opname.upper(), - ', '.join(l), - op.result, - errlabel) - - # exits - if block.exits: - macros = ['CASE'] * (len(block.exits)-1) + ['ELSE'] - for macro, exit in zip(macros, block.exits): - if len(block.exits) == 1: - indent = '\t' - close = '' - else: - print >> f, '\t%s(%s, %s) {' % ( - macro, _eval(block.exitswitch), - self.constname[exit.exitcase]) - indent = '\t\t' - close = '\t}' - sourceargs = exit.args - targetargs = exit.target.inputargs - assert len(sourceargs) == len(targetargs) - for n in range(len(to_release)-1, -1, -1): - var = to_release[n] - if var not in sourceargs: - print >> f, '%sDECREF(%s)' % (indent, var) - for s,t in zip(sourceargs, targetargs): - sname = _eval(s) - if isinstance(s, Constant): - print >> f, '%sINCREF(%s)' % (indent, sname) - print >> f, '%s%s = %s;' % (indent, t, sname) - print >> f, '%sgoto %s;' % (indent, - blockname[exit.target]) - if close: - print >> f, close - elif hasattr(block, 'exc_type'): - xxx_raise - else: - print >> f, '\treturn %s;' % _eval(block.inputargs[0]) + # generate the body of the function + body = list(llfunc.ll_body()) - # error paths - reachable = False - for n in range(len(to_release)-1, -1, -1): - if reachable: - print >> f, '\tDECREF(%s)' % to_release[n] - if n in errlabels: - print >> f, ' %s:' % errlabels[n] - reachable = True - if reachable: - print >> f, '\treturn NULL;' - print >> f + # collect and print all the local variables from the body + llargs, rettype = llfunc.ll_header() + locals = llargs[:] + for line in body: + if isinstance(line, LLOp): + locals += line.args + seen = {} + for a in llargs: + seen[a] = True + for a in locals: + if a not in seen: + print >> f, '\t%s %s;' % (a.type, a.name) + seen[a] = True + print >> f + # print the body + for line in body: + if isinstance(line, LLOp): + args = [a.name for a in line.args] + if line.errtarget: + args.append(line.errtarget) + s = line.name + if s == 'move': + print >> f, '\t%s = %s;' % (args[1], args[0]) + elif s == 'goto': + print >> f, '\tgoto %s;' % tuple(args) + elif s == 'returnerror': + v = llfunc.graph.getreturnvar() + hltype = self.typeset.gethltype(v) + print >> f, '\treturn %s;' % hltype.error_retval + else: # common case of operations + print >> f, '\t' + s % tuple(args) + elif line: # label + print >> f, ' %s:' % line + else: # empty line + print >> f print >> f, '}' @@ -238,26 +262,20 @@ C_ENTRYPOINT_HEADER = ''' static PyObject* c_%(exported)s(PyObject* self, PyObject* args) -{ -''' +{''' -C_ENTRYPOINT_FOOTER = ''' -} -''' +C_ENTRYPOINT_FOOTER = '''}''' C_METHOD_TABLE = ''' static PyMethodDef %(modname)sMethods[] = { \t{"%(exported)s", (PyCFunction)c_%(exported)s, METH_VARARGS}, \t{NULL, NULL} -}; -''' +};''' C_FOOTER = ''' void init%(modname)s(void) { \tPy_InitModule("%(modname)s", %(modname)sMethods); -\tbuild_constants(); -} -''' +}''' # ____________________________________________________________ Modified: pypy/branch/pypy-genc/translator/simplify.py ============================================================================== --- pypy/branch/pypy-genc/translator/simplify.py (original) +++ pypy/branch/pypy-genc/translator/simplify.py Mon Aug 30 12:55:25 2004 @@ -85,3 +85,19 @@ remove_implicit_exceptions(graph) join_blocks(graph) return graph + + +def remove_direct_loops(graph): + """This is useful for code generators: it ensures that no link has + common input and output variables, which could occur if a block's exit + points back directly to the same block. It allows code generators to be + simpler because they don't have to worry about overwriting input + variables when generating a sequence of assignments.""" + def visit(link): + if isinstance(link, Link) and link.prevblock is link.target: + # insert an empty block with fresh variables. + intermediate = [Variable() for a in link.args] + b = Block(intermediate) + b.closeblock(Link(intermediate, link.target)) + link.target = b + traverse(visit, graph) Added: pypy/branch/pypy-genc/translator/typer.py ============================================================================== --- (empty file) +++ pypy/branch/pypy-genc/translator/typer.py Mon Aug 30 12:55:25 2004 @@ -0,0 +1,352 @@ +""" +Graph-to-low-level transformer for C/Assembler code generators. +""" + +from __future__ import generators +from pypy.objspace.flow.model import Constant, Variable, Block, Link, traverse +from pypy.translator.simplify import remove_direct_loops + + +class LLVar: + "A variable in the low-level language." + def __init__(self, type, name): + self.type = type # low-level type in any format, e.g. a C type name + self.name = name + +class LLOp: + "A low-level operation." + def __init__(self, name, args, errtarget=None): + self.name = name # low-level operation name + self.args = args # list of LLVars + self.errtarget = errtarget # label to jump to in case of error + + +# ____________________________________________________________ +# +# below, a 'typeset' is an object with the following methods/attributes: +# +# def gethltype(self, var_or_const) +# Return the high-level type of a var or const. +# +# def represent(self, hltype) +# Return a list of LLTypes that together implement the high-level type. +# +# lloperations = { +# 'opname': { +# (tuple-of-hltypes): (low-level-name, flag-can-fail), +# ... }, +# ...} +# This dict contains the known signatures of each space operation. +# Special opnames: +# 'convert' x y : convert some x to y +# 'incref' z : get a new ref to the object z +# 'decref' z : release (or decref) the object z +# 'caseXXX' z : fails (i.e. jump to errlabel) if z is not XXX +# 'return' z : z is the return value of the function +# +# Low-level-only operation names: +# 'goto' : fails unconditionally (i.e. jump to errlabel) +# 'move' x y : raw copy of the LLVar x to the LLVar y +# 'returnerror' : return an error code from the function +# ____________________________________________________________ + + +class LLFunction: + "A low-level version of a function from a control flow graph." + + def __init__(self, typeset, name, graph): + remove_direct_loops(graph) + self.name = name + self.graph = graph + self.gethltype = typeset.gethltype + self.represent = typeset.represent + self.lloperations = typeset.lloperations + self.hltypes = {} + self.llreprs = {} + + def ll_header(self): + """ + Get the low-level representation of the header. + """ + llrepr = [] + for v in self.graph.getargs(): + self.makevar(v) + llrepr += self.llreprs[v] + v = self.graph.getreturnvar() + self.makevar(v) + llret = self.llreprs[v] + if len(llret) == 0: + retlltype = None + elif len(llret) == 1: + retlltype = llret[0].type + else: + XXX("to do") + return llrepr, retlltype + + def ll_body(self): + """ + Get the body by flattening and low-level-izing the flow graph. + Enumerates low-level operations: LLOps with labels inbetween (strings). + """ + self.blockname = {} + self.release_root = ReleaseNode(None, LLOp('returnerror', []), None) + allblocks = [] + + # collect all blocks + def visit(block): + if isinstance(block, Block): + allblocks.append(block) + self.blockname[block] = 'block%d' % len(self.blockname) + for v in block.inputargs: + self.makevar(v) + traverse(visit, self.graph) + + # generate an incref for each input argument + for v in self.graph.getargs(): + sig = (self.hltypes[v],) + try: + llname, can_fail = self.lloperations['incref'][sig] + except KeyError: + continue # ignore types with no particular incref operation + assert not can_fail + yield LLOp(llname, self.llreprs[v]) + + # generate the body of each block + for block in allblocks: + for op in self.generate_block(block): + yield op + yield '' # empty line + + # generate the code to handle errors + for op in self.release_root.error_code(): + yield op + + def generate_block(self, block): + "Generate the operations for one basic block." + self.to_release = self.release_root + for v in block.inputargs: + self.mark_release(v) + # entry point + self.blockops = [self.blockname[block]] # label + # basic block operations + for op in block.operations: + self.operation('OP_' + op.opname.upper(), list(op.args), op.result) + # exits + if block.exits: + for exit in block.exits[:-1]: + # generate | caseXXX v elselabel + # | copy output vars to next block's input vars + # | jump to next block + # | elselabel: + elselabel = '%s_not%s' % (self.blockname[block], exit.exitcase) + self.operation('case%s' % exit.exitcase, + [block.exitswitch], + errlabel = elselabel) + self.goto(exit) + self.blockops.append(elselabel) + # for the last exit, generate only the jump to next block + exit = block.exits[-1] + self.goto(exit) + + elif hasattr(block, 'exc_type'): + XXX("to do") + else: + self.operation('return', block.inputargs) + return self.blockops + + # __________ Type checking and conversion routines __________ + + def makevar(self, v, hltype=None): + "Record v in self.hltypes and self.llreprs." + if v in self.llreprs: + return + if hltype is None: + hltype = self.gethltype(v) + llrepr = [] + lltypes = self.represent(hltype) + for i, lltype in zip(range(len(lltypes)), lltypes): + if i: + suffix = '_%d' % i + else: + suffix = '' + llrepr.append(LLVar(lltype, v.name + suffix)) + self.hltypes[v] = hltype + self.llreprs[v] = llrepr + + def mark_release(self, v): + sig = (self.hltypes[v],) + try: + llname, can_fail = self.lloperations['decref'][sig] + except KeyError: + return # ignore types with no particular decref operation + assert not can_fail + llop = LLOp(llname, self.llreprs[v]) + # make a new node for the release tree + self.to_release = ReleaseNode(v, llop, self.to_release) + + def convert_from(self, hltype): + # enumerate all types that hltype can be converted to + for frm, to in self.lloperations['convert']: + if frm == hltype: + yield to + + def convert_to(self, hltype): + # enumerate all types that can be converted to hltype + for frm, to in self.lloperations['convert']: + if to == hltype: + yield frm + + def operation(self, opname, args, result=None, errlabel=None): + "Helper to build the LLOps for a single high-level operation." + # get the hltypes of the input arguments + for v in args: + self.makevar(v) + args_t = [self.hltypes[v] for v in args] + directions = [self.convert_from] * len(args) + # append the hltype of the result + if result: + self.makevar(result) + args_t.append(self.hltypes[result]) + directions.append(self.convert_to) + # enumerate possible signatures until we get a match + llsigs = self.lloperations[opname] + for sig in variants(tuple(args_t), directions): + if sig in llsigs: + llname, can_fail = llsigs[sig] + break + else: + raise TypingError, (opname, args_t) + # convert input args to temporary variables + llargs = [] + for v, v_t, s_t in zip(args, args_t, sig): + if v_t != s_t: + tmp = Variable() + self.makevar(tmp, hltype=s_t) + self.operation('convert', [v], tmp) + v = tmp + llargs += self.llreprs[v] + # generate an error label if the operation can fail + if can_fail and errlabel is None: + errlabel = self.to_release.getlabel() + # case-by-case analysis of the result variable + if result: + if args_t[-1] == sig[-1]: + # the result has the correct type + llargs += self.llreprs[result] + self.blockops.append(LLOp(llname, llargs, errlabel)) + self.mark_release(result) + else: + # the result has to be converted + tmp = Variable() + self.makevar(tmp, hltype=sig[-1]) + llargs += self.llreprs[tmp] + self.blockops.append(LLOp(llname, llargs, errlabel)) + self.mark_release(tmp) + self.operation('convert', [tmp], result) + else: + # no result variable + self.blockops.append(LLOp(llname, llargs, errlabel)) + + def goto(self, exit): + # generate the exit.args -> target.inputargs copying operations + to_release_copy = self.to_release + try: + # copy the data, performing any necessary conversion + # See also remove_direct_loops() for why we don't worry about + # the order of the operations + dontrelease = {} + for v, w in zip(exit.args, exit.target.inputargs): + self.makevar(v) + if self.hltypes[v] == self.hltypes[w]: # fast case + for x, y in zip(self.llreprs[v], self.llreprs[w]): + self.blockops.append(LLOp('move', [x, y])) + dontrelease[v] = True + else: + self.operation('convert', [v], w) + dontrelease[w] = True + # then release all the variables that go out of scope minus the + # ones that are carried over to the next block + for node in self.to_release.getbranch(): + if node.var not in dontrelease: + self.blockops.append(node.release_operation) + # finally jump to the target block + self.blockops.append(LLOp('goto', [], self.blockname[exit.target])) + finally: + self.to_release = to_release_copy + # after a call to goto() we are back to generating ops for + # other cases, so we restore the previous self.to_release. + +# ____________________________________________________________ + + +class TypingError(Exception): + pass + + +# In a function, all the variables that have to released can be organized +# in a tree in which each node is a variable: whenever this variable has +# to be released, its parent in the tree has to be release too, and its +# parent's parent and so on up to the root. +class ReleaseNode: + accessible = False + label = None + counter = 0 + + def __init__(self, var, release_operation, parent): + self.var = var + self.release_operation = release_operation + self.parent = parent + self.accessible_children = [] + + def mark_accessible(self): + if not self.accessible: + self.accessible = True + if self.parent: + self.parent.accessible_children.append(self) + self.parent.mark_accessible() + + def nextlabel(self): + while self.parent: + self = self.parent + self.counter += 1 + return 'err%d' % self.counter + + def getlabel(self): + if self.label is None: + self.mark_accessible() + self.label = self.nextlabel() + return self.label + + def getbranch(self): + while self.parent: + yield self + self = self.parent + + def error_code(self): + N = len(self.accessible_children) + for i in range(N): + if i > 0: + yield LLOp('goto', [], self.getlabel()) + node = self.accessible_children[~i] + for op in node.error_code(): + yield op + if self.label: + yield self.label + elif not N: + return + yield self.release_operation + + +def variants(args_t, directions): + # enumerate all variants of the given signature of hltypes + # XXX this can become quadratically slow for large programs because + # XXX it enumerates all conversions of the result from a constant value + if len(args_t): + for sig in variants(args_t[:-1], directions[:-1]): + yield sig + args_t[-1:] + choices_for_last_arg = list(directions[-1](args_t[-1])) + for sig in variants(args_t[:-1], directions[:-1]): + for last_arg in choices_for_last_arg: + yield sig + (last_arg,) + else: + yield () From arigo at codespeak.net Mon Aug 30 13:34:16 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 30 Aug 2004 13:34:16 +0200 (MEST) Subject: [pypy-svn] r6219 - in pypy/branch/pypy-genc/translator: . test Message-ID: <20040830113416.7BFB35C347@thoth.codespeak.net> Author: arigo Date: Mon Aug 30 13:34:15 2004 New Revision: 6219 Added: pypy/branch/pypy-genc/translator/test/test_ctrans.py - copied, changed from r6217, pypy/branch/pypy-genc/translator/test/test_pyrextrans.py Modified: pypy/branch/pypy-genc/translator/translator.py Log: Added a method ccompile() to Translator. Copied test_pyrextrans.py to test_ctrans.py. Most tests fail, but some already pass; now let's make more of them pass... Copied: pypy/branch/pypy-genc/translator/test/test_ctrans.py (from r6217, pypy/branch/pypy-genc/translator/test/test_pyrextrans.py) ============================================================================== --- pypy/branch/pypy-genc/translator/test/test_pyrextrans.py (original) +++ pypy/branch/pypy-genc/translator/test/test_ctrans.py Mon Aug 30 13:34:15 2004 @@ -1,14 +1,14 @@ import autopath from pypy.tool import testit from pypy.tool.udir import udir -from pypy.translator.genpyrex import GenPyrex +from pypy.translator.genc import GenC from pypy.objspace.flow.model import * -from pypy.translator.tool.buildpyxmodule import build_cfunc +from pypy.translator.tool.buildpyxmodule import make_module_from_c from pypy.translator.translator import Translator from pypy.translator.test import snippet -class NoTypePyrexGenTestCase(testit.IntTestCase): +class NoTypeCGenTestCase(testit.IntTestCase): def setUp(self): self.space = testit.objspace('flow') @@ -16,13 +16,8 @@ def build_cfunc(self, func): try: func = func.im_func except AttributeError: pass - - dot = testit.Options.verbose >0 and 1 or 0 - options = { - 'simplify' : 1, - 'dot' : dot, - } - return build_cfunc(func, **options) + t = Translator(func) + return t.ccompile() def test_simple_func(self): cfunc = self.build_cfunc(snippet.simple_func) @@ -101,7 +96,7 @@ spec = spec[0] # use the first type only for the tests argstypelist.append(spec) t.annotate(argstypelist) - return t.compile() + return t.ccompile() def test_set_attr(self): set_attr = self.getcompiled(snippet.set_attr) Modified: pypy/branch/pypy-genc/translator/translator.py ============================================================================== --- pypy/branch/pypy-genc/translator/translator.py (original) +++ pypy/branch/pypy-genc/translator/translator.py Mon Aug 30 13:34:15 2004 @@ -39,6 +39,7 @@ from pypy.translator.gencl import GenCL from pypy.translator.genc import GenC from pypy.translator.tool.buildpyxmodule import make_module_from_pyxstring +from pypy.translator.tool.buildpyxmodule import make_module_from_c from pypy.objspace.flow import FlowObjSpace @@ -157,12 +158,10 @@ """ return self.generatecode(GenCL, input_arg_types, func) - def c(self, input_arg_types=None, func=None): - """c(self[, input_arg_types][, func]) -> C (CPython) translation + def c(self): + """c(self) -> C (CPython) translation - Returns C (CPython) translation. If input_arg_types is provided, - returns type annotated translation. Subsequent calls are - not affected by this. + Returns C (CPython) translation. """ from StringIO import StringIO out = StringIO() @@ -217,6 +216,18 @@ mod = make_module_from_pyxstring(name, udir, pyxcode) return getattr(mod, name) + def ccompile(self): + """Returns compiled function, compiled using the C generator. + """ + from pypy.tool.udir import udir + name = self.entrypoint.func_name + cfile = udir.join('%s.c' % name) + f = cfile.open('w') + GenC(f, self, name) + f.close() + mod = make_module_from_c(cfile) + return getattr(mod, name) + def call(self, *args): """Calls underlying Python function.""" return self.entrypoint(*args) From arigo at codespeak.net Mon Aug 30 14:08:04 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 30 Aug 2004 14:08:04 +0200 (MEST) Subject: [pypy-svn] r6222 - pypy/branch/pypy-genc/translator Message-ID: <20040830120804.A78275C347@thoth.codespeak.net> Author: arigo Date: Mon Aug 30 14:08:04 2004 New Revision: 6222 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/typer.py Log: Slowly progressing... Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Mon Aug 30 14:08:04 2004 @@ -34,9 +34,12 @@ #define OP_IS_TRUE_oi(x,r,err) if ((r=PyObject_IsTrue(x))<0) goto err; #define OP_ADD_ooo(x,y,r,err) if (!(r=PyNumber_Add(x,y))) goto err; +#define OP_MUL_ooo(x,y,r,err) if (!(r=PyNumber_Mul(x,y))) goto err; #define OP_MOD_ooo(x,y,r,err) if (!(r=PyNumber_Remainder(x,y))) goto err; #define OP_INPLACE_ADD_ooo(x,y,r,err) if(!(r=PyNumber_InPlaceAdd(x,y)))goto err; +#define OP_GETITEM_ooo(x,y,r,err) if (!(r=PyObject_GetItem(x,y))) goto err; + /*** conversions ***/ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Aug 30 14:08:04 2004 @@ -24,6 +24,10 @@ return '' % ' '.join(self.impl) +class TypingError(Exception): + pass + + class CTypeSet: "A (small) set of C types that typer.LLFunction can manipulate." @@ -63,6 +67,15 @@ def represent(self, hltype): return hltype.impl + def typingerror(self, opname, hltypes): + # build operations with a variable number of argument on demand + if opname == 'OP_NEWLIST': + opnewlist = self.lloperations.setdefault('OP_NEWLIST', {}) + sig = (self.R_OBJECT,) * len(hltypes) + opnewlist[sig] = 'newlist', True + return # retry + raise TypingError((opname,) + hltypes) + # ____________________________________________________________ def constant_representation(self, value): @@ -84,8 +97,13 @@ # can convert the constant to a PyObject* llname = 'convert_io(' + r.const + ', %s, %s)' conv[r, self.R_OBJECT] = llname, True + elif value is None: + # can convert the constant to Py_None + r.const = 'Py_None' + llname = '%s = ' + r.const + ';' + conv[r, self.R_OBJECT] = llname, False else: - pass # XXX not implemented + print "// XXX not implemented: constant", key return r def parse_operation_templates(self): @@ -178,6 +196,7 @@ llfunc = self.llfunctions[func] llargs, rettype = llfunc.ll_header() l = ['%s %s' % (a.type, a.name) for a in llargs] + l = l or ['void'] return '%s %s(%s)' % (rettype, llfunc.name, ', '.join(l)) @@ -230,23 +249,40 @@ seen[a] = True print >> f + # define special cases for some operations + def print_move(x, y): + print >> f, '\t%s = %s;' % (y, x) + + def print_goto(lbl): + print >> f, '\tgoto %s;' % lbl + + def print_returnerror(): + v = llfunc.graph.getreturnvar() + hltype = self.typeset.gethltype(v) + print >> f, '\treturn %s;' % hltype.error_retval + + def print_newlist(*args): + content = args[:-2] + result = args[-2] + err = args[-1] + print >> f, '\t%s = PyList_New(%d);' % (result, len(content)) + print >> f, '\tif (!%s) goto %s;' % (result, err) + for i in range(len(content)): + print >> f, '\tPyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % ( + result, i, content[i], content[i]) + # print the body for line in body: if isinstance(line, LLOp): args = [a.name for a in line.args] if line.errtarget: args.append(line.errtarget) - s = line.name - if s == 'move': - print >> f, '\t%s = %s;' % (args[1], args[0]) - elif s == 'goto': - print >> f, '\tgoto %s;' % tuple(args) - elif s == 'returnerror': - v = llfunc.graph.getreturnvar() - hltype = self.typeset.gethltype(v) - print >> f, '\treturn %s;' % hltype.error_retval - else: # common case of operations + try: + fn = locals()['print_' + line.name] + except KeyError: # common case operations print >> f, '\t' + s % tuple(args) + else: # special case operations + fn(*args) elif line: # label print >> f, ' %s:' % line else: # empty line Modified: pypy/branch/pypy-genc/translator/typer.py ============================================================================== --- pypy/branch/pypy-genc/translator/typer.py (original) +++ pypy/branch/pypy-genc/translator/typer.py Mon Aug 30 14:08:04 2004 @@ -48,6 +48,11 @@ # 'goto' : fails unconditionally (i.e. jump to errlabel) # 'move' x y : raw copy of the LLVar x to the LLVar y # 'returnerror' : return an error code from the function +# +# def typingerror(self, opname, hltypes): +# Called when no match is found in lloperations. This function must +# either extend lloperations and return (to retry), or raise an +# exception (to stop). # ____________________________________________________________ @@ -61,6 +66,7 @@ self.gethltype = typeset.gethltype self.represent = typeset.represent self.lloperations = typeset.lloperations + self.typingerror = typeset.typingerror self.hltypes = {} self.llreprs = {} @@ -209,13 +215,18 @@ args_t.append(self.hltypes[result]) directions.append(self.convert_to) # enumerate possible signatures until we get a match - llsigs = self.lloperations[opname] + llsigs = self.lloperations.get(opname, {}) for sig in variants(tuple(args_t), directions): if sig in llsigs: llname, can_fail = llsigs[sig] break else: - raise TypingError, (opname, args_t) + self.typingerror(opname, tuple(args_t)) + # if 'typingerror' did not raise an exception, try again. + # infinite recursion here means that 'typingerror' did not + # correctly extend 'lloperations'. + self.operation(opname, args, result, errlabel) + return # convert input args to temporary variables llargs = [] for v, v_t, s_t in zip(args, args_t, sig): @@ -278,11 +289,6 @@ # ____________________________________________________________ - -class TypingError(Exception): - pass - - # In a function, all the variables that have to released can be organized # in a tree in which each node is a variable: whenever this variable has # to be released, its parent in the tree has to be release too, and its From arigo at codespeak.net Mon Aug 30 14:29:33 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 30 Aug 2004 14:29:33 +0200 (MEST) Subject: [pypy-svn] r6224 - in pypy/branch/pypy-genc/translator: . test Message-ID: <20040830122933.B52925C347@thoth.codespeak.net> Author: arigo Date: Mon Aug 30 14:29:33 2004 New Revision: 6224 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/test/test_ctrans.py Log: snippet.sieve_of_erastothenes() passing. Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Mon Aug 30 14:29:33 2004 @@ -34,16 +34,21 @@ #define OP_IS_TRUE_oi(x,r,err) if ((r=PyObject_IsTrue(x))<0) goto err; #define OP_ADD_ooo(x,y,r,err) if (!(r=PyNumber_Add(x,y))) goto err; -#define OP_MUL_ooo(x,y,r,err) if (!(r=PyNumber_Mul(x,y))) goto err; +#define OP_MUL_ooo(x,y,r,err) if (!(r=PyNumber_Multiply(x,y))) goto err; #define OP_MOD_ooo(x,y,r,err) if (!(r=PyNumber_Remainder(x,y))) goto err; #define OP_INPLACE_ADD_ooo(x,y,r,err) if(!(r=PyNumber_InPlaceAdd(x,y)))goto err; #define OP_GETITEM_ooo(x,y,r,err) if (!(r=PyObject_GetItem(x,y))) goto err; +#define OP_SETITEM_ooov(x,y,z,err) if ((PyObject_SetItem(x,y,z))<0) goto err; /*** conversions ***/ #define convert_io(x,r,err) if (!(r=PyInt_FromLong(x))) goto err; +#define convert_vo(r) r = Py_None; Py_INCREF(r); + +#define convert_oi(x,r,err) if ((r=PyInt_AsLong(x)) == -1 \ + && PyErr_Occurred()) goto err; /*** tests ***/ @@ -66,6 +71,17 @@ #define incref_o(o) Py_INCREF(o); #define decref_o(o) Py_DECREF(o); +#define OP_EXCEPTION_ov(x) /* XXX exceptions not implemented */ + +#define OP_ALLOC_AND_SET_ioo(l,o,r,err) { \ + int i; \ + if (!(r = PyList_New(l))) goto err; \ + for (i=l; --i >= 0; ) { \ + PyList_SET_ITEM(r, i, o); \ + Py_INCREF(o); \ + } \ + } + /************************************************************/ /*** The rest is produced by genc.py ***/ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Aug 30 14:29:33 2004 @@ -36,6 +36,7 @@ R_OBJECT = CRepr(['PyObject*'], error_retval='NULL', parse_code='O') REPR_BY_CODE = { + 'v': R_VOID, 'i': R_INT, 'o': R_OBJECT, } @@ -100,7 +101,7 @@ elif value is None: # can convert the constant to Py_None r.const = 'Py_None' - llname = '%s = ' + r.const + ';' + llname = 'convert_vo(%s)' conv[r, self.R_OBJECT] = llname, False else: print "// XXX not implemented: constant", key @@ -120,7 +121,10 @@ can_fail = formalargs.replace(' ','').endswith(',err') ops = self.lloperations.setdefault(opname, {}) assert sig not in ops, llname - l = ['%s'] * (len(typecodes) + can_fail) + siglen = 0 + for hltype in sig: + siglen += len(hltype.impl) + l = ['%s'] * (siglen + can_fail) llname = llname + '(' + ', '.join(l) + ')' ops.setdefault(sig, (llname, can_fail)) @@ -236,14 +240,14 @@ # collect and print all the local variables from the body llargs, rettype = llfunc.ll_header() - locals = llargs[:] + lllocals = llargs[:] for line in body: if isinstance(line, LLOp): - locals += line.args + lllocals += line.args seen = {} for a in llargs: seen[a] = True - for a in locals: + for a in lllocals: if a not in seen: print >> f, '\t%s %s;' % (a.type, a.name) seen[a] = True @@ -280,7 +284,7 @@ try: fn = locals()['print_' + line.name] except KeyError: # common case operations - print >> f, '\t' + s % tuple(args) + print >> f, '\t' + line.name % tuple(args) else: # special case operations fn(*args) elif line: # label Modified: pypy/branch/pypy-genc/translator/test/test_ctrans.py ============================================================================== --- pypy/branch/pypy-genc/translator/test/test_ctrans.py (original) +++ pypy/branch/pypy-genc/translator/test/test_ctrans.py Mon Aug 30 14:29:33 2004 @@ -17,6 +17,7 @@ try: func = func.im_func except AttributeError: pass t = Translator(func) + t.simplify() return t.ccompile() def test_simple_func(self): From arigo at codespeak.net Mon Aug 30 15:12:58 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 30 Aug 2004 15:12:58 +0200 (MEST) Subject: [pypy-svn] r6227 - pypy/branch/pypy-genc/translator Message-ID: <20040830131258.C10435C347@thoth.codespeak.net> Author: arigo Date: Mon Aug 30 15:12:58 2004 New Revision: 6227 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py Log: Constant strings. Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Mon Aug 30 15:12:58 2004 @@ -45,6 +45,7 @@ /*** conversions ***/ #define convert_io(x,r,err) if (!(r=PyInt_FromLong(x))) goto err; +#define convert_so(c,l,r,err) if (!(r=PyString_FromStringAndSize(c,l)))goto err; #define convert_vo(r) r = Py_None; Py_INCREF(r); #define convert_oi(x,r,err) if ((r=PyInt_AsLong(x)) == -1 \ Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Aug 30 15:12:58 2004 @@ -98,6 +98,11 @@ # can convert the constant to a PyObject* llname = 'convert_io(' + r.const + ', %s, %s)' conv[r, self.R_OBJECT] = llname, True + elif isinstance(value, str): + # can convert the constant to a PyObject* + llname = ('convert_so(' + c_str(value) + ', ' + + str(len(value)) + ', %s, %s)') + conv[r, self.R_OBJECT] = llname, True elif value is None: # can convert the constant to Py_None r.const = 'Py_None' @@ -128,6 +133,12 @@ llname = llname + '(' + ', '.join(l) + ')' ops.setdefault(sig, (llname, can_fail)) +def c_str(s): + "Return the C expression for the string 's'." + s = repr(s) + if s.startswith("'"): + s = '"' + s[1:-1].replace('"', r'\"') + '"' + return s # ____________________________________________________________ From arigo at codespeak.net Mon Aug 30 15:26:50 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 30 Aug 2004 15:26:50 +0200 (MEST) Subject: [pypy-svn] r6228 - pypy/branch/pypy-genc/translator Message-ID: <20040830132650.DB55D5C347@thoth.codespeak.net> Author: arigo Date: Mon Aug 30 15:26:50 2004 New Revision: 6228 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/typer.py Log: Added a few operations to genc.h. Fixed a reference counter bug in typer.py. Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Mon Aug 30 15:26:50 2004 @@ -17,6 +17,9 @@ #define OP_IS_TRUE_ii(x,r) r = !(!x); #define OP_ADD_iii(x,y,r) r = x + y; +#define OP_SUB_iii(x,y,r) r = x - y; +#define OP_MUL_iii(x,y,r) r = x * y; +#define OP_DIV_iii(x,y,r) r = x / y; #define OP_MOD_iii(x,y,r) r = x % y; #define OP_INPLACE_ADD_iii(x,y,r) r = x + y; @@ -34,7 +37,9 @@ #define OP_IS_TRUE_oi(x,r,err) if ((r=PyObject_IsTrue(x))<0) goto err; #define OP_ADD_ooo(x,y,r,err) if (!(r=PyNumber_Add(x,y))) goto err; +#define OP_SUB_ooo(x,y,r,err) if (!(r=PyNumber_Subtract(x,y))) goto err; #define OP_MUL_ooo(x,y,r,err) if (!(r=PyNumber_Multiply(x,y))) goto err; +#define OP_DIV_ooo(x,y,r,err) if (!(r=PyNumber_Divide(x,y))) goto err; #define OP_MOD_ooo(x,y,r,err) if (!(r=PyNumber_Remainder(x,y))) goto err; #define OP_INPLACE_ADD_ooo(x,y,r,err) if(!(r=PyNumber_InPlaceAdd(x,y)))goto err; Modified: pypy/branch/pypy-genc/translator/typer.py ============================================================================== --- pypy/branch/pypy-genc/translator/typer.py (original) +++ pypy/branch/pypy-genc/translator/typer.py Mon Aug 30 15:26:50 2004 @@ -265,21 +265,38 @@ # copy the data, performing any necessary conversion # See also remove_direct_loops() for why we don't worry about # the order of the operations - dontrelease = {} + escapingreferences = {} for v, w in zip(exit.args, exit.target.inputargs): self.makevar(v) if self.hltypes[v] == self.hltypes[w]: # fast case for x, y in zip(self.llreprs[v], self.llreprs[w]): self.blockops.append(LLOp('move', [x, y])) - dontrelease[v] = True + escapingreferences.setdefault(v, 0) + escapingreferences[v] += 1 else: self.operation('convert', [v], w) - dontrelease[w] = True + escapingreferences.setdefault(w, 0) + escapingreferences[w] += 1 # then release all the variables that go out of scope minus the # ones that are carried over to the next block for node in self.to_release.getbranch(): - if node.var not in dontrelease: + v = node.var + escaping = escapingreferences.get(v, 0) + if escaping == 0: # no reference escapes the current scope self.blockops.append(node.release_operation) + else: + # sometimes, a value is duplicated, so that several + # references exit to the next scope. In this case we need + # to generate increfs. + while escaping > 1: + sig = (self.hltypes[v],) + try: + llname, can_fail = self.lloperations['incref'][sig] + except KeyError: + break # no incref operation + assert not can_fail + self.blockops.append(LLOp(llname, self.llreprs[v])) + escaping -= 1 # finally jump to the target block self.blockops.append(LLOp('goto', [], self.blockname[exit.target])) finally: From arigo at codespeak.net Mon Aug 30 17:22:47 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 30 Aug 2004 17:22:47 +0200 (MEST) Subject: [pypy-svn] r6232 - pypy/branch/pypy-genc/translator Message-ID: <20040830152247.191345C347@thoth.codespeak.net> Author: arigo Date: Mon Aug 30 17:22:46 2004 New Revision: 6232 Modified: pypy/branch/pypy-genc/translator/genc.h pypy/branch/pypy-genc/translator/genc.py pypy/branch/pypy-genc/translator/typer.py Log: Progress. Modified: pypy/branch/pypy-genc/translator/genc.h ============================================================================== --- pypy/branch/pypy-genc/translator/genc.h (original) +++ pypy/branch/pypy-genc/translator/genc.h Mon Aug 30 17:22:46 2004 @@ -74,6 +74,8 @@ #define return_i(n) return n; #define return_o(o) return o; +#define returnerr_i() return -1; +#define returnerr_o() return NULL; #define incref_o(o) Py_INCREF(o); #define decref_o(o) Py_DECREF(o); Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Aug 30 17:22:46 2004 @@ -12,9 +12,9 @@ class CRepr: "A possible representation of a flow-graph variable as C-level variables." - def __init__(self, impl, error_retval, parse_code=None): + def __init__(self, impl, err_check=None, parse_code=None): self.impl = impl # list [(C type, prefix for variable name)] - self.error_retval = error_retval # return value signalling an error + self.err_check = err_check # condition to check for error return value self.parse_code = parse_code # character(s) for PyArg_ParseTuple() def __repr__(self): @@ -31,9 +31,9 @@ class CTypeSet: "A (small) set of C types that typer.LLFunction can manipulate." - R_VOID = CRepr([], error_retval='-1') - R_INT = CRepr(['int'], error_retval='-1', parse_code='i') - R_OBJECT = CRepr(['PyObject*'], error_retval='NULL', parse_code='O') + R_VOID = CRepr([]) + R_INT = CRepr(['int'], err_check='< 0', parse_code='i') + R_OBJECT = CRepr(['PyObject*'], err_check='== NULL', parse_code='O') REPR_BY_CODE = { 'v': R_VOID, @@ -41,7 +41,8 @@ 'o': R_OBJECT, } - def __init__(self, bindings): + def __init__(self, genc, bindings): + self.genc = genc self.bindings = bindings self.r_constants = {} self.lloperations = {'convert': {}, 'release': {}} @@ -73,7 +74,17 @@ if opname == 'OP_NEWLIST': opnewlist = self.lloperations.setdefault('OP_NEWLIST', {}) sig = (self.R_OBJECT,) * len(hltypes) - opnewlist[sig] = 'newlist', True + def writer(*stuff): + content = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + ls = ['if (!(%s = PyList_New(%d))) goto %s;' % ( + result, len(content), err)] + for i in range(len(content)): + ls.append('PyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % ( + result, i, content[i], content[i])) + return '\n'.join(ls) + opnewlist[sig] = writer, True return # retry raise TypingError((opname,) + hltypes) @@ -85,7 +96,15 @@ return self.r_constants[key] except KeyError: # a constant doesn't need any C variable to be encoded - r = self.r_constants[key] = CRepr([], '-1') + r = self.r_constants[key] = CRepr([]) + # returning a constant + def writer(): + return 'return 0;' + self.lloperations['return'][r,] = writer, False + def writer(): + return 'return -1;' + self.lloperations['returnerr'][r,] = writer, False + # but to convert it to something more general like an int or # a PyObject* we need to revive its value, which is done by # new conversion operations that we define now @@ -93,21 +112,51 @@ if isinstance(value, int): # can convert the constant to a C int r.const = str(int(value)) - llname = '%s = ' + r.const + ';' - conv[r, self.R_INT] = llname, False + def writer(z): + return '%s = %s;' % (z, r.const) + conv[r, self.R_INT] = writer, False # can convert the constant to a PyObject* - llname = 'convert_io(' + r.const + ', %s, %s)' - conv[r, self.R_OBJECT] = llname, True + def writer(z, err): + return 'convert_io(%s, %s, %s)' % (r.const, z, err) + conv[r, self.R_OBJECT] = writer, True elif isinstance(value, str): # can convert the constant to a PyObject* - llname = ('convert_so(' + c_str(value) + ', ' + - str(len(value)) + ', %s, %s)') - conv[r, self.R_OBJECT] = llname, True + def writer(z, err): + return 'convert_so(%s, %d, %s, %s)' % ( + (c_str(value), len(value), z, err)) + conv[r, self.R_OBJECT] = writer, True elif value is None: # can convert the constant to Py_None r.const = 'Py_None' - llname = 'convert_vo(%s)' - conv[r, self.R_OBJECT] = llname, False + def writer(z): + return 'convert_vo(%s)' % (z,) + conv[r, self.R_OBJECT] = writer, False + elif callable(value) and value in self.genc.llfunctions: + # another Python function: can be called with OP_SIMPLE_CALL + llfunc = self.genc.llfunctions[value] + ops = self.lloperations.setdefault('OP_SIMPLE_CALL', {}) + sig = [r] + for v in llfunc.graph.getargs(): + sig.append(self.gethltype(v)) + hltype = self.gethltype(llfunc.graph.getreturnvar()) + sig.append(hltype) + if len(hltype.impl) == 0: # no return value + def writer(*stuff): + args = stuff[:-1] + err = stuff[-1] + return 'if (%s(%s) < 0) goto %s;' % ( + llfunc.name, ', '.join(args), err) + elif len(hltype.impl) == 1: # one LLVar for the return value + def writer(*stuff): + args = stuff[:-2] + result = stuff[-2] + err = stuff[-1] + return ('if ((%s = %s(%s)) %s) goto %s;' % ( + result, llfunc.name, ', '.join(args), + hltype.err_check, err)) + else: + XXX("to do") + ops[tuple(sig)] = writer, True else: print "// XXX not implemented: constant", key return r @@ -120,18 +169,19 @@ for line in C_HEADER.split('\n'): match = rexp.match(line) if match: - opname, typecodes, formalargs = match.groups() - llname = '%s_%s' % (opname, typecodes) - sig = tuple([self.REPR_BY_CODE[code] for code in typecodes]) - can_fail = formalargs.replace(' ','').endswith(',err') - ops = self.lloperations.setdefault(opname, {}) - assert sig not in ops, llname - siglen = 0 - for hltype in sig: - siglen += len(hltype.impl) - l = ['%s'] * (siglen + can_fail) - llname = llname + '(' + ', '.join(l) + ')' - ops.setdefault(sig, (llname, can_fail)) + self.register_operation_template(*match.groups()) + + def register_operation_template(self, opname, typecodes, formalargs): + llname = '%s_%s' % (opname, typecodes) + sig = tuple([self.REPR_BY_CODE[code] for code in typecodes]) + can_fail = formalargs.replace(' ','').endswith(',err') + ops = self.lloperations.setdefault(opname, {}) + assert sig not in ops, llname + # the operation's low-level name is a callable that will + # produce the correct macro call + def writer(*args): + return llname + '(' + ', '.join(args) + ')' + ops.setdefault(sig, (writer, can_fail)) def c_str(s): "Return the C expression for the string 's'." @@ -151,7 +201,8 @@ self.modname = modname or translator.functions[0].__name__ if translator.annotator: bindings = translator.annotator.bindings.copy() - # force the entry point's return type to be 'PyObject*' + # for simplicity, force the entry point's return type to be + # 'PyObject*' try: entrypoint = translator.functions[0] del bindings[translator.flowgraphs[entrypoint].getreturnvar()] @@ -159,7 +210,7 @@ pass else: bindings = {} - self.typeset = CTypeSet(bindings) + self.typeset = CTypeSet(self, bindings) self.llfunctions = {} self.build_llfunctions() self.gen_source() @@ -212,7 +263,7 @@ llargs, rettype = llfunc.ll_header() l = ['%s %s' % (a.type, a.name) for a in llargs] l = l or ['void'] - return '%s %s(%s)' % (rettype, + return '%s %s(%s)' % (rettype or 'int', llfunc.name, ', '.join(l)) @@ -264,40 +315,22 @@ seen[a] = True print >> f - # define special cases for some operations - def print_move(x, y): - print >> f, '\t%s = %s;' % (y, x) - - def print_goto(lbl): - print >> f, '\tgoto %s;' % lbl - - def print_returnerror(): - v = llfunc.graph.getreturnvar() - hltype = self.typeset.gethltype(v) - print >> f, '\treturn %s;' % hltype.error_retval - - def print_newlist(*args): - content = args[:-2] - result = args[-2] - err = args[-1] - print >> f, '\t%s = PyList_New(%d);' % (result, len(content)) - print >> f, '\tif (!%s) goto %s;' % (result, err) - for i in range(len(content)): - print >> f, '\tPyList_SET_ITEM(%s, %d, %s); Py_INCREF(%s);' % ( - result, i, content[i], content[i]) - # print the body for line in body: if isinstance(line, LLOp): args = [a.name for a in line.args] if line.errtarget: args.append(line.errtarget) - try: - fn = locals()['print_' + line.name] - except KeyError: # common case operations - print >> f, '\t' + line.name % tuple(args) - else: # special case operations - fn(*args) + # line.name is actually not a string, but a callable that + # generates the C code -- unless it is a special low-level-only + # operation that comes from typer.py. + writer = line.name + if isinstance(writer, str): + writer = LL_ONLY_OPERATIONS[writer] + code = writer(*args) + for codeline in code.split('\n'): + print >> f, '\t' + codeline + elif line: # label print >> f, ' %s:' % line else: # empty line @@ -305,6 +338,11 @@ print >> f, '}' +LL_ONLY_OPERATIONS = { + 'move': lambda x,y: '%s = %s;' % (y,x), + 'goto': lambda err: 'goto %s;' % err, + } + # ____________________________________________________________ C_HEADER = open(os.path.join(autopath.this_dir, 'genc.h')).read() Modified: pypy/branch/pypy-genc/translator/typer.py ============================================================================== --- pypy/branch/pypy-genc/translator/typer.py (original) +++ pypy/branch/pypy-genc/translator/typer.py Mon Aug 30 17:22:46 2004 @@ -43,11 +43,11 @@ # 'decref' z : release (or decref) the object z # 'caseXXX' z : fails (i.e. jump to errlabel) if z is not XXX # 'return' z : z is the return value of the function +# 'returnerr' z : same, but return an error code instead of z's content # # Low-level-only operation names: # 'goto' : fails unconditionally (i.e. jump to errlabel) # 'move' x y : raw copy of the LLVar x to the LLVar y -# 'returnerror' : return an error code from the function # # def typingerror(self, opname, hltypes): # Called when no match is found in lloperations. This function must @@ -95,7 +95,12 @@ Enumerates low-level operations: LLOps with labels inbetween (strings). """ self.blockname = {} - self.release_root = ReleaseNode(None, LLOp('returnerror', []), None) + v = self.graph.getreturnvar() + self.makevar(v) + sig = (self.hltypes[v],) + llname, can_fail = self.lloperations['returnerr'][sig] + assert not can_fail + self.release_root = ReleaseNode(None, LLOp(llname, []), None) allblocks = [] # collect all blocks From arigo at codespeak.net Mon Aug 30 17:36:14 2004 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 30 Aug 2004 17:36:14 +0200 (MEST) Subject: [pypy-svn] r6233 - pypy/branch/pypy-genc/translator Message-ID: <20040830153614.080C25C347@thoth.codespeak.net> Author: arigo Date: Mon Aug 30 17:36:14 2004 New Revision: 6233 Modified: pypy/branch/pypy-genc/translator/genc.py Log: Cosmetics. Modified: pypy/branch/pypy-genc/translator/genc.py ============================================================================== --- pypy/branch/pypy-genc/translator/genc.py (original) +++ pypy/branch/pypy-genc/translator/genc.py Mon Aug 30 17:36:14 2004 @@ -19,9 +19,9 @@ def __repr__(self): if hasattr(self, 'const'): - return '' % (self.const,) + return '' % (self.const,) else: - return '' % ' '.join(self.impl) + return '' % ' + '.join(self.impl) class TypingError(Exception): @@ -97,6 +97,7 @@ except KeyError: # a constant doesn't need any C variable to be encoded r = self.r_constants[key] = CRepr([]) + r.const = value # returning a constant def writer(): return 'return 0;' @@ -111,13 +112,12 @@ conv = self.lloperations['convert'] if isinstance(value, int): # can convert the constant to a C int - r.const = str(int(value)) def writer(z): - return '%s = %s;' % (z, r.const) + return '%s = %d;' % (z, value) conv[r, self.R_INT] = writer, False # can convert the constant to a PyObject* def writer(z, err): - return 'convert_io(%s, %s, %s)' % (r.const, z, err) + return 'convert_io(%d, %s, %s)' % (value, z, err) conv[r, self.R_OBJECT] = writer, True elif isinstance(value, str): # can convert the constant to a PyObject* @@ -127,7 +127,6 @@ conv[r, self.R_OBJECT] = writer, True elif value is None: # can convert the constant to Py_None - r.const = 'Py_None' def writer(z): return 'convert_vo(%s)' % (z,) conv[r, self.R_OBJECT] = writer, False