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