mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
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 ...
This commit is contained in:
parent
7708d697ee
commit
3ec7e2c4be
4 changed files with 96 additions and 32 deletions
|
@ -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)
|
||||
|
||||
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):
|
||||
self.flags = self.flags | CO_VARARGS
|
||||
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':
|
||||
if node.flags == 'OP_ASSIGN':
|
||||
self.storeName(node.name)
|
||||
elif node.flags == 'OP_DELETE':
|
||||
self.delName(node.name)
|
||||
else:
|
||||
print "oops", node.flags
|
||||
self.storeName(node.name)
|
||||
return 1
|
||||
|
||||
def visitAssAttr(self, node):
|
||||
self.visit(node.expr)
|
||||
|
@ -650,7 +652,8 @@ class CodeGenerator:
|
|||
return 1
|
||||
|
||||
def visitAssTuple(self, node):
|
||||
self.emit('UNPACK_TUPLE', len(node.nodes))
|
||||
if findOp(node) != 'OP_DELETE':
|
||||
self.emit('UNPACK_TUPLE', len(node.nodes))
|
||||
for child in node.nodes:
|
||||
self.visit(child)
|
||||
return 1
|
||||
|
@ -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)
|
||||
|
||||
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):
|
||||
self.flags = self.flags | CO_VARARGS
|
||||
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':
|
||||
if node.flags == 'OP_ASSIGN':
|
||||
self.storeName(node.name)
|
||||
elif node.flags == 'OP_DELETE':
|
||||
self.delName(node.name)
|
||||
else:
|
||||
print "oops", node.flags
|
||||
self.storeName(node.name)
|
||||
return 1
|
||||
|
||||
def visitAssAttr(self, node):
|
||||
self.visit(node.expr)
|
||||
|
@ -650,7 +652,8 @@ class CodeGenerator:
|
|||
return 1
|
||||
|
||||
def visitAssTuple(self, node):
|
||||
self.emit('UNPACK_TUPLE', len(node.nodes))
|
||||
if findOp(node) != 'OP_DELETE':
|
||||
self.emit('UNPACK_TUPLE', len(node.nodes))
|
||||
for child in node.nodes:
|
||||
self.visit(child)
|
||||
return 1
|
||||
|
@ -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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue