1 module dpp.translation.macro_;
2 
3 import dpp.from;
4 
5 string[] translateMacro(in from!"clang".Cursor cursor,
6                         ref from!"dpp.runtime.context".Context context)
7     @safe
8 {
9     import dpp.translation.dlang: maybeRename;
10     import clang: Cursor;
11     import std.algorithm: map;
12     import std..string: join;
13     import std.file: exists;
14     import std.stdio: File;
15     import std.algorithm: startsWith;
16     import std.conv: text;
17 
18     assert(cursor.kind == Cursor.Kind.MacroDefinition);
19 
20     // we want non-built-in macro definitions to be defined and then preprocessed
21     // again
22 
23     auto range = cursor.sourceRange;
24 
25     if(range.path == "" || !range.path.exists ||
26        cursor.isPredefined || cursor.spelling.startsWith("__STDC_")) { //built-in macro
27         return [];
28     }
29 
30     // now we read the header where the macro comes from and copy the text inline
31 
32     const startPos = range.start.offset;
33     const endPos   = range.end.offset;
34 
35     auto file = File(range.path);
36     file.seek(startPos);
37     const chars = () @trusted { return file.rawRead(new char[endPos - startPos]); }();
38 
39     // the only sane way for us to be able to see a macro definition
40     // for a macro that has already been defined is if an #undef happened
41     // in the meanwhile. Unfortunately, libclang has no way of passing
42     // that information to us
43     string maybeUndef;
44     if(context.macroAlreadyDefined(cursor))
45         maybeUndef = "#undef " ~ cursor.spelling ~ "\n";
46 
47     context.rememberMacro(cursor);
48     const spelling = maybeRename(cursor, context);
49     const body_ = chars.text[cursor.spelling.length .. $];
50 
51     return [maybeUndef ~ "#define " ~ spelling ~ translateToD(body_, context) ~ "\n"];
52 }
53 
54 
55 // Some macros define snippets of C code that aren't valid D
56 private string translateToD(in string line, in from!"dpp.runtime.context".Context context) @safe {
57     import std.array: replace;
58     import std.regex: regex, replaceAll;
59 
60     auto sizeofRegex = regex(`sizeof *?\(([^)]+)\)`);
61 
62     return line
63         .replace("->", ".")
64         .replaceNull
65         .replaceAll(sizeofRegex, "($1).sizeof")
66         .replaceAll(context.castRegex, "cast($1)")
67         ;
68 }
69 
70 private string replaceNull(in string str) @safe pure nothrow {
71     import std.array: replace;
72     import std.algorithm: startsWith;
73     // we don't want to translate the definition of NULL itself
74     return str.startsWith("NULL") ? str : str.replace("NULL", "null");
75 }