1""" 2Serializes a Cython code tree to Cython code. This is primarily useful for 3debugging and testing purposes. 4 5The output is in a strict format, no whitespace or comments from the input 6is preserved (and it could not be as it is not present in the code tree). 7""" 8 9from Cython.Compiler.Visitor import TreeVisitor 10from Cython.Compiler.ExprNodes import * 11 12class LinesResult(object): 13 def __init__(self): 14 self.lines = [] 15 self.s = u"" 16 17 def put(self, s): 18 self.s += s 19 20 def newline(self): 21 self.lines.append(self.s) 22 self.s = u"" 23 24 def putline(self, s): 25 self.put(s) 26 self.newline() 27 28class DeclarationWriter(TreeVisitor): 29 30 indent_string = u" " 31 32 def __init__(self, result = None): 33 super(DeclarationWriter, self).__init__() 34 if result is None: 35 result = LinesResult() 36 self.result = result 37 self.numindents = 0 38 self.tempnames = {} 39 self.tempblockindex = 0 40 41 def write(self, tree): 42 self.visit(tree) 43 return self.result 44 45 def indent(self): 46 self.numindents += 1 47 48 def dedent(self): 49 self.numindents -= 1 50 51 def startline(self, s = u""): 52 self.result.put(self.indent_string * self.numindents + s) 53 54 def put(self, s): 55 self.result.put(s) 56 57 def putline(self, s): 58 self.result.putline(self.indent_string * self.numindents + s) 59 60 def endline(self, s = u""): 61 self.result.putline(s) 62 63 def line(self, s): 64 self.startline(s) 65 self.endline() 66 67 def comma_separated_list(self, items, output_rhs=False): 68 if len(items) > 0: 69 for item in items[:-1]: 70 self.visit(item) 71 if output_rhs and item.default is not None: 72 self.put(u" = ") 73 self.visit(item.default) 74 self.put(u", ") 75 self.visit(items[-1]) 76 77 def visit_Node(self, node): 78 raise AssertionError("Node not handled by serializer: %r" % node) 79 80 def visit_ModuleNode(self, node): 81 self.visitchildren(node) 82 83 def visit_StatListNode(self, node): 84 self.visitchildren(node) 85 86 def visit_CDefExternNode(self, node): 87 if node.include_file is None: 88 file = u'*' 89 else: 90 file = u'"%s"' % node.include_file 91 self.putline(u"cdef extern from %s:" % file) 92 self.indent() 93 self.visit(node.body) 94 self.dedent() 95 96 def visit_CPtrDeclaratorNode(self, node): 97 self.put('*') 98 self.visit(node.base) 99 100 def visit_CReferenceDeclaratorNode(self, node): 101 self.put('&') 102 self.visit(node.base) 103 104 def visit_CArrayDeclaratorNode(self, node): 105 self.visit(node.base) 106 self.put(u'[') 107 if node.dimension is not None: 108 self.visit(node.dimension) 109 self.put(u']') 110 111 def visit_CArrayDeclaratorNode(self, node): 112 self.visit(node.base) 113 self.put(u'[') 114 if node.dimension is not None: 115 self.visit(node.dimension) 116 self.put(u']') 117 118 def visit_CFuncDeclaratorNode(self, node): 119 # TODO: except, gil, etc. 120 self.visit(node.base) 121 self.put(u'(') 122 self.comma_separated_list(node.args) 123 self.endline(u')') 124 125 def visit_CNameDeclaratorNode(self, node): 126 self.put(node.name) 127 128 def visit_CSimpleBaseTypeNode(self, node): 129 # See Parsing.p_sign_and_longness 130 if node.is_basic_c_type: 131 self.put(("unsigned ", "", "signed ")[node.signed]) 132 if node.longness < 0: 133 self.put("short " * -node.longness) 134 elif node.longness > 0: 135 self.put("long " * node.longness) 136 self.put(node.name) 137 138 def visit_CComplexBaseTypeNode(self, node): 139 self.put(u'(') 140 self.visit(node.base_type) 141 self.visit(node.declarator) 142 self.put(u')') 143 144 def visit_CNestedBaseTypeNode(self, node): 145 self.visit(node.base_type) 146 self.put(u'.') 147 self.put(node.name) 148 149 def visit_TemplatedTypeNode(self, node): 150 self.visit(node.base_type_node) 151 self.put(u'[') 152 self.comma_separated_list(node.positional_args + node.keyword_args.key_value_pairs) 153 self.put(u']') 154 155 def visit_CVarDefNode(self, node): 156 self.startline(u"cdef ") 157 self.visit(node.base_type) 158 self.put(u" ") 159 self.comma_separated_list(node.declarators, output_rhs=True) 160 self.endline() 161 162 def visit_container_node(self, node, decl, extras, attributes): 163 # TODO: visibility 164 self.startline(decl) 165 if node.name: 166 self.put(u' ') 167 self.put(node.name) 168 if node.cname is not None: 169 self.put(u' "%s"' % node.cname) 170 if extras: 171 self.put(extras) 172 self.endline(':') 173 self.indent() 174 if not attributes: 175 self.putline('pass') 176 else: 177 for attribute in attributes: 178 self.visit(attribute) 179 self.dedent() 180 181 def visit_CStructOrUnionDefNode(self, node): 182 if node.typedef_flag: 183 decl = u'ctypedef ' 184 else: 185 decl = u'cdef ' 186 if node.visibility == 'public': 187 decl += u'public ' 188 if node.packed: 189 decl += u'packed ' 190 decl += node.kind 191 self.visit_container_node(node, decl, None, node.attributes) 192 193 def visit_CppClassNode(self, node): 194 extras = "" 195 if node.templates: 196 extras = u"[%s]" % ", ".join(node.templates) 197 if node.base_classes: 198 extras += "(%s)" % ", ".join(node.base_classes) 199 self.visit_container_node(node, u"cdef cppclass", extras, node.attributes) 200 201 def visit_CEnumDefNode(self, node): 202 self.visit_container_node(node, u"cdef enum", None, node.items) 203 204 def visit_CEnumDefItemNode(self, node): 205 self.startline(node.name) 206 if node.cname: 207 self.put(u' "%s"' % node.cname) 208 if node.value: 209 self.put(u" = ") 210 self.visit(node.value) 211 self.endline() 212 213 def visit_CClassDefNode(self, node): 214 assert not node.module_name 215 if node.decorators: 216 for decorator in node.decorators: 217 self.visit(decorator) 218 self.startline(u"cdef class ") 219 self.put(node.class_name) 220 if node.base_class_name: 221 self.put(u"(") 222 if node.base_class_module: 223 self.put(node.base_class_module) 224 self.put(u".") 225 self.put(node.base_class_name) 226 self.put(u")") 227 self.endline(u":") 228 self.indent() 229 self.visit(node.body) 230 self.dedent() 231 232 def visit_CTypeDefNode(self, node): 233 self.startline(u"ctypedef ") 234 self.visit(node.base_type) 235 self.put(u" ") 236 self.visit(node.declarator) 237 self.endline() 238 239 def visit_FuncDefNode(self, node): 240 self.startline(u"def %s(" % node.name) 241 self.comma_separated_list(node.args) 242 self.endline(u"):") 243 self.indent() 244 self.visit(node.body) 245 self.dedent() 246 247 def visit_CArgDeclNode(self, node): 248 if node.base_type.name is not None: 249 self.visit(node.base_type) 250 self.put(u" ") 251 self.visit(node.declarator) 252 if node.default is not None: 253 self.put(u" = ") 254 self.visit(node.default) 255 256 def visit_CImportStatNode(self, node): 257 self.startline(u"cimport ") 258 self.put(node.module_name) 259 if node.as_name: 260 self.put(u" as ") 261 self.put(node.as_name) 262 self.endline() 263 264 def visit_FromCImportStatNode(self, node): 265 self.startline(u"from ") 266 self.put(node.module_name) 267 self.put(u" cimport ") 268 first = True 269 for pos, name, as_name, kind in node.imported_names: 270 assert kind is None 271 if first: 272 first = False 273 else: 274 self.put(u", ") 275 self.put(name) 276 if as_name: 277 self.put(u" as ") 278 self.put(as_name) 279 self.endline() 280 281 def visit_NameNode(self, node): 282 self.put(node.name) 283 284 def visit_IntNode(self, node): 285 self.put(node.value) 286 287 def visit_NoneNode(self, node): 288 self.put(u"None") 289 290 def visit_NotNode(self, node): 291 self.put(u"(not ") 292 self.visit(node.operand) 293 self.put(u")") 294 295 def visit_DecoratorNode(self, node): 296 self.startline("@") 297 self.visit(node.decorator) 298 self.endline() 299 300 def visit_BinopNode(self, node): 301 self.visit(node.operand1) 302 self.put(u" %s " % node.operator) 303 self.visit(node.operand2) 304 305 def visit_AttributeNode(self, node): 306 self.visit(node.obj) 307 self.put(u".%s" % node.attribute) 308 309 def visit_BoolNode(self, node): 310 self.put(str(node.value)) 311 312 # FIXME: represent string nodes correctly 313 def visit_StringNode(self, node): 314 value = node.value 315 if value.encoding is not None: 316 value = value.encode(value.encoding) 317 self.put(repr(value)) 318 319 def visit_PassStatNode(self, node): 320 self.startline(u"pass") 321 self.endline() 322 323class CodeWriter(DeclarationWriter): 324 325 def visit_SingleAssignmentNode(self, node): 326 self.startline() 327 self.visit(node.lhs) 328 self.put(u" = ") 329 self.visit(node.rhs) 330 self.endline() 331 332 def visit_CascadedAssignmentNode(self, node): 333 self.startline() 334 for lhs in node.lhs_list: 335 self.visit(lhs) 336 self.put(u" = ") 337 self.visit(node.rhs) 338 self.endline() 339 340 def visit_PrintStatNode(self, node): 341 self.startline(u"print ") 342 self.comma_separated_list(node.arg_tuple.args) 343 if not node.append_newline: 344 self.put(u",") 345 self.endline() 346 347 def visit_ForInStatNode(self, node): 348 self.startline(u"for ") 349 self.visit(node.target) 350 self.put(u" in ") 351 self.visit(node.iterator.sequence) 352 self.endline(u":") 353 self.indent() 354 self.visit(node.body) 355 self.dedent() 356 if node.else_clause is not None: 357 self.line(u"else:") 358 self.indent() 359 self.visit(node.else_clause) 360 self.dedent() 361 362 def visit_IfStatNode(self, node): 363 # The IfClauseNode is handled directly without a seperate match 364 # for clariy. 365 self.startline(u"if ") 366 self.visit(node.if_clauses[0].condition) 367 self.endline(":") 368 self.indent() 369 self.visit(node.if_clauses[0].body) 370 self.dedent() 371 for clause in node.if_clauses[1:]: 372 self.startline("elif ") 373 self.visit(clause.condition) 374 self.endline(":") 375 self.indent() 376 self.visit(clause.body) 377 self.dedent() 378 if node.else_clause is not None: 379 self.line("else:") 380 self.indent() 381 self.visit(node.else_clause) 382 self.dedent() 383 384 def visit_SequenceNode(self, node): 385 self.comma_separated_list(node.args) # Might need to discover whether we need () around tuples...hmm... 386 387 def visit_SimpleCallNode(self, node): 388 self.visit(node.function) 389 self.put(u"(") 390 self.comma_separated_list(node.args) 391 self.put(")") 392 393 def visit_GeneralCallNode(self, node): 394 self.visit(node.function) 395 self.put(u"(") 396 posarg = node.positional_args 397 if isinstance(posarg, AsTupleNode): 398 self.visit(posarg.arg) 399 else: 400 self.comma_separated_list(posarg) 401 if node.keyword_args is not None or node.starstar_arg is not None: 402 raise Exception("Not implemented yet") 403 self.put(u")") 404 405 def visit_ExprStatNode(self, node): 406 self.startline() 407 self.visit(node.expr) 408 self.endline() 409 410 def visit_InPlaceAssignmentNode(self, node): 411 self.startline() 412 self.visit(node.lhs) 413 self.put(u" %s= " % node.operator) 414 self.visit(node.rhs) 415 self.endline() 416 417 def visit_WithStatNode(self, node): 418 self.startline() 419 self.put(u"with ") 420 self.visit(node.manager) 421 if node.target is not None: 422 self.put(u" as ") 423 self.visit(node.target) 424 self.endline(u":") 425 self.indent() 426 self.visit(node.body) 427 self.dedent() 428 429 def visit_TryFinallyStatNode(self, node): 430 self.line(u"try:") 431 self.indent() 432 self.visit(node.body) 433 self.dedent() 434 self.line(u"finally:") 435 self.indent() 436 self.visit(node.finally_clause) 437 self.dedent() 438 439 def visit_TryExceptStatNode(self, node): 440 self.line(u"try:") 441 self.indent() 442 self.visit(node.body) 443 self.dedent() 444 for x in node.except_clauses: 445 self.visit(x) 446 if node.else_clause is not None: 447 self.visit(node.else_clause) 448 449 def visit_ExceptClauseNode(self, node): 450 self.startline(u"except") 451 if node.pattern is not None: 452 self.put(u" ") 453 self.visit(node.pattern) 454 if node.target is not None: 455 self.put(u", ") 456 self.visit(node.target) 457 self.endline(":") 458 self.indent() 459 self.visit(node.body) 460 self.dedent() 461 462 def visit_ReturnStatNode(self, node): 463 self.startline("return ") 464 self.visit(node.value) 465 self.endline() 466 467 def visit_ReraiseStatNode(self, node): 468 self.line("raise") 469 470 def visit_ImportNode(self, node): 471 self.put(u"(import %s)" % node.module_name.value) 472 473 def visit_TempsBlockNode(self, node): 474 """ 475 Temporaries are output like $1_1', where the first number is 476 an index of the TempsBlockNode and the second number is an index 477 of the temporary which that block allocates. 478 """ 479 idx = 0 480 for handle in node.temps: 481 self.tempnames[handle] = "$%d_%d" % (self.tempblockindex, idx) 482 idx += 1 483 self.tempblockindex += 1 484 self.visit(node.body) 485 486 def visit_TempRefNode(self, node): 487 self.put(self.tempnames[node.handle]) 488 489 490class PxdWriter(DeclarationWriter): 491 def __call__(self, node): 492 print u'\n'.join(self.write(node).lines) 493 return node 494 495 def visit_CFuncDefNode(self, node): 496 if 'inline' in node.modifiers: 497 return 498 if node.overridable: 499 self.startline(u'cpdef ') 500 else: 501 self.startline(u'cdef ') 502 if node.visibility != 'private': 503 self.put(node.visibility) 504 self.put(u' ') 505 if node.api: 506 self.put(u'api ') 507 self.visit(node.declarator) 508 509 def visit_StatNode(self, node): 510 pass 511 512 513