| 1 | | // dfilter.d - modifications by James Dunne |
|---|
| 2 | | // original author unknown/forgotten by me (feel free to give credit) |
|---|
| 3 | | |
|---|
| 4 | | // These modifications allow version (identifier) { } else { } blocks |
|---|
| 5 | | // to be replaced with corresponding C preprocessor #ifdef blocks which |
|---|
| 6 | | // doxygen recognizes and interprets. The identifiers are translated into |
|---|
| 7 | | // all uppercase with a VERSION_ prefix. An example should illustrate: |
|---|
| 8 | | |
|---|
| 9 | | // version (server) { |
|---|
| 10 | | // code1 |
|---|
| 11 | | // } else version (client) { |
|---|
| 12 | | // code2 |
|---|
| 13 | | // } else { |
|---|
| 14 | | // code3 |
|---|
| 15 | | // } |
|---|
| 16 | | |
|---|
| 17 | | // becomes: |
|---|
| 18 | | |
|---|
| 19 | | // #ifdef VERSION_SERVER |
|---|
| 20 | | // code1 |
|---|
| 21 | | // #elif VERSION_CLIENT |
|---|
| 22 | | // code2 |
|---|
| 23 | | // #else |
|---|
| 24 | | // code3 |
|---|
| 25 | | // #endif |
|---|
| 26 | | |
|---|
| 27 | | // Modifications have also been made to support D interfaces conversion |
|---|
| 28 | | // into C++ classes. |
|---|
| 29 | | |
|---|
| 30 | | import std.file, std.ctype, std.c.stdio, std.string; |
|---|
| 31 | | |
|---|
| 32 | | char [] data; /* Data. */ |
|---|
| 33 | | char *c; /* Current point. */ |
|---|
| 34 | | char *s; /* Previous filter point. */ |
|---|
| 35 | | char *e; /* End of the data. */ |
|---|
| 36 | | char *p; /* Start of this token. */ |
|---|
| 37 | | |
|---|
| 38 | | /* Read in a token. */ |
|---|
| 39 | | char [] token () |
|---|
| 40 | | { |
|---|
| | 1 | /***********************************************************************\ |
|---|
| | 2 | * dfilter.d * |
|---|
| | 3 | * * |
|---|
| | 4 | * D code filter for Doxygen 1.4.2 * |
|---|
| | 5 | * * |
|---|
| | 6 | * Portions written by Hauke Duden, Stewart Gordon, James Dunne, * |
|---|
| | 7 | * probably various anonymous authors.... * |
|---|
| | 8 | \***********************************************************************/ |
|---|
| | 9 | import std.file, std.ctype, std.string, std.c.stdio, std.stdio, std.cstream; |
|---|
| | 10 | |
|---|
| | 11 | char[] data; // Program code |
|---|
| | 12 | char* current; // Current point |
|---|
| | 13 | char* previous; // Previous filter point |
|---|
| | 14 | char* end; // End of the data |
|---|
| | 15 | char* ptoken; // Start of this token |
|---|
| | 16 | |
|---|
| | 17 | // Read in a token |
|---|
| | 18 | char[] token() { |
|---|
| 130 | | c ++; |
|---|
| 131 | | return p [0 .. 1]; |
|---|
| 132 | | } |
|---|
| 133 | | |
|---|
| 134 | | /* Print all text to this point and set s to the current point. */ |
|---|
| 135 | | void flush (char *p) |
|---|
| 136 | | { |
|---|
| 137 | | fwrite (s, cast(int)(p - s), 1, stdout); |
|---|
| 138 | | s = c; |
|---|
| 139 | | } |
|---|
| 140 | | |
|---|
| 141 | | /* Consume a "{ ... }" or "(xxx) { ... }" block. */ |
|---|
| 142 | | void skipBlock (char *p) |
|---|
| 143 | | { |
|---|
| 144 | | char *o = s; |
|---|
| 145 | | |
|---|
| 146 | | flush (p); |
|---|
| | 108 | current++; |
|---|
| | 109 | return ptoken[0..1]; |
|---|
| | 110 | } |
|---|
| | 111 | |
|---|
| | 112 | // Print all text to this point, and set previous to the current point |
|---|
| | 113 | void flush(char* p) { |
|---|
| | 114 | std.c.stdio.fwrite(previous, cast(int) (p - previous), 1, stdout); |
|---|
| | 115 | previous = current; |
|---|
| | 116 | } |
|---|
| | 117 | |
|---|
| | 118 | // Print all text to a set point, which becomes the new previous point |
|---|
| | 119 | void flushTo(char* p) { |
|---|
| | 120 | std.c.stdio.fwrite(previous, cast(int) (p - previous), 1, stdout); |
|---|
| | 121 | previous = p; |
|---|
| | 122 | } |
|---|
| | 123 | |
|---|
| | 124 | // Consume a "{ ... }" or "(xxx) { ... }" block |
|---|
| | 125 | void skipBlock(char* p) { |
|---|
| | 126 | char* o = previous; |
|---|
| | 127 | |
|---|
| | 128 | flush(p); |
|---|
| 195 | | |
|---|
| 196 | | data = cast(char []) read (args [1]); |
|---|
| 197 | | c = s = data; |
|---|
| 198 | | e = s + data.length; |
|---|
| 199 | | |
|---|
| 200 | | char [] t; |
|---|
| 201 | | char [] [] protectRecord; |
|---|
| 202 | | char [] protect = "public"; |
|---|
| 203 | | char [] [] brackets; |
|---|
| 204 | | char [] nextOpenBracket; |
|---|
| 205 | | char [] nextSemiColon; |
|---|
| 206 | | bit insideBrackets, elseif; |
|---|
| 207 | | |
|---|
| 208 | | while (1) |
|---|
| 209 | | { |
|---|
| 210 | | t = token (); |
|---|
| 211 | | if (t == null) |
|---|
| 212 | | { |
|---|
| 213 | | flush (c); |
|---|
| 214 | | return 0; |
|---|
| 215 | | } |
|---|
| 216 | | |
|---|
| 217 | | switch (t) |
|---|
| 218 | | { |
|---|
| 219 | | /* Remove these keywords. */ |
|---|
| 220 | | case "body": |
|---|
| 221 | | flush (p); |
|---|
| 222 | | s = c; |
|---|
| 223 | | break; |
|---|
| 224 | | |
|---|
| 225 | | /* Remove these blocks. */ |
|---|
| 226 | | case "unittest": |
|---|
| 227 | | case "invariant": |
|---|
| 228 | | case "in": |
|---|
| 229 | | case "out": |
|---|
| 230 | | skipBlock (p); |
|---|
| 231 | | break; |
|---|
| 232 | | |
|---|
| 233 | | /* Remove "keyword:" but only if it is followed with a colon. */ |
|---|
| 234 | | case "override": |
|---|
| 235 | | case "abstract": |
|---|
| 236 | | case "final": |
|---|
| 237 | | flush (p); |
|---|
| 238 | | if ((t = token ()) == ":") |
|---|
| 239 | | s = c; |
|---|
| 240 | | break; |
|---|
| 241 | | |
|---|
| 242 | | case ";": |
|---|
| 243 | | flush (c); |
|---|
| 244 | | printf ("%.*s", nextSemiColon); |
|---|
| 245 | | nextSemiColon = null; |
|---|
| 246 | | break; |
|---|
| 247 | | |
|---|
| 248 | | /* "keyword" without "keyword:" into "keyword: ... { ... } antikeyword:" */ |
|---|
| 249 | | case "public": |
|---|
| 250 | | case "private": |
|---|
| 251 | | case "protected": |
|---|
| 252 | | flush (p); |
|---|
| 253 | | if (token () == ":") |
|---|
| 254 | | { |
|---|
| 255 | | printf ("%.*s", t); |
|---|
| 256 | | protect = t; |
|---|
| 257 | | break; |
|---|
| 258 | | } |
|---|
| 259 | | |
|---|
| 260 | | if (t != protect) |
|---|
| 261 | | { |
|---|
| 262 | | printf ("%.*s: ", t); |
|---|
| 263 | | s = p; |
|---|
| 264 | | nextOpenBracket = protect ~ ":"; |
|---|
| 265 | | nextSemiColon = protect ~ ":"; |
|---|
| 266 | | } |
|---|
| 267 | | break; |
|---|
| 268 | | |
|---|
| 269 | | /* Modify into "package". */ |
|---|
| 270 | | /*Not necessary anymore |
|---|
| 271 | | case "module": |
|---|
| 272 | | flush (p); |
|---|
| 273 | | printf ("package ", nextSemiColon); |
|---|
| 274 | | s = c; |
|---|
| 275 | | break;*/ |
|---|
| 276 | | |
|---|
| 277 | | /* Modify into import X.Y.*. */ |
|---|
| 278 | | /* Not necessary anymore |
|---|
| 279 | | case "import": |
|---|
| 280 | | flush (p); |
|---|
| 281 | | printf ("import ", nextSemiColon); |
|---|
| 282 | | |
|---|
| 283 | | while ((t = token ()) != null) |
|---|
| 284 | | { |
|---|
| 285 | | if (t == ";") |
|---|
| 286 | | { |
|---|
| 287 | | printf (";"); |
|---|
| 288 | | break; |
|---|
| 289 | | } |
|---|
| 290 | | else |
|---|
| 291 | | printf ("%.*s", t); |
|---|
| 292 | | } |
|---|
| 293 | | s = c; |
|---|
| 294 | | break;*/ |
|---|
| 295 | | |
|---|
| 296 | | /* Change "version (...) { }" into "#ifdef ... and #endif" */ |
|---|
| 297 | | case "version": { |
|---|
| 298 | | char *o = s; |
|---|
| 299 | | flush (p); |
|---|
| 300 | | |
|---|
| 301 | | char [] [] ver; |
|---|
| 302 | | |
|---|
| | 174 | |
|---|
| | 175 | try { |
|---|
| | 176 | debug (filename) derr.writefln("Filtering file %s", args[1]); |
|---|
| | 177 | data = cast(char[]) read(args[1]); |
|---|
| | 178 | |
|---|
| | 179 | // translate line breaks (to make output more readable for debugging) |
|---|
| | 180 | data = replace(data, "\r\n", "\n"); |
|---|
| | 181 | data = replace(data, "\r", "\n"); |
|---|
| | 182 | |
|---|
| | 183 | current = previous = data.ptr; |
|---|
| | 184 | end = previous + data.length; |
|---|
| | 185 | |
|---|
| | 186 | char[] t; |
|---|
| | 187 | bool elseif, readingContent; |
|---|
| | 188 | |
|---|
| | 189 | while ((t = token()) != null) { |
|---|
| | 190 | debug { |
|---|
| | 191 | derr.writefln("Token '%s', block level %d", |
|---|
| | 192 | t, attrStack.length); |
|---|
| | 193 | derr.writefln("Pending write: '%s'", |
|---|
| | 194 | previous[0..current - previous]); |
|---|
| | 195 | debug (step) { |
|---|
| | 196 | getch(); |
|---|
| | 197 | } |
|---|
| | 198 | } |
|---|
| | 199 | |
|---|
| | 200 | // remove private blocks if they are on the module level |
|---|
| | 201 | if (attrStack.length == 0 && t == "private") { |
|---|
| | 202 | debug { |
|---|
| | 203 | fputs("entered remove module-level private block\n", stderr); |
|---|
| | 204 | } |
|---|
| | 205 | flushTo(ptoken); |
|---|
| | 206 | |
|---|
| | 207 | // FIXME: this doesn't work for constructs of the form |
|---|
| | 208 | // private: |
|---|
| | 209 | // public XXX; |
|---|
| | 210 | // <more private data> |
|---|
| | 211 | |
|---|
| | 212 | bool endReached = false; |
|---|
| | 213 | bool breakAfterDecl = true; |
|---|
| | 214 | uint tokenCount = 0; |
|---|
| | 215 | int blockLevel = 0; |
|---|
| | 216 | |
|---|
| | 217 | while (!endReached) { |
|---|
| | 218 | t = token(); |
|---|
| | 219 | if (t == null) |
|---|
| | 220 | return 0; |
|---|
| | 221 | |
|---|
| | 222 | debug derr.writefln("Private token '%s', block level %d", |
|---|
| | 223 | t, blockLevel); |
|---|
| | 224 | |
|---|
| | 225 | tokenCount++; |
|---|
| | 226 | |
|---|
| | 227 | switch (t) { |
|---|
| | 228 | case ":": |
|---|
| | 229 | if (tokenCount == 1) |
|---|
| | 230 | /* do not break after next declaration; |
|---|
| | 231 | * instead we search for the next public |
|---|
| | 232 | * statement |
|---|
| | 233 | */ |
|---|
| | 234 | breakAfterDecl = false; |
|---|
| | 235 | break; |
|---|
| | 236 | case ";": |
|---|
| | 237 | if (blockLevel == 0 && breakAfterDecl) |
|---|
| | 238 | endReached = true; |
|---|
| | 239 | break; |
|---|
| | 240 | case "{": |
|---|
| | 241 | blockLevel++; |
|---|
| | 242 | break; |
|---|
| | 243 | case "}": |
|---|
| | 244 | blockLevel--; |
|---|
| | 245 | if (blockLevel == 0 && breakAfterDecl) |
|---|
| | 246 | endReached = true; |
|---|
| | 247 | break; |
|---|
| | 248 | case "public": |
|---|
| | 249 | if (blockLevel == 0) { |
|---|
| | 250 | writef("%s", t); |
|---|
| | 251 | endReached = true; |
|---|
| | 252 | } |
|---|
| | 253 | break; |
|---|
| | 254 | default: |
|---|
| | 255 | break; |
|---|
| | 256 | } |
|---|
| | 257 | } |
|---|
| | 258 | |
|---|
| | 259 | previous=current; |
|---|
| | 260 | debug { |
|---|
| | 261 | fputs("exited remove module-level private block\n", stderr); |
|---|
| | 262 | } |
|---|
| | 263 | } |
|---|
| | 264 | else switch (t) { |
|---|
| | 265 | // remove these keywords |
|---|
| | 266 | case "body": |
|---|
| | 267 | flush(ptoken); |
|---|
| | 268 | previous = current; |
|---|
| | 269 | break; |
|---|
| | 270 | |
|---|
| | 271 | // remove these blocks |
|---|
| | 272 | case "unittest": |
|---|
| | 273 | case "invariant": |
|---|
| | 274 | case "in": |
|---|
| | 275 | case "out": |
|---|
| | 276 | skipBlock(ptoken); |
|---|
| | 277 | break; |
|---|
| | 278 | |
|---|
| | 279 | // record attributes to support attr: or attr { ... } syntax |
|---|
| | 280 | case "export": |
|---|
| | 281 | currentAttr.protect = PROT.EXPORT; |
|---|
| | 282 | break; |
|---|
| | 283 | case "public": |
|---|
| | 284 | currentAttr.protect = PROT.PUBLIC; |
|---|
| | 285 | break; |
|---|
| | 286 | case "protected": |
|---|
| | 287 | currentAttr.protect = PROT.PROTECTED; |
|---|
| | 288 | break; |
|---|
| | 289 | case "package": |
|---|
| | 290 | currentAttr.protect = PROT.PACKAGE; |
|---|
| | 291 | break; |
|---|
| | 292 | case "private": |
|---|
| | 293 | currentAttr.protect = PROT.PRIVATE; |
|---|
| | 294 | break; |
|---|
| | 295 | case "deprecated": |
|---|
| | 296 | currentAttr.attr = ATTR.DEPRECATED; |
|---|
| | 297 | break; |
|---|
| | 298 | case "static": |
|---|
| | 299 | currentAttr.attr = ATTR.STATIC; |
|---|
| | 300 | break; |
|---|
| | 301 | case "abstract": |
|---|
| | 302 | currentAttr.attr = ATTR.ABSTRACT; |
|---|
| | 303 | break; |
|---|
| | 304 | case "final": |
|---|
| | 305 | currentAttr.attr = ATTR.FINAL; |
|---|
| | 306 | break; |
|---|
| | 307 | case "override": |
|---|
| | 308 | currentAttr.attr = ATTR.OVERRIDE; |
|---|
| | 309 | break; |
|---|
| | 310 | case "const": |
|---|
| | 311 | currentAttr.attr = ATTR.CONST; |
|---|
| | 312 | break; |
|---|
| | 313 | case "auto": |
|---|
| | 314 | currentAttr.attr = ATTR.AUTO; |
|---|
| | 315 | break; |
|---|
| | 316 | case "synchronized": |
|---|
| | 317 | currentAttr.attr = ATTR.SYNCHRONIZED; |
|---|
| | 318 | break; |
|---|
| | 319 | |
|---|
| | 320 | case ":": |
|---|
| | 321 | if (!readingContent) { |
|---|
| | 322 | currentBlockAttr = currentAttr; |
|---|
| | 323 | previous = current; |
|---|
| | 324 | } |
|---|
| | 325 | break; |
|---|
| | 326 | |
|---|
| | 327 | case ";": |
|---|
| | 328 | if (readingContent) { |
|---|
| | 329 | flush(current); |
|---|
| | 330 | currentAttr = currentBlockAttr; |
|---|
| | 331 | readingContent = false; |
|---|
| | 332 | } |
|---|
| | 333 | break; |
|---|
| | 334 | |
|---|
| | 335 | case "{": |
|---|
| | 336 | if (readingContent) { |
|---|
| | 337 | // is function/structure body |
|---|
| | 338 | flush(current); |
|---|
| | 339 | pushContentBlock(); |
|---|
| | 340 | readingContent = false; |
|---|
| | 341 | } else { |
|---|
| | 342 | // is attribute block |
|---|
| | 343 | pushAttrBlock(); |
|---|
| | 344 | flush(ptoken); |
|---|
| | 345 | } |
|---|
| | 346 | break; |
|---|
| | 347 | |
|---|
| | 348 | case "}": |
|---|
| | 349 | if (currentAttr.isContentBlock) { |
|---|
| | 350 | flush(current); |
|---|
| | 351 | } else { |
|---|
| | 352 | flush(ptoken); |
|---|
| | 353 | } |
|---|
| | 354 | currentAttr = currentBlockAttr |
|---|
| | 355 | = attrStack[attrStack.length-1]; |
|---|
| | 356 | attrStack.length = attrStack.length - 1; |
|---|
| | 357 | readingContent = false; |
|---|
| | 358 | break; |
|---|
| | 359 | |
|---|
| | 360 | // Change "version (...) { }" into "#ifdef ... and #endif" |
|---|
| | 361 | case "version": { |
|---|
| | 362 | char *o = previous; |
|---|
| | 363 | flush(ptoken); |
|---|
| | 364 | |
|---|
| | 365 | char[][] ver; |
|---|
| 419 | | s = c; |
|---|
| 420 | | break; |
|---|
| 421 | | |
|---|
| 422 | | /* "alias" into "typedef". */ |
|---|
| 423 | | case "alias": |
|---|
| 424 | | flush (p); |
|---|
| 425 | | printf ("typedef"); |
|---|
| 426 | | s = c; |
|---|
| 427 | | break; |
|---|
| 428 | | |
|---|
| 429 | | /* "instance" into "typedef". */ |
|---|
| 430 | | case "instance": |
|---|
| 431 | | flush (p); |
|---|
| 432 | | printf ("typedef"); |
|---|
| 433 | | s = c; |
|---|
| 434 | | |
|---|
| 435 | | while ((t = token ()) != null) |
|---|
| 436 | | if (t == "(") |
|---|
| 437 | | { |
|---|
| 438 | | flush (p); |
|---|
| 439 | | printf ("<"); |
|---|
| 440 | | s = c; |
|---|
| 441 | | } |
|---|
| 442 | | else if (t == ")") |
|---|
| 443 | | { |
|---|
| 444 | | flush (p); |
|---|
| 445 | | printf (">"); |
|---|
| 446 | | s = c; |
|---|
| 447 | | break; |
|---|
| 448 | | } |
|---|
| 449 | | |
|---|
| 450 | | break; |
|---|
| 451 | | |
|---|
| 452 | | case "{": |
|---|
| 453 | | brackets ~= nextOpenBracket; |
|---|
| 454 | | nextOpenBracket = null; |
|---|
| 455 | | break; |
|---|
| 456 | | |
|---|
| 457 | | /* "}" into "};" */ |
|---|
| 458 | | case "}": |
|---|
| 459 | | if (protectRecord.length) |
|---|
| 460 | | { |
|---|
| 461 | | protect = protectRecord [protectRecord.length - 1]; |
|---|
| 462 | | protectRecord.length = protectRecord.length - 1; |
|---|
| 463 | | } |
|---|
| 464 | | |
|---|
| 465 | | flush (c); |
|---|
| 466 | | printf (";"); |
|---|
| 467 | | if (brackets.length && brackets [brackets.length - 1]) |
|---|
| 468 | | { |
|---|
| 469 | | printf (" %.*s", brackets [brackets.length - 1]); |
|---|
| 470 | | brackets = brackets [0 .. brackets.length - 1]; |
|---|
| 471 | | } |
|---|
| 472 | | break; |
|---|
| 473 | | |
|---|
| 474 | | /* "class ... {" into "class ... { public:". */ |
|---|
| 475 | | /* |
|---|
| 476 | | Not necessary anymore |
|---|
| 477 | | case "class": |
|---|
| 478 | | */ |
|---|
| 479 | | case "interface": |
|---|
| 480 | | { |
|---|
| 481 | | bit colon = false; |
|---|
| 482 | | |
|---|
| 483 | | flush (p); |
|---|
| 484 | | |
|---|
| 485 | | printf ("class"); |
|---|
| 486 | | |
|---|
| 487 | | protectRecord ~= protect; |
|---|
| 488 | | protect = "public"; |
|---|
| 489 | | |
|---|
| 490 | | while ((t = token ()) != null) |
|---|
| 491 | | { |
|---|
| 492 | | restart: |
|---|
| 493 | | if (t == ":" && !colon) |
|---|
| 494 | | { |
|---|
| 495 | | colon = true; |
|---|
| 496 | | t = token (); |
|---|
| 497 | | if (t != "public" && t != "private" && t != "protected") |
|---|
| 498 | | { |
|---|
| 499 | | flush (p); |
|---|
| 500 | | s = p; |
|---|
| 501 | | printf ("public "); |
|---|
| 502 | | goto restart; |
|---|
| | 477 | } |
|---|
| | 478 | |
|---|
| | 479 | while ((t = token()) != null) |
|---|
| | 480 | if (t == ")") |
|---|
| | 481 | break; |
|---|
| | 482 | previous = current; |
|---|
| | 483 | break; |
|---|
| | 484 | |
|---|
| | 485 | // "alias" into "typedef" |
|---|
| | 486 | case "alias": |
|---|
| | 487 | flush(ptoken); |
|---|
| | 488 | writef("%s typedef", currentAttr.toString); |
|---|
| | 489 | readingContent = true; |
|---|
| | 490 | previous = current; |
|---|
| | 491 | break; |
|---|
| | 492 | |
|---|
| | 493 | // "template name (x)" into "template namespace name <x>" |
|---|
| | 494 | case "template": |
|---|
| | 495 | flush(current); |
|---|
| | 496 | |
|---|
| | 497 | readingContent = true; |
|---|
| | 498 | writef("%s class", currentAttr.toString); |
|---|
| | 499 | |
|---|
| | 500 | while ((t = token()) != null) |
|---|
| | 501 | if (t == "(") { |
|---|
| | 502 | flush(ptoken); |
|---|
| | 503 | writef("<"); |
|---|
| | 504 | previous = current; |
|---|
| | 505 | } else if (t == ")") { |
|---|
| | 506 | flush(ptoken); |
|---|
| | 507 | writef(">"); |
|---|
| | 508 | previous = current; |
|---|
| | 509 | break; |
|---|