Commit 12613c95 authored by Jeremy Hylton's avatar Jeremy Hylton

the previous quick hack to fix def foo((x,y)) failed on some cases

(big surprise).  new solution is a little less hackish.

Code gen adds a TupleArg instance in the argument slot. The tuple arg
includes a copy of the names that it is responsble for binding.  The
PyAssembler uses this information to calculate the correct argcount.

all fix this wacky case: del (a, ((b,), c)), d
which is the same as: del a, b, c, d
(Can't wait for Guido to tell me why.)

solution uses findOp which walks a tree to find out whether it
contains OP_ASSIGN or OP_DELETE or ...
parent d5b86875
......@@ -42,6 +42,15 @@ CO_NEWLOCALS = 0x0002
CO_VARARGS = 0x0004
CO_VARKEYWORDS = 0x0008
class TupleArg:
def __init__(self, count, names):
self.count = count
self.names = names
def __repr__(self):
return "TupleArg(%s, %s)" % (self.count, self.names)
def getName(self):
return ".nested%d" % self.count
class PyAssembler:
"""Creates Python code objects
"""
......@@ -54,6 +63,7 @@ class PyAssembler:
self.insts = []
# used by makeCodeObject
self._getArgCount(args)
print name, args, self.argcount
self.code = ''
self.consts = [docstring]
self.filename = filename
......@@ -61,6 +71,10 @@ class PyAssembler:
self.name = name
self.names = []
self.varnames = list(args) or []
for i in range(len(self.varnames)):
var = self.varnames[i]
if isinstance(var, TupleArg):
self.varnames[i] = var.getName()
# lnotab support
self.firstlineno = 0
self.lastlineno = 0
......@@ -68,14 +82,12 @@ class PyAssembler:
self.lnotab = ''
def _getArgCount(self, args):
if args and args[0][0] == '.':
for i in range(len(args)):
if args[i][0] == '.':
num = i
self.argcount = num + 1
else:
self.argcount = len(args)
if args:
for arg in args:
if isinstance(arg, TupleArg):
numNames = len(misc.flatten(arg.names))
self.argcount = self.argcount - numNames
def __repr__(self):
return "<bytecode: %d instrs>" % len(self.insts)
......@@ -88,7 +100,9 @@ class PyAssembler:
self.flags = self.flags | CO_OPTIMIZED
def setVarArgs(self):
if not self.flags & CO_VARARGS:
self.flags = self.flags | CO_VARARGS
self.argcount = self.argcount - 1
def setKWArgs(self):
self.flags = self.flags | CO_VARKEYWORDS
......
......@@ -6,7 +6,7 @@ a generic tool and CodeGenerator as a specific tool.
"""
from p2c import transformer, ast
from pyassem import StackRef, PyAssembler
from pyassem import StackRef, PyAssembler, TupleArg
import dis
import misc
import marshal
......@@ -203,7 +203,7 @@ class CodeGenerator:
if type(elt) == types.StringType:
args.append(elt)
elif type(elt) == types.TupleType:
args.append(".nested%d" % count)
args.append(TupleArg(count, elt))
count = count + 1
extra.extend(misc.flatten(elt))
else:
......@@ -343,7 +343,6 @@ class CodeGenerator:
def visitLambda(self, node):
node.name = '<lambda>'
node.varargs = node.kwargs = None
self._visitFuncOrLambda(node, 'Lambda')
return 1
......@@ -633,10 +632,13 @@ class CodeGenerator:
return 1
def visitAssName(self, node):
# XXX handle OP_DELETE
if node.flags != 'OP_ASSIGN':
print "oops", node.flags
if node.flags == 'OP_ASSIGN':
self.storeName(node.name)
elif node.flags == 'OP_DELETE':
self.delName(node.name)
else:
print "oops", node.flags
return 1
def visitAssAttr(self, node):
self.visit(node.expr)
......@@ -650,6 +652,7 @@ class CodeGenerator:
return 1
def visitAssTuple(self, node):
if findOp(node) != 'OP_DELETE':
self.emit('UNPACK_TUPLE', len(node.nodes))
for child in node.nodes:
self.visit(child)
......@@ -838,6 +841,7 @@ class CodeGenerator:
else:
self.visit(node.globals)
self.emit('EXEC_STMT')
return 1
class LocalNameFinder:
def __init__(self, names=()):
......@@ -882,6 +886,20 @@ class LocalNameFinder:
def visitAssName(self, node):
self.names.add(node.name)
class OpFinder:
def __init__(self):
self.op = None
def visitAssName(self, node):
if self.op is None:
self.op = node.flags
elif self.op != node.flags:
raise ValueError, "mixed ops in stmt"
def findOp(node):
v = OpFinder()
walk(node, v)
return v.op
class Loop:
def __init__(self):
self.startAnchor = StackRef()
......
......@@ -42,6 +42,15 @@ CO_NEWLOCALS = 0x0002
CO_VARARGS = 0x0004
CO_VARKEYWORDS = 0x0008
class TupleArg:
def __init__(self, count, names):
self.count = count
self.names = names
def __repr__(self):
return "TupleArg(%s, %s)" % (self.count, self.names)
def getName(self):
return ".nested%d" % self.count
class PyAssembler:
"""Creates Python code objects
"""
......@@ -54,6 +63,7 @@ class PyAssembler:
self.insts = []
# used by makeCodeObject
self._getArgCount(args)
print name, args, self.argcount
self.code = ''
self.consts = [docstring]
self.filename = filename
......@@ -61,6 +71,10 @@ class PyAssembler:
self.name = name
self.names = []
self.varnames = list(args) or []
for i in range(len(self.varnames)):
var = self.varnames[i]
if isinstance(var, TupleArg):
self.varnames[i] = var.getName()
# lnotab support
self.firstlineno = 0
self.lastlineno = 0
......@@ -68,14 +82,12 @@ class PyAssembler:
self.lnotab = ''
def _getArgCount(self, args):
if args and args[0][0] == '.':
for i in range(len(args)):
if args[i][0] == '.':
num = i
self.argcount = num + 1
else:
self.argcount = len(args)
if args:
for arg in args:
if isinstance(arg, TupleArg):
numNames = len(misc.flatten(arg.names))
self.argcount = self.argcount - numNames
def __repr__(self):
return "<bytecode: %d instrs>" % len(self.insts)
......@@ -88,7 +100,9 @@ class PyAssembler:
self.flags = self.flags | CO_OPTIMIZED
def setVarArgs(self):
if not self.flags & CO_VARARGS:
self.flags = self.flags | CO_VARARGS
self.argcount = self.argcount - 1
def setKWArgs(self):
self.flags = self.flags | CO_VARKEYWORDS
......
......@@ -6,7 +6,7 @@ a generic tool and CodeGenerator as a specific tool.
"""
from p2c import transformer, ast
from pyassem import StackRef, PyAssembler
from pyassem import StackRef, PyAssembler, TupleArg
import dis
import misc
import marshal
......@@ -203,7 +203,7 @@ class CodeGenerator:
if type(elt) == types.StringType:
args.append(elt)
elif type(elt) == types.TupleType:
args.append(".nested%d" % count)
args.append(TupleArg(count, elt))
count = count + 1
extra.extend(misc.flatten(elt))
else:
......@@ -343,7 +343,6 @@ class CodeGenerator:
def visitLambda(self, node):
node.name = '<lambda>'
node.varargs = node.kwargs = None
self._visitFuncOrLambda(node, 'Lambda')
return 1
......@@ -633,10 +632,13 @@ class CodeGenerator:
return 1
def visitAssName(self, node):
# XXX handle OP_DELETE
if node.flags != 'OP_ASSIGN':
print "oops", node.flags
if node.flags == 'OP_ASSIGN':
self.storeName(node.name)
elif node.flags == 'OP_DELETE':
self.delName(node.name)
else:
print "oops", node.flags
return 1
def visitAssAttr(self, node):
self.visit(node.expr)
......@@ -650,6 +652,7 @@ class CodeGenerator:
return 1
def visitAssTuple(self, node):
if findOp(node) != 'OP_DELETE':
self.emit('UNPACK_TUPLE', len(node.nodes))
for child in node.nodes:
self.visit(child)
......@@ -838,6 +841,7 @@ class CodeGenerator:
else:
self.visit(node.globals)
self.emit('EXEC_STMT')
return 1
class LocalNameFinder:
def __init__(self, names=()):
......@@ -882,6 +886,20 @@ class LocalNameFinder:
def visitAssName(self, node):
self.names.add(node.name)
class OpFinder:
def __init__(self):
self.op = None
def visitAssName(self, node):
if self.op is None:
self.op = node.flags
elif self.op != node.flags:
raise ValueError, "mixed ops in stmt"
def findOp(node):
v = OpFinder()
walk(node, v)
return v.op
class Loop:
def __init__(self):
self.startAnchor = StackRef()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment