1 /**
2    Cursor translations
3  */
4 module dpp.cursor.translation;
5 
6 import dpp.from;
7 
8 alias Translator = string[] function(
9     in from!"clang".Cursor cursor,
10     ref from!"dpp.runtime.context".Context context,
11 ) @safe;
12 
13 string translateTopLevelCursor(in from!"clang".Cursor cursor,
14                                ref from!"dpp.runtime.context".Context context,
15                                in string file = __FILE__,
16                                in size_t line = __LINE__)
17     @safe
18 {
19     import std.array: join;
20     import std.algorithm: map;
21 
22     return cursor.skipTopLevel
23         ? ""
24         : translate(cursor, context, file, line).map!(a => "    " ~ a).join("\n");
25 }
26 
27 private bool skipTopLevel(in from!"clang".Cursor cursor) @safe pure {
28     import dpp.cursor.aggregate: isAggregateC;
29     import clang: Cursor;
30     import std.algorithm: startsWith, canFind;
31 
32     // don't bother translating top-level anonymous aggregates
33     if(isAggregateC(cursor) && cursor.spelling == "")
34         return true;
35 
36     static immutable forbiddenSpellings =
37         [
38             "ulong", "ushort", "uint",
39             "va_list", "__gnuc_va_list",
40             "_IO_2_1_stdin_", "_IO_2_1_stdout_", "_IO_2_1_stderr_",
41         ];
42 
43     return forbiddenSpellings.canFind(cursor.spelling) ||
44         cursor.isPredefined ||
45         cursor.kind == Cursor.Kind.MacroExpansion
46         ;
47 }
48 
49 
50 string[] translate(in from!"clang".Cursor cursor,
51                    ref from!"dpp.runtime.context".Context context,
52                    in string file = __FILE__,
53                    in size_t line = __LINE__)
54     @safe
55 {
56     import std.conv: text;
57 
58     debugCursor(cursor, context);
59 
60     if(cursor.kind !in translators)
61         throw new Exception(text("Cannot translate unknown cursor kind ", cursor.kind),
62                             file,
63                             line);
64 
65     const indentation = context.indentation;
66     scope(exit) context.setIndentation(indentation);
67     context.indent;
68 
69     try
70         return translators[cursor.kind](cursor, context);
71     catch(Exception e) {
72         import std.stdio: stderr;
73         debug {
74             () @trusted {
75                 stderr.writeln("\nCould not translate cursor ", cursor,
76                                " sourceRange: ", cursor.sourceRange,
77                                " children: ", cursor.children, "\n");
78             }();
79         }
80         throw e;
81     }
82 }
83 
84 void debugCursor(in from!"clang".Cursor cursor,
85                  in from!"dpp.runtime.context".Context context)
86     @safe
87 {
88     import clang: Cursor;
89     import std.algorithm: startsWith, canFind;
90 
91     version(unittest) {}
92     else if(!context.debugOutput) return;
93 
94     const isMacro = cursor.kind == Cursor.Kind.MacroDefinition;
95     const isOkMacro =
96         !cursor.spelling.startsWith("__") &&
97         !["_LP64", "unix", "linux"].canFind(cursor.spelling);
98     const canonical = cursor.isCanonical ? " CAN" : "";
99     const definition = cursor.isDefinition ? " DEF" : "";
100 
101     if(!isMacro || isOkMacro) {
102         context.log(cursor, canonical, definition, " @ ", cursor.sourceRange);
103     }
104 }
105 
106 Translator[from!"clang".Cursor.Kind] translators() @safe {
107     import dpp.cursor;
108     import clang: Cursor;
109     import dpp.expansion: expand;
110 
111     static string[] ignore(
112         in Cursor cursor,
113         ref from!"dpp.runtime.context".Context context)
114     {
115         return [];
116     }
117 
118     static string[] translateUnexposed(
119         in Cursor cursor,
120         ref from!"dpp.runtime.context".Context context)
121     {
122         import clang: Type;
123         import std.conv: text;
124 
125         switch(cursor.type.kind) with(Type.Kind) {
126             default:
127                 throw new Exception(text("Unknown unexposed declaration type ", cursor.type));
128             case Invalid:
129                 return [];
130         }
131         assert(0);
132     }
133 
134     static string[] translateAccess(
135         in Cursor cursor,
136         ref from!"dpp.runtime.context".Context context)
137     {
138         import clang: AccessSpecifier;
139 
140         final switch(cursor.accessSpecifier) with(AccessSpecifier) {
141             case InvalidAccessSpecifier: assert(0);
142             case Public: return ["public:"];
143             case Protected: return ["protected:"];
144             case Private: return ["private:"];
145         }
146 
147         assert(0);
148     }
149 
150     with(Cursor.Kind) {
151         return [
152             ClassDecl:                &translateClass,
153             StructDecl:               &translateStruct,
154             UnionDecl:                &translateUnion,
155             EnumDecl:                 &translateEnum,
156             FunctionDecl:             &translateFunction,
157             FieldDecl:                &translateField,
158             TypedefDecl:              &translateTypedef,
159             MacroDefinition:          &translateMacro,
160             InclusionDirective:       &ignore,
161             EnumConstantDecl:         &translateEnumConstant,
162             VarDecl:                  &translateVariable,
163             UnexposedDecl:            &translateUnexposed,
164             CXXAccessSpecifier:       &translateAccess,
165             CXXMethod:                &translateFunction,
166             Constructor:              &translateFunction,
167             Destructor:               &translateFunction,
168             TypeAliasDecl:            &translateTypedef,
169             ClassTemplate:            &translateClass,
170             TemplateTypeParameter:    &ignore,
171             NonTypeTemplateParameter: &ignore,
172         ];
173     }
174 }