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 ...@@ -42,6 +42,15 @@ CO_NEWLOCALS = 0x0002
CO_VARARGS = 0x0004 CO_VARARGS = 0x0004
CO_VARKEYWORDS = 0x0008 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: class PyAssembler:
"""Creates Python code objects """Creates Python code objects
""" """
...@@ -54,6 +63,7 @@ class PyAssembler: ...@@ -54,6 +63,7 @@ class PyAssembler:
self.insts = [] self.insts = []
# used by makeCodeObject # used by makeCodeObject
self._getArgCount(args) self._getArgCount(args)
print name, args, self.argcount
self.code = '' self.code = ''
self.consts = [docstring] self.consts = [docstring]
self.filename = filename self.filename = filename
...@@ -61,6 +71,10 @@ class PyAssembler: ...@@ -61,6 +71,10 @@ class PyAssembler:
self.name = name self.name = name
self.names = [] self.names = []
self.varnames = list(args) or [] 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 # lnotab support
self.firstlineno = 0 self.firstlineno = 0
self.lastlineno = 0 self.lastlineno = 0
...@@ -68,14 +82,12 @@ class PyAssembler: ...@@ -68,14 +82,12 @@ class PyAssembler:
self.lnotab = '' self.lnotab = ''
def _getArgCount(self, args): 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) 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): def __repr__(self):
return "<bytecode: %d instrs>" % len(self.insts) return "<bytecode: %d instrs>" % len(self.insts)
...@@ -88,7 +100,9 @@ class PyAssembler: ...@@ -88,7 +100,9 @@ class PyAssembler:
self.flags = self.flags | CO_OPTIMIZED self.flags = self.flags | CO_OPTIMIZED
def setVarArgs(self): def setVarArgs(self):
if not self.flags & CO_VARARGS:
self.flags = self.flags | CO_VARARGS self.flags = self.flags | CO_VARARGS
self.argcount = self.argcount - 1
def setKWArgs(self): def setKWArgs(self):
self.flags = self.flags | CO_VARKEYWORDS self.flags = self.flags | CO_VARKEYWORDS
......
...@@ -6,7 +6,7 @@ a generic tool and CodeGenerator as a specific tool. ...@@ -6,7 +6,7 @@ a generic tool and CodeGenerator as a specific tool.
""" """
from p2c import transformer, ast from p2c import transformer, ast
from pyassem import StackRef, PyAssembler from pyassem import StackRef, PyAssembler, TupleArg
import dis import dis
import misc import misc
import marshal import marshal
...@@ -203,7 +203,7 @@ class CodeGenerator: ...@@ -203,7 +203,7 @@ class CodeGenerator:
if type(elt) == types.StringType: if type(elt) == types.StringType:
args.append(elt) args.append(elt)
elif type(elt) == types.TupleType: elif type(elt) == types.TupleType:
args.append(".nested%d" % count) args.append(TupleArg(count, elt))
count = count + 1 count = count + 1
extra.extend(misc.flatten(elt)) extra.extend(misc.flatten(elt))
else: else:
...@@ -343,7 +343,6 @@ class CodeGenerator: ...@@ -343,7 +343,6 @@ class CodeGenerator:
def visitLambda(self, node): def visitLambda(self, node):
node.name = '<lambda>' node.name = '<lambda>'
node.varargs = node.kwargs = None
self._visitFuncOrLambda(node, 'Lambda') self._visitFuncOrLambda(node, 'Lambda')
return 1 return 1
...@@ -633,10 +632,13 @@ class CodeGenerator: ...@@ -633,10 +632,13 @@ class CodeGenerator:
return 1 return 1
def visitAssName(self, node): def visitAssName(self, node):
# XXX handle OP_DELETE if node.flags == 'OP_ASSIGN':
if node.flags != 'OP_ASSIGN':
print "oops", node.flags
self.storeName(node.name) self.storeName(node.name)
elif node.flags == 'OP_DELETE':
self.delName(node.name)
else:
print "oops", node.flags
return 1
def visitAssAttr(self, node): def visitAssAttr(self, node):
self.visit(node.expr) self.visit(node.expr)
...@@ -650,6 +652,7 @@ class CodeGenerator: ...@@ -650,6 +652,7 @@ class CodeGenerator:
return 1 return 1
def visitAssTuple(self, node): def visitAssTuple(self, node):
if findOp(node) != 'OP_DELETE':
self.emit('UNPACK_TUPLE', len(node.nodes)) self.emit('UNPACK_TUPLE', len(node.nodes))
for child in node.nodes: for child in node.nodes:
self.visit(child) self.visit(child)
...@@ -838,6 +841,7 @@ class CodeGenerator: ...@@ -838,6 +841,7 @@ class CodeGenerator:
else: else:
self.visit(node.globals) self.visit(node.globals)
self.emit('EXEC_STMT') self.emit('EXEC_STMT')
return 1
class LocalNameFinder: class LocalNameFinder:
def __init__(self, names=()): def __init__(self, names=()):
...@@ -882,6 +886,20 @@ class LocalNameFinder: ...@@ -882,6 +886,20 @@ class LocalNameFinder:
def visitAssName(self, node): def visitAssName(self, node):
self.names.add(node.name) 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: class Loop:
def __init__(self): def __init__(self):
self.startAnchor = StackRef() self.startAnchor = StackRef()
......
...@@ -42,6 +42,15 @@ CO_NEWLOCALS = 0x0002 ...@@ -42,6 +42,15 @@ CO_NEWLOCALS = 0x0002
CO_VARARGS = 0x0004 CO_VARARGS = 0x0004
CO_VARKEYWORDS = 0x0008 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: class PyAssembler:
"""Creates Python code objects """Creates Python code objects
""" """
...@@ -54,6 +63,7 @@ class PyAssembler: ...@@ -54,6 +63,7 @@ class PyAssembler:
self.insts = [] self.insts = []
# used by makeCodeObject # used by makeCodeObject
self._getArgCount(args) self._getArgCount(args)
print name, args, self.argcount
self.code = '' self.code = ''
self.consts = [docstring] self.consts = [docstring]
self.filename = filename self.filename = filename
...@@ -61,6 +71,10 @@ class PyAssembler: ...@@ -61,6 +71,10 @@ class PyAssembler:
self.name = name self.name = name
self.names = [] self.names = []
self.varnames = list(args) or [] 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 # lnotab support
self.firstlineno = 0 self.firstlineno = 0
self.lastlineno = 0 self.lastlineno = 0
...@@ -68,14 +82,12 @@ class PyAssembler: ...@@ -68,14 +82,12 @@ class PyAssembler:
self.lnotab = '' self.lnotab = ''
def _getArgCount(self, args): 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) 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): def __repr__(self):
return "<bytecode: %d instrs>" % len(self.insts) return "<bytecode: %d instrs>" % len(self.insts)
...@@ -88,7 +100,9 @@ class PyAssembler: ...@@ -88,7 +100,9 @@ class PyAssembler:
self.flags = self.flags | CO_OPTIMIZED self.flags = self.flags | CO_OPTIMIZED
def setVarArgs(self): def setVarArgs(self):
if not self.flags & CO_VARARGS:
self.flags = self.flags | CO_VARARGS self.flags = self.flags | CO_VARARGS
self.argcount = self.argcount - 1
def setKWArgs(self): def setKWArgs(self):
self.flags = self.flags | CO_VARKEYWORDS self.flags = self.flags | CO_VARKEYWORDS
......
...@@ -6,7 +6,7 @@ a generic tool and CodeGenerator as a specific tool. ...@@ -6,7 +6,7 @@ a generic tool and CodeGenerator as a specific tool.
""" """
from p2c import transformer, ast from p2c import transformer, ast
from pyassem import StackRef, PyAssembler from pyassem import StackRef, PyAssembler, TupleArg
import dis import dis
import misc import misc
import marshal import marshal
...@@ -203,7 +203,7 @@ class CodeGenerator: ...@@ -203,7 +203,7 @@ class CodeGenerator:
if type(elt) == types.StringType: if type(elt) == types.StringType:
args.append(elt) args.append(elt)
elif type(elt) == types.TupleType: elif type(elt) == types.TupleType:
args.append(".nested%d" % count) args.append(TupleArg(count, elt))
count = count + 1 count = count + 1
extra.extend(misc.flatten(elt)) extra.extend(misc.flatten(elt))
else: else:
...@@ -343,7 +343,6 @@ class CodeGenerator: ...@@ -343,7 +343,6 @@ class CodeGenerator:
def visitLambda(self, node): def visitLambda(self, node):
node.name = '<lambda>' node.name = '<lambda>'
node.varargs = node.kwargs = None
self._visitFuncOrLambda(node, 'Lambda') self._visitFuncOrLambda(node, 'Lambda')
return 1 return 1
...@@ -633,10 +632,13 @@ class CodeGenerator: ...@@ -633,10 +632,13 @@ class CodeGenerator:
return 1 return 1
def visitAssName(self, node): def visitAssName(self, node):
# XXX handle OP_DELETE if node.flags == 'OP_ASSIGN':
if node.flags != 'OP_ASSIGN':
print "oops", node.flags
self.storeName(node.name) self.storeName(node.name)
elif node.flags == 'OP_DELETE':
self.delName(node.name)
else:
print "oops", node.flags
return 1
def visitAssAttr(self, node): def visitAssAttr(self, node):
self.visit(node.expr) self.visit(node.expr)
...@@ -650,6 +652,7 @@ class CodeGenerator: ...@@ -650,6 +652,7 @@ class CodeGenerator:
return 1 return 1
def visitAssTuple(self, node): def visitAssTuple(self, node):
if findOp(node) != 'OP_DELETE':
self.emit('UNPACK_TUPLE', len(node.nodes)) self.emit('UNPACK_TUPLE', len(node.nodes))
for child in node.nodes: for child in node.nodes:
self.visit(child) self.visit(child)
...@@ -838,6 +841,7 @@ class CodeGenerator: ...@@ -838,6 +841,7 @@ class CodeGenerator:
else: else:
self.visit(node.globals) self.visit(node.globals)
self.emit('EXEC_STMT') self.emit('EXEC_STMT')
return 1
class LocalNameFinder: class LocalNameFinder:
def __init__(self, names=()): def __init__(self, names=()):
...@@ -882,6 +886,20 @@ class LocalNameFinder: ...@@ -882,6 +886,20 @@ class LocalNameFinder:
def visitAssName(self, node): def visitAssName(self, node):
self.names.add(node.name) 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: class Loop:
def __init__(self): def __init__(self):
self.startAnchor = StackRef() 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