1 | const char HTMLLogger_html[] = |
2 | R"x(<!doctype html>)x" "\n" |
3 | R"x(<html>)x" "\n" |
4 | R"x(<!-- HTMLLogger.cpp ----------------------------------------------------)x" "\n" |
5 | R"x()x" "\n" |
6 | R"x( Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.)x" "\n" |
7 | R"x( See https://llvm.org/LICENSE.txt for license information.)x" "\n" |
8 | R"x( SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception)x" "\n" |
9 | R"x()x" "\n" |
10 | R"x(//===------------------------------------------------------------------------>)x" "\n" |
11 | R"x()x" "\n" |
12 | R"x(<head>)x" "\n" |
13 | R"x(<?INJECT?>)x" "\n" |
14 | R"x()x" "\n" |
15 | R"x(<template id="value-template">)x" "\n" |
16 | R"x( <details class="value" open>)x" "\n" |
17 | R"x( <summary>)x" "\n" |
18 | R"x( <span>{{v.kind}})x" "\n" |
19 | R"x( <template data-if="v.value_id"><span class="address">#{{v.value_id}}</span></template>)x" "\n" |
20 | R"x( </span>)x" "\n" |
21 | R"x( <template data-if="v.location">)x" "\n" |
22 | R"x( <span class="location">{{v.type}} <span class="address">@{{v.location}}</span></span>)x" "\n" |
23 | R"x( </template>)x" "\n" |
24 | R"x( </summary>)x" "\n" |
25 | R"x( <template)x" "\n" |
26 | R"x( data-for="kv in Object.entries(v)")x" "\n" |
27 | R"x( data-if="['kind', 'value_id', 'type', 'location'].indexOf(kv[0]) < 0">)x" "\n" |
28 | R"x( <div class="property"><span class="key">{{kv[0]}}</span>)x" "\n" |
29 | R"x( <template data-if="typeof(kv[1]) != 'object'">{{kv[1]}}</template>)x" "\n" |
30 | R"x( <template data-if="typeof(kv[1]) == 'object'" data-let="v = kv[1]">)x" "\n" |
31 | R"x( <template data-use="value-template"></template>)x" "\n" |
32 | R"x( </template>)x" "\n" |
33 | R"x( </div>)x" "\n" |
34 | R"x( </template>)x" "\n" |
35 | R"x( </details>)x" "\n" |
36 | R"x(</template>)x" "\n" |
37 | R"x()x" "\n" |
38 | R"x(</head>)x" "\n" |
39 | R"x()x" "\n" |
40 | R"x(<body>)x" "\n" |
41 | R"x()x" "\n" |
42 | R"x(<section id="timeline" data-selection="">)x" "\n" |
43 | R"x(<header>Timeline</header>)x" "\n" |
44 | R"x(<template data-for="entry in timeline">)x" "\n" |
45 | R"x( <div id="{{entry.block}}:{{entry.iter}}" data-bb="{{entry.block}}" class="entry">)x" "\n" |
46 | R"x( <span class="counter"></span>)x" "\n" |
47 | R"x( {{entry.block}})x" "\n" |
48 | R"x( <template data-if="entry.post_visit">(post-visit)</template>)x" "\n" |
49 | R"x( <template data-if="!entry.post_visit">({{entry.iter}})</template>)x" "\n" |
50 | R"x( <template data-if="entry.converged"> →|<!--Rightwards arrow, vertical line--></template>)x" "\n" |
51 | R"x( </div>)x" "\n" |
52 | R"x(</template>)x" "\n" |
53 | R"x(</section>)x" "\n" |
54 | R"x()x" "\n" |
55 | R"x(<section id="function" data-selection="">)x" "\n" |
56 | R"x(<header>Function</header>)x" "\n" |
57 | R"x(<div id="code"></div>)x" "\n" |
58 | R"x(<div id="cfg"></div>)x" "\n" |
59 | R"x(</section>)x" "\n" |
60 | R"x()x" "\n" |
61 | R"x(<section id="block" data-selection="bb">)x" "\n" |
62 | R"x(<header><template>Block {{selection.bb}}</template></header>)x" "\n" |
63 | R"x(<div id="iterations">)x" "\n" |
64 | R"x( <template data-for="iter in cfg[selection.bb].iters">)x" "\n" |
65 | R"x( <a class="chooser {{selection.bb}}:{{iter.iter}}" data-iter="{{selection.bb}}:{{iter.iter}}">)x" "\n" |
66 | R"x( <template data-if="iter.post_visit">Post-visit</template>)x" "\n" |
67 | R"x( <template data-if="!iter.post_visit">{{iter.iter}}</template>)x" "\n" |
68 | R"x( <template data-if="iter.converged"> →|<!--Rightwards arrow, vertical line--></template>)x" "\n" |
69 | R"x( </a>)x" "\n" |
70 | R"x( </template>)x" "\n" |
71 | R"x(</div>)x" "\n" |
72 | R"x(<table id="bb-elements">)x" "\n" |
73 | R"x(<template>)x" "\n" |
74 | R"x( <tr id="{{selection.bb}}.0">)x" "\n" |
75 | R"x( <td class="{{selection.bb}}">{{selection.bb}}.0</td>)x" "\n" |
76 | R"x( <td>(initial state)</td>)x" "\n" |
77 | R"x( </tr>)x" "\n" |
78 | R"x(</template>)x" "\n" |
79 | R"x(<template data-for="elt in cfg[selection.bb].elements">)x" "\n" |
80 | R"x( <tr id="{{selection.bb}}.{{elt_index+1}}">)x" "\n" |
81 | R"x( <td class="{{selection.bb}}">{{selection.bb}}.{{elt_index+1}}</td>)x" "\n" |
82 | R"x( <td>{{elt}}</td>)x" "\n" |
83 | R"x( </tr>)x" "\n" |
84 | R"x(</template>)x" "\n" |
85 | R"x(</table>)x" "\n" |
86 | R"x(</section>)x" "\n" |
87 | R"x()x" "\n" |
88 | R"x(<section id="element" data-selection="iter,elt">)x" "\n" |
89 | R"x(<template data-let="state = states[selection.iter + '_' + selection.elt]">)x" "\n" |
90 | R"x(<header>)x" "\n" |
91 | R"x( <template data-if="state.element == 0">{{state.block}} initial state</template>)x" "\n" |
92 | R"x( <template data-if="state.element != 0">Element {{selection.elt}}</template>)x" "\n" |
93 | R"x( <template data-if="state.post_visit"> (post-visit)</template>)x" "\n" |
94 | R"x( <template data-if="!state.post_visit"> (iteration {{state.iter}})</template>)x" "\n" |
95 | R"x(</header>)x" "\n" |
96 | R"x(<template data-if="state.value" data-let="v = state.value">)x" "\n" |
97 | R"x( <h2>Value</h2>)x" "\n" |
98 | R"x( <template data-use="value-template"></template>)x" "\n" |
99 | R"x(</template>)x" "\n" |
100 | R"x(<template data-if="state.logs">)x" "\n" |
101 | R"x( <h2>Logs</h2>)x" "\n" |
102 | R"x( <pre>{{state.logs}}</pre>)x" "\n" |
103 | R"x(</template>)x" "\n" |
104 | R"x(<h2>Built-in lattice</h2>)x" "\n" |
105 | R"x(<pre>{{state.builtinLattice}}</pre>)x" "\n" |
106 | R"x(</template>)x" "\n" |
107 | R"x(</section>)x" "\n" |
108 | R"x()x" "\n" |
109 | R"x(<script>)x" "\n" |
110 | R"x(addBBColors(Object.keys(HTMLLoggerData.cfg).length);)x" "\n" |
111 | R"x(watchSelection(HTMLLoggerData);)x" "\n" |
112 | R"x(updateSelection({}, HTMLLoggerData);)x" "\n" |
113 | R"x(// Copy code and cfg from <template>s into the body.)x" "\n" |
114 | R"x(for (tmpl of document.querySelectorAll('template[data-copy]')))x" "\n" |
115 | R"x( document.getElementById(tmpl.dataset.copy).replaceChildren()x" "\n" |
116 | R"x( ...tmpl.content.cloneNode(/*deep=*/true).childNodes);)x" "\n" |
117 | R"x(</script>)x" "\n" |
118 | R"x()x" "\n" |
119 | R"x(</body>)x" "\n" |
120 | R"x(</html>)x" "\n" |
121 | R"x()x" "\n" |
122 | ; |
123 | const char HTMLLogger_css[] = |
124 | R"x(/*===-- HTMLLogger.css ----------------------------------------------------===)x" "\n" |
125 | R"x(*)x" "\n" |
126 | R"x(* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.)x" "\n" |
127 | R"x(* See https://llvm.org/LICENSE.txt for license information.)x" "\n" |
128 | R"x(* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception)x" "\n" |
129 | R"x(*)x" "\n" |
130 | R"x(*===----------------------------------------------------------------------===*/)x" "\n" |
131 | R"x(html { font-family: sans-serif; })x" "\n" |
132 | R"x(body { margin: 0; display: flex; justify-content: left; })x" "\n" |
133 | R"x(body > * { box-sizing: border-box; })x" "\n" |
134 | R"x(body > section {)x" "\n" |
135 | R"x( border: 1px solid black;)x" "\n" |
136 | R"x( min-width: 20em;)x" "\n" |
137 | R"x( overflow: auto;)x" "\n" |
138 | R"x( max-height: 100vh;)x" "\n" |
139 | R"x(})x" "\n" |
140 | R"x(section header {)x" "\n" |
141 | R"x( background-color: #008;)x" "\n" |
142 | R"x( color: white;)x" "\n" |
143 | R"x( font-weight: bold;)x" "\n" |
144 | R"x( font-size: large;)x" "\n" |
145 | R"x( padding-right: 0.5em;)x" "\n" |
146 | R"x(})x" "\n" |
147 | R"x(section h2 {)x" "\n" |
148 | R"x( font-size: medium;)x" "\n" |
149 | R"x( margin-bottom: 0.5em;)x" "\n" |
150 | R"x( padding-top: 0.5em;)x" "\n" |
151 | R"x( border-top: 1px solid #aaa;)x" "\n" |
152 | R"x(})x" "\n" |
153 | R"x(#timeline {)x" "\n" |
154 | R"x( min-width: max-content;)x" "\n" |
155 | R"x( counter-reset: entry_counter;)x" "\n" |
156 | R"x(})x" "\n" |
157 | R"x(#timeline .entry .counter::before {)x" "\n" |
158 | R"x( counter-increment: entry_counter;)x" "\n" |
159 | R"x( content: counter(entry_counter) ":";)x" "\n" |
160 | R"x(})x" "\n" |
161 | R"x(#timeline .entry .counter {)x" "\n" |
162 | R"x( display: inline-block;)x" "\n" |
163 | R"x( min-width: 2em; /* Enough space for two digits and a colon */)x" "\n" |
164 | R"x( text-align: right;)x" "\n" |
165 | R"x(})x" "\n" |
166 | R"x(#timeline .entry.hover {)x" "\n" |
167 | R"x( background-color: #aaa;)x" "\n" |
168 | R"x(})x" "\n" |
169 | R"x(#timeline .entry.iter-select {)x" "\n" |
170 | R"x( background-color: #aac;)x" "\n" |
171 | R"x(})x" "\n" |
172 | R"x()x" "\n" |
173 | R"x(#bb-elements {)x" "\n" |
174 | R"x( font-family: monospace;)x" "\n" |
175 | R"x( font-size: x-small;)x" "\n" |
176 | R"x( border-collapse: collapse;)x" "\n" |
177 | R"x(})x" "\n" |
178 | R"x(#bb-elements td:nth-child(1) {)x" "\n" |
179 | R"x( text-align: right;)x" "\n" |
180 | R"x( width: 4em;)x" "\n" |
181 | R"x( border-right: 1px solid #008;)x" "\n" |
182 | R"x( padding: 0.3em 0.5em;)x" "\n" |
183 | R"x()x" "\n" |
184 | R"x( font-weight: bold;)x" "\n" |
185 | R"x( color: #888;)x" "\n" |
186 | R"x(};)x" "\n" |
187 | R"x(#bb-elements tr.hover {)x" "\n" |
188 | R"x( background-color: #abc;)x" "\n" |
189 | R"x(})x" "\n" |
190 | R"x(#bb-elements tr.elt-select {)x" "\n" |
191 | R"x( background-color: #acf;)x" "\n" |
192 | R"x(})x" "\n" |
193 | R"x(#iterations {)x" "\n" |
194 | R"x( display: flex;)x" "\n" |
195 | R"x(})x" "\n" |
196 | R"x(#iterations .chooser {)x" "\n" |
197 | R"x( flex-grow: 1;)x" "\n" |
198 | R"x( text-align: center;)x" "\n" |
199 | R"x( padding-left: 0.2em;)x" "\n" |
200 | R"x(})x" "\n" |
201 | R"x(#iterations .chooser :last-child {)x" "\n" |
202 | R"x( padding-right: 0.2em;)x" "\n" |
203 | R"x(})x" "\n" |
204 | R"x(#iterations .chooser:not(.iter-select).hover {)x" "\n" |
205 | R"x( background-color: #ddd;)x" "\n" |
206 | R"x(})x" "\n" |
207 | R"x(#iterations .iter-select {)x" "\n" |
208 | R"x( font-weight: bold;)x" "\n" |
209 | R"x(})x" "\n" |
210 | R"x(#iterations .chooser:not(.iter-select) {)x" "\n" |
211 | R"x( text-decoration: underline;)x" "\n" |
212 | R"x( color: blue;)x" "\n" |
213 | R"x( cursor: pointer;)x" "\n" |
214 | R"x( background-color: #ccc;)x" "\n" |
215 | R"x(})x" "\n" |
216 | R"x()x" "\n" |
217 | R"x(code.filename {)x" "\n" |
218 | R"x( font-weight: bold;)x" "\n" |
219 | R"x( color: black;)x" "\n" |
220 | R"x( background-color: #ccc;)x" "\n" |
221 | R"x( display: block;)x" "\n" |
222 | R"x( text-align: center;)x" "\n" |
223 | R"x(})x" "\n" |
224 | R"x(code.line {)x" "\n" |
225 | R"x( display: block;)x" "\n" |
226 | R"x( white-space: pre;)x" "\n" |
227 | R"x(})x" "\n" |
228 | R"x(code.line:before { /* line numbers */)x" "\n" |
229 | R"x( content: attr(data-line);)x" "\n" |
230 | R"x( display: inline-block;)x" "\n" |
231 | R"x( width: 2em;)x" "\n" |
232 | R"x( text-align: right;)x" "\n" |
233 | R"x( padding-right: 2px;)x" "\n" |
234 | R"x( background-color: #ccc;)x" "\n" |
235 | R"x( border-right: 1px solid #888;)x" "\n" |
236 | R"x( margin-right: 8px;)x" "\n" |
237 | R"x(})x" "\n" |
238 | R"x(code.line:has(.bb-select):before {)x" "\n" |
239 | R"x( border-right: 4px solid black;)x" "\n" |
240 | R"x( margin-right: 5px;)x" "\n" |
241 | R"x(})x" "\n" |
242 | R"x(.c.hover, .bb.hover {)x" "\n" |
243 | R"x( filter: saturate(200%) brightness(90%);)x" "\n" |
244 | R"x(})x" "\n" |
245 | R"x(.c.elt-select {)x" "\n" |
246 | R"x( box-shadow: inset 0 -4px 2px -2px #a00;)x" "\n" |
247 | R"x(})x" "\n" |
248 | R"x(.bb.bb-select polygon {)x" "\n" |
249 | R"x( stroke-width: 4px;)x" "\n" |
250 | R"x( filter: brightness(70%) saturate(150%);)x" "\n" |
251 | R"x(})x" "\n" |
252 | R"x(.bb { user-select: none; })x" "\n" |
253 | R"x(.bb polygon { fill: white; })x" "\n" |
254 | R"x(#cfg {)x" "\n" |
255 | R"x( position: relative;)x" "\n" |
256 | R"x( margin-left: 0.5em;)x" "\n" |
257 | R"x(})x" "\n" |
258 | R"x()x" "\n" |
259 | R"x(.value {)x" "\n" |
260 | R"x( border: 1px solid #888;)x" "\n" |
261 | R"x( font-size: x-small;)x" "\n" |
262 | R"x( flex-grow: 1;)x" "\n" |
263 | R"x(})x" "\n" |
264 | R"x(.value > summary {)x" "\n" |
265 | R"x( background-color: #ace;)x" "\n" |
266 | R"x( display: flex;)x" "\n" |
267 | R"x( cursor: pointer;)x" "\n" |
268 | R"x(})x" "\n" |
269 | R"x(.value > summary::before {)x" "\n" |
270 | R"x( content: '\25ba'; /* Black Right-Pointing Pointer */)x" "\n" |
271 | R"x( margin-right: 0.5em;)x" "\n" |
272 | R"x( font-size: 0.9em;)x" "\n" |
273 | R"x(})x" "\n" |
274 | R"x(.value[open] > summary::before {)x" "\n" |
275 | R"x( content: '\25bc'; /* Black Down-Pointing Triangle */)x" "\n" |
276 | R"x(})x" "\n" |
277 | R"x(.value > summary > .location {)x" "\n" |
278 | R"x( margin-left: auto;)x" "\n" |
279 | R"x(})x" "\n" |
280 | R"x(.value .address {)x" "\n" |
281 | R"x( font-size: xx-small;)x" "\n" |
282 | R"x( font-family: monospace;)x" "\n" |
283 | R"x( color: #888;)x" "\n" |
284 | R"x(})x" "\n" |
285 | R"x(.value .property {)x" "\n" |
286 | R"x( display: flex;)x" "\n" |
287 | R"x( margin-top: 0.5em;)x" "\n" |
288 | R"x(})x" "\n" |
289 | R"x(.value .property .key {)x" "\n" |
290 | R"x( font-weight: bold;)x" "\n" |
291 | R"x( min-width: 5em;)x" "\n" |
292 | R"x(})x" "\n" |
293 | R"x()x" "\n" |
294 | ; |
295 | const char HTMLLogger_js[] = |
296 | R"x(//===-- HTMLLogger.js -----------------------------------------------------===//)x" "\n" |
297 | R"x(//)x" "\n" |
298 | R"x(// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.)x" "\n" |
299 | R"x(// See https://llvm.org/LICENSE.txt for license information.)x" "\n" |
300 | R"x(// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception)x" "\n" |
301 | R"x(//)x" "\n" |
302 | R"x(//===----------------------------------------------------------------------===//)x" "\n" |
303 | R"x()x" "\n" |
304 | R"x(// Based on selected objects, hide/show sections & populate data from templates.)x" "\n" |
305 | R"x(//)x" "\n" |
306 | R"x(// For example, if the selection is {bb="BB4", elt="BB4.6" iter="BB4:2"}:)x" "\n" |
307 | R"x(// - show the "block" and "element" sections)x" "\n" |
308 | R"x(// - re-render templates within these sections (if selection changed))x" "\n" |
309 | R"x(// - apply "bb-select" to items with class class "BB4", etc)x" "\n" |
310 | R"x(let selection = {};)x" "\n" |
311 | R"x(function updateSelection(changes, data) {)x" "\n" |
312 | R"x( Object.assign(selection, changes);)x" "\n" |
313 | R"x()x" "\n" |
314 | R"x( data = Object.create(data);)x" "\n" |
315 | R"x( data.selection = selection;)x" "\n" |
316 | R"x( for (root of document.querySelectorAll('[data-selection]')))x" "\n" |
317 | R"x( updateSection(root, data);)x" "\n" |
318 | R"x()x" "\n" |
319 | R"x( for (var k in changes))x" "\n" |
320 | R"x( applyClassIf(k + '-select', classSelector(changes[k]));)x" "\n" |
321 | R"x(})x" "\n" |
322 | R"x()x" "\n" |
323 | R"x(// Given <section data-selection="x,y">:)x" "\n" |
324 | R"x(// - hide section if selections x or y are null)x" "\n" |
325 | R"x(// - re-render templates if x or y have changed)x" "\n" |
326 | R"x(function updateSection(root, data) {)x" "\n" |
327 | R"x( let changed = root.selection == null;)x" "\n" |
328 | R"x( root.selection ||= {};)x" "\n" |
329 | R"x( for (key of root.dataset.selection.split(',')) {)x" "\n" |
330 | R"x( if (!key) continue;)x" "\n" |
331 | R"x( if (data.selection[key] != root.selection[key]) {)x" "\n" |
332 | R"x( root.selection[key] = data.selection[key];)x" "\n" |
333 | R"x( changed = true;)x" "\n" |
334 | R"x( })x" "\n" |
335 | R"x( if (data.selection[key] == null) {)x" "\n" |
336 | R"x( root.hidden = true;)x" "\n" |
337 | R"x( return;)x" "\n" |
338 | R"x( })x" "\n" |
339 | R"x( })x" "\n" |
340 | R"x( if (changed) {)x" "\n" |
341 | R"x( root.hidden = false;)x" "\n" |
342 | R"x( for (tmpl of root.getElementsByTagName('template')))x" "\n" |
343 | R"x( reinflate(tmpl, data);)x" "\n" |
344 | R"x( })x" "\n" |
345 | R"x(})x" "\n" |
346 | R"x()x" "\n" |
347 | R"x(// Expands template `tmpl` based on input `data`:)x" "\n" |
348 | R"x(// - interpolates {{expressions}} in text and attributes)x" "\n" |
349 | R"x(// - <template> tags can modify expansion: if, for etc)x" "\n" |
350 | R"x(// Outputs to `parent` element, inserting before `next`.)x" "\n" |
351 | R"x(function inflate(tmpl, data, parent, next) {)x" "\n" |
352 | R"x( // We use eval() as our expression language in templates!)x" "\n" |
353 | R"x( // The templates are static and trusted.)x" "\n" |
354 | R"x( let evalExpr = (expr, data) => eval('with (data) { ' + expr + ' }');)x" "\n" |
355 | R"x( let interpolate = (str, data) =>)x" "\n" |
356 | R"x( str.replace(/\{\{(.*?)\}\}/g, (_, expr) => evalExpr(expr, data)))x" "\n" |
357 | R"x( // Anything other than <template> tag: copy, interpolate, recursively inflate.)x" "\n" |
358 | R"x( if (tmpl.nodeName != 'TEMPLATE') {)x" "\n" |
359 | R"x( let clone = tmpl.cloneNode();)x" "\n" |
360 | R"x( clone.inflated = true;)x" "\n" |
361 | R"x( if (clone instanceof Text))x" "\n" |
362 | R"x( clone.textContent = interpolate(clone.textContent, data);)x" "\n" |
363 | R"x( if (clone instanceof Element) {)x" "\n" |
364 | R"x( for (attr of clone.attributes))x" "\n" |
365 | R"x( attr.value = interpolate(attr.value, data);)x" "\n" |
366 | R"x( for (c of tmpl.childNodes))x" "\n" |
367 | R"x( inflate(c, data, clone, /*next=*/null);)x" "\n" |
368 | R"x( })x" "\n" |
369 | R"x( return parent.insertBefore(clone, next);)x" "\n" |
370 | R"x( })x" "\n" |
371 | R"x( // data-use="xyz": use <template id="xyz"> instead. (Allows recursion.))x" "\n" |
372 | R"x( if ('use' in tmpl.dataset))x" "\n" |
373 | R"x( return inflate(document.getElementById(tmpl.dataset.use), data, parent, next);)x" "\n" |
374 | R"x( // <template> tag handling. Base case: recursively inflate.)x" "\n" |
375 | R"x( function handle(data) {)x" "\n" |
376 | R"x( for (c of tmpl.content.childNodes))x" "\n" |
377 | R"x( inflate(c, data, parent, next);)x" "\n" |
378 | R"x( })x" "\n" |
379 | R"x( // Directives on <template> tags modify behavior.)x" "\n" |
380 | R"x( const directives = {)x" "\n" |
381 | R"x( // data-for="x in expr": expr is enumerable, bind x to each in turn)x" "\n" |
382 | R"x( 'for': (nameInExpr, data, proceed) => {)x" "\n" |
383 | R"x( let [name, expr] = nameInExpr.split(' in ');)x" "\n" |
384 | R"x( let newData = Object.create(data);)x" "\n" |
385 | R"x( let index = 0;)x" "\n" |
386 | R"x( for (val of evalExpr(expr, data) || []) {)x" "\n" |
387 | R"x( newData[name] = val;)x" "\n" |
388 | R"x( newData[name + '_index'] = index++;)x" "\n" |
389 | R"x( proceed(newData);)x" "\n" |
390 | R"x( })x" "\n" |
391 | R"x( },)x" "\n" |
392 | R"x( // data-if="expr": only include contents if expression is truthy)x" "\n" |
393 | R"x( 'if': (expr, data, proceed) => { if (evalExpr(expr, data)) proceed(data); },)x" "\n" |
394 | R"x( // data-let="x = expr": bind x to value of expr)x" "\n" |
395 | R"x( 'let': (nameEqExpr, data, proceed) => {)x" "\n" |
396 | R"x( let [name, expr] = nameEqExpr.split(' = ');)x" "\n" |
397 | R"x( let newData = Object.create(data);)x" "\n" |
398 | R"x( newData[name] = evalExpr(expr, data);)x" "\n" |
399 | R"x( proceed(newData);)x" "\n" |
400 | R"x( },)x" "\n" |
401 | R"x( })x" "\n" |
402 | R"x( // Compose directive handlers on top of the base handler.)x" "\n" |
403 | R"x( for (let [dir, value] of Object.entries(tmpl.dataset).reverse()) {)x" "\n" |
404 | R"x( if (dir in directives) {)x" "\n" |
405 | R"x( let proceed = handle;)x" "\n" |
406 | R"x( handle = (data) => directives[dir](value, data, proceed);)x" "\n" |
407 | R"x( })x" "\n" |
408 | R"x( })x" "\n" |
409 | R"x( handle(data);)x" "\n" |
410 | R"x(})x" "\n" |
411 | R"x(// Expand a template, after first removing any prior expansion of it.)x" "\n" |
412 | R"x(function reinflate(tmpl, data) {)x" "\n" |
413 | R"x( // Clear previously rendered template contents.)x" "\n" |
414 | R"x( while (tmpl.nextSibling && tmpl.nextSibling.inflated))x" "\n" |
415 | R"x( tmpl.parentNode.removeChild(tmpl.nextSibling);)x" "\n" |
416 | R"x( inflate(tmpl, data, tmpl.parentNode, tmpl.nextSibling);)x" "\n" |
417 | R"x(})x" "\n" |
418 | R"x()x" "\n" |
419 | R"x(// Handle a mouse event on a region containing selectable items.)x" "\n" |
420 | R"x(// This might end up changing the hover state or the selection state.)x" "\n" |
421 | R"x(//)x" "\n" |
422 | R"x(// targetSelector describes what target HTML element is selectable.)x" "\n" |
423 | R"x(// targetToID specifies how to determine the selection from it:)x" "\n" |
424 | R"x(// hover: a function from target to the class name to highlight)x" "\n" |
425 | R"x(// bb: a function from target to the basic-block name to select (BB4))x" "\n" |
426 | R"x(// elt: a function from target to the CFG element name to select (BB4.5))x" "\n" |
427 | R"x(// iter: a function from target to the BB iteration to select (BB4:2))x" "\n" |
428 | R"x(// If an entry is missing, the selection is unmodified.)x" "\n" |
429 | R"x(// If an entry is null, the selection is always cleared.)x" "\n" |
430 | R"x(function mouseEventHandler(event, targetSelector, targetToID, data) {)x" "\n" |
431 | R"x( var target = event.type == "mouseout" ? null : event.target.closest(targetSelector);)x" "\n" |
432 | R"x( let selTarget = k => (target && targetToID[k]) ? targetToID[k](target) : null;)x" "\n" |
433 | R"x( if (event.type == "click") {)x" "\n" |
434 | R"x( let newSel = {};)x" "\n" |
435 | R"x( for (var k in targetToID) {)x" "\n" |
436 | R"x( if (k == 'hover') continue;)x" "\n" |
437 | R"x( let t = selTarget(k);)x" "\n" |
438 | R"x( newSel[k] = t;)x" "\n" |
439 | R"x( })x" "\n" |
440 | R"x( updateSelection(newSel, data);)x" "\n" |
441 | R"x( } else if ("hover" in targetToID) {)x" "\n" |
442 | R"x( applyClassIf("hover", classSelector(selTarget("hover")));)x" "\n" |
443 | R"x( })x" "\n" |
444 | R"x(})x" "\n" |
445 | R"x(function watch(rootSelector, targetSelector, targetToID, data) {)x" "\n" |
446 | R"x( var root = document.querySelector(rootSelector);)x" "\n" |
447 | R"x( for (event of ['mouseout', 'mousemove', 'click']))x" "\n" |
448 | R"x( root.addEventListener(event, e => mouseEventHandler(e, targetSelector, targetToID, data));)x" "\n" |
449 | R"x(})x" "\n" |
450 | R"x(function watchSelection(data) {)x" "\n" |
451 | R"x( let lastIter = (bb) => `${bb}:${data.cfg[bb].iters}`;)x" "\n" |
452 | R"x( watch('#code', '.c', {)x" "\n" |
453 | R"x( hover: e => e.dataset.elt,)x" "\n" |
454 | R"x( bb: e => e.dataset.bb,)x" "\n" |
455 | R"x( elt: e => e.dataset.elt,)x" "\n" |
456 | R"x( // If we're already viewing an iteration of this BB, stick with the same.)x" "\n" |
457 | R"x( iter: e => (selection.iter && selection.bb == e.dataset.bb) ? selection.iter : lastIter(e.dataset.bb),)x" "\n" |
458 | R"x( }, data);)x" "\n" |
459 | R"x( watch('#cfg', '.bb', {)x" "\n" |
460 | R"x( hover: e => e.id,)x" "\n" |
461 | R"x( bb: e => e.id,)x" "\n" |
462 | R"x( elt: e => e.id + ".0",)x" "\n" |
463 | R"x( iter: e => lastIter(e.id),)x" "\n" |
464 | R"x( }, data);)x" "\n" |
465 | R"x( watch('#timeline', '.entry', {)x" "\n" |
466 | R"x( hover: e => [e.id, e.dataset.bb],)x" "\n" |
467 | R"x( bb: e => e.dataset.bb,)x" "\n" |
468 | R"x( elt: e => e.dataset.bb + ".0",)x" "\n" |
469 | R"x( iter: e => e.id,)x" "\n" |
470 | R"x( }, data);)x" "\n" |
471 | R"x( watch('#bb-elements', 'tr', {)x" "\n" |
472 | R"x( hover: e => e.id,)x" "\n" |
473 | R"x( elt: e => e.id,)x" "\n" |
474 | R"x( }, data);)x" "\n" |
475 | R"x( watch('#iterations', '.chooser', {)x" "\n" |
476 | R"x( hover: e => e.dataset.iter,)x" "\n" |
477 | R"x( iter: e => e.dataset.iter,)x" "\n" |
478 | R"x( }, data);)x" "\n" |
479 | R"x( updateSelection({}, data);)x" "\n" |
480 | R"x(})x" "\n" |
481 | R"x(function applyClassIf(cls, query) {)x" "\n" |
482 | R"x( document.querySelectorAll('.' + cls).forEach(elt => elt.classList.remove(cls));)x" "\n" |
483 | R"x( document.querySelectorAll(query).forEach(elt => elt.classList.add(cls));)x" "\n" |
484 | R"x(})x" "\n" |
485 | R"x(// Turns a class name into a CSS selector matching it, with some wrinkles:)x" "\n" |
486 | R"x(// - we treat id="foo" just like class="foo" to avoid repetition in the HTML)x" "\n" |
487 | R"x(// - cls can be an array of strings, we match them all)x" "\n" |
488 | R"x(function classSelector(cls) {)x" "\n" |
489 | R"x( if (cls == null) return null;)x" "\n" |
490 | R"x( if (Array.isArray(cls)) return cls.map(classSelector).join(', ');)x" "\n" |
491 | R"x( var escaped = cls.replace('.', '\\.').replace(':', '\\:');)x" "\n" |
492 | R"x( // don't require id="foo" class="foo")x" "\n" |
493 | R"x( return '.' + escaped + ", #" + escaped;)x" "\n" |
494 | R"x(})x" "\n" |
495 | R"x()x" "\n" |
496 | R"x(// Add a stylesheet defining colors for n basic blocks.)x" "\n" |
497 | R"x(function addBBColors(n) {)x" "\n" |
498 | R"x( let sheet = new CSSStyleSheet();)x" "\n" |
499 | R"x( // hex values to subtract from fff to get a base color)x" "\n" |
500 | R"x( options = [0x001, 0x010, 0x011, 0x100, 0x101, 0x110, 0x111];)x" "\n" |
501 | R"x( function color(hex) {)x" "\n" |
502 | R"x( return "#" + hex.toString(16).padStart(3, "0");)x" "\n" |
503 | R"x( })x" "\n" |
504 | R"x( function add(selector, property, hex) {)x" "\n" |
505 | R"x( sheet.insertRule(`${selector} { ${property}: ${color(hex)}; }`))x" "\n" |
506 | R"x( })x" "\n" |
507 | R"x( for (var i = 0; i < n; ++i) {)x" "\n" |
508 | R"x( let opt = options[i%options.length];)x" "\n" |
509 | R"x( add(`.B${i}`, 'background-color', 0xfff - 2*opt);)x" "\n" |
510 | R"x( add(`#B${i} polygon`, 'fill', 0xfff - 2*opt);)x" "\n" |
511 | R"x( add(`#B${i} polygon`, 'stroke', 0x888 - 4*opt);)x" "\n" |
512 | R"x( })x" "\n" |
513 | R"x( document.adoptedStyleSheets.push(sheet);)x" "\n" |
514 | R"x(})x" "\n" |
515 | R"x()x" "\n" |
516 | ; |
517 | |