1 /**
2    Type translations
3  */
4 module dpp.type;
5 
6 import dpp.from: from;
7 
8 alias Translator = string function(
9     in from!"clang".Type type,
10     ref from!"dpp.runtime.context".Context context,
11     in from!"std.typecons".Flag!"translatingFunction" translatingFunction
12 ) @safe pure;
13 
14 alias Translators = Translator[from!"clang".Type.Kind];
15 
16 string translate(in from!"clang".Type type,
17                  ref from!"dpp.runtime.context".Context context,
18                  in from!"std.typecons".Flag!"translatingFunction" translatingFunction = from!"std.typecons".No.translatingFunction)
19     @safe pure
20 {
21     import std.conv: text;
22     if(type.kind !in translators)
23         throw new Exception(text("Type kind ", type.kind, " not supported: ", type));
24 
25     return translators[type.kind](type, context, translatingFunction);
26 }
27 
28 Translators translators() @safe pure {
29     import clang: Type;
30 
31     with(Type.Kind) {
32         return [
33             Long: &simple!"c_long",
34             ULong: &simple!"c_ulong",
35             Void: &simple!"void",
36             NullPtr: &simple!"void*",
37             Bool: &simple!"bool",
38             WChar: &simple!"wchar",
39             SChar: &simple!"byte",
40             Char16: &simple!"wchar",
41             Char32: &simple!"dchar",
42             UChar: &simple!"ubyte",
43             UShort: &simple!"ushort",
44             Short: &simple!"short",
45             Int: &simple!"int",
46             UInt: &simple!"uint",
47             LongLong: &simple!"long",
48             ULongLong: &simple!"ulong",
49             Float: &simple!"float",
50             Double: &simple!"double",
51             Char_U: &simple!"ubyte",
52             Char_S: &simple!"char",
53             Int128: &simple!"cent",
54             UInt128: &simple!"ucent",
55             Float128: &simple!"real",
56             Half: &simple!"float",
57             LongDouble: &simple!"real",
58             Enum: &translateAggregate,
59             Pointer: &translatePointer,
60             FunctionProto: &translateFunctionProto,
61             Record: &translateRecord,
62             FunctionNoProto: &translateFunctionProto,
63             Elaborated: &translateAggregate,
64             ConstantArray: &translateConstantArray,
65             IncompleteArray: &translateIncompleteArray,
66             Typedef: &translateTypedef,
67             LValueReference: &translateLvalueRef,
68             RValueReference: &translateRvalueRef,
69             Complex: &translateComplex,
70             Unexposed: &translateUnexposed,
71             DependentSizedArray: &translateDependentSizedArray,
72         ];
73     }
74 }
75 
76 
77 private string simple(string translation)
78                      (in from!"clang".Type type,
79                       ref from!"dpp.runtime.context".Context context,
80                       in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
81 @safe pure
82 {
83     return addModifiers(type, translation);
84 }
85 
86 
87 private string translateRecord(in from!"clang".Type type,
88                                ref from!"dpp.runtime.context".Context context,
89                                in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
90 @safe pure
91 {
92     // see it.compile.projects.va_list
93     return type.spelling == "struct __va_list_tag"
94         ? "va_list"
95         : translateAggregate(type, context, translatingFunction);
96 }
97 
98 private string translateAggregate(in from!"clang".Type type,
99                                   ref from!"dpp.runtime.context".Context context,
100                                   in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
101     @safe pure
102 {
103     import std.array: replace;
104 
105     // if it's anonymous, find the nickname, otherwise return the spelling
106     string spelling() {
107         import std.algorithm: canFind;
108         // clang names anonymous types with a long name indicating where the type
109         // was declared
110         return type.spelling.canFind("(anonymous") ? context.popLastNickName : type.spelling;
111     }
112 
113     return addModifiers(type, spelling)
114         // "struct Foo" -> Foo, "union Foo" -> Foo, "enum Foo" -> Foo
115         .replace("struct ", "").replace("union ", "").replace("enum ", "")
116         ;
117 }
118 
119 
120 private string translateConstantArray(in from!"clang".Type type,
121                                       ref from!"dpp.runtime.context".Context context,
122                                       in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
123 @safe pure
124 {
125     import std.conv: text;
126 
127     context.indent.log("Constant array of # ", type.numElements);
128 
129     return translatingFunction
130         ? translate(type.elementType, context) ~ `*`
131         : translate(type.elementType, context) ~ `[` ~ type.numElements.text ~ `]`;
132 }
133 
134 
135 private string translateDependentSizedArray(
136     in from!"clang".Type type,
137     ref from!"dpp.runtime.context".Context context,
138     in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
139 @safe pure
140 {
141     import std.conv: text;
142     import std.algorithm: find, countUntil;
143 
144     // FIXME: hacky, only works for the only test in it.cpp.class_.template (array)
145     auto start = type.spelling.find("["); start = start[1 .. $];
146     auto endIndex = start.countUntil("]");
147 
148     return translate(type.elementType, context) ~ `[` ~ start[0 .. endIndex] ~ `]`;
149 }
150 
151 
152 private string translateIncompleteArray(in from!"clang".Type type,
153                                         ref from!"dpp.runtime.context".Context context,
154                                         in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
155 @safe pure
156 {
157     const dType = translate(type.elementType, context);
158     // if translating a function, we want C's T[] to translate
159     // to T*, otherwise we want a flexible array
160     return translatingFunction ? dType ~ `*` : dType ~ "[0]";
161 
162 }
163 
164 private string translateTypedef(in from!"clang".Type type,
165                                 ref from!"dpp.runtime.context".Context context,
166                                 in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
167 @safe pure
168 {
169     // Here we may get a Typedef with a canonical type of Enum. It might be worth
170     // translating to int for function parameters
171     return addModifiers(type, type.spelling);
172 }
173 
174 private string translatePointer(in from!"clang".Type type,
175                                 ref from!"dpp.runtime.context".Context context,
176                                 in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
177     @safe pure
178 {
179     import clang: Type;
180     import std.conv: text;
181 
182     assert(type.kind == Type.Kind.Pointer, "type kind not Pointer");
183     if(type.pointee is null) throw new Exception("null pointee for " ~ type.toString);
184     assert(type.pointee !is null, "Pointee is null for " ~ type.toString);
185 
186     const isFunction =
187         type.pointee.kind == Type.Kind.Unexposed &&
188         (type.pointee.canonical.kind == Type.Kind.FunctionProto ||
189          type.pointee.canonical.kind == Type.Kind.FunctionNoProto);
190 
191     // usually "*" but sometimes not needed if already a reference type
192     const maybeStar = isFunction ? "" : "*";
193     context.log("Pointee:           ", *type.pointee);
194     context.log("Pointee canonical: ", type.pointee.canonical);
195 
196     const translateCanonical = type.pointee.kind == Type.Kind.Unexposed;
197     context.log("Translate canonical? ", translateCanonical);
198 
199     const indentation = context.indentation;
200     const rawType = translateCanonical
201         ? translate(type.pointee.canonical, context.indent)
202         : translate(*type.pointee, context.indent);
203     context.setIndentation(indentation);
204 
205     context.log("Raw type: ", rawType);
206 
207     // Only add top-level const if it's const all the way down
208     bool addConst() @trusted {
209         auto ptr = &type;
210         while(ptr.kind == Type.Kind.Pointer) {
211             if(!ptr.isConstQualified || !ptr.pointee.isConstQualified)
212                 return false;
213             ptr = ptr.pointee;
214         }
215 
216         return true;
217     }
218 
219     const ptrType = addConst
220         ? `const(` ~ rawType ~ maybeStar ~ `)`
221         : rawType ~ maybeStar;
222 
223     return ptrType;
224 }
225 
226 // currently only getting here from function pointer variables
227 // with have kind unexposed but canonical kind FunctionProto
228 private string translateFunctionProto(in from!"clang".Type type,
229                                       ref from!"dpp.runtime.context".Context context,
230                                       in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
231     @safe pure
232 {
233     import std.conv: text;
234     import std.algorithm: map;
235     import std.array: join, array;
236 
237     const params = type.paramTypes.map!(a => translate(a, context)).array;
238     const isVariadic = params.length > 0 && type.isVariadicFunction;
239     const variadicParams = isVariadic ? ["..."] : [];
240     const allParams = params ~ variadicParams;
241     return text(translate(type.returnType, context), " function(", allParams.join(", "), ")");
242 }
243 
244 private string translateLvalueRef(in from!"clang".Type type,
245                                   ref from!"dpp.runtime.context".Context context,
246                                   in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
247     @safe pure
248 {
249     return "ref " ~ translate(*type.canonical.pointee, context, translatingFunction);
250 }
251 
252 // we cheat and pretend it's a value
253 private string translateRvalueRef(in from!"clang".Type type,
254                                   ref from!"dpp.runtime.context".Context context,
255                                   in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
256     @safe pure
257 {
258     return translate(*type.canonical.pointee, context, translatingFunction);
259 }
260 
261 
262 private string translateComplex(in from!"clang".Type type,
263                                 ref from!"dpp.runtime.context".Context context,
264                                 in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
265     @safe pure
266 {
267     return "c" ~ translate(type.elementType, context, translatingFunction);
268 }
269 
270 private string translateUnexposed(in from!"clang".Type type,
271                                   ref from!"dpp.runtime.context".Context context,
272                                   in from!"std.typecons".Flag!"translatingFunction" translatingFunction)
273     @safe pure
274 {
275     return type.spelling;
276 }
277 
278 
279 private string addModifiers(in from!"clang".Type type, in string translation) @safe pure {
280     import std.array: replace;
281     const realTranslation = translation.replace("const ", "");
282     return type.isConstQualified
283         ? `const(` ~  realTranslation ~ `)`
284         : realTranslation;
285 }