1 /**
2    The context the translation happens in, to avoid global variables
3  */
4 module dpp.runtime.context;
5 
6 alias LineNumber = size_t;
7 
8 // A function or global variable
9 struct Linkable {
10     LineNumber lineNumber;
11     string mangling;
12 }
13 
14 enum Language {
15     C,
16     Cpp,
17 }
18 
19 
20 /**
21    Context for the current translation, to avoid global variables
22  */
23 struct Context {
24 
25     import dpp.runtime.options: Options;
26     import dpp.runtime.namespace: Namespace;
27     import clang: Cursor;
28 
29     alias SeenCursors = bool[CursorId];
30 
31     /**
32        The lines of output so far. This is needed in order to fix
33        any name collisions between functions or variables with aggregates
34        such as structs, unions and enums.
35      */
36     private string[] lines;
37 
38     /**
39        Structs can be anonymous in C, and it's even common
40        to typedef them to a name. We come up with new names
41        that we track here so as to be able to properly translate
42        those typedefs.
43     */
44     private string[Cursor.Hash] cursorNickNames;
45 
46     // FIXME - there must be a better way
47     /// Used to find the last nickname we coined (e.g. "_Anonymous_1")
48     private string[] nickNames;
49 
50     /**
51        Remembers the seen struct pointers so that if any are undeclared in C,
52        we do so in D at the end.
53      */
54     private bool[string] fieldStructSpellings;
55 
56     /**
57        Remembers the field spellings in aggregates in case we need to change any
58        of them.
59      */
60     private LineNumber[string] fieldDeclarations;
61 
62     /**
63        All the aggregates that have been declared
64      */
65     private bool[string] _aggregateDeclarations;
66 
67     /**
68        A linkable is a function or a global variable.  We remember all
69        the ones we saw here so that if there's a name clash we can
70        come back and fix the declarations after the fact with
71        pragma(mangle).
72      */
73     private Linkable[string] linkableDeclarations;
74 
75     /**
76        All the function-like macros that have been declared
77      */
78     private bool[string] functionMacroDeclarations;
79 
80     /**
81        Remember all the macros already defined
82      */
83     private bool[string] macros;
84 
85     /**
86        All previously seen cursors
87      */
88     private SeenCursors seenCursors;
89 
90     /**
91        Deals with C++ namespaces
92      */
93     private Namespace _namespace;
94 
95     /// Command-line options
96     Options options;
97 
98     /*
99       Remember all declared types so that C-style casts can be recognised
100      */
101     private string[] _types = [
102         `void ?\*`,
103         `char`, `unsigned char`, `signed char`, `short`, `unsigned short`,
104         `int`, `unsigned`, `unsigned int`, `long`, `unsigned long`, `long long`,
105         `unsigned long long`, `float`, `double`, `long double`,
106     ];
107 
108     /// to generate unique names
109     private int _anonymousIndex;
110 
111     const(Language) language;
112 
113     this(Options options, in Language language) @safe pure {
114         this.options = options;
115         this.language = language;
116     }
117 
118     ref Context indent() @safe pure return {
119         options = options.indent;
120         return this;
121     }
122 
123     string indentation() @safe @nogc pure const {
124         return options.indentation;
125     }
126 
127     void setIndentation(in string indentation) @safe pure {
128         options.indentation = indentation;
129     }
130 
131     void log(A...)(auto ref A args) const {
132         import std.functional: forward;
133         options.log(forward!args);
134     }
135 
136     void indentLog(A...)(auto ref A args) const {
137         import std.functional: forward;
138         options.indent.log(forward!args);
139     }
140 
141     bool debugOutput() @safe @nogc pure nothrow const {
142         return options.debugOutput;
143     }
144 
145     bool hasSeen(in Cursor cursor) @safe pure nothrow const {
146         if(cursor.kind == Cursor.Kind.Namespace) return false;
147         return cast(bool)(CursorId(cursor) in seenCursors);
148     }
149 
150     void rememberCursor(in Cursor cursor) @safe pure nothrow {
151         // EnumDecl can have no spelling but end up defining an enum anyway
152         // See "it.compile.projects.double enum typedef"
153         if(cursor.spelling != "" || cursor.kind == Cursor.Kind.EnumDecl)
154             seenCursors[CursorId(cursor)] = true;
155     }
156 
157     string translation() @safe pure nothrow const {
158         import std.array: join;
159         return lines.join("\n");
160     }
161 
162     void writeln(in string line) @safe pure nothrow {
163         lines ~= line.dup;
164     }
165 
166     void writeln(in string[] lines) @safe pure nothrow {
167         this.lines ~= lines;
168     }
169 
170     // remember a function or variable declaration
171     string rememberLinkable(in Cursor cursor) @safe pure nothrow {
172         import dpp.translation.dlang: maybeRename;
173         const spelling = maybeRename(cursor, this);
174         // since linkables produce one-line translations, the next
175         // will be the linkable
176         linkableDeclarations[spelling] = Linkable(lines.length, cursor.mangling);
177         return spelling;
178     }
179 
180     void fixNames() @safe pure {
181         declareUnknownStructs;
182         fixLinkables;
183         fixFields;
184     }
185 
186     void fixLinkables() @safe pure {
187         foreach(declarations; [_aggregateDeclarations, functionMacroDeclarations]) {
188             foreach(name, _; declarations) {
189                 // if there's a name clash, fix it
190                 auto clashingLinkable = name in linkableDeclarations;
191                 if(clashingLinkable) {
192                     resolveClash(lines[clashingLinkable.lineNumber], name, clashingLinkable.mangling);
193                 }
194             }
195         }
196     }
197 
198     void fixFields() @safe pure {
199 
200         import dpp.translation.dlang: pragmaMangle, rename;
201         import std..string: replace;
202 
203         foreach(spelling, lineNumber; fieldDeclarations) {
204             if(spelling in _aggregateDeclarations) {
205                 lines[lineNumber] = lines[lineNumber]
206                     .replace(spelling ~ `;`, rename(spelling, this) ~ `;`);
207             }
208         }
209     }
210 
211     /**
212        Tells the context to remember a struct type encountered in an aggregate field.
213        Typically this will be a pointer to a structure but it could also be the return
214        type or parameter types of a function pointer field.
215      */
216     void rememberFieldStruct(in string typeSpelling) @safe pure {
217         fieldStructSpellings[typeSpelling] = true;
218     }
219 
220     /**
221        In C it's possible for a struct field name to have the same name as a struct
222        because of elaborated names. We remember them here in case we need to fix them.
223      */
224     void rememberField(in string spelling) @safe pure {
225         fieldDeclarations[spelling] = lines.length;
226     }
227 
228     /**
229        Remember this aggregate cursor
230      */
231     void rememberAggregate(in Cursor cursor) @safe pure {
232         const spelling = spellingOrNickname(cursor);
233         _aggregateDeclarations[spelling] = true;
234         rememberType(spelling);
235     }
236 
237     /**
238        If unknown structs show up in functions or fields (as a pointer),
239         define them now so the D file can compile
240         See `it.c.compile.delayed`.
241     */
242     void declareUnknownStructs() @safe pure {
243         foreach(name, _; fieldStructSpellings) {
244             if(name !in _aggregateDeclarations) {
245                 log("Could not find '", name, "' in aggregate declarations, defining it");
246                 writeln("struct " ~ name ~ ";");
247                 _aggregateDeclarations[name] = true;
248             }
249         }
250     }
251 
252     const(typeof(_aggregateDeclarations)) aggregateDeclarations() @safe pure nothrow const {
253         return _aggregateDeclarations;
254     }
255 
256     /// return the spelling if it exists, or our made-up nickname for it if not
257     string spellingOrNickname(in Cursor cursor) @safe pure {
258         import dpp.translation.dlang: rename, isKeyword;
259         if(cursor.spelling == "") return nickName(cursor);
260         return cursor.spelling.isKeyword ? rename(cursor.spelling, this) : cursor.spelling;
261     }
262 
263     private string nickName(in Cursor cursor) @safe pure {
264         if(cursor.hash !in cursorNickNames) {
265             auto nick = newAnonymousTypeName;
266             nickNames ~= nick;
267             cursorNickNames[cursor.hash] = nick;
268         }
269 
270         return cursorNickNames[cursor.hash];
271     }
272 
273     private string newAnonymousTypeName() @safe pure {
274         import std.conv: text;
275         return text("_Anonymous_", _anonymousIndex++);
276     }
277 
278     string newAnonymousMemberName() @safe pure {
279         import std..string: replace;
280         return newAnonymousTypeName.replace("_A", "_a");
281     }
282 
283     private void resolveClash(ref string line, in string spelling, in string mangling) @safe pure const {
284         import dpp.translation.dlang: pragmaMangle;
285         line = `    ` ~ pragmaMangle(mangling) ~ replaceSpelling(line, spelling);
286     }
287 
288     private string replaceSpelling(in string line, in string spelling) @safe pure const {
289         import dpp.translation.dlang: rename;
290         import std.array: replace;
291         return line
292             .replace(spelling ~ `;`, rename(spelling, this) ~ `;`)
293             .replace(spelling ~ `(`, rename(spelling, this) ~ `(`)
294             ;
295     }
296 
297     void rememberType(in string type) @safe pure nothrow {
298         _types ~= type;
299     }
300 
301     /// Matches a C-type cast
302     auto castRegex() @safe const {
303         import std.array: join, array;
304         import std.regex: regex;
305         import std.algorithm: map;
306         import std.range: chain;
307 
308         // const and non const versions of each type
309         const typesConstOpt = _types.map!(a => `(?:const )?` ~ a).array;
310 
311         const typeSelectionStr =
312             chain(typesConstOpt,
313                   // pointers thereof
314                   typesConstOpt.map!(a => a ~ ` ?\*`))
315             .join("|");
316 
317         // parens and a type inside, where "a type" is any we know about
318         const regexStr = `\(( *?(?:` ~ typeSelectionStr ~ `) *?)\)`;
319 
320         return regex(regexStr);
321     }
322 
323     void rememberMacro(in Cursor cursor) @safe pure {
324         macros[cursor.spelling] = true;
325         if(cursor.isMacroFunction)
326             functionMacroDeclarations[cursor.spelling] = true;
327     }
328 
329     bool macroAlreadyDefined(in Cursor cursor) @safe pure const {
330         return cast(bool) (cursor.spelling in macros);
331     }
332 
333     string[] pushNamespace(in string name) @safe pure {
334         return _namespace.push(name);
335     }
336 
337     string[] popNamespace() @safe pure {
338         return _namespace.pop;
339     }
340 
341     void addNamespaceSymbol(in string symbol) @safe pure {
342         _namespace.addSymbol(symbol);
343     }
344 }
345 
346 
347 // to identify a cursor
348 private struct CursorId {
349     import clang: Cursor, Type;
350 
351     string cursorSpelling;
352     Cursor.Kind cursorKind;
353     string typeSpelling;
354     Type.Kind typeKind;
355 
356     this(in Cursor cursor) @safe pure nothrow {
357         cursorSpelling = cursor.spelling;
358         cursorKind = cursor.kind;
359         typeSpelling = cursor.type.spelling;
360         typeKind = cursor.type.kind;
361     }
362 }