root/trunk/src/tutorials/d2html/d2html.d

Revision 6, 17.4 kB (checked in by jcc7, 3 years ago)

Made more progress in developing convertToWiki and added some files.

Line 
1 /*
2  * Copyright (c) 2001
3  * Pavel "EvilOne" Minayev
4  *
5  * Permission to use, copy, modify, distribute and sell this software
6  * and its documentation for any purpose is hereby granted without fee,
7  * provided that the above copyright notice appear in all copies and
8  * that both that copyright notice and this permission notice appear
9  * in supporting documentation.  Author makes no representations about
10  * the suitability of this software for any purpose. It is provided
11  * "as is" without express or implied warranty.
12  
13  * Updated by J C Calvarese, http://jcc_7.tripod.com/d/, 2003/12/18
14     Modifications donated to the public domain. 
15
16     Fixed:
17      + Compiles with DMD 0.76
18      + "Naked" escape sequences (\n, \r, etc.).
19      + Escape sequences in character literals ('\'').
20      + Supporsts WYSIWYG (``) strings.
21      + Endless loop for 0x1 or 0b1.
22      + Nestable (/+ +/) comments.
23      + Escape sequence for backslashes (\\).
24      + Added identifier output option.
25      + Fixed problem with single-line comments added an extra CR or LF.
26
27     To-Do:
28      +  Teach the program to expect the end of the file rather than just react to it.
29      +  Unicode support.
30      + <br> in embedded strings is mis-handled (should convert to "&lt;br&gr;".
31      + r"WYSIWYG" is probably not handled correctly for special case such as r"\" (but maybe it is right).
32      + d2html should say what the problem is if quotes (or block comments) are unmatched.
33 */
34
35
36 import std.c.stdio;
37 import std.string;
38 import std.stream;
39
40
41
42
43 /* Colors for syntax highlighting, default values are
44    Pavel Minayev's preferences in Microsoft Visual Studio editor   */
45 class Colors
46 {
47     static char[] keyword = "0000FF";
48     static char[] number  = "008000";
49     static char[] string  = "000080";
50     static char[] comment = "808080";
51 }
52
53 const int tabsize = 4;  /* number of spaces in tab */
54 const char[24] symbols = "()[]{}.,;:=<>+-*/%&|^!~?";
55 char[][] keywords;
56 version(talkative) int lineNumber = 0;
57
58
59 bit isspace(char c)
60 /* true if c is whitespace, false otherwise */
61 {
62     version(talkative) if (c==10) lineNumber++;
63     return std.string.find(whitespace, c) >= 0;
64 }
65
66 bit isalpha(char c)
67 /* true if c is a letter or an underscore, false otherwise */
68 {
69     /* underscore doesn't differ from letters in D anyhow... */
70     return c == '_' || std.string.find(letters, c) >= 0;
71 }
72
73 bit isdigit(char c)
74 /* true if c is a decimal digit, false otherwise */
75 {  
76     return cast(bit) ((std.string.find(digits, c) >= 0) ? true : (c == '_'));
77    
78 }
79
80 bit ishexdigit(char c)
81 /* true if c is a hexadecimal digit, false otherwise */
82 {
83     return cast(bit) ((std.string.find(hexdigits, c) >= 0) ? true : (c == '_'));
84 }
85
86 bit isoctdigit(char c)
87 /* true if c is an octal digit, false otherwise */
88 {
89     return cast(bit) ((std.string.find(octdigits, c) >= 0) ? true : (c == '_'));
90 }
91
92 bit issymbol(char c)
93 /* true if c is legal D symbol other than above, false otherwise */
94 {
95     return find(symbols, c) >= 0;
96 }
97
98 bit iskeyword(char[] token)
99 /* true if token is a D keyword, false otherwise */
100 {
101     for (int i = 0; i < keywords.length; i++)
102         if (!cmp(keywords[i], token))
103             return true;
104     return false;
105 }
106
107
108
109 char[] baseFilenameWithPath(char[] r)
110 {   /*  By J C Calvarese
111         Returns the path & filename without the extension. */
112
113     int i;
114     char[] s;
115
116
117     version(very_talkative) printf("Looking for \".\" in \"%.*s\"...\n\0", r);
118     if(r.length > 0) i = rfind(r, '.');
119
120     version(very_talkative) printf("Result: %d\n\0", i);
121    
122     if (i > 0) s = r[0..i];
123     else s = r;
124     version(very_talkative) printf("Return baseFilenameWithPath\n\0");
125    
126     return s;
127
128 }
129
130
131
132 int main(char[][] args)
133 {
134     bit outputIdentifiers;
135     File idFile;
136     char[] fn;
137     char[] fnIdent;
138     bit useStyleSheet;
139     bit xhtmlFormat;
140
141
142    
143     void analyzeOptions(char[] s)
144     { 
145        
146         switch(s)
147         { 
148             case "-i":
149                 version(very_talkative) printf("Produce identifier list: true.\n");
150                 outputIdentifiers = true;
151                 fnIdent = null;
152                 fnIdent = baseFilenameWithPath(args[1]) ~ ".id";
153                 break;
154                
155             case "-s":
156                 version(very_talkative) printf("Use a style-sheet.\n");             
157                 useStyleSheet = true;
158                 break;
159            
160             case "-x":
161                 version(very_talkative) printf("XHTML format.\n");             
162                 xhtmlFormat = true;
163                 break;
164                
165             default:
166                 printf("Warning: Unknown option (%.*s).\n", s);
167         }
168        
169
170     }
171
172    
173     version(very_talkative) printf("Starting program...\n\0");
174    
175     /* need help? */
176     if (args.length < 2) // || args.length > 3)
177     {
178         printf("D to HTML converter\n"
179                "Usage: D2HTML program.d [file.html] [-i]\n"
180                \n
181                "program.d: source file\nfile.html: output file\n"
182                "-i:        produce list of identifiers\n"
183                "-s:        use a stylesheet\n"
184                "-x:        XHTML format\n");
185         return 0;
186     }
187    
188    
189     /* auto-name output file */
190     fn = baseFilenameWithPath(args[1]) ~ ".html";
191     fnIdent = baseFilenameWithPath(args[1]) ~ ".id";
192    
193     version(very_talkative) printf("Checking argument list...\n\0");
194
195     if (args.length > 2) for(int i = 2; i < args.length; i++)
196     {
197         if(args[i][0..1] == "-")
198             analyzeOptions(args[i]);
199         else
200         { 
201             if(args[i].length > 2)
202                 if(args[i][0..1] == "-")
203                     analyzeOptions(args[i]);
204                 else
205                 {
206                     version(very_talkative) printf("Override default output name.\n\0");
207                     fn = null;
208                     fn = args[i]; /* override auto-name */
209                 }
210             else
211             {
212                 version(very_talkative) printf("Override default output name.\n\0");
213                 fn = null;
214                 fn = args[i]; /* override auto-name */
215             }
216         }
217     }
218    
219     /* load keywords */
220     version(talkative) printf("Reading in keywords...\t\0");
221     File kwd = new File("d2html.kwd");
222     while (!kwd.eof())
223     {
224         keywords.length = keywords.length + 1;
225         keywords[][keywords.length - 1] = kwd.readLine();
226     }
227     version(talkative)
228     {
229         printf("Read in keywords.\n\0");
230         for(int i=0; i < keywords.length; i++)
231             printf(cast(char*) (keywords[i] ~ "\t" ~ "\0"));
232         version(talkative) printf("\n\0");
233     }
234     kwd.close();
235    
236     /* open input and output files */
237     File src = new File(args[1]), dst = new File;
238     dst.create(fn);
239    
240     if(outputIdentifiers)
241     {
242         idFile = new File;
243         idFile.create(fnIdent);
244     }
245
246     /* write HTML header */
247     if(xhtmlFormat)
248         dst.writeLine("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
249     else
250         dst.writeLine("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">");
251     dst.writeLine("<html><head><title>" ~ args[1] ~ "</title>");
252     if(useStyleSheet)
253         dst.writeLine("<link rel=\"stylesheet\" href=\"style.css\"" ~ (xhtmlFormat ? "\\" : "") ~ ">");
254     dst.writeLine("</head>");
255     if(useStyleSheet)
256         dst.writeLine("<body><pre><code>");
257     else
258         dst.writeLine("<body color='#000000' bgcolor='#FFFFFF'><pre><code>");
259    
260     /* the main part is wrapped into try..catch block because
261        when end of file is reached, an exception is raised;
262        so we can omit any checks for EOF inside this block... */
263     try
264     {
265         int linestart = 0;  /* for tabs */
266         char c;
267         char c2;
268        
269         src.read(c);
270         while (true)
271         {
272             if (isspace(c))     /* whitespace */
273             {
274                 do
275                 {
276                    
277                     switch(c)
278                     {
279                         case 9: /* tab character */
280                             /* expand tabs to spaces */
281                             int spaces = tabsize -
282                                 (src.position() - linestart) % tabsize;
283                             for (int i = 0; i < spaces; i++)
284                                 dst.writeString(" ");
285                             linestart = src.position() - tabsize + 1;
286                             break;
287                            
288                         case 10: /* linefeed */
289                             linestart = src.position() + 1; /* reset line start on newline */
290                             dst.write(c);
291
292                             break;
293                            
294                         case 13: /* control return */
295                             linestart = src.position() + 1; /* reset line start on newline */
296                            
297                             if (c2 != 10)
298                                 dst.write(c);                               
299                             break;
300                            
301                         default: /* space is left, anything else? */
302                             dst.write(c);
303                             break;
304
305                     }
306                    
307                     c2 = c;
308                     src.read(c);
309                
310                 } while (isspace(c));
311             }
312             else if (isalpha(c))    /* keyword or identifier */
313             {
314                 char[] token;
315                 do
316                 {
317                     token ~= c;
318                     src.read(c);
319                 } while (isalpha(c) || isdigit(c));
320                 if (iskeyword(token))   /* keyword */
321                     if(useStyleSheet)
322                         dst.writeString("<span class=\"keyword\">" ~ token ~ "</span>");
323                     else
324                         dst.writeString("<font color='#" ~ Colors.keyword ~
325                         "'>" ~ token ~ "</font>");
326                 else    /* simple identifier */
327                 {   
328                     /* add identifier to the identifier list, if desired */
329                
330                     if(outputIdentifiers)
331                         idFile.writeString(token ~ \n);             
332                    
333                     dst.writeString(token);                 
334                 }
335             }
336             else if (c == '0')  /* binary, octal or hexadecimal number */
337             {
338                 if(useStyleSheet)
339                     dst.writeString("<span class=\"number\">");                 
340                 else
341                     dst.writeString("<font color='#" ~ Colors.number ~ "'>");
342                 dst.write(c);
343                 src.read(c);
344                 if (c == 'X' || c == 'x')   /* hexadecimal */
345                 {
346                     dst.write(c);
347                     src.read(c);
348                     while (ishexdigit(c))
349                     {
350                         dst.write(c);
351                         src.read(c);
352                     }
353                     /* TODO: add support for hexadecimal floats */
354                 }
355                 else if (c == 'B' || c == 'b')  /* binary */
356                 {
357                     dst.write(c);
358                     src.read(c);
359                     while (c == '0' || c == '1' || c == '_' )
360                     {
361                         dst.write(c);
362                         src.read(c);
363                     }
364                                     }
365                 else    /* octal */
366                 {
367                     do
368                     {
369                         dst.write(c);
370                         src.read(c);
371                     } while (isoctdigit(c))
372                     {
373                         dst.write(c);
374                         src.read(c);
375                     }
376                 }
377                 if(useStyleSheet)
378                     dst.writeString("</span>");
379                 else
380                     dst.writeString("</font>");
381             }
382             else if (isdigit(c))    /* decimal number */
383             {
384                 if(useStyleSheet)
385                     dst.writeString("<span class=\"number\">");
386                 else
387                     dst.writeString("<font color='#" ~ Colors.number ~ "'>");
388                 /* integral part */
389                 do
390                 {
391                     dst.write(c);
392                     src.read(c);
393                 } while (isdigit(c));
394                 /* fractional part */
395                 if (c == '.')
396                 {
397                     dst.write(c);
398                     src.read(c);
399                     while (isdigit(c))
400                     {
401                         dst.write(c);
402                         src.read(c);
403                     }
404                 }
405                 /* scientific notation */
406                 if (c == 'E' || c == 'e')
407                 {
408                     dst.write(c);
409                     src.read(c);
410                     if (c == '+' || c == '-')
411                     {
412                         dst.write(c);
413                         src.read(c);
414                     }
415                     while (isdigit(c))
416                     {
417                         dst.write(c);
418                         src.read(c);
419                     }
420                 }
421                 /* suffixes */
422                 while (c == 'U' || c == 'u' || c == 'L' ||
423                     c == 'l' || c == 'F' || c == 'f')
424                 {
425                     dst.write(c);
426                     src.read(c);
427                 }
428                 dst.writeString(useStyleSheet ? "</span>" : "</font>");             
429             }
430             else if (c == '\\')   /* naked escape sequence (\) */
431             {
432                 if(useStyleSheet)
433                     dst.writeString("<span class=\"string\">");
434                 else   
435                     dst.writeString("<font color='#" ~ Colors.string ~ "'>");
436                 char prev;  /* used to handle \" properly */
437                 dst.write(c);
438                 prev = c;
439                 src.read(c);
440                 dst.write(c);
441                 src.read(c);
442
443                 dst.writeString(useStyleSheet ? "</span>" : "</font>");             
444             }
445             else if (c == '"')  /* string (") with escape sequences  */
446             {
447                 if(useStyleSheet)
448                     dst.writeString("<span class=\"string\">");
449                 else   
450                     dst.writeString("<font color='#" ~ Colors.string ~ "'>");
451                 char prev;  /* used to handle \" properly */
452                 bit isEscape;
453                 do
454                 {
455                     if (c == '<')   /* special symbol in HTML */
456                         dst.writeString("&lt;");
457                     else
458                         dst.write(c);
459                    
460                     prev = c;
461                     if ((prev == '\\' && c == '"') || (prev == '\\' && c == '\\') && !isEscape)
462                         isEscape = true;
463                     else
464                         isEscape = false;
465                    
466                     src.read(c);
467                 } while (isEscape || c != '"');
468                
469                 dst.write(c);
470                 src.read(c);
471                
472                 dst.writeString(useStyleSheet ? "</span>" : "</font>");             
473             }
474             else if (c == '\'') /* character (') with escape sequences  */
475             {
476                 if(useStyleSheet)
477                     dst.writeString("<span class=\"string\">");
478                 else   
479                     dst.writeString("<font color='#" ~ Colors.string ~ "'>");
480                 char prev;  /* used to handle \" properly */
481                 bit isEscape;
482                 do
483                 {
484                     if (c == '<')   /* special symbol in HTML */
485                         dst.writeString("&lt;");
486                     else
487                         dst.write(c);
488                        
489                     prev = c;
490                    
491                     if ((prev == '\\' && c == '\'') || (prev == '\\' && c == '\\') && !isEscape)
492                         isEscape = true;
493                     else
494                         isEscape = false;
495                    
496                     src.read(c);
497                 } while (isEscape || c != '\'');
498                 dst.write(c);
499                 src.read(c);
500                
501                 dst.writeString(useStyleSheet ? "</span>" : "</font>");             
502             }
503             else if (c == '`')  /* character (`) with no escape sequences */
504             {
505                 if(useStyleSheet)
506                     dst.writeString("<span class=\"string\">");
507                 else   
508                     dst.writeString("<font color='#" ~ Colors.string ~ "'>");
509                 do
510                 {
511                     if (c == '<')   /* special symbol in HTML */
512                         dst.writeString("&lt;");
513                     else
514                         dst.write(c);
515                     src.read(c);
516                 } while (!(c == '`')); /* 96 */
517                 dst.write(c);
518                 src.read(c);
519                
520                 dst.writeString(useStyleSheet ? "</span>" : "</font>");             
521             }
522             else if (issymbol(c))   /* either operator or comment */
523             {
524                 if (c == '<')   /* special symbol in HTML */
525                 {
526                     dst.writeString("&lt;");
527                     src.read(c);
528                 }
529                 else if (c == '/')  /* could be a comment... */
530                 {
531                     src.read(c);
532                     if (c == '/')   /* single-line one */
533                     {
534                         if(useStyleSheet)
535                             dst.writeString("<span class=\"comment\">/");
536                         else                       
537                             dst.writeString("<font color='#" ~ Colors.comment ~ "'>/");
538                         while (c != 10 && c != 13)
539                         {
540                             if (c == '<')   /* special symbol in HTML */
541                                 dst.writeString("&lt;");
542                             else if (c == 9)
543                             {
544                                 /* expand tabs */
545                                 int spaces = tabsize -
546                                     (src.position() - linestart) % tabsize;
547                                 for (int i = 0; i < spaces; i++)
548                                     dst.writeString(" ");
549                                 linestart = src.position() - tabsize + 1;
550                             }
551                             else
552                                 dst.write(c);
553                             c2 = c;
554                             src.read(c);
555                         }
556                         dst.writ