1 module ddoc;
2 
3 class Symbol {}
4 
5 string generateDdoc(string macroDefinitionText, bool delegate(Symbol) filter = null) {
6 	return expandDdocMacros("$(DDOC)", macroDefinitionText, ["BODY":"cool"]);
7 }
8 
9 string expandDdocMacros(string inputText, string macroDefinitionText, string[string] macros) {
10 	Macro* tbl;
11 
12 	parseMacros(&tbl, ddoc_default);
13 
14 	if(macroDefinitionText !is null)
15 		parseMacros(&tbl, macroDefinitionText);
16 
17 
18 	foreach(k, v; macros)
19 		Macro.define(&tbl, k, v);
20 
21 	OutBuffer buf;
22 	buf.write(inputText);
23 
24 	auto end = buf.offset;
25 	tbl.expand(&buf, 0, &end, null, 0);
26 
27 	return finalize(&buf);
28 }
29 
30 string finalize(OutBuffer* buf) {
31 	ubyte[] d = cast(ubyte[]) (buf.data[0..buf.offset]);
32 	string s;
33 	s.reserve(d.length);
34 	bool skipNext = false;
35 	foreach(b; d) {
36 		if(skipNext) {
37 			skipNext = false;
38 			continue;
39 		}
40 		if(b != 0xff) {
41 			s ~= cast(char) b;
42 		} else {
43 			skipNext = true;
44 		}
45 	}
46 
47 	return s;
48 }
49 
50 
51 
52 // Copyright (c) 1999-2006 by Digital Mars
53 // All Rights Reserved
54 // written by Walter Bright
55 // http://www.digitalmars.com
56 // License for redistribution is by either the Artistic License
57 // in artistic.txt, or the GNU General Public License in gnu.txt.
58 // See the included readme.txt for details.
59 
60 /* Simple macro text processor.
61  */
62 
63 
64 
65 
66 
67 string ddoc_default = "
68 DDOC =  <html><head>
69         <META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">
70         <title>$(TITLE)</title>
71         </head><body>
72         <h1>$(TITLE)</h1>
73         $(BODY)
74         <hr>$(SMALL Page generated by $(LINK2 http://www.digitalmars.com/d/2.0/ddoc.html, Ddoc). $(COPYRIGHT))
75         </body></html>
76 
77 B =     <b>$0</b>
78 I =     <i>$0</i>
79 U =     <u>$0</u>
80 P =     <p>$0</p>
81 DL =    <dl>$0</dl>
82 DT =    <dt>$0</dt>
83 DD =    <dd>$0</dd>
84 TABLE = <table>$0</table>
85 TR =    <tr>$0</tr>
86 TH =    <th>$0</th>
87 TD =    <td>$0</td>
88 OL =    <ol>$0</ol>
89 UL =    <ul>$0</ul>
90 LI =    <li>$0</li>
91 BIG =   <big>$0</big>
92 SMALL = <small>$0</small>
93 BR =    <br>
94 LINK =  <a href=\"$0\">$0</a>
95 LINK2 = <a href=\"$1\">$+</a>
96 LPAREN= (
97 RPAREN= )
98 DOLLAR= $
99 
100 RED =   <font color=red>$0</font>
101 BLUE =  <font color=blue>$0</font>
102 GREEN = <font color=green>$0</font>
103 YELLOW =<font color=yellow>$0</font>
104 BLACK = <font color=black>$0</font>
105 WHITE = <font color=white>$0</font>
106 
107 D_CODE = <pre class=\"d_code\">$0</pre>
108 D_COMMENT = $(GREEN $0)
109 D_STRING  = $(RED $0)
110 D_KEYWORD = $(BLUE $0)
111 D_PSYMBOL = $(U $0)
112 D_PARAM   = $(I $0)
113 
114 DDOC_COMMENT   = <!-- $0 -->
115 DDOC_DECL      = $(DT $(BIG $0))
116 DDOC_DECL_DD   = $(DD $0)
117 DDOC_DITTO     = $(BR)$0
118 DDOC_SECTIONS  = $0
119 DDOC_SUMMARY   = $0$(BR)$(BR)
120 DDOC_DESCRIPTION = $0$(BR)$(BR)
121 DDOC_AUTHORS   = $(B Authors:)$(BR)\n$0$(BR)$(BR)
122 DDOC_BUGS      = $(RED BUGS:)$(BR)\n$0$(BR)$(BR)
123 DDOC_COPYRIGHT = $(B Copyright:)$(BR)\n$0$(BR)$(BR)
124 DDOC_DATE      = $(B Date:)$(BR)\n$0$(BR)$(BR)
125 DDOC_DEPRECATED = $(RED Deprecated:)$(BR)\n$0$(BR)$(BR)
126 DDOC_EXAMPLES  = $(B Examples:)$(BR)\n$0$(BR)$(BR)
127 DDOC_HISTORY   = $(B History:)$(BR)\n$0$(BR)$(BR)
128 DDOC_LICENSE   = $(B License:)$(BR)\n$0$(BR)$(BR)
129 DDOC_RETURNS   = $(B Returns:)$(BR)\n$0$(BR)$(BR)
130 DDOC_SEE_ALSO  = $(B See Also:)$(BR)\n$0$(BR)$(BR)
131 DDOC_STANDARDS = $(B Standards:)$(BR)\n$0$(BR)$(BR)
132 DDOC_THROWS    = $(B Throws:)$(BR)\n$0$(BR)$(BR)
133 DDOC_VERSION   = $(B Version:)$(BR)\n$0$(BR)$(BR)
134 DDOC_SECTION_H = $(B $0)$(BR)
135 DDOC_SECTION   = $0$(BR)$(BR)
136 DDOC_MEMBERS   = $(DL $0)
137 DDOC_MODULE_MEMBERS = $(DDOC_MEMBERS $0)
138 DDOC_CLASS_MEMBERS  = $(DDOC_MEMBERS $0)
139 DDOC_STRUCT_MEMBERS = $(DDOC_MEMBERS $0)
140 DDOC_ENUM_MEMBERS   = $(DDOC_MEMBERS $0)
141 DDOC_TEMPLATE_MEMBERS = $(DDOC_MEMBERS $0)
142 DDOC_PARAMS    = $(B Params:)$(BR)\n$(TABLE $0)$(BR)
143 DDOC_PARAM_ROW = $(TR $0)
144 DDOC_PARAM_ID  = $(TD $0)
145 DDOC_PARAM_DESC = $(TD $0)
146 DDOC_BLANKLINE  = $(BR)$(BR)
147 
148 DDOC_PSYMBOL    = $(U $0)
149 DDOC_KEYWORD    = $(B $0)
150 DDOC_PARAM      = $(I $0)
151 
152 ESCAPES = /</&lt;/
153           />/&gt;/
154           /&/&amp;/
155 ";
156 
157 struct Escape
158 {
159     const(char)*[256] strings;
160 
161     static const(char) *escapeChar(unsigned c) {
162 	const(char) *s;
163 
164 	switch (c) {
165 		case '<':
166 			s = "&lt;";
167 		break;
168 		case '>':
169 			s = "&gt;";
170 		break;
171 		case '&':
172 			s = "&amp;";
173 		break;
174 		default:
175 			s = null;
176 		break;
177 	}
178 	return s;
179     }
180 }
181 
182 
183 void parseMacros(Macro** macroTable, in char[] m) {
184 	parseMacros(null, macroTable, m.ptr, m.length);
185 }
186 
187 /************************************************
188  * Parse macros out of Macros: section.
189  * Macros are of the form:
190  *      name1 = value1
191  *
192  *      name2 = value2
193  */
194 
195 void parseMacros(Escape **pescapetable, Macro **pmacrotable, in char *m, unsigned mlen)
196 {
197     const(char) *p = m;
198     unsigned len = mlen;
199     const(char) *pend = p + len;
200 
201     const(char) *tempstart;
202     unsigned templen;
203 
204     const(char) *namestart;
205     unsigned namelen = 0;       // !=0 if line continuation
206 
207     const(char) *textstart;
208     unsigned textlen;
209 
210     while (p < pend)
211     {
212         // Skip to start of macro
213         while (1)
214         {
215             if (p >= pend)
216                 goto Ldone;
217             switch (*p)
218             {
219                 case ' ':
220                 case '\t':
221                     p++;
222                     continue;
223 
224                 case '\n':
225                     p++;
226                     goto Lcont;
227 
228                 default:
229                     if (isIdStart(p))
230                         break;
231                     if (namelen)
232                         goto Ltext;             // continuation of prev macro
233                     goto Lskipline;
234             }
235             break;
236         }
237         tempstart = p;
238 
239         while (1)
240         {
241             if (p >= pend)
242                 goto Ldone;
243             if (!isIdTail(p))
244                 break;
245             p += utfStride(p);
246         }
247         templen = p - tempstart;
248 
249         while (1)
250         {
251             if (p >= pend)
252                 goto Ldone;
253             if (!(*p == ' ' || *p == '\t'))
254                 break;
255             p++;
256         }
257 
258         if (*p != '=')
259         {   if (namelen)
260                 goto Ltext;             // continuation of prev macro
261             goto Lskipline;
262         }
263         p++;
264         if (p >= pend)
265             goto Ldone;
266 
267         if (namelen)
268         {   // Output existing macro
269         L1:
270             //printf("macro '%.*s' = '%.*s'\n", namelen, namestart, textlen, textstart);
271             //if (icmp("ESCAPES", namestart, namelen) == 0)
272                 //parseEscapes(pescapetable, textstart, textlen);
273             //else
274                 Macro.define(pmacrotable, namestart[0..namelen].idup, textstart[0..textlen].idup);
275             namelen = 0;
276             if (p >= pend)
277                 break;
278         }
279 
280         namestart = tempstart;
281         namelen = templen;
282 
283         while (p < pend && (*p == ' ' || *p == '\t'))
284             p++;
285         textstart = p;
286 
287       Ltext:
288         while (p < pend && *p != '\n')
289             p++;
290         textlen = p - textstart;
291 
292         // Remove trailing \r if there is one
293         if (p > m && p[-1] == '\r')
294             textlen--;
295 
296         p++;
297         //printf("p = %p, pend = %p\n", p, pend);
298 
299      Lcont:
300         continue;
301 
302      Lskipline:
303         // Ignore this line
304         while (p < pend && *p++ != '\n')
305             {}
306     }
307 Ldone:
308     if (namelen)
309         goto L1;                // write out last one
310 }
311 
312 
313 
314 
315 import std.ascii;
316 
317 import core.stdc..string;
318 
319     char* memdup(char* arg, unsigned arglen) {
320 	return arg[0 .. arglen].dup.ptr;
321     }
322 
323 struct OutBuffer {
324 	void write(string t) {
325 		write(t.ptr, t.length);
326 	}
327 
328 	void write(const void *data, unsigned nbytes) {
329 		reserve(nbytes);
330 		memcpy(this.data + offset, data, nbytes);
331 		offset += nbytes;
332 	}
333 	void reserve(unsigned nbytes) out { assert(data !is null); } body {
334 		if (size - offset < nbytes) {
335 			size = (offset + nbytes) * 2;
336 			auto nl = new char[size];
337 			auto n = nl.ptr;
338 			assert(n);
339 			if(data)
340 				n[0 .. offset] = data[0 .. offset];
341 			data = n;
342 		}
343 	}
344 
345 	unsigned insert(unsigned offset, string s) {
346 		return insert(offset, s.ptr, s.length);
347 	}
348 
349 	unsigned insert(unsigned offset, const void *p, unsigned nbytes) {
350 		spread(offset, nbytes);
351 		memmove(data + offset, p, nbytes);
352 		return offset + nbytes;
353 	}
354 
355 	void remove(unsigned offset, unsigned nbytes) {
356 		memmove(data + offset, data + offset + nbytes, this.offset - (offset + nbytes));
357 		this.offset -= nbytes;
358 	}
359 	void spread(unsigned offset, unsigned nbytes) {
360 		reserve(nbytes);
361 		memmove(data + offset + nbytes, data + offset,
362 		this.offset - offset);
363 		this.offset += nbytes;
364 	}
365 
366 	unsigned offset;
367 	char *data;
368 	unsigned size;
369 }
370 
371 struct Macro {
372   private:
373 	Macro *next;                // next in list
374 	string name;
375 	string text;        // macro replacement text
376 	int inuse;                  // macro is in use (don't expand)
377 
378 	this(string name, string text) {
379 		next = null;
380 		this.name = name;
381 		this.text = text;
382 		inuse = 0;
383 	}
384 
385 	Macro *search(in char[] name) {
386 		Macro *table;
387 
388 		for (table = &this; table; table = table.next) {
389 			if (table.name == name)
390 				break;
391 		}
392 
393 		return table;
394 	}
395 
396   public:
397 	static Macro *define(Macro **ptable, string name, string text) {
398 		Macro *table;
399 
400 		//assert(ptable);
401 		for (table = *ptable; table; table = table.next) {
402 			if(table.name == name) {
403 				table.text = text;
404 				return table;
405 			}
406 		}
407 
408 		table = new Macro(name, text);
409 		table.next = *ptable;
410 		*ptable = table;
411 		return table;
412 	}
413 
414 /*****************************************************
415  * Expand macro in place in buf.
416  * Only look at the text in buf from start to end.
417  */
418 
419 void expand(OutBuffer *buf, unsigned start, unsigned *pend,
420         char *arg, unsigned arglen)
421 {
422 
423     static int nest;
424     if (nest > 100)             // limit recursive expansion
425         return;
426     nest++;
427 
428     unsigned end = *pend;
429     assert(start <= end);
430     assert(end <= buf.offset);
431 
432     /* First pass - replace $0
433      */
434     arg = memdup(arg, arglen);
435     for (unsigned u = start; u + 1 < end; )
436     {
437         char *p = buf.data;   // buf.data is not loop invariant
438 
439         /* Look for $0, but not $$0, and replace it with arg.
440          */
441         if (p[u] == '$' && (isDigit(p[u + 1]) || p[u + 1] == '+'))
442         {
443             if (u > start && p[u - 1] == '$')
444             {   // Don't expand $$0, but replace it with $0
445                 buf.remove(u - 1, 1);
446                 end--;
447                 u += 1; // now u is one past the closing '1'
448                 continue;
449             }
450 
451             char c = p[u + 1];
452             int n = (c == '+') ? -1 : c - '0';
453 
454             char *marg;
455             unsigned marglen;
456             extractArgN(arg, arglen, &marg, &marglen, n);
457             if (marglen == 0)
458             {   // Just remove macro invocation
459                 //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg);
460                 buf.remove(u, 2);
461                 end -= 2;
462             }
463             else if (c == '+')
464             {
465                 // Replace '$+' with 'arg'
466                 //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg);
467                 buf.remove(u, 2);
468                 buf.insert(u, marg, marglen);
469                 end += marglen - 2;
470 
471                 // Scan replaced text for further expansion
472                 unsigned mend = u + marglen;
473                 expand(buf, u, &mend, null, 0);
474                 end += mend - (u + marglen);
475                 u = mend;
476             }
477             else
478             {
479                 // Replace '$1' with '\xFF{arg\xFF}'
480                 //printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], marglen, marg);
481                 buf.data[u] = 0xFF;
482                 buf.data[u + 1] = '{';
483                 buf.insert(u + 2, marg, marglen);
484                 buf.insert(u + 2 + marglen, "\xFF}".ptr, 2);
485                 end += -2 + 2 + marglen + 2;
486 
487                 // Scan replaced text for further expansion
488                 unsigned mend = u + 2 + marglen;
489                 expand(buf, u + 2, &mend, null, 0);
490                 end += mend - (u + 2 + marglen);
491                 u = mend;
492             }
493             //printf("u = %d, end = %d\n", u, end);
494             //printf("#%.*s#\n", end, &buf.data[0]);
495             continue;
496         }
497 
498         u++;
499     }
500 
501     /* Second pass - replace other macros
502      */
503     for (unsigned u = start; u + 4 < end; )
504     {
505         char *p = buf.data;   // buf.data is not loop invariant
506 
507         /* A valid start of macro expansion is $(c, where c is
508          * an id start character, and not $$(c.
509          */
510         if (p[u] == '$' && p[u + 1] == '(' && isidstart(p[u + 2]))
511         {
512             //printf("\tfound macro start '%c'\n", p[u + 2]);
513             char *name = p + u + 2;
514             unsigned namelen = 0;
515 
516             char *marg;
517             unsigned marglen;
518 
519             unsigned v;
520             /* Scan forward to find end of macro name and
521              * beginning of macro argument (marg).
522              */
523             for (v = u + 2; v < end; v++)
524             {   char c = p[v];
525 
526                 if (!isidchar(c))
527                 {   // We've gone past the end of the macro name.
528                     namelen = v - (u + 2);
529                     break;
530                 }
531             }
532 
533             v += extractArgN(p + v, end - v, &marg, &marglen, 0);
534             assert(v <= end);
535 
536             if (v < end)
537             {   // v is on the closing ')'
538                 if (u > start && p[u - 1] == '$')
539                 {   // Don't expand $$(NAME), but replace it with $(NAME)
540                     buf.remove(u - 1, 1);
541                     end--;
542                     u = v;      // now u is one past the closing ')'
543                     continue;
544                 }
545 
546                 Macro *m = search(name[0 .. namelen]);
547                 if (m)
548                 {
549                     if (m.inuse && marglen == 0)
550                     {   // Remove macro invocation
551                         buf.remove(u, v + 1 - u);
552                         end -= v + 1 - u;
553                     }
554                     else if (m.inuse && arg == marg)
555                     {   // Recursive expansion; just leave in place
556 
557                     }
558                     else
559                     {
560                         //printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", m.namelen, m.name, marglen, marg, m.textlen, m.text);
561                         marg = memdup(marg, marglen);
562                         // Insert replacement text
563                         buf.spread(v + 1, 2 + m.text.length + 2);
564                         buf.data[v + 1] = 0xFF;
565                         buf.data[v + 2] = '{';
566                         //memcpy(buf.data + v + 3, m.text, m.text.length);
567 			buf.data[v+3 .. v+3 + m.text.length] = m.text;
568                         buf.data[v + 3 + m.text.length] = 0xFF;
569                         buf.data[v + 3 + m.text.length + 1] = '}';
570 
571                         end += 2 + m.text.length + 2;
572 
573                         // Scan replaced text for further expansion
574                         m.inuse++;
575                         unsigned mend = v + 1 + 2+m.text.length+2;
576                         expand(buf, v + 1, &mend, marg, marglen);
577                         end += mend - (v + 1 + 2+m.text.length+2);
578                         m.inuse--;
579 
580                         buf.remove(u, v + 1 - u);
581                         end -= v + 1 - u;
582                         u += mend - (v + 1);
583                         //mem.free(marg);
584                         //printf("u = %d, end = %d\n", u, end);
585                         //printf("#%.*s#\n", end - u, &buf.data[u]);
586                         continue;
587                     }
588                 }
589                 else
590                 {
591                     // Replace $(NAME) with nothing
592                     buf.remove(u, v + 1 - u);
593                     end -= (v + 1 - u);
594                     continue;
595                 }
596             }
597         }
598         u++;
599     }
600     //mem.free(arg);
601     *pend = end;
602     nest--;
603 }
604 
605 
606 
607 };
608 
609 
610 bool isidstart(char c) { return isAlpha(c) || (c) == '_'; }
611 bool isidchar(char c)  { return isAlphaNum(c) || (c) == '_'; }
612 
613 /**********************************************************
614  * Given buffer p[0..end], extract argument marg[0..marglen].
615  * Params:
616  *      n       0:      get entire argument
617  *              1..9:   get nth argument
618  *              -1:     get 2nd through end
619  */
620 
621 alias uint unsigned;
622 
623 unsigned extractArgN(char *p, unsigned end, char **pmarg, unsigned *pmarglen, int n)
624 {
625     /* Scan forward for matching right parenthesis.
626      * Nest parentheses.
627      * Skip over $( and $)
628      * Skip over "..." and '...' strings inside HTML tags.
629      * Skip over <!-- ... -. comments.
630      * Skip over previous macro insertions
631      * Set marglen.
632      */
633     unsigned parens = 1;
634     char instring = 0;
635     unsigned incomment = 0;
636     unsigned intag = 0;
637     unsigned inexp = 0;
638     unsigned argn = 0;
639 
640     unsigned v = 0;
641 
642   Largstart:
643 
644     // Skip first space, if any, to find the start of the macro argument
645     if (v < end && isWhite(p[v]))
646         v++;
647     *pmarg = p + v;
648 
649     for (; v < end; v++)
650     {   char c = p[v];
651 
652         switch (c)
653         {
654             case ',':
655                 if (!inexp && !instring && !incomment && parens == 1)
656                 {
657                     argn++;
658                     if (argn == 1 && n == -1)
659                     {   v++;
660                         goto Largstart;
661                     }
662                     if (argn == n)
663                         break;
664                     if (argn + 1 == n)
665                     {   v++;
666                         goto Largstart;
667                     }
668                 }
669                 continue;
670 
671             case '(':
672                 if (!inexp && !instring && !incomment)
673                     parens++;
674                 continue;
675 
676             case ')':
677                 if (!inexp && !instring && !incomment && --parens == 0)
678                 {
679                     break;
680                 }
681                 continue;
682 
683             case '"':
684             case '\'':
685                 if (!inexp && !incomment && intag)
686                 {
687                     if (c == instring)
688                         instring = 0;
689                     else if (!instring)
690                         instring = c;
691                 }
692                 continue;
693 
694             case '<':
695                 if (!inexp && !instring && !incomment)
696                 {
697                     if (v + 6 < end &&
698                         p[v + 1] == '!' &&
699                         p[v + 2] == '-' &&
700                         p[v + 3] == '-')
701                     {
702                         incomment = 1;
703                         v += 3;
704                     }
705                     else if (v + 2 < end &&
706                         isAlpha(p[v + 1]))
707                         intag = 1;
708                 }
709                 continue;
710 
711             case '>':
712                 if (!inexp)
713                     intag = 0;
714                 continue;
715 
716             case '-':
717                 if (!inexp &&
718                     !instring &&
719                     incomment &&
720                     v + 2 < end &&
721                     p[v + 1] == '-' &&
722                     p[v + 2] == '>')
723                 {
724                     incomment = 0;
725                     v += 2;
726                 }
727                 continue;
728 
729             case 0xFF:
730                 if (v + 1 < end)
731                 {
732                     if (p[v + 1] == '{')
733                         inexp++;
734                     else if (p[v + 1] == '}')
735                         inexp--;
736                 }
737                 continue;
738 
739             default:
740                 continue;
741         }
742         break;
743     }
744     if (argn == 0 && n == -1)
745         *pmarg = p + v;
746     *pmarglen = p + v - *pmarg;
747     //printf("extractArg%d('%.*s') = '%.*s'\n", n, end, p, *pmarglen, *pmarg);
748     return v;
749 }
750 
751 
752 
753 
754 
755 // ///////////////////////////////////////////////////////////////////////
756 //             END MACRO.C
757 // ///////////////////////////////////////////////////////////////////////
758 
759 
760 
761 
762 
763 
764 
765 
766 
767 
768 
769 
770 
771 /****************************************
772  * Determine if p points to the start of an identifier.
773  */
774 
775 int isIdStart(in char *p)
776 {
777     unsigned c = *p;
778     if (isAlpha(c) || c == '_')
779         return 1;
780     /*if (c >= 0x80)
781     {   size_t i = 0;
782         if (utf_decodeChar(p, 4, &i, &c))
783             return 0;   // ignore errors
784         if (isUniAlpha(c))
785             return 1;
786     }*/
787     return 0;
788 }
789 
790 /****************************************
791  * Determine if p points to the rest of an identifier.
792  */
793 
794 int isIdTail(in char *p)
795 {
796     unsigned c = *p;
797     if (isAlphaNum(c) || c == '_')
798         return 1;
799 	/*
800     if (c >= 0x80)
801     {   size_t i = 0;
802         if (utf_decodeChar(p, 4, &i, &c))
803             return 0;   // ignore errors
804         if (isUniAlpha(c))
805             return 1;
806     }*/
807     return 0;
808 }
809 
810 /*****************************************
811  * Return number of bytes in UTF character.
812  */
813 
814 int utfStride(in char *p)
815 {
816     unsigned c = *p;
817     if (c < 0x80)
818         return 1;
819 assert(0);
820 	/+
821     size_t i = 0;
822     utf_decodeChar(p, 4, &i, &c);       // ignore errors, but still consume input
823     return i;
824     +/
825 }
826 
827 
828 
829 /*****************************************
830  * Parse next paragraph out of *pcomment.
831  * Update *pcomment to point past paragraph.
832  * Returns null if no more paragraphs.
833  * If paragraph ends in 'identifier:',
834  * then (*pcomment)[0 .. idlen] is the identifier.
835  */
836 
837 Section*[] parseSections(char *comment)
838 {   char *p;
839     char *pstart;
840     char *pend;
841     char *idstart;
842     unsigned idlen;
843 
844     Section* summary;
845     Section*[] sections;
846 
847     char *name = null;
848     unsigned namelen = 0;
849 
850     //printf("parseSections('%s')\n", comment);
851     p = comment;
852     while (*p)
853     {
854         p = skipwhitespace(p);
855         pstart = p;
856         pend = p;
857 
858         /* Find end of section, which is ended by one of:
859          *      'identifier:' (but not inside a code section)
860          *      '\0'
861          */
862         idlen = 0;
863         int inCode = 0;
864         while (1)
865         {
866             // Check for start/end of a code section
867             if (*p == '-')
868             {
869                 int numdash = 0;
870                 while (*p == '-')
871                 {
872                     ++numdash;
873                     p++;
874                 }
875                 // BUG: handle UTF PS and LS too
876                 if (!*p || *p == '\r' || *p == '\n' && numdash >= 3)
877                     inCode ^= 1;
878                 pend = p;
879             }
880 
881             if (!inCode && isIdStart(p))
882             {
883                 char *q = p + utfStride(p);
884                 while (isIdTail(q))
885                     q += utfStride(q);
886                 if (*q == ':')  // identifier: ends it
887                 {   idlen = q - p;
888                     idstart = p;
889                     for (pend = p; pend > pstart; pend--)
890                     {   if (pend[-1] == '\n')
891                             break;
892                     }
893                     p = q + 1;
894                     break;
895                 }
896             }
897             while (1)
898             {
899                 if (!*p)
900                     goto L1;
901                 if (*p == '\n')
902                 {   p++;
903                     if (*p == '\n' && !summary && !namelen && !inCode)
904                     {
905                         pend = p;
906                         p++;
907                         goto L1;
908                     }
909                     break;
910                 }
911                 p++;
912                 pend = p;
913             }
914             p = skipwhitespace(p);
915         }
916       L1:
917 
918         if (namelen || pstart < pend)
919         {
920             Section *s;
921 	    /* // FIXME
922             if (icmp("Params", name, namelen) == 0)
923                 s = new ParamSection();
924             else if (icmp("Macros", name, namelen) == 0)
925                 s = new MacroSection();
926             else
927 	    */
928                 s = new Section();
929             s.name = name;
930             s.namelen = namelen;
931             s.bodyTxt = pstart;
932             s.bodylen = pend - pstart;
933             s.nooutput = 0;
934 
935             //printf("Section: '%.*s' = '%.*s'\n", s.namelen, s.name, s.bodylen, s.body);
936 
937             sections ~= s;
938 
939             //if (!summary && !namelen)
940                 //summary = s;
941         }
942 
943         if (idlen)
944         {   name = idstart;
945             namelen = idlen;
946         }
947         else
948         {   name = null;
949             namelen = 0;
950             if (!*p)
951                 break;
952         }
953     }
954 
955     return sections;
956 }
957 
958 
959 char *skipwhitespace(char *p)
960 {
961     for (; 1; p++)
962     {   switch (*p)
963         {
964             case ' ':
965             case '\t':
966             case '\n':
967                 continue;
968 	    default: 
969         }
970         break;
971     }
972     return p;
973 }
974 
975 
976 
977 
978 struct Section {
979 	char* name;
980 	unsigned namelen;
981 	char* bodyTxt;
982 	unsigned bodylen;
983 	int nooutput;
984 }
985 
986 int icmp(in char[] what, in char* n, unsigned len) {
987 	const(char)[] lol = n[0 .. len];
988 
989 	if(what == lol)
990 		return 0;
991 
992 	import std..string;
993 	if(toLower(lol) == toLower(what))
994 		return 0;
995 
996 	return 1;
997 }
998 
999 
1000 /**************************************
1001  * Parse escapes of the form:
1002  *      /c/string/
1003  * where c is a single character.
1004  * Multiple escapes can be separated
1005  * by whitespace and/or commas.
1006  */
1007 
1008 void parseEscapes(Escape **pescapetable, char *textstart, unsigned textlen)
1009 {   Escape *escapetable = *pescapetable;
1010 
1011     if (!escapetable)
1012     {   escapetable = new Escape;
1013         *pescapetable = escapetable;
1014     }
1015     char *p = textstart;
1016     char *pend = p + textlen;
1017 
1018     while (1)
1019     {
1020         while (1)
1021         {
1022             if (p + 4 >= pend)
1023                 return;
1024             if (!(*p == ' ' || *p == '\t' || *p == '\n' || *p == ','))
1025                 break;
1026             p++;
1027         }
1028         if (p[0] != '/' || p[2] != '/')
1029             return;
1030         char c = p[1];
1031         p += 3;
1032         char *start = p;
1033         while (1)
1034         {
1035             if (p >= pend)
1036                 return;
1037             if (*p == '/')
1038                 break;
1039             p++;
1040         }
1041         size_t len = p - start;
1042 	char[] newbuffer = new char[len + 1];
1043         char *s = cast(char *)memcpy(newbuffer.ptr, start, len);
1044         s[len] = 0;
1045         escapetable.strings[c] = s;
1046         //printf("%c = '%s'\n", c, s);
1047         p++;
1048     }
1049 }
1050 
1051 
1052 /****************************************************
1053  */
1054 
1055 int isKeyword(char *p, unsigned len)
1056 {
1057     static string[] table = [ "true", "false", "null" ];
1058 
1059     foreach(i; table)
1060     	if(i == p[0..len])
1061 		return 1;
1062     return 0;
1063 }
1064 
1065 
1066 /****************************************************
1067  * Having unmatched parentheses can hose the output of Ddoc,
1068  * as the macros depend on properly nested parentheses.
1069 
1070  * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN).
1071  */
1072 void escapeStrayParenthesis(OutBuffer *buf, unsigned start)
1073 {
1074     unsigned par_open = 0;
1075 
1076     for (unsigned u = start; u < buf.offset; u++)
1077     {
1078         char c = buf.data[u];
1079         switch(c)
1080         {
1081             case '(':
1082                 par_open++;
1083                 break;
1084 
1085             case ')':
1086                 if (par_open == 0)
1087                 {
1088                     //stray ')'
1089                     //if (global.params.warnings)
1090                         //warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output."
1091                         //" Use $(RPAREN) instead for unpaired right parentheses.");
1092                     buf.remove(u, 1); //remove the )
1093                     buf.insert(u, "$(RPAREN)"); //insert this instead
1094                     u += 8; //skip over newly inserted macro
1095                 }
1096                 else
1097                     par_open--;
1098                 break;
1099 
1100 	default:
1101         }
1102     }
1103 
1104     if (par_open)                       // if any unmatched lparens
1105     {   par_open = 0;
1106         for (unsigned u = buf.offset; u > start;)
1107         {   u--;
1108             char c = buf.data[u];
1109             switch(c)
1110             {
1111                 case ')':
1112                     par_open++;
1113                     break;
1114 
1115                 case '(':
1116                     if (par_open == 0)
1117                     {
1118                         //stray '('
1119                         //if (global.params.warnings)
1120                             //warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output."
1121                             //" Use $(LPAREN) instead for unpaired left parentheses.");
1122                         buf.remove(u, 1); //remove the (
1123                         buf.insert(u, "$(LPAREN)"); //insert this instead
1124                     }
1125                     else
1126                         par_open--;
1127                     break;
1128 		    default:
1129             }
1130         }
1131     }
1132 }
1133 
1134 /****************************************************
1135  * Having unmatched parentheses can hose the output of Ddoc,
1136  * as the macros depend on properly nested parentheses.
1137  * This function replaces all ( with $(LPAREN) and ) with $(RPAREN)
1138  * to preserve text literally. This also means macros in the
1139  * text won't be expanded.
1140  */
1141 void escapeDdocString(OutBuffer *buf, unsigned start)
1142 {
1143     for (unsigned u = start; u < buf.offset; u++)
1144     {
1145         char c = buf.data[u];
1146         switch(c)
1147         {
1148             case '$':
1149                 buf.remove(u, 1);
1150                 buf.insert(u, "$(DOLLAR)");
1151                 u += 8;
1152                 break;
1153 
1154             case '(':
1155                 buf.remove(u, 1); //remove the (
1156                 buf.insert(u, "$(LPAREN)"); //insert this instead
1157                 u += 8; //skip over newly inserted macro
1158                 break;
1159 
1160             case ')':
1161                 buf.remove(u, 1); //remove the )
1162                 buf.insert(u, "$(RPAREN)"); //insert this instead
1163                 u += 8; //skip over newly inserted macro
1164                 break;
1165 		default:
1166         }
1167     }
1168 }
1169 
1170 
1171 
1172 
1173 
1174 
1175 
1176 /+
1177 
1178 
1179 
1180 /****************************************************
1181  */
1182 
1183 void Module::gendocfile()
1184 {
1185     static OutBuffer mbuf;
1186     static int mbuf_done;
1187 
1188     OutBuffer buf;
1189 
1190     //printf("Module::gendocfile()\n");
1191 
1192     if (!mbuf_done)             // if not already read the ddoc files
1193     {   mbuf_done = 1;
1194 
1195         // Use our internal default
1196         mbuf.write(ddoc_default, sizeof(ddoc_default) - 1);
1197 
1198         // Override with DDOCFILE specified in the sc.ini file
1199         char *p = getenv("DDOCFILE");
1200         if (p)
1201             global.params.ddocfiles->shift(p);
1202 
1203         // Override with the ddoc macro files from the command line
1204         for (size_t i = 0; i < global.params.ddocfiles->dim; i++)
1205         {
1206             FileName f((*global.params.ddocfiles)[i], 0);
1207             File file(&f);
1208             file.readv();
1209             // BUG: convert file contents to UTF-8 before use
1210 
1211             //printf("file: '%.*s'\n", file.len, file.buffer);
1212             mbuf.write(file.buffer, file.len);
1213         }
1214     }
1215     DocComment::parseMacros(&escapetable, &macrotable, mbuf.data, mbuf.offset);
1216 
1217     Scope *sc = Scope::createGlobal(this);      // create root scope
1218     sc->docbuf = &buf;
1219 
1220     DocComment *dc = DocComment::parse(sc, this, comment);
1221     dc->pmacrotable = &macrotable;
1222     dc->pescapetable = &escapetable;
1223 
1224     // Generate predefined macros
1225 
1226     // Set the title to be the name of the module
1227     {   const char *p = toPrettyChars();
1228         Macro::define(&macrotable, (unsigned char *)"TITLE", 5, (unsigned char *)p, strlen(p));
1229     }
1230 
1231     // Set time macros
1232     {   time_t t;
1233         time(&t);
1234         char *p = ctime(&t);
1235         p = mem.strdup(p);
1236         Macro::define(&macrotable, (unsigned char *)"DATETIME", 8, (unsigned char *)p, strlen(p));
1237         Macro::define(&macrotable, (unsigned char *)"YEAR", 4, (unsigned char *)p + 20, 4);
1238     }
1239 
1240     char *srcfilename = srcfile->toChars();
1241     Macro::define(&macrotable, (unsigned char *)"SRCFILENAME", 11, (unsigned char *)srcfilename, strlen(srcfilename));
1242 
1243     char *docfilename = docfile->toChars();
1244     Macro::define(&macrotable, (unsigned char *)"DOCFILENAME", 11, (unsigned char *)docfilename, strlen(docfilename));
1245 
1246     if (dc->copyright)
1247     {
1248         dc->copyright->nooutput = 1;
1249         Macro::define(&macrotable, (unsigned char *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen);
1250     }
1251 
1252     buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars());
1253     if (isDocFile)
1254     {
1255         size_t commentlen = strlen((char *)comment);
1256         if (dc->macros)
1257         {
1258             commentlen = dc->macros->name - comment;
1259             dc->macros->write(dc, sc, this, sc->docbuf);
1260         }
1261         sc->docbuf->write(comment, commentlen);
1262         highlightText(NULL, this, sc->docbuf, 0);
1263     }
1264     else
1265     {
1266         dc->writeSections(sc, this, sc->docbuf);
1267         emitMemberComments(sc);
1268     }
1269 
1270     //printf("BODY= '%.*s'\n", buf.offset, buf.data);
1271     Macro::define(&macrotable, (unsigned char *)"BODY", 4, buf.data, buf.offset);
1272 
1273     OutBuffer buf2;
1274     buf2.writestring("$(DDOC)\n");
1275     unsigned end = buf2.offset;
1276     macrotable->expand(&buf2, 0, &end, NULL, 0);
1277 
1278 #if 1
1279     /* Remove all the escape sequences from buf2,
1280      * and make CR-LF the newline.
1281      */
1282     {
1283         buf.setsize(0);
1284         buf.reserve(buf2.offset);
1285         unsigned char *p = buf2.data;
1286         for (unsigned j = 0; j < buf2.offset; j++)
1287         {
1288             unsigned char c = p[j];
1289             if (c == 0xFF && j + 1 < buf2.offset)
1290             {
1291                 j++;
1292                 continue;
1293             }
1294             if (c == '\n')
1295                 buf.writeByte('\r');
1296             else if (c == '\r')
1297             {
1298                 buf.writestring("\r\n");
1299                 if (j + 1 < buf2.offset && p[j + 1] == '\n')
1300                 {
1301                     j++;
1302                 }
1303                 continue;
1304             }
1305             buf.writeByte(c);
1306         }
1307     }
1308 
1309     // Transfer image to file
1310     assert(docfile);
1311     docfile->setbuffer(buf.data, buf.offset);
1312     docfile->ref = 1;
1313     char *pt = FileName::path(docfile->toChars());
1314     if (*pt)
1315         FileName::ensurePathExists(pt);
1316     mem.free(pt);
1317     docfile->writev();
1318 #else
1319     /* Remove all the escape sequences from buf2
1320      */
1321     {   unsigned i = 0;
1322         unsigned char *p = buf2.data;
1323         for (unsigned j = 0; j < buf2.offset; j++)
1324         {
1325             if (p[j] == 0xFF && j + 1 < buf2.offset)
1326             {
1327                 j++;
1328                 continue;
1329             }
1330             p[i] = p[j];
1331             i++;
1332         }
1333         buf2.setsize(i);
1334     }
1335 
1336     // Transfer image to file
1337     docfile->setbuffer(buf2.data, buf2.offset);
1338     docfile->ref = 1;
1339     char *pt = FileName::path(docfile->toChars());
1340     if (*pt)
1341         FileName::ensurePathExists(pt);
1342     mem.free(pt);
1343     docfile->writev();
1344 #endif
1345 }
1346 
1347 +/
Suggestion Box / Bug Report