Skip to content

Commit 8c71d37

Browse files
authored
Merge pull request #1779 from Shaikh-Ubaid/visualize_and_indent
Support --no-indent and --visualize flags
2 parents ff38f75 + 8d1c9c3 commit 8c71d37

File tree

493 files changed

+945
-743
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

493 files changed

+945
-743
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ inst/bin/*
8989
*_ldd.txt
9090
*_lines.dat.txt
9191
*__tmp__generated__.c
92+
visualize*.html
9293
a.c
9394
a.h
9495
a.py

run_tests.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def is_included(backend):
5858
run_test(
5959
filename,
6060
"ast",
61-
"lpython --show-ast --indent --no-color {infile} -o {outfile}",
61+
"lpython --show-ast --no-color {infile} -o {outfile}",
6262
filename,
6363
update_reference,
6464
extra_args)
@@ -67,7 +67,7 @@ def is_included(backend):
6767
run_test(
6868
filename,
6969
"ast_new",
70-
"lpython --show-ast --indent --new-parser --no-color {infile} -o {outfile}",
70+
"lpython --show-ast --new-parser --no-color {infile} -o {outfile}",
7171
filename,
7272
update_reference,
7373
extra_args)
@@ -76,7 +76,7 @@ def is_included(backend):
7676
run_test(
7777
filename,
7878
"asr",
79-
"lpython --show-asr --indent --no-color {infile} -o {outfile}",
79+
"lpython --show-asr --no-color {infile} -o {outfile}",
8080
filename,
8181
update_reference,
8282
extra_args)
@@ -92,7 +92,7 @@ def is_included(backend):
9292

9393
if pass_ is not None:
9494
cmd = "lpython --pass=" + pass_ + \
95-
" --show-asr --indent --no-color {infile} -o {outfile}"
95+
" --show-asr --no-color {infile} -o {outfile}"
9696
run_test(filename, "pass_{}".format(pass_), cmd,
9797
filename, update_reference, extra_args)
9898

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#/usr/bin/env bash
22

33
complete \
4-
-W "-h --help -S -c -o -v -E -I --version --cpp --fixed-form --show-prescan --show-tokens --show-ast --show-asr --with-intrinsic-modules --show-ast-f90 --no-color --indent --pass --show-llvm --show-cpp --show-stacktrace --time-report --static --backend --openmp fmt kernel mod pywrap" \
4+
-W "-h --help -S -c -o -v -E -I --version --cpp --fixed-form --show-prescan --show-tokens --show-ast --show-asr --with-intrinsic-modules --show-ast-f90 --no-color --pass --show-llvm --show-cpp --show-stacktrace --time-report --static --backend --openmp fmt kernel mod pywrap" \
55
-df \
66
lfortran

src/bin/lpython.cpp

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,29 @@ std::string get_kokkos_dir()
7676
throw LCompilers::LCompilersException("LFORTRAN_KOKKOS_DIR is not defined");
7777
}
7878

79+
int visualize_json(std::string &astr_data_json, LCompilers::Platform os) {
80+
using namespace LCompilers;
81+
std::string file_loc = LCompilers::LPython::generate_visualize_html(astr_data_json);
82+
std::string open_cmd = "";
83+
switch (os) {
84+
case Linux: open_cmd = "xdg-open"; break;
85+
case Windows: open_cmd = "start"; break;
86+
case macOS_Intel:
87+
case macOS_ARM: open_cmd = "open"; break;
88+
default:
89+
std::cerr << "Unsupported Platform " << pf2s(os) <<std::endl;
90+
std::cerr << "Please open file " << file_loc << " manually" <<std::endl;
91+
return 11;
92+
}
93+
std::string cmd = open_cmd + " " + file_loc;
94+
int err = system(cmd.data());
95+
if (err) {
96+
std::cout << "The command '" + cmd + "' failed." << std::endl;
97+
return 11;
98+
}
99+
return 0;
100+
}
101+
79102
#ifdef HAVE_LFORTRAN_LLVM
80103

81104
#endif
@@ -158,6 +181,18 @@ int emit_ast(const std::string &infile,
158181
lm.file_ends.push_back(input.size());
159182
}
160183
std::cout << LCompilers::LPython::pickle_json(*ast, lm) << std::endl;
184+
} else if (compiler_options.visualize) {
185+
LCompilers::LocationManager lm;
186+
{
187+
LCompilers::LocationManager::FileLocations fl;
188+
fl.in_filename = infile;
189+
lm.files.push_back(fl);
190+
std::string input = LCompilers::read_file(infile);
191+
lm.init_simple(input);
192+
lm.file_ends.push_back(input.size());
193+
}
194+
LCompilers::Result<std::string> r = LCompilers::LPython::pickle_json(*ast, lm);
195+
return visualize_json(r.result, compiler_options.platform);
161196
} else {
162197
std::cout << LCompilers::LPython::pickle_python(*ast,
163198
compiler_options.use_colors, compiler_options.indent) << std::endl;
@@ -211,6 +246,9 @@ int emit_asr(const std::string &infile,
211246
compiler_options.use_colors, with_intrinsic_modules) << std::endl;
212247
} else if (compiler_options.json) {
213248
std::cout << LCompilers::LPython::pickle_json(*asr, lm, with_intrinsic_modules) << std::endl;
249+
} else if (compiler_options.visualize) {
250+
std::string astr_data_json = LCompilers::LPython::pickle_json(*asr, lm, with_intrinsic_modules);
251+
return visualize_json(astr_data_json, compiler_options.platform);
214252
} else {
215253
std::cout << LCompilers::LPython::pickle(*asr, compiler_options.use_colors,
216254
compiler_options.indent, with_intrinsic_modules) << std::endl;
@@ -1433,6 +1471,7 @@ int main(int argc, char *argv[])
14331471
std::string arg_pass;
14341472
std::string skip_pass;
14351473
bool arg_no_color = false;
1474+
bool arg_no_indent = false;
14361475
bool show_llvm = false;
14371476
bool show_asm = false;
14381477
bool show_wat = false;
@@ -1496,9 +1535,10 @@ int main(int argc, char *argv[])
14961535
app.add_flag("--show-stacktrace", compiler_options.show_stacktrace, "Show internal stacktrace on compiler errors");
14971536
app.add_flag("--with-intrinsic-mods", with_intrinsic_modules, "Show intrinsic modules in ASR");
14981537
app.add_flag("--no-color", arg_no_color, "Turn off colored AST/ASR");
1499-
app.add_flag("--indent", compiler_options.indent, "Indented print ASR/AST");
1538+
app.add_flag("--no-indent", arg_no_indent, "Turn off Indented print ASR/AST");
15001539
app.add_flag("--tree", compiler_options.tree, "Tree structure print ASR/AST");
15011540
app.add_flag("--json", compiler_options.json, "Print ASR/AST Json format");
1541+
app.add_flag("--visualize", compiler_options.visualize, "Print ASR/AST Visualization");
15021542
app.add_option("--pass", arg_pass, "Apply the ASR pass and show ASR (implies --show-asr)");
15031543
app.add_option("--skip-pass", skip_pass, "Skip an ASR pass in default pipeline");
15041544
app.add_flag("--disable-main", compiler_options.disable_main, "Do not generate any code for the `main` function");
@@ -1566,16 +1606,7 @@ int main(int argc, char *argv[])
15661606
if (arg_version) {
15671607
std::string version = LFORTRAN_VERSION;
15681608
std::cout << "LPython version: " << version << std::endl;
1569-
std::cout << "Platform: ";
1570-
switch (compiler_options.platform) {
1571-
case (LCompilers::Platform::Linux) : std::cout << "Linux"; break;
1572-
case (LCompilers::Platform::macOS_Intel) : std::cout << "macOS Intel"; break;
1573-
case (LCompilers::Platform::macOS_ARM) : std::cout << "macOS ARM"; break;
1574-
case (LCompilers::Platform::Windows) : std::cout << "Windows"; break;
1575-
case (LCompilers::Platform::FreeBSD) : std::cout << "FreeBSD"; break;
1576-
case (LCompilers::Platform::OpenBSD) : std::cout << "OpenBSD"; break;
1577-
}
1578-
std::cout << std::endl;
1609+
std::cout << "Platform: " << pf2s(compiler_options.platform) << std::endl;
15791610
#ifdef HAVE_LFORTRAN_LLVM
15801611
std::cout << "Default target: " << LCompilers::LLVMEvaluator::get_default_target_triple() << std::endl;
15811612
#endif
@@ -1603,6 +1634,7 @@ int main(int argc, char *argv[])
16031634
}
16041635

16051636
compiler_options.use_colors = !arg_no_color;
1637+
compiler_options.indent = !arg_no_indent;
16061638

16071639
// if (fmt) {
16081640
// return format(arg_fmt_file, arg_fmt_inplace, !arg_fmt_no_color,

src/libasr/utils.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ enum Platform {
1717
OpenBSD,
1818
};
1919

20+
std::string pf2s(Platform);
2021
Platform get_platform();
2122

2223
struct CompilerOptions {
@@ -34,9 +35,10 @@ struct CompilerOptions {
3435
bool symtab_only = false;
3536
bool show_stacktrace = false;
3637
bool use_colors = true;
37-
bool indent = false;
38+
bool indent = true;
3839
bool json = false;
3940
bool tree = false;
41+
bool visualize = false;
4042
bool fast = false;
4143
bool openmp = false;
4244
bool generate_object_code = false;

src/libasr/utils2.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ bool present(char** const v, size_t n, const std::string name) {
4646
return false;
4747
}
4848

49+
std::string pf2s(Platform p) {
50+
switch (p) {
51+
case (Platform::Linux) : return "Linux";
52+
case (Platform::macOS_Intel) : return "macOS Intel";
53+
case (Platform::macOS_ARM) : return "macOS ARM";
54+
case (Platform::Windows) : return "Windows";
55+
case (Platform::FreeBSD) : return "FreeBSD";
56+
case (Platform::OpenBSD) : return "OpenBSD";
57+
}
58+
return "Unsupported Platform";
59+
}
60+
4961
Platform get_platform()
5062
{
5163
#if defined(_WIN32)

src/lpython/utils.cpp

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,157 @@ int32_t get_exit_status(int32_t err) {
131131
return (((err) >> 8) & 0x000000ff);
132132
}
133133

134+
std::string generate_visualize_html(std::string &astr_data_json) {
135+
std::hash<std::string> hasher;
136+
std::ofstream out;
137+
std::string file_name = "visualize" + std::to_string(hasher(astr_data_json)) + ".html";
138+
out.open(file_name);
139+
out << R"(<!DOCTYPE html>
140+
<html>
141+
<head>
142+
<title>LCompilers AST/R Visualization</title>
143+
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
144+
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
145+
146+
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
147+
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-flow-renderer/10.3.17/umd/index.js"></script>
148+
<script src="https://dagrejs.github.io/project/dagre/latest/dagre.min.js"></script>
149+
<script> )";
150+
out << "var astr_data = " << astr_data_json << "; </script>\n";
151+
out << R"(</head>
152+
153+
<body style="margin: 0px;">
154+
<script type="text/babel" data-type="module">
155+
function TreeNode({ node }) {
156+
if (node.literals.length === 0) return <p><b>{node.node}</b></p>;
157+
return (
158+
<div>
159+
<p><b>{node.node}</b></p>
160+
<div style={{ backgroundColor: "#FBBD23", padding: "2px" }}>
161+
{
162+
node.literals.map((val, idx) => <p style={{ margin: "0px", padding: "1px" }} key={idx}>{val[0]}: {val[1]}</p>)
163+
}
164+
</div>
165+
</div>
166+
);
167+
}
168+
169+
const getLayoutedElements = (nodes, edges, direction = 'TB') => {
170+
const nodeWidth = 180;
171+
const isHorizontal = direction === 'LR';
172+
173+
const dagreGraph = new dagre.graphlib.Graph();
174+
dagreGraph.setDefaultEdgeLabel(() => ({}));
175+
dagreGraph.setGraph({ rankdir: direction });
176+
177+
nodes.forEach(node => dagreGraph.setNode(node.id, { width: nodeWidth, height: node.nodeHeight }));
178+
edges.forEach(edge => dagreGraph.setEdge(edge.source, edge.target));
179+
180+
dagre.layout(dagreGraph);
181+
182+
nodes.forEach((node) => {
183+
const nodeWithPosition = dagreGraph.node(node.id);
184+
node.targetPosition = isHorizontal ? 'left' : 'top';
185+
node.sourcePosition = isHorizontal ? 'right' : 'bottom';
186+
// Shifting the dagre node position (anchor=center center) to the top left
187+
// so it matches the React Flow node anchor point (top left).
188+
node.position = {
189+
x: nodeWithPosition.x - nodeWidth / 2,
190+
y: nodeWithPosition.y - node.nodeHeight / 2,
191+
};
192+
return node;
193+
});
194+
195+
return [nodes, edges];
196+
};
197+
198+
class Graph {
199+
constructor() {
200+
this.nodes = [];
201+
this.edges = [];
202+
this.idx = 1;
203+
return this;
204+
}
205+
206+
createNode(cur_node) {
207+
cur_node.idx = this.idx++;
208+
cur_node.literals = [];
209+
let obj = cur_node.fields;
210+
for (let prop in obj) {
211+
let neigh = obj[prop];
212+
if (typeof neigh === 'object') {
213+
if (neigh.hasOwnProperty("node")) {
214+
this.createEdge(cur_node.idx, neigh, prop);
215+
} else {
216+
if (neigh.length > 0) {
217+
for (let i in neigh) {
218+
let arrayElement = neigh[i];
219+
if (typeof arrayElement === 'object') {
220+
if (arrayElement.hasOwnProperty("node")) {
221+
this.createEdge(cur_node.idx, arrayElement, `${prop}[${i}]`);
222+
} else {
223+
console.log("ERROR: Unexpected 2D Array found");
224+
}
225+
} else {
226+
cur_node.literals.push([`${prop}[${i}]`, `${arrayElement}`]);
227+
}
228+
}
229+
} else {
230+
// 0 length array, show as literal
231+
cur_node.literals.push([prop, "[]"]);
232+
}
233+
}
234+
} else {
235+
cur_node.literals.push([prop, `${neigh}`]);
236+
}
237+
}
238+
239+
this.nodes.push({ id: `${cur_node.idx}`, data: { label: <TreeNode node={cur_node} /> }, nodeHeight: 70 + 20 * (cur_node.literals.length) });
240+
}
241+
242+
createEdge(parent_idx, cur_node, edge_label) {
243+
this.edges.push({
244+
id: `${parent_idx}-${this.idx}`,
245+
source: `${parent_idx}`,
246+
target: `${this.idx}`,
247+
label: edge_label,
248+
labelStyle: { fontWeight: 700 },
249+
labelBgPadding: [8, 4],
250+
labelBgStyle: { fill: '#FBBD23' },
251+
});
252+
this.createNode(cur_node);
253+
}
254+
}
255+
256+
function Flow({ nodes, edges }) {
257+
return (
258+
<div style={{ height: '100vh' }}>
259+
<ReactFlow.default
260+
defaultNodes={nodes}
261+
defaultEdges={edges}
262+
style={{ backgroundColor: '#e5e7eb' }}
263+
>
264+
<ReactFlow.Background />
265+
<ReactFlow.Controls />
266+
<ReactFlow.MiniMap />
267+
</ReactFlow.default>
268+
</div>
269+
);
270+
}
271+
272+
function MyApp() {
273+
var g = new Graph();
274+
g.createNode(astr_data);
275+
var [layoutedNodes, layoutedEdges] = getLayoutedElements(g.nodes, g.edges);
276+
return (<Flow nodes={layoutedNodes} edges={layoutedEdges} />);
277+
}
278+
279+
ReactDOM.render(<MyApp />, document.body);
280+
</script>
281+
</body>
282+
283+
</html>)";
284+
return file_name;
285+
}
286+
134287
}

src/lpython/utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ bool path_exists(std::string path);
1515
// Decodes the exit status code of the process (in Unix)
1616
int32_t get_exit_status(int32_t err);
1717

18+
std::string generate_visualize_html(std::string &astr_data_json);
19+
1820
} // LFortran
1921

2022
#endif // LFORTRAN_UTILS_H

tests/reference/asr-array_01_decl-f955627.json renamed to tests/reference/asr-array_01_decl-39cf894.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
2-
"basename": "asr-array_01_decl-f955627",
3-
"cmd": "lpython --show-asr --indent --no-color {infile} -o {outfile}",
2+
"basename": "asr-array_01_decl-39cf894",
3+
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
44
"infile": "tests/../integration_tests/array_01_decl.py",
55
"infile_hash": "3dff59bab7475d254ce0470065c11e797e52a5b2a3d7546acc0e6705",
66
"outfile": null,
77
"outfile_hash": null,
8-
"stdout": "asr-array_01_decl-f955627.stdout",
8+
"stdout": "asr-array_01_decl-39cf894.stdout",
99
"stdout_hash": "ae255051c7b45506791318e252b42358043c41c3f3c13848d11e91b8",
1010
"stderr": null,
1111
"stderr_hash": null,

tests/reference/asr-array_02_decl-8860c8a.json renamed to tests/reference/asr-array_02_decl-e8f6874.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
2-
"basename": "asr-array_02_decl-8860c8a",
3-
"cmd": "lpython --show-asr --indent --no-color {infile} -o {outfile}",
2+
"basename": "asr-array_02_decl-e8f6874",
3+
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
44
"infile": "tests/../integration_tests/array_02_decl.py",
55
"infile_hash": "8daa77dd2d5fe6c6f5f3ce867746c5e13290305ef7e1723ac9669285",
66
"outfile": null,
77
"outfile_hash": null,
8-
"stdout": "asr-array_02_decl-8860c8a.stdout",
8+
"stdout": "asr-array_02_decl-e8f6874.stdout",
99
"stdout_hash": "c836b9c2ff4199c1e0f360b2587181ea9999ee1ff9bbf42750a7094e",
1010
"stderr": null,
1111
"stderr_hash": null,

0 commit comments

Comments
 (0)