Commits

Armin Rigo  committed 6fa4328

Write and test pieces of the final solution

  • Participants
  • Parent commits d3860e3
  • Branches generator-in-rpython

Comments (0)

Files changed (2)

File pypy/translator/generator.py

+from pypy.objspace.flow.model import Block, Link, SpaceOperation, checkgraph
+from pypy.objspace.flow.model import Variable, Constant, FunctionGraph
+from pypy.translator.unsimplify import insert_empty_startblock
+from pypy.translator.unsimplify import split_block
+from pypy.translator.simplify import eliminate_empty_blocks
+
+
+class AbstractPosition(object):
+    _immutable_ = True
+    _attrs_ = ()
+
+
+def replace_graph_with_bootstrap(graph, graph_of_body, Entry):
+    #
+    class GeneratorIterator(object):
+        graph = graph_of_body
+        def __init__(self, entry):
+            self.current = entry
+    GeneratorIterator.Entry = Entry
+    #
+    newblock = Block(graph.startblock.inputargs)
+    v_generator = Variable('generator')
+    v_entry = Variable('entry')
+    newblock.operations.append(
+        SpaceOperation('simple_call', [Constant(Entry)], v_entry))
+    assert len(graph.startblock.inputargs) == len(Entry.varnames)
+    for v, name in zip(graph.startblock.inputargs, Entry.varnames):
+        newblock.operations.append(
+            SpaceOperation('setattr', [v_entry, Constant(name), v],
+                           Variable()))
+    newblock.operations.append(
+        SpaceOperation('simple_call', [Constant(GeneratorIterator), v_entry],
+                       v_generator))
+    newblock.closeblock(Link([v_generator], graph.returnblock))
+    graph.startblock = newblock
+    return GeneratorIterator
+
+def get_variable_names(variables):
+    seen = set()
+    result = []
+    for v in variables:
+        name = v._name.strip('_')
+        while name in seen:
+            name += '_'
+        result.append('g_' + name)
+        seen.add(name)
+    return result
+
+def _insert_reads(block, varnames):
+    assert len(varnames) == len(block.inputargs)
+    v_entry1 = Variable('entry')
+    for i, name in enumerate(varnames):
+        block.operations.insert(i,
+            SpaceOperation('getattr', [v_entry1, Constant(name)],
+                           block.inputargs[i]))
+    block.inputargs = [v_entry1]
+
+def tweak_generator_body_graph(graph):
+    assert graph.startblock.operations[0].opname == 'generator_mark'
+    graph.startblock.operations.pop(0)
+    #
+    entryvarnames = get_variable_names(graph.startblock.inputargs)
+    insert_empty_startblock(None, graph)
+    _insert_reads(graph.startblock, entryvarnames)
+    #
+    class Entry(AbstractPosition):
+        block = graph.startblock
+        varnames = entryvarnames
+    mappings = [Entry]
+    #
+    for block in list(graph.iterblocks()):
+        for exit in block.exits:
+            if exit.target is graph.returnblock:
+                exit.args = [Constant(StopIteration),
+                             Constant(StopIteration())]
+                exit.target = graph.exceptblock
+        for index in range(len(block.operations)-1, -1, -1):
+            op = block.operations[index]
+            if op.opname == 'yield':
+                [v_yielded_value] = op.args
+                del block.operations[index]
+                newlink = split_block(None, block, index)
+                newblock = newlink.target
+                #
+                class Resume(AbstractPosition):
+                    block = newblock
+                Resume.__name__ = 'Resume%d' % len(mappings)
+                mappings.append(Resume)
+                varnames = get_variable_names(newlink.args)
+                #
+                _insert_reads(newblock, varnames)
+                #
+                v_resume = Variable('resume')
+                block.operations.append(
+                    SpaceOperation('simple_call', [Constant(Resume)],
+                                   v_resume))
+                for i, name in enumerate(varnames):
+                    block.operations.append(
+                        SpaceOperation('setattr', [v_resume, Constant(name),
+                                                   newlink.args[i]],
+                                       Variable()))
+                v_pair = Variable('pair')
+                block.operations.append(
+                    SpaceOperation('newtuple', [v_resume, v_yielded_value],
+                                   v_pair))
+                newlink.args = [v_pair]
+                newlink.target = graph.returnblock
+    #
+    regular_entry_block = Block([Variable('entry')])
+    block = regular_entry_block
+    for Resume in mappings:
+        v_check = Variable()
+        block.operations.append(
+            SpaceOperation('simple_call', [Constant(isinstance),
+                                           block.inputargs[0],
+                                           Constant(Resume)],
+                           v_check))
+        block.exitswitch = v_check
+        link1 = Link([block.inputargs[0]], Resume.block)
+        link1.exitcase = True
+        nextblock = Block([Variable('entry')])
+        link2 = Link([block.inputargs[0]], nextblock)
+        link2.exitcase = False
+        block.closeblock(link1, link2)
+        block = nextblock
+    block.closeblock(Link([Constant(AssertionError),
+                           Constant(AssertionError("bad generator class"))],
+                          graph.exceptblock))
+    graph.startblock = regular_entry_block
+    checkgraph(graph)
+    eliminate_empty_blocks(graph)
+    try:
+        graph.func._always_inline_ = True
+    except AttributeError:
+        pass
+    return Entry

File pypy/translator/test/test_generator.py

 from pypy.translator.translator import TranslationContext
 from pypy.translator.generator import replace_graph_with_bootstrap
 from pypy.translator.generator import get_variable_names
+from pypy.translator.generator import tweak_generator_body_graph
 
 
 # ____________________________________________________________
         space = FlowObjSpace()
         graph = space.build_flow(func)
         assert graph.startblock.operations[0].opname == 'generator_mark'
-        replace_graph_with_bootstrap(graph, 'newgraph')
+        class Entry:
+            varnames = ['g_n', 'g_x', 'g_y', 'g_z']
+        replace_graph_with_bootstrap(graph, 'newgraph', Entry)
         if option.view:
             graph.show()
         block = graph.startblock
         ops = block.operations
-        assert ops[0].opname == 'call'      # e = Entry1()
-        assert ops[1].opname == 'setattr'   # e.g_n = n
+        assert ops[0].opname == 'simple_call' # e = Entry1()
+        assert ops[1].opname == 'setattr'     # e.g_n = n
         assert ops[1].args[1].value == 'g_n'
-        assert ops[2].opname == 'setattr'   # e.g_x = x
+        assert ops[2].opname == 'setattr'     # e.g_x = x
         assert ops[2].args[1].value == 'g_x'
-        assert ops[3].opname == 'setattr'   # e.g_y = y
+        assert ops[3].opname == 'setattr'     # e.g_y = y
         assert ops[3].args[1].value == 'g_y'
-        assert ops[4].opname == 'setattr'   # e.g_z = z
+        assert ops[4].opname == 'setattr'     # e.g_z = z
         assert ops[4].args[1].value == 'g_z'
-        assert ops[5].opname == 'call'      # g = GeneratorIterator(e)
+        assert ops[5].opname == 'simple_call' # g = GeneratorIterator(e)
         assert ops[5].args[1] == ops[0].result
         assert len(ops) == 6
         assert len(block.exits) == 1
         assert block.exits[0].target is graph.returnblock
 
-    def test_make_generator_body_graph(self):
+    def test_tweak_generator_body_graph(self):
         def f(n, x, y, z):
             z *= 10
-            yield n
+            yield n + 1
             z -= 10
         #
-        def f__next(generator):
-            n = generator.n_0
-            x = generator.x_0
-            y = generator.y_0
-            z = generator.z_0
-            e = generator.current
-            generator.current = None
-            if isinstance(e, "some class"):
-                xxx
-        #
         space = FlowObjSpace()
-        graph = space.build_flow(func)
-        newgraph = make_generator_body_graph(graph)
-        assert len(newgraph.startblock.inputargs) == 1
-        [v_generator] = newgraph.startblock.inputargs
-        ops = newgraph.startblock.operations
-        assert ops[0].opname == 'getattr'   # n = g.n_0
-        assert ops[0].args[0] == v_generator
-        assert ops[0].args[1].value.startswith('n_')
-        assert ops[1].opname == 'getattr'   # x = g.x_0
-        assert ops[1].args[0] == v_generator
-        assert ops[1].args[1].value.startswith('x_')
-        assert ops[2].opname == 'getattr'   # y = g.y_0
-        assert ops[2].args[0] == v_generator
-        assert ops[2].args[1].value.startswith('y_')
-        assert ops[3].opname == 'getattr'   # z = g.z_0
-        assert ops[3].args[0] == v_generator
-        assert ops[3].args[1].value.startswith('z_')
-        assert ops[4].opname == 'getattr'   # e = g.current
-        assert ops[4].args[0] == v_generator
-        assert ops[4].args[1].value == 'current'
-        assert ops[5].opname == 'setattr'   # g.current = None
-        assert ops[5].args[0] == v_generator
-        assert ops[5].args[1].value == 'current'
-        assert ops[6].opname == 'call'      # isinstance(e, Yield1)
-        assert ops[6].args[0].value == isinstance
-        assert len(ops) == 7
+        graph = space.build_flow(f)
+        tweak_generator_body_graph(graph)
+        if option.view:
+            graph.show()
+        # XXX how to test directly that the graph is correct?  :-(