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 11 import mambo.core._; 12 13 import clang.c.Index; 14 import clang.Cursor; 15 import clang.File; 16 import clang.TranslationUnit; 17 import clang.Type; 18 import clang.Util; 19 20 import dstep.translator.Declaration; 21 import dstep.translator.Enum; 22 import dstep.translator.IncludeHandler; 23 import dstep.translator.objc.Category; 24 import dstep.translator.objc.ObjcInterface; 25 import dstep.translator.Output; 26 import dstep.translator.Record; 27 import dstep.translator.Type; 28 29 private static string[Cursor] anonymousNames; 30 31 class Translator 32 { 33 static struct Options 34 { 35 string outputFile; 36 Language language = Language.c; 37 } 38 39 private 40 { 41 TranslationUnit translationUnit; 42 43 string outputFile; 44 string inputFilename; 45 File inputFile; 46 Language language; 47 string[string] deferredDeclarations; 48 } 49 50 this (string inputFilename, TranslationUnit translationUnit, const Options options = Options.init) 51 { 52 this.inputFilename = inputFilename; 53 this.translationUnit = translationUnit; 54 outputFile = options.outputFile; 55 language = options.language; 56 57 inputFile = translationUnit.file(inputFilename); 58 } 59 60 void translate () 61 { 62 foreach (cursor, parent ; translationUnit.declarations) 63 { 64 if (skipDeclaration(cursor)) 65 continue; 66 67 output.newContext(); 68 auto code = translate(cursor, parent); 69 70 with (CXCursorKind) 71 switch (cursor.kind) 72 { 73 case CXCursor_ObjCInterfaceDecl: 74 case CXCursor_ObjCProtocolDecl: 75 case CXCursor_ObjCCategoryDecl: 76 output.classes ~= code; 77 break; 78 79 case CXCursor_StructDecl: 80 if (cursor.isDefinition) 81 output.structs ~= code; 82 break; 83 case CXCursor_EnumDecl: output.enums ~= code; break; 84 case CXCursor_UnionDecl: output.unions ~= code; break; 85 case CXCursor_VarDecl: output.variables ~= code; break; 86 case CXCursor_FunctionDecl: output.functions ~= code; break; 87 case CXCursor_TypedefDecl: output.typedefs ~= code; break; 88 89 default: continue; 90 } 91 } 92 93 output.structs ~= deferredDeclarations.values; 94 output.externDeclaration = externDeclaration(); 95 96 auto data = output.toString; 97 write(outputFile, data); 98 } 99 100 string translate (Cursor cursor, Cursor parent = Cursor.empty) 101 { 102 with (CXCursorKind) 103 switch (cursor.kind) 104 { 105 case CXCursor_ObjCInterfaceDecl: 106 return (new ObjcInterface!(ClassData)(cursor, parent, this)).translate; 107 break; 108 109 case CXCursor_ObjCProtocolDecl: 110 return (new ObjcInterface!(InterfaceData)(cursor, parent, this)).translate; 111 break; 112 113 case CXCursor_ObjCCategoryDecl: 114 return (new Category(cursor, parent, this)).translate; 115 break; 116 117 case CXCursor_VarDecl: 118 { 119 auto context = output.newContext(); 120 version (D1) 121 context ~= "extern "; 122 else 123 context ~= "extern __gshared "; 124 return variable(cursor, context); 125 } 126 break; 127 128 case CXCursor_FunctionDecl: 129 { 130 auto name = translateIdentifier(cursor.spelling); 131 return translateFunction(cursor.func, name, output) ~ ";"; 132 } 133 break; 134 135 case CXCursor_TypedefDecl: 136 return typedef_(cursor, output.newContext); 137 break; 138 139 case CXCursor_StructDecl: 140 auto code = (new Record!(StructData)(cursor, parent, this)).translate; 141 if (cursor.isDefinition) 142 { 143 if (cursor.spelling in deferredDeclarations) 144 deferredDeclarations.remove(cursor.spelling); 145 return code; 146 } 147 else 148 { 149 deferredDeclarations[cursor.spelling] = code; 150 return ""; 151 } 152 break; 153 case CXCursor_EnumDecl: return (new Enum(cursor, parent, this)).translate; break; 154 case CXCursor_UnionDecl: return (new Record!(UnionData)(cursor, parent, this)).translate; break; 155 156 default: 157 return ""; 158 //assert(0, `Translator.translate: missing implementation for "` ~ cursor.kind.toString ~ `".`); 159 } 160 } 161 162 string variable (Cursor cursor, String context = null) 163 { 164 if (!context) 165 context = output; 166 167 context ~= translateType(cursor.type); 168 context ~= " " ~ translateIdentifier(cursor.spelling); 169 context ~= ";"; 170 171 return context.data; 172 } 173 174 string typedef_ (Cursor cursor, String context = output) 175 { 176 context ~= "alias "; 177 context ~= translateType(cursor.type.canonicalType); 178 context ~= " " ~ cursor.spelling; 179 context ~= ";"; 180 181 return context.data; 182 } 183 184 private: 185 186 bool skipDeclaration (Cursor cursor) 187 { 188 return inputFile != cursor.location.spelling.file; 189 } 190 191 string externDeclaration () 192 { 193 final switch (language) 194 { 195 case Language.c: return "extern (C):"; 196 case Language.objC: return "extern (Objective-C):"; 197 // case Language.cpp: return "extern (C++):"; 198 } 199 } 200 } 201 202 string translateFunction (FunctionCursor func, string name, String context, bool isStatic = false) 203 { 204 if (isStatic) 205 context ~= "static "; 206 207 Parameter[] params; 208 209 if (func.type.isValid) // This will be invalid of Objective-C methods 210 params.reserve(func.type.func.arguments.length); 211 212 foreach (param ; func.parameters) 213 { 214 auto type = translateType(param.type); 215 params ~= Parameter(type, param.spelling); 216 } 217 218 auto resultType = translateType(func.resultType); 219 220 return translateFunction(resultType, name, params, func.isVariadic, context); 221 } 222 223 string getAnonymousName (Cursor cursor) 224 { 225 if (auto name = cursor in anonymousNames) 226 return *name; 227 228 return ""; 229 } 230 231 string generateAnonymousName (Cursor cursor) 232 { 233 auto name = getAnonymousName(cursor); 234 235 if (name.isBlank) 236 { 237 name = "_Anonymous_" ~ anonymousNames.length.toString; 238 anonymousNames[cursor] = name; 239 } 240 241 return name; 242 } 243 244 package struct Parameter 245 { 246 string type; 247 string name; 248 bool isConst; 249 } 250 251 package string translateFunction (string result, string name, Parameter[] parameters, bool variadic, String context) 252 { 253 context ~= result; 254 context ~= ' '; 255 context ~= name ~ " ("; 256 257 string[] params; 258 params.reserve(parameters.length); 259 260 foreach (param ; parameters) 261 { 262 string p; 263 264 version(D1) 265 { 266 p ~= param.type; 267 } 268 else 269 { 270 if (param.isConst) 271 p ~= "const("; 272 273 p ~= param.type; 274 275 if (param.isConst) 276 p ~= ')'; 277 } 278 279 if (param.name.any) 280 p ~= " " ~ translateIdentifier(param.name); 281 282 params ~= p; 283 } 284 285 if (variadic) 286 params ~= "..."; 287 288 context ~= params.join(", "); 289 context ~= ')'; 290 291 return context.data; 292 } 293 294 string translateIdentifier (string str) 295 { 296 return isDKeyword(str) ? str ~ '_' : str; 297 } 298 299 string getInclude (Type type) 300 in 301 { 302 assert(type.isValid); 303 } 304 body 305 { 306 return type.declaration.location.spelling.file.name; 307 } 308 309 void handleInclude (Type type) 310 { 311 includeHandler.addInclude(getInclude(type)); 312 } 313 314 bool isDKeyword (string str) 315 { 316 switch (str) 317 { 318 case "abstract": 319 case "alias": 320 case "align": 321 case "asm": 322 case "assert": 323 case "auto": 324 325 case "body": 326 case "bool": 327 case "break": 328 case "byte": 329 330 case "case": 331 case "cast": 332 case "catch": 333 case "cdouble": 334 case "cent": 335 case "cfloat": 336 case "char": 337 case "class": 338 case "const": 339 case "continue": 340 case "creal": 341 342 case "dchar": 343 case "debug": 344 case "default": 345 case "delegate": 346 case "delete": 347 case "deprecated": 348 case "do": 349 case "double": 350 351 case "else": 352 case "enum": 353 case "export": 354 case "extern": 355 356 case "false": 357 case "final": 358 case "finally": 359 case "float": 360 case "for": 361 case "foreach": 362 case "foreach_reverse": 363 case "function": 364 365 case "goto": 366 367 case "idouble": 368 case "if": 369 case "ifloat": 370 case "import": 371 case "in": 372 case "inout": 373 case "int": 374 case "interface": 375 case "invariant": 376 case "ireal": 377 case "is": 378 379 case "lazy": 380 case "long": 381 382 case "macro": 383 case "mixin": 384 case "module": 385 386 case "new": 387 case "nothrow": 388 case "null": 389 390 case "out": 391 case "override": 392 393 case "package": 394 case "pragma": 395 case "private": 396 case "protected": 397 case "public": 398 case "pure": 399 400 case "real": 401 case "ref": 402 case "return": 403 404 case "scope": 405 case "shared": 406 case "short": 407 case "static": 408 case "struct": 409 case "super": 410 case "switch": 411 case "synchronized": 412 413 case "template": 414 case "this": 415 case "throw": 416 case "true": 417 case "try": 418 case "typedef": 419 case "typeid": 420 case "typeof": 421 422 case "ubyte": 423 case "ucent": 424 case "uint": 425 case "ulong": 426 case "union": 427 case "unittest": 428 case "ushort": 429 430 case "version": 431 case "void": 432 case "volatile": 433 434 case "wchar": 435 case "while": 436 case "with": 437 438 case "__FILE__": 439 case "__LINE__": 440 case "__DATE__": 441 case "__TIME__": 442 case "__TIMESTAMP__": 443 case "__VENDOR__": 444 case "__VERSION__": 445 return true; 446 447 default: break; 448 } 449 450 if (true /*D2*/) 451 { 452 switch (str) 453 { 454 case "immutable": 455 case "nothrow": 456 case "pure": 457 case "shared": 458 459 case "__gshared": 460 case "__thread": 461 case "__traits": 462 463 case "__EOF__": 464 return true; 465 466 default: return str.any && str.first == '@'; 467 } 468 } 469 470 return false; 471 } 472 473 enum Language 474 { 475 c, 476 objC 477 // Can't handle C++ yet 478 // cpp 479 }