Skip to content

Commit

Permalink
feat(irDump): Use pretty-printer in CFG dumps
Browse files Browse the repository at this point in the history
Closes #11
  • Loading branch information
byakuren-hijiri committed Jul 28, 2024
1 parent d57f3b9 commit 46a0e87
Showing 1 changed file with 51 additions and 10 deletions.
61 changes: 51 additions & 10 deletions src/internals/irDump.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AstStatement } from "@tact-lang/compiler/dist/grammar/ast";
import { CompilationUnit, CFG, Node } from "./ir";

import { InternalException } from "./exceptions";
import { prettyPrint } from "./prettyPrinter";
import { AstStatement } from "@tact-lang/compiler/dist/grammar/ast";
import JSONbig from "json-bigint";

/**
Expand All @@ -17,20 +18,20 @@ export class GraphvizDumper {
* @returns The Graphviz dot representation of the compilation unit.
*/
public static dumpCU(cu: CompilationUnit, dumpStdlib: boolean): string {
let graph = `digraph ${cu.projectName} {\n`;
let graph = `digraph "${cu.projectName}" {\n`;
graph += " node [shape=box];\n";
cu.functions.forEach((cfg) => {
if (!dumpStdlib && cfg.origin == "stdlib") {
return;
}
graph += this.dumpCFG(cfg, cfg.name);
graph += this.dumpCFG(cu, cfg, cfg.name);
});
cu.contracts.forEach((contract) => {
contract.methods.forEach((cfg) => {
if (!dumpStdlib && cfg.origin == "stdlib") {
return;
}
graph += this.dumpCFG(cfg, `${contract.name}__${cfg.name}`);
graph += this.dumpCFG(cu, cfg, `${contract.name}__${cfg.name}`);
});
});
graph += this.connectFunctionCalls(cu);
Expand Down Expand Up @@ -63,22 +64,62 @@ export class GraphvizDumper {
* @param prefix A prefix to uniquely identify nodes across multiple CFGs.
* @returns The Graphviz dot representation of the CFG.
*/
private static dumpCFG(cfg: CFG, prefix: string): string {
let output = ` subgraph cluster_${prefix} {\n`;
private static dumpCFG(
cu: CompilationUnit,
cfg: CFG,
prefix: string,
): string {
let output = ` subgraph "cluster_${prefix}" {\n`;
output += ` label="${prefix}";\n`;
if (cfg.origin == "stdlib") {
output += ` style=filled;\n`;
output += ` color=lightgrey;\n`;
}
cfg.nodes.forEach((node) => {
output += ` "${prefix}_${node.idx}" [label="Node ${node.idx}"];\n`;
cfg.forEachNode(cu.ast, (stmt, node) => {
output += ` "${prefix}_${node.idx}" [label="${this.ppSummary(stmt)}"];\n`;
});
cfg.edges.forEach((edge) => {
cfg.forEachEdge((edge) => {
output += ` "${prefix}_${edge.src}" -> "${prefix}_${edge.dst}";\n`;
});
output += " }\n";
return output;
}

/**
* Formats a summary of some statements defined within nodes in order to get more concise graphical representation.
*/
private static ppSummary(stmt: AstStatement): string {
const removeTrailingSemicolon = (str: string): string =>
str.replace(/;$/, "");
const result = (() => {
switch (stmt.kind) {
case "statement_let":
case "statement_return":
case "statement_expression":
case "statement_assign":
case "statement_augmentedassign":
return prettyPrint(stmt);
case "statement_condition":
return `if (${prettyPrint(stmt.condition)})`;
case "statement_while":
return `while (${prettyPrint(stmt.condition)})`;
case "statement_until":
return `until (${prettyPrint(stmt.condition)})`;
case "statement_repeat":
return `repeat (${prettyPrint(stmt.iterations)})`;
case "statement_try":
return "try";
case "statement_try_catch":
return `try ... catch (${prettyPrint(stmt.catchName)})`;
case "statement_foreach":
return `foreach ((${prettyPrint(stmt.keyName)}, ${prettyPrint(stmt.valueName)}) of ${prettyPrint(stmt.map)})`;
default:
throw InternalException.make("Unsupported statement", { node: stmt });
}
})();

return removeTrailingSemicolon(result);
}
}

/**
Expand Down

0 comments on commit 46a0e87

Please sign in to comment.