root/trunk/dfilter.d

Revision 206, 14.2 kB (checked in by smjg, 7 years ago)

Replaced an ancient version of dfilter

Line 
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() {
19 restart:
20     ptoken = current;
21
22     if (current >= end)
23         return null;
24
25     if (isalpha(*current) || *current == '_') {
26         for (current++; current < end; current++)
27             if (!isalnum(*current) && *current != '_')
28                 break;
29
30         return ptoken[0 .. cast(int) (current - ptoken)];
31     }
32
33     if (*current == ' ' || *current == '\r' || *current == '\n'
34           || *current == '\t') {
35         current++;
36         goto restart;
37     }
38
39     if (*current == '"') {
40         for (current++; current < end; current++)
41             if (*current == '\\') {
42                 current++;
43             } else if (*current == '"') {
44                 current++;
45                 break;
46             }
47         goto restart;
48     }
49
50     if (*current == '\'') {
51         for (current++; current < end; current++)
52             if (*current == '\\') {
53                 current++;
54             } else if (*current == '\'') {
55                 current++;
56                 break;
57             }
58         goto restart;
59     }
60
61     if (current < end - 1) {
62         if (current[0..2] == "//") {
63             for (current += 2; ; current++)
64                 if (current >= end || *current == '\n') {
65                     current++;
66                     goto restart;
67                 }
68         }
69
70         if (current[0..2] == "/*") {
71             for (current += 2; ; current++)
72                 if (current >= end - 1
73                       || (current[0..2] == "*/")) {
74                     current += 2;
75                     goto restart;
76                 }
77         }
78
79         if (current[0..2] == "/+") {
80             char* commentStart = current;
81             int depth = 1;
82
83             for (current += 2; ; current++) {
84                 if (current >= end - 1) {
85                     throw new Error("Unterminated /+...+/ comment");
86                 } else if (current[0..2] == "/+") {
87                     current++;
88                     depth++;
89                 } else if (current[0..2] == "+/") {
90                     current++;
91                     depth--;
92                     if (!depth) {
93                         /*  remove comment
94                          *  can be kept once Doxygen understands them
95                          */
96                         commentStart[0..end-current]
97                           = current[0..end-current].dup;
98                         end -= current - commentStart;
99                         current = commentStart;
100                         current[0] = ' ';
101                         goto restart;
102                     }
103                 }
104             }
105         }
106     }
107
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);
129
130     int depth = 0;
131     char[] t = token();
132
133     if (t == "(") {
134         while (1) {
135             t = token();
136             if (t == ")" || t == null)
137                 break;
138         }
139         t = token();
140     }
141
142     if (t != "{") {
143         previous = p;
144         flush(current);
145         return;
146     }
147
148     while (1) {
149         if (t == null)
150             break;
151         if (t == "{")
152             depth++;
153         if (t == "}") {
154             depth--;
155             if (depth == 0)
156                 break;
157         }
158
159         t = token();
160     }
161
162     previous = current;
163 }
164
165
166 int main(char[][] args) {
167     if (args.length == 1) {
168         writefln("Dfilter for Doxygen 1.4.2
169 %s FILENAME
170
171 Preprocesses the file in preparation for Doxygen.", args[0]);
172         return 1;
173     }
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;
366                     int depth = 0;
367
368                     t = token();
369
370                     ver.length = 0;
371                     if (t == "(") {
372                         while (1) {
373                             t = token();
374                             if (t == ")" || t == null)
375                                 break;
376                             ver.length = ver.length + 1;
377                             ver[length - 1] = t;
378                         }
379                         t = token();
380                     }
381
382                     if (elseif) {
383                         writefln("\n#elif VERSION_%s",
384                           std.string.toupper(join(ver, "")));
385                     } else {
386                         writefln("\n#ifdef VERSION_%s",
387                           std.string.toupper(join(ver, "")));
388                     }
389                     elseif = false;
390
391                     if (t == "{") {
392                         char *savec = current;
393                         while (1) {
394                             if (t == null)
395                                 break;
396                             if (t == "{")
397                                 depth++;
398                             if (t == "}") {
399                                 depth--;
400                                 if (depth == 0) {
401                                     // We found the last '}', now remove it!
402                                     *(current-1) = 0x01;
403                                     current = savec;
404                                     break;
405                                 }
406                             }
407
408                             t = token();
409                         }
410
411                         previous = current;
412                     } else {
413                         previous = ptoken;
414
415                         // Parse until the next semicolon:
416                         while (1) {
417                             if (t == null)
418                                 break;
419                             if (t == ";")
420                                 break;
421                             t = token();
422                         }
423                         flush(current);
424                         writefln("\n#endif");
425                         previous = current;
426                     }
427                     break;
428                 }
429
430                 // marked end of version block
431                 case "\x01":
432                     flush(ptoken);
433
434                     if ((t = token()) != "else") {
435                         writefln("\n#endif");
436                         current = ptoken;
437                         break;
438                     }
439
440                     if ((t = token()) != "version") {
441                         elseif = false;
442                         writefln("\n#else");
443                         char *savec = current;
444                         int depth = 0;
445
446                         while (1) {
447                             if (t == null)
448                                 break;
449                             if (t == "{")
450                                 depth++;
451                             if (t == "}") {
452                                 depth--;
453                                 if (depth == 0) {
454                                     // We found the last '}', now mark it!
455                                     *(current-1) = 0x01;
456                                     break;
457                                 }
458                             }
459
460                             t = token();
461                         }
462                         ptoken = savec;
463                     } else {
464                         elseif = true;
465                     }
466                     previous = current = ptoken;
467                     break;
468
469                 // Remove "extern (...)" and "align (...)"
470                 case "extern":
471                 case "align":
472                 case "pragma":
473                     flush(ptoken);
474                     if ((t = token()) != "(") {
475                         current = ptoken;
476                         break;
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;
510                         }
511
512                     while ((t = token()) != null)
513                         if (t == "{") {
514                             pushContentBlock();
515                             readingContent = false;
516                             flush(current);
517                             break;
518                         } else if (t == ";")
519                             break;
520                     break;
521
522                 /* "delegate (...) name" into "(*name) (...)". */
523                 case "function":
524                 case "delegate":
525                     flush(ptoken);
526                     previous = current;
527
528                     readingContent = true;
529                     writef("%s ", currentAttr.toString);
530
531                     while ((t = token()) != "(") {}
532
533                     // reach end of function signature
534                     uint bracketLevel = 1;
535                     while (bracketLevel != 0) {
536                         switch (t = token()) {
537                             case "(":
538                                 bracketLevel++;
539                                 break;
540                             case ")":
541                                 bracketLevel--;
542                                 break;
543                             default:
544                         }
545                     }
546                     t = token();
547                     if (t == "{") {
548                         /*  is function/delegate literal - don't need to
549                          *  preserve so just do this
550                          */
551                         writef("delegate {");
552                         pushContentBlock();
553                     } else {
554                         while (t == "[") {
555                             uint arrayLevel = 1;
556                             while (arrayLevel != 0) {
557                                 switch (t = token()) {
558                                     case "[":
559                                         arrayLevel++;
560                                         break;
561                                     case "]":
562                                         arrayLevel--;
563                                         break;
564                                     default:
565                                 }
566                             }
567                             t = token();
568                         }
569                         writef("(*%s)", t);
570                     }
571
572                     flush(ptoken);
573                     previous = current;
574                     break;
575
576                 default:
577                     if (!readingContent) {
578                         readingContent = true;
579                         flushTo(ptoken);
580                         writef("%s ", currentAttr.toString);
581                     }
582             }
583         }
584         flush(current);
585         return 0;
586     } catch (Object o) {
587         derr.writefln("Error: %s", o.toString());
588         return 1;
589     }
590 }
591
592
593 enum PROT { NONE, PRIVATE, PACKAGE, PROTECTED, PUBLIC, EXPORT }
594 enum ATTR { DEPRECATED, STATIC, ABSTRACT, FINAL, OVERRIDE, CONST, AUTO,
595   SYNCHRONIZED }
596
597 const char[][PROT.max + 1] PROT_WORD = [
598     PROT.NONE:      "",
599     PROT.PRIVATE:   "private",
600     PROT.PACKAGE:   "package private",
601         // because Doxygen doesn't recgonise "package"
602     PROT.PROTECTED: "protected",
603     PROT.PUBLIC:    "public",
604     PROT.EXPORT:    "export"
605 ];
606
607 const char[][ATTR.max + 1] ATTR_WORD = [
608     ATTR.DEPRECATED:    "deprecated",
609     ATTR.STATIC:        "static",
610     ATTR.ABSTRACT:      "abstract",
611     ATTR.FINAL:         "final",
612     ATTR.OVERRIDE:      "override",
613     ATTR.CONST:         "const",
614     ATTR.AUTO:          "auto",
615     ATTR.SYNCHRONIZED:  "synchronized"
616 ];
617
618 struct Attributes {
619     PROT _protect = PROT.NONE;
620     bool[ATTR.max + 1] _attr;
621     bool isContentBlock = false;
622
623     char[] toString() {
624         char[] s = PROT_WORD[_protect];
625
626         foreach (uint i, bool b; _attr) {
627             if (b) s ~= " " ~ ATTR_WORD[i];
628         }
629
630         return s;
631     }
632
633     void protect(PROT p) {
634         _protect = p;
635         flush(ptoken);
636     }
637
638     void attr(ATTR a) {
639         _attr[a] = true;
640         flush(ptoken);
641     }
642 }
643
644 Attributes currentAttr, currentBlockAttr;
645 Attributes[] attrStack;
646
647 void pushContentBlock() {
648     attrStack ~= currentBlockAttr;
649     currentAttr = Attributes.init;
650     currentAttr.isContentBlock = true;
651     currentBlockAttr = currentAttr;
652 }
653
654 void pushAttrBlock() {
655     attrStack ~= currentBlockAttr;
656     currentAttr.isContentBlock = false;
657     currentBlockAttr = currentAttr;
658 }
Note: See TracBrowser for help on using the browser.