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