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