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 if (underlying.isEmpty || 282 (underlying.spelling != typedef_.spelling && 283 underlying.spelling != "")) 284 { 285 auto canonical = typedef_.type.canonical; 286 auto spelling = typedef_.spelling; 287 auto type = translateType(context, typedef_, canonical); 288 289 version (D1) 290 { 291 output.adaptiveSourceNode( 292 type.wrapWith("alias ", " " ~ spelling ~ ";")); 293 } 294 else 295 { 296 output.adaptiveSourceNode( 297 type.wrapWith("alias " ~ spelling ~ " = ", ";")); 298 } 299 300 context.markAsDefined(typedef_); 301 } 302 } 303 } 304 305 void translateMacroDefinition(Output output, Cursor cursor, Cursor parent) 306 { 307 if (context.options.translateMacros) 308 { 309 if (auto definition = cursor.spelling in typedMacroDefinitions) 310 { 311 dstep.translator.MacroDefinition 312 .translateMacroDefinition(output, context, *definition); 313 } 314 } 315 } 316 317 void variable (Output output, Cursor cursor, string prefix = "") 318 { 319 translateVariable(output, context, cursor, prefix); 320 } 321 322 private: 323 324 bool skipDeclaration (Cursor cursor) 325 { 326 return (inputFilename != "" && 327 inputFile != cursor.location.spelling.file) 328 || context.options.skipSymbols.contains(cursor.spelling) 329 || cursor.isPredefined; 330 } 331 332 void moduleDeclaration (Output output) 333 { 334 if (context.options.packageName != "") 335 { 336 output.singleLine("module %s;", fullModuleName( 337 context.options.packageName, 338 context.options.outputFile, 339 context.options.normalizeModules)); 340 341 output.separator(); 342 } 343 } 344 345 void externDeclaration (Output output) 346 { 347 final switch (language) 348 { 349 case Language.c: 350 output.singleLine("extern (C):"); 351 break; 352 353 case Language.objC: 354 output.singleLine("extern (Objective-C):"); 355 break; 356 } 357 358 foreach (attribute; context.options.globalAttributes) 359 output.singleLine("%s:", attribute); 360 361 output.separator(); 362 } 363 } 364 365 SourceNode translateFunction ( 366 Context context, 367 FunctionCursor func, 368 string name, 369 bool isStatic = false) 370 { 371 bool isVariadic(Context context, size_t numParams, FunctionCursor func) 372 { 373 if (func.isVariadic) 374 { 375 if (context.options.zeroParamIsVararg) 376 return true; 377 else if (numParams == 0) 378 return false; 379 else 380 return true; 381 } 382 383 return false; 384 } 385 386 Parameter[] params; 387 388 if (func.type.isValid) // This will be invalid for Objective-C methods 389 params.reserve(func.type.func.arguments.length); 390 391 foreach (param ; func.parameters) 392 { 393 auto type = translateType(context, param); 394 params ~= Parameter(type, param.spelling); 395 } 396 397 auto resultType = translateType(context, func, func.resultType); 398 auto multiline = func.extent.isMultiline && 399 !context.options.singleLineFunctionSignatures; 400 auto spacer = context.options.spaceAfterFunctionName ? " " : ""; 401 402 return translateFunction( 403 resultType, 404 name, 405 params, 406 isVariadic(context, params.length, func), 407 isStatic ? "static " : "", 408 spacer, 409 multiline); 410 } 411 412 package struct Parameter 413 { 414 SourceNode type; 415 string name; 416 bool isConst; 417 } 418 419 package SourceNode translateFunction ( 420 SourceNode resultType, 421 string name, 422 Parameter[] parameters, 423 bool variadic, 424 string prefix = "", 425 string spacer = " ", 426 bool multiline = false) 427 { 428 import std.format : format; 429 430 string[] params; 431 params.reserve(parameters.length); 432 433 foreach (param ; parameters) 434 { 435 string p; 436 437 version(D1) 438 { 439 p ~= param.type; 440 } 441 else 442 { 443 if (param.isConst) 444 p ~= "const("; 445 446 p ~= param.type.makeString(); 447 448 if (param.isConst) 449 p ~= ')'; 450 } 451 452 if (param.name.length) 453 p ~= " " ~ translateIdentifier(param.name); 454 455 params ~= p; 456 } 457 458 if (variadic) 459 params ~= "..."; 460 461 auto result = makeSourceNode( 462 format("%s%s %s%s(", prefix, resultType.makeString(), name, spacer), 463 params, 464 ",", 465 ")"); 466 467 return multiline ? result : result.flatten(); 468 } 469 470 void translateVariable (Output output, Context context, Cursor cursor, string prefix = "") 471 { 472 if (!context.alreadyDefined(cursor.canonical)) 473 { 474 auto type = translateType(context, cursor, cursor.type); 475 auto identifier = translateIdentifier(cursor.spelling); 476 output.adaptiveSourceNode(type.wrapWith(prefix, " " ~ identifier ~ ";")); 477 context.markAsDefined(cursor.canonical); 478 } 479 } 480 481 string translateIdentifier (string str) 482 { 483 return isDKeyword(str) ? str ~ '_' : str; 484 } 485 486 void handleInclude (Context context, Type type) 487 { 488 import std.algorithm.searching; 489 import std.path; 490 491 if (type.kind == CXTypeKind.typedef_ 492 && type.spelling == "time_t" 493 && type.declaration.path.asNormalizedPath.array.endsWith("sys\\types.h")) 494 context.includeHandler.addInclude("time.h"); 495 else 496 context.includeHandler.addInclude(type.declaration.path); 497 } 498 499 bool isDKeyword (string str) 500 { 501 switch (str) 502 { 503 case "abstract": 504 case "alias": 505 case "align": 506 case "asm": 507 case "assert": 508 case "auto": 509 510 case "body": 511 case "bool": 512 case "break": 513 case "byte": 514 515 case "case": 516 case "cast": 517 case "catch": 518 case "cdouble": 519 case "cent": 520 case "cfloat": 521 case "char": 522 case "class": 523 case "const": 524 case "continue": 525 case "creal": 526 527 case "dchar": 528 case "debug": 529 case "default": 530 case "delegate": 531 case "delete": 532 case "deprecated": 533 case "do": 534 case "double": 535 536 case "else": 537 case "enum": 538 case "export": 539 case "extern": 540 541 case "false": 542 case "final": 543 case "finally": 544 case "float": 545 case "for": 546 case "foreach": 547 case "foreach_reverse": 548 case "function": 549 550 case "goto": 551 552 case "idouble": 553 case "if": 554 case "ifloat": 555 case "import": 556 case "in": 557 case "inout": 558 case "int": 559 case "interface": 560 case "invariant": 561 case "ireal": 562 case "is": 563 564 case "lazy": 565 case "long": 566 567 case "macro": 568 case "mixin": 569 case "module": 570 571 case "new": 572 case "nothrow": 573 case "null": 574 575 case "out": 576 case "override": 577 578 case "package": 579 case "pragma": 580 case "private": 581 case "protected": 582 case "public": 583 case "pure": 584 585 case "real": 586 case "ref": 587 case "return": 588 589 case "scope": 590 case "shared": 591 case "short": 592 case "static": 593 case "struct": 594 case "super": 595 case "switch": 596 case "synchronized": 597 598 case "template": 599 case "this": 600 case "throw": 601 case "true": 602 case "try": 603 case "typedef": 604 case "typeid": 605 case "typeof": 606 607 case "ubyte": 608 case "ucent": 609 case "uint": 610 case "ulong": 611 case "union": 612 case "unittest": 613 case "ushort": 614 615 case "version": 616 case "void": 617 case "volatile": 618 619 case "wchar": 620 case "while": 621 case "with": 622 623 case "__FILE__": 624 case "__LINE__": 625 case "__DATE__": 626 case "__TIME__": 627 case "__TIMESTAMP__": 628 case "__VENDOR__": 629 case "__VERSION__": 630 return true; 631 632 default: break; 633 } 634 635 if (true /*D2*/) 636 { 637 switch (str) 638 { 639 case "immutable": 640 case "nothrow": 641 case "pure": 642 case "shared": 643 644 case "__gshared": 645 case "__thread": 646 case "__traits": 647 648 case "__EOF__": 649 return true; 650 651 default: return str.length && str[0] == '@'; 652 } 653 } 654 655 return false; 656 }