1 module dpp.translation.variable; 2 3 import dpp.from; 4 import dpp.translation.docs; 5 6 string[] translateVariable(in from!"clang".Cursor cursor, 7 ref from!"dpp.runtime.context".Context context) 8 @safe 9 in(cursor.kind == from!"clang".Cursor.Kind.VarDecl) 10 do 11 { 12 import dpp.translation.exception: UntranslatableException; 13 import dpp.translation.dlang: maybePragma; 14 import dpp.translation.translation: translateCursor = translate; 15 import dpp.translation.type: translateType = translate, hasAnonymousSpelling; 16 import dpp.translation.tokens: translateTokens; 17 import clang: Cursor, Type, Token; 18 import std.conv: text; 19 import std.typecons: No; 20 import std.algorithm: canFind, find, map; 21 import std.array: empty, popFront, join, replace; 22 23 string[] ret; 24 25 const isAnonymous = cursor.type.hasAnonymousSpelling; 26 // If the type is anonymous, then we need to define it before we declare 27 // ourselves of that type, unless that type is an enum. See #54. 28 if(isAnonymous && cursor.type.canonical.declaration.kind != Cursor.Kind.EnumDecl) { 29 ret ~= translateCursor(cursor.type.canonical.declaration, context); 30 } 31 32 // variables can be declared multiple times in C but only one in D 33 if(!cursor.isCanonical) return []; 34 35 // Don't bother if we don't have a definition anywhere - C allows this but D 36 // doesn't. See it.compile.projects.ASN1_ITEM or try #including <openssl/ssl.h>. 37 // There will be a problem with the global variables such as DHparams_it that 38 // have a struct with an unknown type unless one includes <openssl/asn1t.h> 39 // as well. In C, as long as one doesn't try to do anything with the variable, 40 // that's ok. In D, it's not. Essentially: 41 // struct Foo; 42 // extern Foo gFoo; 43 if(isRecordWithoutDefinition(cursor, context)) return []; 44 45 const spelling = context.rememberLinkable(cursor); 46 47 // global variable or static member of a struct/class? 48 const static_ = cursor.semanticParent.type.canonical.kind == Type.Kind.Record 49 ? "static " 50 : ""; 51 // e.g. enum foo = 42; 52 const constexpr = cursor.tokens.canFind(Token(Token.Kind.Keyword, "constexpr")); 53 54 if(constexpr) 55 ret ~= translateConstexpr(spelling, cursor, context); 56 else { 57 const maybeTypeSpelling = translateType(cursor.type, context, No.translatingFunction); 58 // In C it is possible to have an extern void variable 59 const typeSpelling = cursor.type.kind == Type.Kind.Void 60 ? maybeTypeSpelling.replace("void", "void*") 61 : maybeTypeSpelling; 62 ret ~= 63 maybePragma(cursor, context) ~ 64 text("extern __gshared ", static_, typeSpelling, " ", spelling, ";"); 65 } 66 67 // attach variable docs 68 ret = getComment(cursor) ~ ret; 69 70 return ret; 71 } 72 73 74 private string[] translateConstexpr(in string spelling, 75 in from!"clang".Cursor cursor, 76 ref from!"dpp.runtime.context".Context context) 77 @safe 78 { 79 import dpp.translation.tokens: translateTokens; 80 import dpp.translation.exception: UntranslatableException; 81 import dpp.translation.type: translate; 82 import clang: Cursor, Token; 83 import std.algorithm: find, canFind; 84 import std.conv: text; 85 import std.array: empty, popFront; 86 import std.typecons: No; 87 88 auto tokens = cursor.tokens; 89 tokens = tokens.find!(a => a.kind == Token.Kind.Punctuation && a.spelling == "="); 90 91 const init = () { 92 // see contract.constexpr.variable.init.braces 93 if(cursor.children.canFind!(c => c.kind == Cursor.Kind.InitListExpr)) 94 return " = " ~ translate(cursor.type, context, No.translatingFunction) ~ ".init"; 95 96 if(!tokens.empty) { 97 tokens.popFront; 98 return " = " ~ translateTokens(tokens); 99 } 100 101 throw new UntranslatableException( 102 text("Could not find assignment in ", cursor.tokens, 103 "\ncursor: ", cursor, "\n@ ", cursor.sourceRange)); 104 }(); 105 106 return [ text("enum ", spelling, init, ";") ]; 107 } 108 109 private bool isRecordWithoutDefinition( 110 in from!"clang".Cursor cursor, 111 ref from!"dpp.runtime.context".Context context) 112 @safe 113 { 114 import clang: Cursor, Type; 115 116 const canonicalType = cursor.type.canonical; 117 118 if(canonicalType.kind != Type.Kind.Record) 119 return false; 120 121 const declaration = canonicalType.declaration; 122 const definition = declaration.definition; 123 const specializedTemplate = declaration.specializedCursorTemplate; 124 125 context.log("canonicalType: ", canonicalType); 126 context.log("declaration: ", declaration); 127 context.log("definition: ", definition); 128 context.log("specialised cursor template: ", specializedTemplate); 129 130 return 131 definition.isInvalid && 132 // See #97 133 specializedTemplate.kind != Cursor.Kind.ClassTemplate 134 ; 135 }