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 }