Spaces:
Runtime error
Runtime error
| """ | |
| Serializes a Cython code tree to Cython code. This is primarily useful for | |
| debugging and testing purposes. | |
| The output is in a strict format, no whitespace or comments from the input | |
| is preserved (and it could not be as it is not present in the code tree). | |
| """ | |
| from __future__ import absolute_import, print_function | |
| from .Compiler.Visitor import TreeVisitor | |
| from .Compiler.ExprNodes import * | |
| class LinesResult(object): | |
| def __init__(self): | |
| self.lines = [] | |
| self.s = u"" | |
| def put(self, s): | |
| self.s += s | |
| def newline(self): | |
| self.lines.append(self.s) | |
| self.s = u"" | |
| def putline(self, s): | |
| self.put(s) | |
| self.newline() | |
| class DeclarationWriter(TreeVisitor): | |
| indent_string = u" " | |
| def __init__(self, result=None): | |
| super(DeclarationWriter, self).__init__() | |
| if result is None: | |
| result = LinesResult() | |
| self.result = result | |
| self.numindents = 0 | |
| self.tempnames = {} | |
| self.tempblockindex = 0 | |
| def write(self, tree): | |
| self.visit(tree) | |
| return self.result | |
| def indent(self): | |
| self.numindents += 1 | |
| def dedent(self): | |
| self.numindents -= 1 | |
| def startline(self, s=u""): | |
| self.result.put(self.indent_string * self.numindents + s) | |
| def put(self, s): | |
| self.result.put(s) | |
| def putline(self, s): | |
| self.result.putline(self.indent_string * self.numindents + s) | |
| def endline(self, s=u""): | |
| self.result.putline(s) | |
| def line(self, s): | |
| self.startline(s) | |
| self.endline() | |
| def comma_separated_list(self, items, output_rhs=False): | |
| if len(items) > 0: | |
| for item in items[:-1]: | |
| self.visit(item) | |
| if output_rhs and item.default is not None: | |
| self.put(u" = ") | |
| self.visit(item.default) | |
| self.put(u", ") | |
| self.visit(items[-1]) | |
| def visit_Node(self, node): | |
| raise AssertionError("Node not handled by serializer: %r" % node) | |
| def visit_ModuleNode(self, node): | |
| self.visitchildren(node) | |
| def visit_StatListNode(self, node): | |
| self.visitchildren(node) | |
| def visit_CDefExternNode(self, node): | |
| if node.include_file is None: | |
| file = u'*' | |
| else: | |
| file = u'"%s"' % node.include_file | |
| self.putline(u"cdef extern from %s:" % file) | |
| self.indent() | |
| self.visit(node.body) | |
| self.dedent() | |
| def visit_CPtrDeclaratorNode(self, node): | |
| self.put('*') | |
| self.visit(node.base) | |
| def visit_CReferenceDeclaratorNode(self, node): | |
| self.put('&') | |
| self.visit(node.base) | |
| def visit_CArrayDeclaratorNode(self, node): | |
| self.visit(node.base) | |
| self.put(u'[') | |
| if node.dimension is not None: | |
| self.visit(node.dimension) | |
| self.put(u']') | |
| def visit_CArrayDeclaratorNode(self, node): | |
| self.visit(node.base) | |
| self.put(u'[') | |
| if node.dimension is not None: | |
| self.visit(node.dimension) | |
| self.put(u']') | |
| def visit_CFuncDeclaratorNode(self, node): | |
| # TODO: except, gil, etc. | |
| self.visit(node.base) | |
| self.put(u'(') | |
| self.comma_separated_list(node.args) | |
| self.endline(u')') | |
| def visit_CNameDeclaratorNode(self, node): | |
| self.put(node.name) | |
| def visit_CSimpleBaseTypeNode(self, node): | |
| # See Parsing.p_sign_and_longness | |
| if node.is_basic_c_type: | |
| self.put(("unsigned ", "", "signed ")[node.signed]) | |
| if node.longness < 0: | |
| self.put("short " * -node.longness) | |
| elif node.longness > 0: | |
| self.put("long " * node.longness) | |
| self.put(node.name) | |
| def visit_CComplexBaseTypeNode(self, node): | |
| self.put(u'(') | |
| self.visit(node.base_type) | |
| self.visit(node.declarator) | |
| self.put(u')') | |
| def visit_CNestedBaseTypeNode(self, node): | |
| self.visit(node.base_type) | |
| self.put(u'.') | |
| self.put(node.name) | |
| def visit_TemplatedTypeNode(self, node): | |
| self.visit(node.base_type_node) | |
| self.put(u'[') | |
| self.comma_separated_list(node.positional_args + node.keyword_args.key_value_pairs) | |
| self.put(u']') | |
| def visit_CVarDefNode(self, node): | |
| self.startline(u"cdef ") | |
| self.visit(node.base_type) | |
| self.put(u" ") | |
| self.comma_separated_list(node.declarators, output_rhs=True) | |
| self.endline() | |
| def visit_container_node(self, node, decl, extras, attributes): | |
| # TODO: visibility | |
| self.startline(decl) | |
| if node.name: | |
| self.put(u' ') | |
| self.put(node.name) | |
| if node.cname is not None: | |
| self.put(u' "%s"' % node.cname) | |
| if extras: | |
| self.put(extras) | |
| self.endline(':') | |
| self.indent() | |
| if not attributes: | |
| self.putline('pass') | |
| else: | |
| for attribute in attributes: | |
| self.visit(attribute) | |
| self.dedent() | |
| def visit_CStructOrUnionDefNode(self, node): | |
| if node.typedef_flag: | |
| decl = u'ctypedef ' | |
| else: | |
| decl = u'cdef ' | |
| if node.visibility == 'public': | |
| decl += u'public ' | |
| if node.packed: | |
| decl += u'packed ' | |
| decl += node.kind | |
| self.visit_container_node(node, decl, None, node.attributes) | |
| def visit_CppClassNode(self, node): | |
| extras = "" | |
| if node.templates: | |
| extras = u"[%s]" % ", ".join(node.templates) | |
| if node.base_classes: | |
| extras += "(%s)" % ", ".join(node.base_classes) | |
| self.visit_container_node(node, u"cdef cppclass", extras, node.attributes) | |
| def visit_CEnumDefNode(self, node): | |
| self.visit_container_node(node, u"cdef enum", None, node.items) | |
| def visit_CEnumDefItemNode(self, node): | |
| self.startline(node.name) | |
| if node.cname: | |
| self.put(u' "%s"' % node.cname) | |
| if node.value: | |
| self.put(u" = ") | |
| self.visit(node.value) | |
| self.endline() | |
| def visit_CClassDefNode(self, node): | |
| assert not node.module_name | |
| if node.decorators: | |
| for decorator in node.decorators: | |
| self.visit(decorator) | |
| self.startline(u"cdef class ") | |
| self.put(node.class_name) | |
| if node.base_class_name: | |
| self.put(u"(") | |
| if node.base_class_module: | |
| self.put(node.base_class_module) | |
| self.put(u".") | |
| self.put(node.base_class_name) | |
| self.put(u")") | |
| self.endline(u":") | |
| self.indent() | |
| self.visit(node.body) | |
| self.dedent() | |
| def visit_CTypeDefNode(self, node): | |
| self.startline(u"ctypedef ") | |
| self.visit(node.base_type) | |
| self.put(u" ") | |
| self.visit(node.declarator) | |
| self.endline() | |
| def visit_FuncDefNode(self, node): | |
| self.startline(u"def %s(" % node.name) | |
| self.comma_separated_list(node.args) | |
| self.endline(u"):") | |
| self.indent() | |
| self.visit(node.body) | |
| self.dedent() | |
| def visit_CArgDeclNode(self, node): | |
| if node.base_type.name is not None: | |
| self.visit(node.base_type) | |
| self.put(u" ") | |
| self.visit(node.declarator) | |
| if node.default is not None: | |
| self.put(u" = ") | |
| self.visit(node.default) | |
| def visit_CImportStatNode(self, node): | |
| self.startline(u"cimport ") | |
| self.put(node.module_name) | |
| if node.as_name: | |
| self.put(u" as ") | |
| self.put(node.as_name) | |
| self.endline() | |
| def visit_FromCImportStatNode(self, node): | |
| self.startline(u"from ") | |
| self.put(node.module_name) | |
| self.put(u" cimport ") | |
| first = True | |
| for pos, name, as_name, kind in node.imported_names: | |
| assert kind is None | |
| if first: | |
| first = False | |
| else: | |
| self.put(u", ") | |
| self.put(name) | |
| if as_name: | |
| self.put(u" as ") | |
| self.put(as_name) | |
| self.endline() | |
| def visit_NameNode(self, node): | |
| self.put(node.name) | |
| def visit_IntNode(self, node): | |
| self.put(node.value) | |
| def visit_NoneNode(self, node): | |
| self.put(u"None") | |
| def visit_NotNode(self, node): | |
| self.put(u"(not ") | |
| self.visit(node.operand) | |
| self.put(u")") | |
| def visit_DecoratorNode(self, node): | |
| self.startline("@") | |
| self.visit(node.decorator) | |
| self.endline() | |
| def visit_BinopNode(self, node): | |
| self.visit(node.operand1) | |
| self.put(u" %s " % node.operator) | |
| self.visit(node.operand2) | |
| def visit_AttributeNode(self, node): | |
| self.visit(node.obj) | |
| self.put(u".%s" % node.attribute) | |
| def visit_BoolNode(self, node): | |
| self.put(str(node.value)) | |
| # FIXME: represent string nodes correctly | |
| def visit_StringNode(self, node): | |
| value = node.value | |
| if value.encoding is not None: | |
| value = value.encode(value.encoding) | |
| self.put(repr(value)) | |
| def visit_PassStatNode(self, node): | |
| self.startline(u"pass") | |
| self.endline() | |
| class CodeWriter(DeclarationWriter): | |
| def visit_SingleAssignmentNode(self, node): | |
| self.startline() | |
| self.visit(node.lhs) | |
| self.put(u" = ") | |
| self.visit(node.rhs) | |
| self.endline() | |
| def visit_CascadedAssignmentNode(self, node): | |
| self.startline() | |
| for lhs in node.lhs_list: | |
| self.visit(lhs) | |
| self.put(u" = ") | |
| self.visit(node.rhs) | |
| self.endline() | |
| def visit_PrintStatNode(self, node): | |
| self.startline(u"print ") | |
| self.comma_separated_list(node.arg_tuple.args) | |
| if not node.append_newline: | |
| self.put(u",") | |
| self.endline() | |
| def visit_ForInStatNode(self, node): | |
| self.startline(u"for ") | |
| self.visit(node.target) | |
| self.put(u" in ") | |
| self.visit(node.iterator.sequence) | |
| self.endline(u":") | |
| self.indent() | |
| self.visit(node.body) | |
| self.dedent() | |
| if node.else_clause is not None: | |
| self.line(u"else:") | |
| self.indent() | |
| self.visit(node.else_clause) | |
| self.dedent() | |
| def visit_IfStatNode(self, node): | |
| # The IfClauseNode is handled directly without a separate match | |
| # for clariy. | |
| self.startline(u"if ") | |
| self.visit(node.if_clauses[0].condition) | |
| self.endline(":") | |
| self.indent() | |
| self.visit(node.if_clauses[0].body) | |
| self.dedent() | |
| for clause in node.if_clauses[1:]: | |
| self.startline("elif ") | |
| self.visit(clause.condition) | |
| self.endline(":") | |
| self.indent() | |
| self.visit(clause.body) | |
| self.dedent() | |
| if node.else_clause is not None: | |
| self.line("else:") | |
| self.indent() | |
| self.visit(node.else_clause) | |
| self.dedent() | |
| def visit_SequenceNode(self, node): | |
| self.comma_separated_list(node.args) # Might need to discover whether we need () around tuples...hmm... | |
| def visit_SimpleCallNode(self, node): | |
| self.visit(node.function) | |
| self.put(u"(") | |
| self.comma_separated_list(node.args) | |
| self.put(")") | |
| def visit_GeneralCallNode(self, node): | |
| self.visit(node.function) | |
| self.put(u"(") | |
| posarg = node.positional_args | |
| if isinstance(posarg, AsTupleNode): | |
| self.visit(posarg.arg) | |
| else: | |
| self.comma_separated_list(posarg.args) # TupleNode.args | |
| if node.keyword_args: | |
| if isinstance(node.keyword_args, DictNode): | |
| for i, (name, value) in enumerate(node.keyword_args.key_value_pairs): | |
| if i > 0: | |
| self.put(', ') | |
| self.visit(name) | |
| self.put('=') | |
| self.visit(value) | |
| else: | |
| raise Exception("Not implemented yet") | |
| self.put(u")") | |
| def visit_ExprStatNode(self, node): | |
| self.startline() | |
| self.visit(node.expr) | |
| self.endline() | |
| def visit_InPlaceAssignmentNode(self, node): | |
| self.startline() | |
| self.visit(node.lhs) | |
| self.put(u" %s= " % node.operator) | |
| self.visit(node.rhs) | |
| self.endline() | |
| def visit_WithStatNode(self, node): | |
| self.startline() | |
| self.put(u"with ") | |
| self.visit(node.manager) | |
| if node.target is not None: | |
| self.put(u" as ") | |
| self.visit(node.target) | |
| self.endline(u":") | |
| self.indent() | |
| self.visit(node.body) | |
| self.dedent() | |
| def visit_TryFinallyStatNode(self, node): | |
| self.line(u"try:") | |
| self.indent() | |
| self.visit(node.body) | |
| self.dedent() | |
| self.line(u"finally:") | |
| self.indent() | |
| self.visit(node.finally_clause) | |
| self.dedent() | |
| def visit_TryExceptStatNode(self, node): | |
| self.line(u"try:") | |
| self.indent() | |
| self.visit(node.body) | |
| self.dedent() | |
| for x in node.except_clauses: | |
| self.visit(x) | |
| if node.else_clause is not None: | |
| self.visit(node.else_clause) | |
| def visit_ExceptClauseNode(self, node): | |
| self.startline(u"except") | |
| if node.pattern is not None: | |
| self.put(u" ") | |
| self.visit(node.pattern) | |
| if node.target is not None: | |
| self.put(u", ") | |
| self.visit(node.target) | |
| self.endline(":") | |
| self.indent() | |
| self.visit(node.body) | |
| self.dedent() | |
| def visit_ReturnStatNode(self, node): | |
| self.startline("return ") | |
| self.visit(node.value) | |
| self.endline() | |
| def visit_ReraiseStatNode(self, node): | |
| self.line("raise") | |
| def visit_ImportNode(self, node): | |
| self.put(u"(import %s)" % node.module_name.value) | |
| def visit_TempsBlockNode(self, node): | |
| """ | |
| Temporaries are output like $1_1', where the first number is | |
| an index of the TempsBlockNode and the second number is an index | |
| of the temporary which that block allocates. | |
| """ | |
| idx = 0 | |
| for handle in node.temps: | |
| self.tempnames[handle] = "$%d_%d" % (self.tempblockindex, idx) | |
| idx += 1 | |
| self.tempblockindex += 1 | |
| self.visit(node.body) | |
| def visit_TempRefNode(self, node): | |
| self.put(self.tempnames[node.handle]) | |
| class PxdWriter(DeclarationWriter): | |
| def __call__(self, node): | |
| print(u'\n'.join(self.write(node).lines)) | |
| return node | |
| def visit_CFuncDefNode(self, node): | |
| if 'inline' in node.modifiers: | |
| return | |
| if node.overridable: | |
| self.startline(u'cpdef ') | |
| else: | |
| self.startline(u'cdef ') | |
| if node.visibility != 'private': | |
| self.put(node.visibility) | |
| self.put(u' ') | |
| if node.api: | |
| self.put(u'api ') | |
| self.visit(node.declarator) | |
| def visit_StatNode(self, node): | |
| pass | |
| class ExpressionWriter(TreeVisitor): | |
| def __init__(self, result=None): | |
| super(ExpressionWriter, self).__init__() | |
| if result is None: | |
| result = u"" | |
| self.result = result | |
| self.precedence = [0] | |
| def write(self, tree): | |
| self.visit(tree) | |
| return self.result | |
| def put(self, s): | |
| self.result += s | |
| def remove(self, s): | |
| if self.result.endswith(s): | |
| self.result = self.result[:-len(s)] | |
| def comma_separated_list(self, items): | |
| if len(items) > 0: | |
| for item in items[:-1]: | |
| self.visit(item) | |
| self.put(u", ") | |
| self.visit(items[-1]) | |
| def visit_Node(self, node): | |
| raise AssertionError("Node not handled by serializer: %r" % node) | |
| def visit_NameNode(self, node): | |
| self.put(node.name) | |
| def visit_NoneNode(self, node): | |
| self.put(u"None") | |
| def visit_EllipsisNode(self, node): | |
| self.put(u"...") | |
| def visit_BoolNode(self, node): | |
| self.put(str(node.value)) | |
| def visit_ConstNode(self, node): | |
| self.put(str(node.value)) | |
| def visit_ImagNode(self, node): | |
| self.put(node.value) | |
| self.put(u"j") | |
| def emit_string(self, node, prefix=u""): | |
| repr_val = repr(node.value) | |
| if repr_val[0] in 'ub': | |
| repr_val = repr_val[1:] | |
| self.put(u"%s%s" % (prefix, repr_val)) | |
| def visit_BytesNode(self, node): | |
| self.emit_string(node, u"b") | |
| def visit_StringNode(self, node): | |
| self.emit_string(node) | |
| def visit_UnicodeNode(self, node): | |
| self.emit_string(node, u"u") | |
| def emit_sequence(self, node, parens=(u"", u"")): | |
| open_paren, close_paren = parens | |
| items = node.subexpr_nodes() | |
| self.put(open_paren) | |
| self.comma_separated_list(items) | |
| self.put(close_paren) | |
| def visit_ListNode(self, node): | |
| self.emit_sequence(node, u"[]") | |
| def visit_TupleNode(self, node): | |
| self.emit_sequence(node, u"()") | |
| def visit_SetNode(self, node): | |
| if len(node.subexpr_nodes()) > 0: | |
| self.emit_sequence(node, u"{}") | |
| else: | |
| self.put(u"set()") | |
| def visit_DictNode(self, node): | |
| self.emit_sequence(node, u"{}") | |
| def visit_DictItemNode(self, node): | |
| self.visit(node.key) | |
| self.put(u": ") | |
| self.visit(node.value) | |
| unop_precedence = { | |
| 'not': 3, '!': 3, | |
| '+': 11, '-': 11, '~': 11, | |
| } | |
| binop_precedence = { | |
| 'or': 1, | |
| 'and': 2, | |
| # unary: 'not': 3, '!': 3, | |
| 'in': 4, 'not_in': 4, 'is': 4, 'is_not': 4, '<': 4, '<=': 4, '>': 4, '>=': 4, '!=': 4, '==': 4, | |
| '|': 5, | |
| '^': 6, | |
| '&': 7, | |
| '<<': 8, '>>': 8, | |
| '+': 9, '-': 9, | |
| '*': 10, '@': 10, '/': 10, '//': 10, '%': 10, | |
| # unary: '+': 11, '-': 11, '~': 11 | |
| '**': 12, | |
| } | |
| def operator_enter(self, new_prec): | |
| old_prec = self.precedence[-1] | |
| if old_prec > new_prec: | |
| self.put(u"(") | |
| self.precedence.append(new_prec) | |
| def operator_exit(self): | |
| old_prec, new_prec = self.precedence[-2:] | |
| if old_prec > new_prec: | |
| self.put(u")") | |
| self.precedence.pop() | |
| def visit_NotNode(self, node): | |
| op = 'not' | |
| prec = self.unop_precedence[op] | |
| self.operator_enter(prec) | |
| self.put(u"not ") | |
| self.visit(node.operand) | |
| self.operator_exit() | |
| def visit_UnopNode(self, node): | |
| op = node.operator | |
| prec = self.unop_precedence[op] | |
| self.operator_enter(prec) | |
| self.put(u"%s" % node.operator) | |
| self.visit(node.operand) | |
| self.operator_exit() | |
| def visit_BinopNode(self, node): | |
| op = node.operator | |
| prec = self.binop_precedence.get(op, 0) | |
| self.operator_enter(prec) | |
| self.visit(node.operand1) | |
| self.put(u" %s " % op.replace('_', ' ')) | |
| self.visit(node.operand2) | |
| self.operator_exit() | |
| def visit_BoolBinopNode(self, node): | |
| self.visit_BinopNode(node) | |
| def visit_PrimaryCmpNode(self, node): | |
| self.visit_BinopNode(node) | |
| def visit_IndexNode(self, node): | |
| self.visit(node.base) | |
| self.put(u"[") | |
| if isinstance(node.index, TupleNode): | |
| self.emit_sequence(node.index) | |
| else: | |
| self.visit(node.index) | |
| self.put(u"]") | |
| def visit_SliceIndexNode(self, node): | |
| self.visit(node.base) | |
| self.put(u"[") | |
| if node.start: | |
| self.visit(node.start) | |
| self.put(u":") | |
| if node.stop: | |
| self.visit(node.stop) | |
| if node.slice: | |
| self.put(u":") | |
| self.visit(node.slice) | |
| self.put(u"]") | |
| def visit_SliceNode(self, node): | |
| if not node.start.is_none: | |
| self.visit(node.start) | |
| self.put(u":") | |
| if not node.stop.is_none: | |
| self.visit(node.stop) | |
| if not node.step.is_none: | |
| self.put(u":") | |
| self.visit(node.step) | |
| def visit_CondExprNode(self, node): | |
| self.visit(node.true_val) | |
| self.put(u" if ") | |
| self.visit(node.test) | |
| self.put(u" else ") | |
| self.visit(node.false_val) | |
| def visit_AttributeNode(self, node): | |
| self.visit(node.obj) | |
| self.put(u".%s" % node.attribute) | |
| def visit_SimpleCallNode(self, node): | |
| self.visit(node.function) | |
| self.put(u"(") | |
| self.comma_separated_list(node.args) | |
| self.put(")") | |
| def emit_pos_args(self, node): | |
| if node is None: | |
| return | |
| if isinstance(node, AddNode): | |
| self.emit_pos_args(node.operand1) | |
| self.emit_pos_args(node.operand2) | |
| elif isinstance(node, TupleNode): | |
| for expr in node.subexpr_nodes(): | |
| self.visit(expr) | |
| self.put(u", ") | |
| elif isinstance(node, AsTupleNode): | |
| self.put("*") | |
| self.visit(node.arg) | |
| self.put(u", ") | |
| else: | |
| self.visit(node) | |
| self.put(u", ") | |
| def emit_kwd_args(self, node): | |
| if node is None: | |
| return | |
| if isinstance(node, MergedDictNode): | |
| for expr in node.subexpr_nodes(): | |
| self.emit_kwd_args(expr) | |
| elif isinstance(node, DictNode): | |
| for expr in node.subexpr_nodes(): | |
| self.put(u"%s=" % expr.key.value) | |
| self.visit(expr.value) | |
| self.put(u", ") | |
| else: | |
| self.put(u"**") | |
| self.visit(node) | |
| self.put(u", ") | |
| def visit_GeneralCallNode(self, node): | |
| self.visit(node.function) | |
| self.put(u"(") | |
| self.emit_pos_args(node.positional_args) | |
| self.emit_kwd_args(node.keyword_args) | |
| self.remove(u", ") | |
| self.put(")") | |
| def emit_comprehension(self, body, target, | |
| sequence, condition, | |
| parens=(u"", u"")): | |
| open_paren, close_paren = parens | |
| self.put(open_paren) | |
| self.visit(body) | |
| self.put(u" for ") | |
| self.visit(target) | |
| self.put(u" in ") | |
| self.visit(sequence) | |
| if condition: | |
| self.put(u" if ") | |
| self.visit(condition) | |
| self.put(close_paren) | |
| def visit_ComprehensionAppendNode(self, node): | |
| self.visit(node.expr) | |
| def visit_DictComprehensionAppendNode(self, node): | |
| self.visit(node.key_expr) | |
| self.put(u": ") | |
| self.visit(node.value_expr) | |
| def visit_ComprehensionNode(self, node): | |
| tpmap = {'list': u"[]", 'dict': u"{}", 'set': u"{}"} | |
| parens = tpmap[node.type.py_type_name()] | |
| body = node.loop.body | |
| target = node.loop.target | |
| sequence = node.loop.iterator.sequence | |
| condition = None | |
| if hasattr(body, 'if_clauses'): | |
| # type(body) is Nodes.IfStatNode | |
| condition = body.if_clauses[0].condition | |
| body = body.if_clauses[0].body | |
| self.emit_comprehension(body, target, sequence, condition, parens) | |
| def visit_GeneratorExpressionNode(self, node): | |
| body = node.loop.body | |
| target = node.loop.target | |
| sequence = node.loop.iterator.sequence | |
| condition = None | |
| if hasattr(body, 'if_clauses'): | |
| # type(body) is Nodes.IfStatNode | |
| condition = body.if_clauses[0].condition | |
| body = body.if_clauses[0].body.expr.arg | |
| elif hasattr(body, 'expr'): | |
| # type(body) is Nodes.ExprStatNode | |
| body = body.expr.arg | |
| self.emit_comprehension(body, target, sequence, condition, u"()") | |