mirror of
https://github.com/python/cpython.git
synced 2025-08-30 21:48:47 +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_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:
|
||||||
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:
|
||||||
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:
|
||||||
self.lnotab = ''
|
self.lnotab = ''
|
||||||
|
|
||||||
def _getArgCount(self, args):
|
def _getArgCount(self, args):
|
||||||
if args and args[0][0] == '.':
|
self.argcount = len(args)
|
||||||
for i in range(len(args)):
|
if args:
|
||||||
if args[i][0] == '.':
|
for arg in args:
|
||||||
num = i
|
if isinstance(arg, TupleArg):
|
||||||
self.argcount = num + 1
|
numNames = len(misc.flatten(arg.names))
|
||||||
else:
|
self.argcount = self.argcount - numNames
|
||||||
self.argcount = len(args)
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
self.flags = self.flags | CO_OPTIMIZED
|
self.flags = self.flags | CO_OPTIMIZED
|
||||||
|
|
||||||
def setVarArgs(self):
|
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):
|
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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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:
|
||||||
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:
|
||||||
|
|
||||||
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:
|
||||||
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':
|
self.storeName(node.name)
|
||||||
|
elif node.flags == 'OP_DELETE':
|
||||||
|
self.delName(node.name)
|
||||||
|
else:
|
||||||
print "oops", node.flags
|
print "oops", node.flags
|
||||||
self.storeName(node.name)
|
return 1
|
||||||
|
|
||||||
def visitAssAttr(self, node):
|
def visitAssAttr(self, node):
|
||||||
self.visit(node.expr)
|
self.visit(node.expr)
|
||||||
|
@ -650,7 +652,8 @@ class CodeGenerator:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def visitAssTuple(self, node):
|
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:
|
for child in node.nodes:
|
||||||
self.visit(child)
|
self.visit(child)
|
||||||
return 1
|
return 1
|
||||||
|
@ -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:
|
||||||
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
|
||||||
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:
|
||||||
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:
|
||||||
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:
|
||||||
self.lnotab = ''
|
self.lnotab = ''
|
||||||
|
|
||||||
def _getArgCount(self, args):
|
def _getArgCount(self, args):
|
||||||
if args and args[0][0] == '.':
|
self.argcount = len(args)
|
||||||
for i in range(len(args)):
|
if args:
|
||||||
if args[i][0] == '.':
|
for arg in args:
|
||||||
num = i
|
if isinstance(arg, TupleArg):
|
||||||
self.argcount = num + 1
|
numNames = len(misc.flatten(arg.names))
|
||||||
else:
|
self.argcount = self.argcount - numNames
|
||||||
self.argcount = len(args)
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
self.flags = self.flags | CO_OPTIMIZED
|
self.flags = self.flags | CO_OPTIMIZED
|
||||||
|
|
||||||
def setVarArgs(self):
|
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):
|
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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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:
|
||||||
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:
|
||||||
|
|
||||||
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:
|
||||||
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':
|
self.storeName(node.name)
|
||||||
|
elif node.flags == 'OP_DELETE':
|
||||||
|
self.delName(node.name)
|
||||||
|
else:
|
||||||
print "oops", node.flags
|
print "oops", node.flags
|
||||||
self.storeName(node.name)
|
return 1
|
||||||
|
|
||||||
def visitAssAttr(self, node):
|
def visitAssAttr(self, node):
|
||||||
self.visit(node.expr)
|
self.visit(node.expr)
|
||||||
|
@ -650,7 +652,8 @@ class CodeGenerator:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def visitAssTuple(self, node):
|
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:
|
for child in node.nodes:
|
||||||
self.visit(child)
|
self.visit(child)
|
||||||
return 1
|
return 1
|
||||||
|
@ -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:
|
||||||
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()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue