OLD | NEW |
(Empty) | |
| 1 """ |
| 2 Serializes a Cython code tree to Cython code. This is primarily useful for |
| 3 debugging and testing purposes. |
| 4 |
| 5 The output is in a strict format, no whitespace or comments from the input |
| 6 is preserved (and it could not be as it is not present in the code tree). |
| 7 """ |
| 8 |
| 9 from Cython.Compiler.Visitor import TreeVisitor |
| 10 from Cython.Compiler.ExprNodes import * |
| 11 |
| 12 class 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 |
| 28 class 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_v
alue_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.attribute
s) |
| 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 |
| 323 class 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 |
| 490 class 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 |
OLD | NEW |