1 module pegged.tohtml; 2 3 import std.stdio; 4 import std.conv, std..string; 5 import std.algorithm.searching; 6 import pegged.peg; 7 8 enum Expand 9 { 10 no, ifMatch, ifNotMatch 11 } 12 13 /** 14 * Writes a parse tree to a html file. 15 * 16 * Params: 17 * e = Defines if the tree is expanded. 18 * Details = Defines the details, as a list of strings, that are expanded 19 * when e is equal to `Expand.ifMatch`, and not expanded when e is equal to 20 * `Expand.ifNotMatch`. When no details are passed, each node is expanded. 21 * p = The ParseTree to represent. 22 * file = The file where to write the tree. 23 */ 24 void toHTML(Expand e = Expand.no, Details...)(const ref ParseTree p, 25 File file) 26 { 27 file.write(` 28 <!DOCTYPE html> 29 <html> 30 <head> 31 <meta charset="UTF-8"> 32 <title>Pegged produced parse tree</title> 33 <style> 34 code { 35 position: relative; 36 font-family: monospace; 37 white-space: pre; 38 } 39 code.failure { 40 color: red; 41 } 42 /* hide tooltip */ 43 code span { 44 display: none; 45 } 46 /* show and style tooltip */ 47 code:hover span { 48 /* show tooltip */ 49 display: block; 50 /* position relative to container div.tooltip */ 51 position: absolute; 52 top: -1.6em; 53 left: -0.6em; 54 background: #ffffff; 55 padding: 0.5em; 56 color: #000000; 57 border: 0.1em solid #b7ddf2; 58 border-radius: 0.5em; 59 white-space: nowrap; 60 z-index: 1; 61 } 62 details { 63 margin-left:25px; 64 white-space: nowrap; 65 } 66 details.leaf summary::-webkit-details-marker { 67 opacity: 0; 68 } 69 </style> 70 </head> 71 <body> 72 `); 73 74 void treeToHTML(const ref ParseTree p) 75 { 76 file.write("<details"); 77 static if (e != Expand.no) 78 { 79 static if (!Details.length) 80 { 81 file.write(" open"); 82 } 83 else 84 { 85 static if (e == Expand.ifMatch) 86 { 87 foreach(detail; Details) 88 if (indexOf(p.name, detail) > -1L) 89 { 90 file.write(" open"); 91 break; 92 } 93 } 94 else 95 { 96 bool w; 97 foreach(detail; Details) 98 if (indexOf(p.name, detail) > -1L) 99 { 100 w = true; 101 break; 102 } 103 if (!w) 104 file.write(" open"); 105 } 106 } 107 } 108 109 if (p.children.length == 0) 110 file.write(` class="leaf"`); 111 file.write("><summary>", p.name, " ", to!string([p.begin, p.end])); 112 113 if (p.children.length == 0 && !p.successful) 114 { // Failure leaf. 115 Position pos = position(p); 116 auto left = pos.index < 10 ? p.input[0 .. pos.index] : p.input[pos.index-10 .. pos.index]; 117 auto right = pos.index + 10 < p.input.length ? p.input[pos.index .. pos.index + 10] : p.input[pos.index .. $]; 118 file.write(" failure at line ", to!string(pos.line), ", col ", to!string(pos.col), ", "); 119 if (left.length > 0) 120 file.write("after <code>", left.stringified, "</code> "); 121 file.write("expected <code>"); 122 if (p.matches.length > 0) 123 file.write(p.matches[$-1].stringified); 124 else 125 file.write("NO MATCH"); 126 file.write(`</code>, but got <code class="failure">`, right.stringified, "</code>\n"); 127 } 128 else 129 { 130 file.write(" <code"); 131 if (!p.successful) 132 file.write(` class="failure"`); 133 auto firstNewLine = p.input[p.begin .. p.end].countUntil('\n'); 134 auto firstLine = p.input[p.begin .. firstNewLine >= 0 ? p.begin + firstNewLine : p.end]; 135 if (firstLine.length > 0) 136 file.write(">", firstLine, "<span><pre>", p.input[p.begin .. p.end], "</pre></span></code>"); 137 else // Insert the return-symbol so the mouse has something to hover over. 138 file.write(">⏎<span><pre>⏎", p.input[p.begin .. p.end], "</pre></span></code>"); 139 } 140 141 file.write("</summary>\n"); 142 foreach (child; p.children) 143 treeToHTML(child); 144 file.write("</details>\n"); 145 } 146 147 treeToHTML(p); 148 149 file.write(` 150 </body> 151 </html> 152 `); 153 } 154 155 /** 156 * Writes a parse tree to a html file. 157 * 158 * Params: 159 * e = Defines if the tree is expanded. 160 * Details = Defines the details, as a list of strings, that are expanded 161 * when e is equal to `Expand.yes`, and not exapnded when e is equal to 162 * `Expand.invert`. When no details are passed, each node is expanded. 163 * p = The ParseTree to represent. 164 * filename = The name of file where tree is written. 165 */ 166 void toHTML(Expand e = Expand.no, Details...)(const ref ParseTree p, 167 string filename) 168 { 169 if (filename.endsWith(".html", ".htm") == 0) 170 filename ~= ".html"; 171 toHTML!(e, Details)(p, File(filename, "w")); 172 }