1 /** 2 * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved. 3 * Authors: Jacob Carlborg 4 * Version: Initial created: Oct 6, 2011 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 */ 7 module dstep.translator.Translator; 8 9 import std.file; 10 import std.array; 11 12 import clang.c.Index; 13 import clang.Cursor; 14 import clang.File; 15 import clang.Index; 16 import clang.TranslationUnit; 17 import clang.Type; 18 import clang.Util; 19 20 import dstep.core.Exceptions; 21 import dstep.Configuration; 22 23 import dstep.translator.Context; 24 import dstep.translator.Declaration; 25 import dstep.translator.Enum; 26 import dstep.translator.IncludeHandler; 27 import dstep.translator.objc.Category; 28 import dstep.translator.objc.ObjcInterface; 29 import dstep.translator.Options; 30 import dstep.translator.Output; 31 import dstep.translator.MacroDefinition; 32 import dstep.translator.Record; 33 import dstep.translator.Type; 34 import dstep.translator.TypeInference; 35 36 public import dstep.translator.Options; 37 38 class TranslationException : DStepException 39 { 40 this (string message, string file = __FILE__, size_t line = __LINE__) 41 { 42 super(message, file, line); 43 } 44 } 45 46 class Translator 47 { 48 private 49 { 50 TranslationUnit translationUnit; 51 52 string outputFile; 53 string inputFilename; 54 File inputFile; 55 Language language; 56 string[string] deferredDeclarations; 57 } 58 59 TypedMacroDefinition[string] typedMacroDefinitions; 60 Context context; 61 62 this (TranslationUnit translationUnit, Options options = Options.init) 63 { 64 this.inputFilename = translationUnit.spelling; 65 this.translationUnit = translationUnit; 66 outputFile = options.outputFile; 67 language = options.language; 68 69 inputFile = translationUnit.file(inputFilename); 70 context = new Context(translationUnit, options, this); 71 } 72 73 void translate () 74 { 75 write(outputFile, translateToString()); 76 } 77 78 Output translateCursors() 79 { 80 Output result = new Output(context.commentIndex); 81 typedMacroDefinitions = inferMacroSignatures(context); 82 83 bool first = true; 84 85 foreach (cursor, parent; translationUnit.cursor.allInOrder) 86 { 87 if (!skipDeclaration(cursor)) 88 { 89 if (first) 90 { 91 if (result.flushHeaderComment()) 92 result.separator(); 93 94 externDeclaration(result); 95 first = false; 96 } 97 98 translateInGlobalScope(result, cursor, parent); 99 } 100 } 101 102 if (context.commentIndex) 103 result.flushLocation(context.commentIndex.queryLastLocation()); 104 105 foreach (value; deferredDeclarations.values) 106 result.singleLine(value); 107 108 result.finalize(); 109 110 return result; 111 } 112 113 string translateToString() 114 { 115 import std.algorithm.mutation : strip; 116 117 Output main = translateCursors(); 118 Output head = new Output(); 119 120 moduleDeclaration(head); 121 context.includeHandler.toImports(head); 122 123 return main.header ~ head.data ~ main.content; 124 } 125 126 void translateInGlobalScope( 127 Output output, 128 Cursor cursor, 129 Cursor parent = Cursor.empty) 130 { 131 translate(output, cursor, parent); 132 133 if (!context.globalScope.empty) 134 { 135 output.separator(); 136 output.output(context.globalScope); 137 output.separator(); 138 context.globalScope.reset(); 139 } 140 } 141 142 void translate ( 143 Output output, 144 Cursor cursor, 145 Cursor parent = Cursor.empty) 146 { 147 with (CXCursorKind) 148 { 149 switch (cursor.kind) 150 { 151 case objCInterfaceDecl: 152 output.flushLocation(cursor.extent, false); 153 translateObjCInterfaceDecl(output, cursor, parent); 154 break; 155 156 case objCProtocolDecl: 157 output.flushLocation(cursor.extent, false); 158 translateObjCProtocolDecl(output, cursor, parent); 159 break; 160 161 case objCCategoryDecl: 162 output.flushLocation(cursor.extent, false); 163 translateObjCCategoryDecl(output, cursor, parent); 164 break; 165 166 case varDecl: 167 output.flushLocation(cursor.extent); 168 translateVarDecl(output, cursor, parent); 169 break; 170 171 case functionDecl: 172 translateFunctionDecl(output, cursor, parent); 173 break; 174 175 case typedefDecl: 176 translateTypedefDecl(output, cursor); 177 break; 178 179 case structDecl: 180 translateRecord(output, context, cursor); 181 break; 182 183 case enumDecl: 184 translateEnum(output, context, cursor); 185 break; 186 187 case unionDecl: 188 translateRecord(output, context, cursor); 189 break; 190 191 case macroDefinition: 192 output.flushLocation(cursor.extent); 193 translateMacroDefinition(output, cursor, parent); 194 break; 195 196 case macroExpansion: 197 output.flushLocation(cursor.extent); 198 break; 199 200 default: 201 break; 202 } 203 } 204 } 205 206 void translateObjCInterfaceDecl(Output output, Cursor cursor, Cursor parent) 207 { 208 (new ObjcInterface!(ClassData)(cursor, parent, this)).translate(output); 209 } 210 211 void translateObjCProtocolDecl(Output output, Cursor cursor, Cursor parent) 212 { 213 (new ObjcInterface!(InterfaceData)(cursor, parent, this)).translate(output); 214 } 215 216 void translateObjCCategoryDecl(Output output, Cursor cursor, Cursor parent) 217 { 218 (new Category(cursor, parent, this)).translate(output); 219 } 220 221 void translateVarDecl(Output output, Cursor cursor, Cursor parent) 222 { 223 version (D1) 224 string storageClass = "extern "; 225 else 226 string storageClass = "extern __gshared "; 227 228 variable(output, cursor, storageClass); 229 } 230 231 void translateFunctionDecl(Output output, Cursor cursor, Cursor parent) 232 { 233 output.flushLocation(cursor.extent); 234 235 immutable auto name = translateIdentifier(cursor.spelling); 236 output.adaptiveSourceNode(translateFunction(context, cursor.func, name)); 237 output.append(";"); 238 } 239 240 void declareRecordForTypedef(Output output, Cursor typedef_) 241 { 242 assert(typedef_.kind == CXCursorKind.typedefDecl); 243 244 auto underlying = typedef_.underlyingCursor(); 245 246 if (underlying.isEmpty) 247 return; 248 249 if (underlying.isEmpty || 250 underlying.kind != CXCursorKind.structDecl && 251 underlying.kind != CXCursorKind.unionDecl) 252 return; 253 254 if (context.alreadyDefined(underlying.canonical)) 255 return; 256 257 bool skipdef = shouldSkipRecordDefinition(context, underlying); 258 259 if (underlying.definition.isEmpty || skipdef) 260 translateRecordDecl(output, context, underlying); 261 } 262 263 bool shouldSkipAlias(Cursor typedef_) 264 { 265 assert(typedef_.kind == CXCursorKind.typedefDecl); 266 return context.options.reduceAliases && typedef_.type.isAliasReducible; 267 } 268 269 void translateTypedefDecl(Output output, Cursor typedef_) 270 { 271 assert(typedef_.kind == CXCursorKind.typedefDecl); 272 273 output.flushLocation(typedef_.extent); 274 275 auto underlying = typedef_.underlyingCursor; 276 277 if (!context.shouldSkipRecord(underlying) && !shouldSkipAlias(typedef_)) 278 { 279 declareRecordForTypedef(output, typedef_); 280 281 auto typedefp = context.typedefParent(underlying); 282 283 if (typedef_ != typedefp || 284 underlying.isEmpty || 285 (underlying.spelling != typedef_.spelling && 286 underlying.spelling != "")) 287 { 288 auto canonical = typedef_.type.canonical; 289 290 auto type = translateType(context, typedef_, canonical); 291 292 auto typeSpelling = type.makeString(); 293 294 // Do not alias itself 295 if (typedef_.spelling != typeSpelling) 296 { 297 auto spelling = translateIdentifier(typedef_.spelling); 298 299 version (D1) 300 { 301 output.adaptiveSourceNode( 302 type.wrapWith("alias ", " " ~ spelling ~ ";")); 303 } 304 else 305 { 306 output.adaptiveSourceNode( 307 type.wrapWith("alias " ~ spelling ~ " = ", ";")); 308 } 309 } 310 311 context.markAsDefined(typedef_); 312 } 313 } 314 } 315 316 void translateMacroDefinition(Output output, Cursor cursor, Cursor parent) 317 { 318 if (context.options.translateMacros) 319 { 320 if (auto definition = cursor.spelling in typedMacroDefinitions) 321 { 322 dstep.translator.MacroDefinition 323 .translateMacroDefinition(output, context, *definition); 324 } 325 } 326 } 327 328 void variable (Output output, Cursor cursor, string prefix = "") 329 { 330 translateVariable(output, context, cursor, prefix); 331 } 332 333 private: 334 335 bool skipDeclaration (Cursor cursor) 336 { 337 return (inputFilename != "" && 338 inputFile != cursor.location.spelling.file) 339 || context.options.skipSymbols.contains(cursor.spelling) 340 || cursor.isPredefined; 341 } 342 343 void moduleDeclaration (Output output) 344 { 345 if (context.options.packageName != "") 346 { 347 output.singleLine("module %s;", fullModuleName( 348 context.options.packageName, 349 context.options.outputFile, 350 context.options.normalizeModules)); 351 352 output.separator(); 353 } 354 } 355 356 void externDeclaration (Output output) 357 { 358 final switch (language) 359 { 360 case Language.c: 361 output.singleLine("extern (C):"); 362 break; 363 364 case Language.objC: 365 output.singleLine("extern (Objective-C):"); 366 break; 367 } 368 369 foreach (attribute; context.options.globalAttributes) 370 output.singleLine("%s:", attribute); 371 372 output.separator(); 373 } 374 } 375 376 SourceNode translateFunction ( 377 Context context, 378 FunctionCursor func, 379 string name, 380 bool isStatic = false) 381 { 382 bool isVariadic(Context context, size_t numParams, FunctionCursor func) 383 { 384 if (func.isVariadic) 385 { 386 if (context.options.zeroParamIsVararg) 387 return true; 388 else if (numParams == 0) 389 return false; 390 else 391 return true; 392 } 393 394 return false; 395 } 396 397 Parameter[] params; 398 399 if (func.type.isValid) // This will be invalid for Objective-C methods 400 params.reserve(func.type.func.arguments.length); 401 402 foreach (param ; func.parameters) 403 { 404 auto type = translateType(context, param); 405 params ~= Parameter(type, param.spelling); 406 } 407 408 auto resultType = translateType(context, func, func.resultType); 409 auto multiline = func.extent.isMultiline && 410 !context.options.singleLineFunctionSignatures; 411 auto spacer = context.options.spaceAfterFunctionName ? " " : ""; 412 413 return translateFunction( 414 resultType, 415 name, 416 params, 417 isVariadic(context, params.length, func), 418 isStatic ? "static " : "", 419 spacer, 420 multiline); 421 } 422 423 package struct Parameter 424 { 425 SourceNode type; 426 string name; 427 bool isConst; 428 } 429 430 package SourceNode translateFunction ( 431 SourceNode resultType, 432 string name, 433 Parameter[] parameters, 434 bool variadic, 435 string prefix = "", 436 string spacer = " ", 437 bool multiline = false) 438 { 439 import std.format : format; 440 441 string[] params; 442 params.reserve(parameters.length); 443 444 foreach (param ; parameters) 445 { 446 string p; 447 448 version(D1) 449 { 450 p ~= param.type; 451 } 452 else 453 { 454 if (param.isConst) 455 p ~= "const("; 456 457 p ~= param.type.makeString(); 458 459 if (param.isConst) 460 p ~= ')'; 461 } 462 463 if (param.name.length) 464 p ~= " " ~ translateIdentifier(param.name); 465 466 params ~= p; 467 } 468 469 if (variadic) 470 params ~= "..."; 471 472 auto result = makeSourceNode( 473 format("%s%s %s%s(", prefix, resultType.makeString(), name, spacer), 474 params, 475 ",", 476 ")"); 477 478 return multiline ? result : result.flatten(); 479 } 480 481 void translateVariable (Output output, Context context, Cursor cursor, string prefix = "") 482 { 483 if (!context.alreadyDefined(cursor.canonical)) 484 { 485 auto type = translateType(context, cursor, cursor.type); 486 auto identifier = translateIdentifier(cursor.spelling); 487 output.adaptiveSourceNode(type.wrapWith(prefix, " " ~ identifier ~ ";")); 488 context.markAsDefined(cursor.canonical); 489 } 490 } 491 492 void handleInclude (Context context, Type type) 493 { 494 import std.algorithm.searching; 495 import std.path; 496 497 if (!context.includeHandler.resolveDependency(type.declaration)) { 498 context.includeHandler.addInclude(type.declaration.path); 499 } 500 } 501 502 bool isDKeyword (string str) 503 { 504 switch (str) 505 { 506 case "abstract": 507 case "alias": 508 case "align": 509 case "asm": 510 case "assert": 511 case "auto": 512 513 case "body": 514 case "bool": 515 case "break": 516 case "byte": 517 518 case "case": 519 case "cast": 520 case "catch": 521 case "cdouble": 522 case "cent": 523 case "cfloat": 524 case "char": 525 case "class": 526 case "const": 527 case "continue": 528 case "creal": 529 530 case "dchar": 531 case "debug": 532 case "default": 533 case "delegate": 534 case "delete": 535 case "deprecated": 536 case "do": 537 case "double": 538 539 case "else": 540 case "enum": 541 case "export": 542 case "extern": 543 544 case "false": 545 case "final": 546 case "finally": 547 case "float": 548 case "for": 549 case "foreach": 550 case "foreach_reverse": 551 case "function": 552 553 case "goto": 554 555 case "idouble": 556 case "if": 557 case "ifloat": 558 case "import": 559 case "in": 560 case "inout": 561 case "int": 562 case "interface": 563 case "invariant": 564 case "ireal": 565 case "is": 566 567 case "lazy": 568 case "long": 569 570 case "macro": 571 case "mixin": 572 case "module": 573 574 case "new": 575 case "nothrow": 576 case "null": 577 578 case "out": 579 case "override": 580 581 case "package": 582 case "pragma": 583 case "private": 584 case "protected": 585 case "public": 586 case "pure": 587 588 case "real": 589 case "ref": 590 case "return": 591 592 case "scope": 593 case "shared": 594 case "short": 595 case "static": 596 case "struct": 597 case "super": 598 case "switch": 599 case "synchronized": 600 601 case "template": 602 case "this": 603 case "throw": 604 case "true": 605 case "try": 606 case "typedef": 607 case "typeid": 608 case "typeof": 609 610 case "ubyte": 611 case "ucent": 612 case "uint": 613 case "ulong": 614 case "union": 615 case "unittest": 616 case "ushort": 617 618 case "version": 619 case "void": 620 case "volatile": 621 622 case "wchar": 623 case "while": 624 case "with": 625 626 case "__FILE__": 627 case "__LINE__": 628 case "__DATE__": 629 case "__TIME__": 630 case "__TIMESTAMP__": 631 case "__VENDOR__": 632 case "__VERSION__": 633 return true; 634 635 default: break; 636 } 637 638 if (true /*D2*/) 639 { 640 switch (str) 641 { 642 case "immutable": 643 case "nothrow": 644 case "pure": 645 case "shared": 646 647 case "__gshared": 648 case "__thread": 649 case "__traits": 650 651 case "__EOF__": 652 return true; 653 654 default: return str.length && str[0] == '@'; 655 } 656 } 657 658 return false; 659 } 660 661 string renameDKeyword (string str) 662 { 663 return str ~ '_'; 664 } 665 666 string translateIdentifier (string str) 667 { 668 return isDKeyword(str) ? renameDKeyword(str) : str; 669 }