def visit_ListComp(self, t):
result_append = ast.Attribute(ast.Name('.0', load), 'append', load)
body = ast.Expr(Call(result_append, [t.elt]))
for loop in reversed(t.generators):
for test in reversed(loop.ifs):
body = ast.If(test, [body], [])
body = ast.For(loop.target, loop.iter, [body], [])
fn = [body,
ast.Return(ast.Name('.0', load))]
args = ast.arguments([ast.arg('.0', None)], None, [], None, [], [])
return Call(Function('<listcomp>', args, fn),
[ast.List([], load)])
python类If()的实例源码
def visit_Assert(self, t):
t = self.generic_visit(t)
result = ast.If(t.test,
[],
[ast.Raise(Call(ast.Name('AssertionError', load),
[] if t.msg is None else [t.msg]),
None)])
return ast.copy_location(result, t)
def visit_ListComp(self, t):
t = self.generic_visit(t)
add_element = ast.Attribute(ast.Name('.elements', load), 'append', load)
body = ast.Expr(Call(add_element, [t.elt]))
for loop in reversed(t.generators):
for test in reversed(loop.ifs):
body = ast.If(test, [body], [])
body = ast.For(loop.target, loop.iter, [body], [])
fn = [body,
ast.Return(ast.Name('.elements', load))]
args = ast.arguments([ast.arg('.elements', None)], None, [], None, [], [])
result = Call(Function('<listcomp>', args, fn),
[ast.List([], load)])
return ast.copy_location(result, t)
def visit_ListComp(self, t):
t = self.generic_visit(t)
add_element = ast.Attribute(ast.Name('.elements', load), 'append', load)
body = ast.Expr(Call(add_element, [t.elt]))
for loop in reversed(t.generators):
for test in reversed(loop.ifs):
body = ast.If(test, [body], [])
body = ast.For(loop.target, loop.iter, [body], [])
fn = [body,
ast.Return(ast.Name('.elements', load))]
args = ast.arguments([ast.arg('.elements', None)], None, [], None, [], [])
result = Call(Function('<listcomp>', args, fn),
[ast.List([], load)])
return ast.copy_location(result, t)
def visit_Assert(self, t):
t = self.generic_visit(t)
result = ast.If(t.test,
[],
[ast.Raise(Call(ast.Name('AssertionError', load),
[] if t.msg is None else [t.msg]),
None)])
return ast.copy_location(result, t)
def visit_ListComp(self, t):
t = self.generic_visit(t)
add_element = ast.Attribute(ast.Name('.elements', load), 'append', load)
body = ast.Expr(Call(add_element, [t.elt]))
for loop in reversed(t.generators):
for test in reversed(loop.ifs):
body = ast.If(test, [body], [])
body = ast.For(loop.target, loop.iter, [body], [])
fn = [body,
ast.Return(ast.Name('.elements', load))]
args = ast.arguments([ast.arg('.elements', None)], None, [], None, [], [])
result = Call(Function('<listcomp>', args, fn),
[ast.List([], load)])
return ast.copy_location(result, t)
def _translate_body(self, body, types, allow_loose_in_edges=False, allow_loose_out_edges=False):
cfg_factory = CFGFactory(self._id_gen)
for child in body:
if isinstance(child, (ast.AnnAssign, ast.Expr)):
cfg_factory.add_stmts(self.visit(child, types))
elif isinstance(child, ast.If):
cfg_factory.complete_basic_block()
if_cfg = self.visit(child, types)
cfg_factory.append_cfg(if_cfg)
elif isinstance(child, ast.While):
cfg_factory.complete_basic_block()
while_cfg = self.visit(child, types)
cfg_factory.append_cfg(while_cfg)
elif isinstance(child, ast.Break):
cfg_factory.complete_basic_block()
break_cfg = self.visit(child, types)
cfg_factory.append_cfg(break_cfg)
elif isinstance(child, ast.Continue):
cfg_factory.complete_basic_block()
cont_cfg = self.visit(child, types)
cfg_factory.append_cfg(cont_cfg)
elif isinstance(child, ast.Pass):
if cfg_factory.incomplete_block():
pass
else:
cfg_factory.append_cfg(_dummy_cfg(self._id_gen))
else:
raise NotImplementedError(f"The statement {str(type(child))} is not yet translatable to CFG!")
cfg_factory.complete_basic_block()
if not allow_loose_in_edges and cfg_factory.cfg and cfg_factory.cfg.loose_in_edges:
cfg_factory.prepend_cfg(_dummy_cfg(self._id_gen))
if not allow_loose_out_edges and cfg_factory.cfg and cfg_factory.cfg.loose_out_edges:
cfg_factory.append_cfg(_dummy_cfg(self._id_gen))
return cfg_factory.cfg
def handle_or_else(self, orelse, test):
"""Handle the orelse part of an if node.
Returns:
The last nodes of the orelse branch
"""
if isinstance(orelse[0], ast.If):
control_flow_node = self.visit(orelse[0])
self.add_elif_label(control_flow_node.test)
test.connect(control_flow_node.test)
return control_flow_node.last_nodes
else:
else_connect_statements = self.stmt_star_handler(orelse)
test.connect(else_connect_statements.first_statement)
return else_connect_statements.last_statements
def visit_BoolOp(self, boolop):
res_var = self.variable()
expl_list = self.assign(ast.List([], ast.Load()))
app = ast.Attribute(expl_list, "append", ast.Load())
is_or = int(isinstance(boolop.op, ast.Or))
body = save = self.statements
fail_save = self.on_failure
levels = len(boolop.values) - 1
self.push_format_context()
# Process each operand, short-circuting if needed.
for i, v in enumerate(boolop.values):
if i:
fail_inner = []
# cond is set in a prior loop iteration below
self.on_failure.append(ast.If(cond, fail_inner, [])) # noqa
self.on_failure = fail_inner
self.push_format_context()
res, expl = self.visit(v)
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
expl_format = self.pop_format_context(ast.Str(expl))
call = ast_Call(app, [expl_format], [])
self.on_failure.append(ast.Expr(call))
if i < levels:
cond = res
if is_or:
cond = ast.UnaryOp(ast.Not(), cond)
inner = []
self.statements.append(ast.If(cond, inner, []))
self.statements = body = inner
self.statements = save
self.on_failure = fail_save
expl_template = self.helper("format_boolop", expl_list, ast.Num(is_or))
expl = self.pop_format_context(expl_template)
return ast.Name(res_var, ast.Load()), self.explanation_param(expl)
def visit_If(self, node):
node = self.generic_visit(node)
if (node.orelse
and len(node.orelse) == 1
and isinstance(node.orelse[0], ast.Pass)
):
node.orelse = []
if (len(node.body) == 1
and isinstance(node.body[0], ast.Pass)
):
if node.orelse:
node_test = ast.UnaryOp(op=ast.Not(), operand=node.test)
if (len(node.orelse) == 1
and isinstance(node.orelse[0], ast.If)
):
node_test = ast.BoolOp\
( op = ast.And()
, values = [node_test, node.orelse[0].test]
)
node.test = ast.copy_location(node_test, node.orelse[0].test)
node.body = node.orelse[0].body
node.orelse = node.orelse[0].orelse
else:
node.test = ast.copy_location(node_test, node.test)
node.body = node.orelse
node.orelse = []
else:
node = None
return node
def noneSpecialFunction(cv):
"""If the old type is 'None' (which won't show up in the original), move up in the AST to get the metadata"""
if (not isinstance(cv, AddVector)) and cv.oldSubtree == None:
cvCopy = cv.deepcopy()
if cv.path[0] == ('value', 'Return'):
cv.oldSubtree = deepcopy(cvCopy.traverseTree(cv.start))
cv.newSubtree = ast.Return(cv.newSubtree)
cv.path = cv.path[1:]
elif cv.path[0] == ('value', 'Name Constant'):
cv.oldSubtree = deepcopy(cvCopy.traverseTree(cv.start))
cv.newSubtree = ast.NameConstant(cv.newSubtree)
cv.path = cv.path[1:]
elif cv.path[0] in [('lower', 'Slice'), ('upper', 'Slice'), ('step', 'Slice')]:
tmpNew = cv.newSubtree
cvCopy = cv.deepcopy()
cv.oldSubtree = deepcopy(cvCopy.traverseTree(cv.start))
cv.newSubtree = deepcopy(cv.oldSubtree) # use the same slice
if cv.path[0][0] == 'lower':
cv.newSubtree.lower = tmpNew
elif cv.path[0][0] == 'upper':
cv.newSubtree.upper = tmpNew
else:
cv.newSubtree.step = tmpNew
cv.path = cv.path[1:] # get rid of None and the val
else:
log("Individualize\tmapEdit\tMissing option in None special case 1: " + str(cv.path[0]), "bug")
elif cv.oldSubtree == "None":
cv.path = cv.path[1:] # get rid of None and the id
cvCopy = cv.deepcopy()
cv.oldSubtree = deepcopy(cvCopy.traverseTree(cv.start))
if cv.path[0] == ('value', 'Return'):
cv.newSubtree = ast.Return(ast.Name(cv.newSubtree, ast.Load()))
else:
log("Individualize\tmapEdit\tMissing option in None special case 2: " + str(cv.path[0]), "bug")
cv.path = cv.path[1:]
return cv
def movedLineAfterSpecialFunction(cv, startingTree, startingPath, orig):
"""Sometimes, with Move Vectors, items that got combined are no longer combined. Fix this by moving up the tree."""
if isinstance(cv, MoveVector):
cvCopy = cv.deepcopy()
origSpot = deepcopy(cvCopy.traverseTree(cv.start))
if len(origSpot) <= cv.oldSubtree or len(origSpot) <= cv.newSubtree:
cvCopy.path = startingPath[1:]
parentSpot = deepcopy(cvCopy.traverseTree(startingTree))
if type(parentSpot) == ast.BoolOp:
# Change this to a ChangeVector at the parent's level
newSpot = deepcopy(parentSpot)
newSpot.values.insert(cv.newSubtree, newSpot.values[cv.oldSubtree])
newSpot.values.pop(cv.oldSubtree + (0 if cv.oldSubtree < cv.newSubtree else 1)) # adjust for length change
cv = ChangeVector(cv.path[2:], parentSpot, newSpot, cv.start)
cv.wasMoveVector = True
return cv
elif cv.path[1][0] == 'body': # If we're in a set of statements
lineToMove = parentSpot.body[cv.oldSubtree]
# First, just delete this line
if hasattr(lineToMove, "global_id"):
path = generatePathToId(orig, lineToMove.global_id)
else:
log("Individualize\tmovedLineAfterSpecialFunction\tWhere is the global id? " + printFunction(lineToMove), "bug")
firstEdit = DeleteVector(path, lineToMove, None, start=orig)
# Then, add the line back in, but in the correct position
newPath = [cv.newSubtree] + cv.path[1:]
secondEdit = AddVector(newPath, None, lineToMove, start=cv.start)
return [firstEdit, secondEdit]
else:
log("Individualize\tmapEdit\tMissing option in Move Vector special case: " + str(type(parentSpot)), "bug")
return cv
def anonymizeNames(a, namesToKeep, imports):
"""Anonymize all of variables/names that occur in the given AST"""
"""If we run this on an anonymized AST, it will fix the names again to get rid of any gaps!"""
if type(a) != ast.Module:
return a
globalMap = { }
for var in namesToKeep:
globalMap[var] = var
anonymizeStatementNames(a, globalMap, "", imports, goBackwards=True)
return a
def cleanupEquals(a):
"""Gets rid of silly blah == True statements that students make"""
if not isinstance(a, ast.AST):
return a
if type(a) == ast.Call:
a.func = cleanupEquals(a.func)
for i in range(len(a.args)):
# But test expressions don't carry through to function arguments
a.args[i] = cleanupEquals(a.args[i])
return a
elif type(a) == ast.Compare and type(a.ops[0]) in [ast.Eq, ast.NotEq]:
l = a.left = cleanupEquals(a.left)
r = cleanupEquals(a.comparators[0])
a.comparators = [r]
if type(l) == ast.NameConstant and l.value in [True, False]:
(l,r) = (r,l)
# If we have (boolean expression) == True
if type(r) == ast.NameConstant and r.value in [True, False] and (eventualType(l) == bool):
# Matching types
if (type(a.ops[0]) == ast.Eq and r.value == True) or \
(type(a.ops[0]) == ast.NotEq and r.value == False):
transferMetaData(a, l) # make sure to keep the original location
return l
else:
tmp = ast.UnaryOp(ast.Not(addedNotOp=True), l)
transferMetaData(a, tmp)
return tmp
else:
return a
else:
return applyToChildren(a, cleanupEquals)
def cleanupBoolOps(a):
"""When possible, combine adjacent boolean expressions"""
"""Note- we are assuming that all ops are the first op (as is done in the simplify function)"""
if not isinstance(a, ast.AST):
return a
if type(a) == ast.BoolOp:
allTypesWork = True
for i in range(len(a.values)):
a.values[i] = cleanupBoolOps(a.values[i])
if eventualType(a.values[i]) != bool or hasattr(a.values[i], "multiComp"):
allTypesWork = False
# We can't reduce if the types aren't all booleans
if not allTypesWork:
return a
i = 0
while i < len(a.values) - 1:
current = a.values[i]
next = a.values[i+1]
# (a and b and c and d) or (a and e and d) == a and ((b and c) or e) and d
if type(current) == type(next) == ast.BoolOp:
if type(current.op) == type(next.op):
minlength = min(len(current.values), len(next.values)) # shortest length
# First, check for all identical values from the front
j = 0
while j < minlength:
if compareASTs(current.values[j], next.values[j], checkEquality=True) != 0:
break
j += 1
# Same values in both, so get rid of the latter line
if j == len(current.values) == len(next.values):
a.values.pop(i+1)
continue
i += 1
### If reduced to one item, just return that item
return a.values[0] if (len(a.values) == 1) else a
return applyToChildren(a, cleanupBoolOps)
def cleanupTypes(a):
"""Remove any unneccessary type mappings"""
if not isinstance(a, ast.AST):
return a
# No need to cast something if it'll be changed anyway by a binary operation
if type(a) == ast.BinOp:
a.left = cleanupTypes(a.left)
a.right = cleanupTypes(a.right)
# Ints become floats naturally
if eventualType(a.left) == eventualType(a.right) == float:
if type(a.right) == ast.Call and type(a.right.func) == ast.Name and \
a.right.func.id == "float" and len(a.right.args) == 1 and len(a.right.keywords) == 0 and \
eventualType(a.right.args[0]) in [int, float]:
a.right = a.right.args[0]
elif type(a.left) == ast.Call and type(a.left.func) == ast.Name and \
a.left.func.id == "float" and len(a.left.args) == 1 and len(a.left.keywords) == 0 and \
eventualType(a.left.args[0]) in [int, float]:
a.left = a.left.args[0]
return a
elif type(a) == ast.Call and type(a.func) == ast.Name and len(a.args) == 1 and len(a.keywords) == 0:
a.func = cleanupTypes(a.func)
a.args = [cleanupTypes(a.args[0])]
# If the type already matches, no need to cast it
funName = a.func.id
argType = eventualType(a.args[0])
if type(a.func) == ast.Name:
if (funName == "float" and argType == float) or \
(funName == "int" and argType == int) or \
(funName == "bool" and argType == bool) or \
(funName == "str" and argType == str):
return a.args[0]
return applyToChildren(a, cleanupTypes)
def getIfBranches(a):
"""Gets all the branches of an if statement. Will only work if each else has a single line"""
if type(a) != ast.If:
return None
if len(a.orelse) == 0:
return [a]
elif len(a.orelse) == 1:
tmp = getIfBranches(a.orelse[0])
if tmp == None:
return None
return [a] + tmp
else:
return None
def isStatement(a):
"""Determine whether the given node is a statement (vs an expression)"""
return type(a) in [ ast.Module, ast.Interactive, ast.Expression, ast.Suite,
ast.FunctionDef, ast.ClassDef, ast.Return, ast.Delete,
ast.Assign, ast.AugAssign, ast.For, ast.While,
ast.If, ast.With, ast.Raise, ast.Try,
ast.Assert, ast.Import, ast.ImportFrom, ast.Global,
ast.Expr, ast.Pass, ast.Break, ast.Continue ]
def handle_or_else(self, orelse, test):
"""Handle the orelse part of an if or try node.
Returns:
The last nodes of the orelse branch.
"""
if isinstance(orelse[0], ast.If):
control_flow_node = self.visit(orelse[0])
self.add_elif_label(control_flow_node.test)
test.connect(control_flow_node.test)
return control_flow_node.last_nodes
else:
else_connect_statements = self.stmt_star_handler(orelse, prev_node_to_avoid=self.nodes[-1])
test.connect(else_connect_statements.first_statement)
return else_connect_statements.last_statements
def is_condition(self, cfg_node):
if isinstance(cfg_node.ast_node, (ast.If, ast.While)):
return True
elif self.is_output(cfg_node):
return True
return False