|
1 | 1 | <script lang="ts"> |
2 | | - import Parser from "web-tree-sitter"; |
3 | | - import { newCFGBuilder, type Language } from "../control-flow/cfg"; |
4 | | - import { |
5 | | - mergeNodeAttrs, |
6 | | - remapNodeTargets, |
7 | | - type CFG, |
8 | | - } from "../control-flow/cfg-defs"; |
9 | | - import { simplifyCFG, trimFor } from "../control-flow/graph-ops"; |
10 | | - import { Graphviz } from "@hpcc-js/wasm-graphviz"; |
11 | | - import { |
12 | | - getFirstFunction, |
13 | | - initialize as initializeUtils, |
14 | | - type Parsers, |
15 | | - } from "./utils"; |
| 2 | +import { Graphviz } from "@hpcc-js/wasm-graphviz"; |
| 3 | +import Parser from "web-tree-sitter"; |
| 4 | +import { type Language, newCFGBuilder } from "../control-flow/cfg"; |
| 5 | +import { |
| 6 | + type CFG, |
| 7 | + mergeNodeAttrs, |
| 8 | + remapNodeTargets, |
| 9 | +} from "../control-flow/cfg-defs"; |
| 10 | +import { simplifyCFG, trimFor } from "../control-flow/graph-ops"; |
| 11 | +import { |
| 12 | + type Parsers, |
| 13 | + getFirstFunction, |
| 14 | + initialize as initializeUtils, |
| 15 | +} from "./utils"; |
16 | 16 |
|
17 | | - let parsers: Parsers; |
18 | | - let graphviz: Graphviz; |
19 | | - let nodeColors: NodeColors; |
20 | | - export let simplify: boolean = true; |
21 | | - export let trim: boolean = true; |
22 | | - export let code: string; |
23 | | - export let language: Language; |
| 17 | +let parsers: Parsers; |
| 18 | +let graphviz: Graphviz; |
| 19 | +let nodeColors: NodeColors; |
| 20 | +export let simplify: boolean = true; |
| 21 | +export let trim: boolean = true; |
| 22 | +export let code: string; |
| 23 | +export let language: Language; |
24 | 24 |
|
25 | | - async function initialize() { |
26 | | - const utils = await initializeUtils(); |
27 | | - parsers = utils.parsers; |
28 | | - graphviz = utils.graphviz; |
29 | | - } |
| 25 | +async function initialize() { |
| 26 | + const utils = await initializeUtils(); |
| 27 | + parsers = utils.parsers; |
| 28 | + graphviz = utils.graphviz; |
| 29 | +} |
30 | 30 |
|
31 | | - function withBackground(text: string, color: string): string { |
32 | | - return `<span style="background: ${color};">${text}</span>`; |
33 | | - } |
| 31 | +function withBackground(text: string, color: string): string { |
| 32 | + return `<span style="background: ${color};">${text}</span>`; |
| 33 | +} |
34 | 34 |
|
35 | | - type NodeColors = Map<string, string>; |
| 35 | +type NodeColors = Map<string, string>; |
36 | 36 |
|
37 | | - function createNodeColors(cfg: CFG): Map<string, string> { |
38 | | - const nodes = new Set(cfg.offsetToNode.values()); |
39 | | - const nodeColors = new Map( |
40 | | - [...nodes.keys()].map((node, i, { length }) => [ |
41 | | - node, |
42 | | - `hsl(${(360 * i) / length}deg 60% 60%)`, |
43 | | - ]), |
44 | | - ); |
45 | | - return nodeColors; |
46 | | - } |
| 37 | +function createNodeColors(cfg: CFG): Map<string, string> { |
| 38 | + const nodes = new Set(cfg.offsetToNode.values()); |
| 39 | + const nodeColors = new Map( |
| 40 | + [...nodes.keys()].map((node, i, { length }) => [ |
| 41 | + node, |
| 42 | + `hsl(${(360 * i) / length}deg 60% 60%)`, |
| 43 | + ]), |
| 44 | + ); |
| 45 | + return nodeColors; |
| 46 | +} |
47 | 47 |
|
48 | | - function renderRanges( |
49 | | - cfg: CFG, |
50 | | - functionSyntax: Parser.SyntaxNode, |
51 | | - sourceText: string, |
52 | | - nodeColors: NodeColors, |
53 | | - ): string { |
54 | | - let result = ""; |
55 | | - const funcStart = functionSyntax.startIndex; |
56 | | - const funcEnd = functionSyntax.endIndex; |
57 | | - for (const { start, stop, value: node } of cfg.offsetToNode) { |
58 | | - if ((stop ?? 0) < funcStart || start > funcEnd) { |
59 | | - continue; |
60 | | - } |
61 | | - result += withBackground( |
62 | | - sourceText.slice( |
63 | | - Math.max(start, funcStart), |
64 | | - Math.min(funcEnd, stop ?? sourceText.length), |
65 | | - ), |
66 | | - nodeColors.get(node) ?? "red", |
67 | | - ); |
| 48 | +function renderRanges( |
| 49 | + cfg: CFG, |
| 50 | + functionSyntax: Parser.SyntaxNode, |
| 51 | + sourceText: string, |
| 52 | + nodeColors: NodeColors, |
| 53 | +): string { |
| 54 | + let result = ""; |
| 55 | + const funcStart = functionSyntax.startIndex; |
| 56 | + const funcEnd = functionSyntax.endIndex; |
| 57 | + for (const { start, stop, value: node } of cfg.offsetToNode) { |
| 58 | + if ((stop ?? 0) < funcStart || start > funcEnd) { |
| 59 | + continue; |
68 | 60 | } |
69 | | -
|
70 | | - let legend = [...nodeColors.entries()] |
71 | | - .map(([name, color]) => withBackground(name, color)) |
72 | | - .join("\n"); |
73 | | - return result + "\n\n\n" + legend; |
| 61 | + result += withBackground( |
| 62 | + sourceText.slice( |
| 63 | + Math.max(start, funcStart), |
| 64 | + Math.min(funcEnd, stop ?? sourceText.length), |
| 65 | + ), |
| 66 | + nodeColors.get(node) ?? "red", |
| 67 | + ); |
74 | 68 | } |
75 | 69 |
|
76 | | - type Options = { simplify: boolean; trim: boolean }; |
77 | | - function visualizeCodeSegmentation( |
78 | | - code: string, |
79 | | - language: Language, |
80 | | - options: Options, |
81 | | - ) { |
82 | | - const { trim, simplify } = options; |
83 | | - const tree = parsers[language].parse(code); |
84 | | - const functionSyntax = getFirstFunction(tree, language); |
85 | | - const builder = newCFGBuilder(language, {}); |
| 70 | + let legend = [...nodeColors.entries()] |
| 71 | + .map(([name, color]) => withBackground(name, color)) |
| 72 | + .join("\n"); |
| 73 | + return `${result}\n\n\n${legend}`; |
| 74 | +} |
86 | 75 |
|
87 | | - let cfg = builder.buildCFG(functionSyntax); |
| 76 | +type Options = { simplify: boolean; trim: boolean }; |
| 77 | +function visualizeCodeSegmentation( |
| 78 | + code: string, |
| 79 | + language: Language, |
| 80 | + options: Options, |
| 81 | +) { |
| 82 | + const { trim, simplify } = options; |
| 83 | + const tree = parsers[language].parse(code); |
| 84 | + const functionSyntax = getFirstFunction(tree, language); |
| 85 | + const builder = newCFGBuilder(language, {}); |
88 | 86 |
|
89 | | - if (!cfg) return ""; |
90 | | - if (trim) cfg = trimFor(cfg); |
91 | | - if (simplify) cfg = simplifyCFG(cfg, mergeNodeAttrs); |
92 | | - cfg = remapNodeTargets(cfg); |
93 | | - nodeColors = createNodeColors(cfg); |
94 | | - return renderRanges(cfg, functionSyntax, code, nodeColors); |
95 | | - // return renderPointRanges(cfg, functionSyntax, code, nodeColors); |
96 | | - } |
| 87 | + let cfg = builder.buildCFG(functionSyntax); |
97 | 88 |
|
98 | | - function renderWrapper(code: string, language: Language, options: Options) { |
99 | | - try { |
100 | | - return visualizeCodeSegmentation(code, language, options); |
101 | | - } catch (error) { |
102 | | - console.trace(error); |
103 | | - return `<p style='border: 2px red solid;'>${error.toString()}</p>`; |
104 | | - } |
| 89 | + if (!cfg) return ""; |
| 90 | + if (trim) cfg = trimFor(cfg); |
| 91 | + if (simplify) cfg = simplifyCFG(cfg, mergeNodeAttrs); |
| 92 | + cfg = remapNodeTargets(cfg); |
| 93 | + nodeColors = createNodeColors(cfg); |
| 94 | + return renderRanges(cfg, functionSyntax, code, nodeColors); |
| 95 | + // return renderPointRanges(cfg, functionSyntax, code, nodeColors); |
| 96 | +} |
| 97 | +
|
| 98 | +function renderWrapper(code: string, language: Language, options: Options) { |
| 99 | + try { |
| 100 | + return visualizeCodeSegmentation(code, language, options); |
| 101 | + } catch (error) { |
| 102 | + console.trace(error); |
| 103 | + return `<p style='border: 2px red solid;'>${error.toString()}</p>`; |
105 | 104 | } |
| 105 | +} |
106 | 106 |
|
107 | | - function recolorNodes() { |
108 | | - const nodes = document.querySelectorAll("svg .node"); |
109 | | - for (const node of nodes) { |
110 | | - const color = nodeColors.get(node.id) ?? "white"; |
111 | | - for (const polygon of node.querySelectorAll("polygon")) { |
112 | | - polygon.setAttribute("fill", color); |
113 | | - } |
| 107 | +function recolorNodes() { |
| 108 | + const nodes = document.querySelectorAll("svg .node"); |
| 109 | + for (const node of nodes) { |
| 110 | + const color = nodeColors.get(node.id) ?? "white"; |
| 111 | + for (const polygon of node.querySelectorAll("polygon")) { |
| 112 | + polygon.setAttribute("fill", color); |
114 | 113 | } |
115 | 114 | } |
| 115 | +} |
116 | 116 | </script> |
117 | 117 |
|
118 | 118 | {#await initialize() then _} |
|
0 commit comments