Skip to content

Support --no-indent and --visualize flags #1779

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ inst/bin/*
*_ldd.txt
*_lines.dat.txt
*__tmp__generated__.c
visualize*.html
a.c
a.h
a.py
Expand Down
8 changes: 4 additions & 4 deletions run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def is_included(backend):
run_test(
filename,
"ast",
"lpython --show-ast --indent --no-color {infile} -o {outfile}",
"lpython --show-ast --no-color {infile} -o {outfile}",
filename,
update_reference,
extra_args)
Expand All @@ -67,7 +67,7 @@ def is_included(backend):
run_test(
filename,
"ast_new",
"lpython --show-ast --indent --new-parser --no-color {infile} -o {outfile}",
"lpython --show-ast --new-parser --no-color {infile} -o {outfile}",
filename,
update_reference,
extra_args)
Expand All @@ -76,7 +76,7 @@ def is_included(backend):
run_test(
filename,
"asr",
"lpython --show-asr --indent --no-color {infile} -o {outfile}",
"lpython --show-asr --no-color {infile} -o {outfile}",
filename,
update_reference,
extra_args)
Expand All @@ -92,7 +92,7 @@ def is_included(backend):

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

Expand Down
2 changes: 1 addition & 1 deletion share/lpython/lfortran-completion.bash
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#/usr/bin/env bash

complete \
-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" \
-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" \
-df \
lfortran
54 changes: 43 additions & 11 deletions src/bin/lpython.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,29 @@ std::string get_kokkos_dir()
throw LCompilers::LCompilersException("LFORTRAN_KOKKOS_DIR is not defined");
}

int visualize_json(std::string &astr_data_json, LCompilers::Platform os) {
using namespace LCompilers;
std::string file_loc = LCompilers::LPython::generate_visualize_html(astr_data_json);
std::string open_cmd = "";
switch (os) {
case Linux: open_cmd = "xdg-open"; break;
case Windows: open_cmd = "start"; break;
case macOS_Intel:
case macOS_ARM: open_cmd = "open"; break;
default:
std::cerr << "Unsupported Platform " << pf2s(os) <<std::endl;
std::cerr << "Please open file " << file_loc << " manually" <<std::endl;
return 11;
}
std::string cmd = open_cmd + " " + file_loc;
int err = system(cmd.data());
if (err) {
std::cout << "The command '" + cmd + "' failed." << std::endl;
return 11;
}
return 0;
}

#ifdef HAVE_LFORTRAN_LLVM

#endif
Expand Down Expand Up @@ -158,6 +181,18 @@ int emit_ast(const std::string &infile,
lm.file_ends.push_back(input.size());
}
std::cout << LCompilers::LPython::pickle_json(*ast, lm) << std::endl;
} else if (compiler_options.visualize) {
LCompilers::LocationManager lm;
{
LCompilers::LocationManager::FileLocations fl;
fl.in_filename = infile;
lm.files.push_back(fl);
std::string input = LCompilers::read_file(infile);
lm.init_simple(input);
lm.file_ends.push_back(input.size());
}
LCompilers::Result<std::string> r = LCompilers::LPython::pickle_json(*ast, lm);
return visualize_json(r.result, compiler_options.platform);
} else {
std::cout << LCompilers::LPython::pickle_python(*ast,
compiler_options.use_colors, compiler_options.indent) << std::endl;
Expand Down Expand Up @@ -211,6 +246,9 @@ int emit_asr(const std::string &infile,
compiler_options.use_colors, with_intrinsic_modules) << std::endl;
} else if (compiler_options.json) {
std::cout << LCompilers::LPython::pickle_json(*asr, lm, with_intrinsic_modules) << std::endl;
} else if (compiler_options.visualize) {
std::string astr_data_json = LCompilers::LPython::pickle_json(*asr, lm, with_intrinsic_modules);
return visualize_json(astr_data_json, compiler_options.platform);
} else {
std::cout << LCompilers::LPython::pickle(*asr, compiler_options.use_colors,
compiler_options.indent, with_intrinsic_modules) << std::endl;
Expand Down Expand Up @@ -1433,6 +1471,7 @@ int main(int argc, char *argv[])
std::string arg_pass;
std::string skip_pass;
bool arg_no_color = false;
bool arg_no_indent = false;
bool show_llvm = false;
bool show_asm = false;
bool show_wat = false;
Expand Down Expand Up @@ -1496,9 +1535,10 @@ int main(int argc, char *argv[])
app.add_flag("--show-stacktrace", compiler_options.show_stacktrace, "Show internal stacktrace on compiler errors");
app.add_flag("--with-intrinsic-mods", with_intrinsic_modules, "Show intrinsic modules in ASR");
app.add_flag("--no-color", arg_no_color, "Turn off colored AST/ASR");
app.add_flag("--indent", compiler_options.indent, "Indented print ASR/AST");
app.add_flag("--no-indent", arg_no_indent, "Turn off Indented print ASR/AST");
app.add_flag("--tree", compiler_options.tree, "Tree structure print ASR/AST");
app.add_flag("--json", compiler_options.json, "Print ASR/AST Json format");
app.add_flag("--visualize", compiler_options.visualize, "Print ASR/AST Visualization");
app.add_option("--pass", arg_pass, "Apply the ASR pass and show ASR (implies --show-asr)");
app.add_option("--skip-pass", skip_pass, "Skip an ASR pass in default pipeline");
app.add_flag("--disable-main", compiler_options.disable_main, "Do not generate any code for the `main` function");
Expand Down Expand Up @@ -1566,16 +1606,7 @@ int main(int argc, char *argv[])
if (arg_version) {
std::string version = LFORTRAN_VERSION;
std::cout << "LPython version: " << version << std::endl;
std::cout << "Platform: ";
switch (compiler_options.platform) {
case (LCompilers::Platform::Linux) : std::cout << "Linux"; break;
case (LCompilers::Platform::macOS_Intel) : std::cout << "macOS Intel"; break;
case (LCompilers::Platform::macOS_ARM) : std::cout << "macOS ARM"; break;
case (LCompilers::Platform::Windows) : std::cout << "Windows"; break;
case (LCompilers::Platform::FreeBSD) : std::cout << "FreeBSD"; break;
case (LCompilers::Platform::OpenBSD) : std::cout << "OpenBSD"; break;
}
std::cout << std::endl;
std::cout << "Platform: " << pf2s(compiler_options.platform) << std::endl;
#ifdef HAVE_LFORTRAN_LLVM
std::cout << "Default target: " << LCompilers::LLVMEvaluator::get_default_target_triple() << std::endl;
#endif
Expand Down Expand Up @@ -1603,6 +1634,7 @@ int main(int argc, char *argv[])
}

compiler_options.use_colors = !arg_no_color;
compiler_options.indent = !arg_no_indent;

// if (fmt) {
// return format(arg_fmt_file, arg_fmt_inplace, !arg_fmt_no_color,
Expand Down
4 changes: 3 additions & 1 deletion src/libasr/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ enum Platform {
OpenBSD,
};

std::string pf2s(Platform);
Platform get_platform();

struct CompilerOptions {
Expand All @@ -34,9 +35,10 @@ struct CompilerOptions {
bool symtab_only = false;
bool show_stacktrace = false;
bool use_colors = true;
bool indent = false;
bool indent = true;
bool json = false;
bool tree = false;
bool visualize = false;
bool fast = false;
bool openmp = false;
bool generate_object_code = false;
Expand Down
12 changes: 12 additions & 0 deletions src/libasr/utils2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ bool present(char** const v, size_t n, const std::string name) {
return false;
}

std::string pf2s(Platform p) {
switch (p) {
case (Platform::Linux) : return "Linux";
case (Platform::macOS_Intel) : return "macOS Intel";
case (Platform::macOS_ARM) : return "macOS ARM";
case (Platform::Windows) : return "Windows";
case (Platform::FreeBSD) : return "FreeBSD";
case (Platform::OpenBSD) : return "OpenBSD";
}
return "Unsupported Platform";
}

Platform get_platform()
{
#if defined(_WIN32)
Expand Down
153 changes: 153 additions & 0 deletions src/lpython/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,157 @@ int32_t get_exit_status(int32_t err) {
return (((err) >> 8) & 0x000000ff);
}

std::string generate_visualize_html(std::string &astr_data_json) {
std::hash<std::string> hasher;
std::ofstream out;
std::string file_name = "visualize" + std::to_string(hasher(astr_data_json)) + ".html";
out.open(file_name);
out << R"(<!DOCTYPE html>
<html>
<head>
<title>LCompilers AST/R Visualization</title>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-flow-renderer/10.3.17/umd/index.js"></script>
<script src="https://dagrejs.github.io/project/dagre/latest/dagre.min.js"></script>
<script> )";
out << "var astr_data = " << astr_data_json << "; </script>\n";
out << R"(</head>

<body style="margin: 0px;">
<script type="text/babel" data-type="module">
function TreeNode({ node }) {
if (node.literals.length === 0) return <p><b>{node.node}</b></p>;
return (
<div>
<p><b>{node.node}</b></p>
<div style={{ backgroundColor: "#FBBD23", padding: "2px" }}>
{
node.literals.map((val, idx) => <p style={{ margin: "0px", padding: "1px" }} key={idx}>{val[0]}: {val[1]}</p>)
}
</div>
</div>
);
}

const getLayoutedElements = (nodes, edges, direction = 'TB') => {
const nodeWidth = 180;
const isHorizontal = direction === 'LR';

const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
dagreGraph.setGraph({ rankdir: direction });

nodes.forEach(node => dagreGraph.setNode(node.id, { width: nodeWidth, height: node.nodeHeight }));
edges.forEach(edge => dagreGraph.setEdge(edge.source, edge.target));

dagre.layout(dagreGraph);

nodes.forEach((node) => {
const nodeWithPosition = dagreGraph.node(node.id);
node.targetPosition = isHorizontal ? 'left' : 'top';
node.sourcePosition = isHorizontal ? 'right' : 'bottom';
// Shifting the dagre node position (anchor=center center) to the top left
// so it matches the React Flow node anchor point (top left).
node.position = {
x: nodeWithPosition.x - nodeWidth / 2,
y: nodeWithPosition.y - node.nodeHeight / 2,
};
return node;
});

return [nodes, edges];
};

class Graph {
constructor() {
this.nodes = [];
this.edges = [];
this.idx = 1;
return this;
}

createNode(cur_node) {
cur_node.idx = this.idx++;
cur_node.literals = [];
let obj = cur_node.fields;
for (let prop in obj) {
let neigh = obj[prop];
if (typeof neigh === 'object') {
if (neigh.hasOwnProperty("node")) {
this.createEdge(cur_node.idx, neigh, prop);
} else {
if (neigh.length > 0) {
for (let i in neigh) {
let arrayElement = neigh[i];
if (typeof arrayElement === 'object') {
if (arrayElement.hasOwnProperty("node")) {
this.createEdge(cur_node.idx, arrayElement, `${prop}[${i}]`);
} else {
console.log("ERROR: Unexpected 2D Array found");
}
} else {
cur_node.literals.push([`${prop}[${i}]`, `${arrayElement}`]);
}
}
} else {
// 0 length array, show as literal
cur_node.literals.push([prop, "[]"]);
}
}
} else {
cur_node.literals.push([prop, `${neigh}`]);
}
}

this.nodes.push({ id: `${cur_node.idx}`, data: { label: <TreeNode node={cur_node} /> }, nodeHeight: 70 + 20 * (cur_node.literals.length) });
}

createEdge(parent_idx, cur_node, edge_label) {
this.edges.push({
id: `${parent_idx}-${this.idx}`,
source: `${parent_idx}`,
target: `${this.idx}`,
label: edge_label,
labelStyle: { fontWeight: 700 },
labelBgPadding: [8, 4],
labelBgStyle: { fill: '#FBBD23' },
});
this.createNode(cur_node);
}
}

function Flow({ nodes, edges }) {
return (
<div style={{ height: '100vh' }}>
<ReactFlow.default
defaultNodes={nodes}
defaultEdges={edges}
style={{ backgroundColor: '#e5e7eb' }}
>
<ReactFlow.Background />
<ReactFlow.Controls />
<ReactFlow.MiniMap />
</ReactFlow.default>
</div>
);
}

function MyApp() {
var g = new Graph();
g.createNode(astr_data);
var [layoutedNodes, layoutedEdges] = getLayoutedElements(g.nodes, g.edges);
return (<Flow nodes={layoutedNodes} edges={layoutedEdges} />);
}

ReactDOM.render(<MyApp />, document.body);
</script>
</body>

</html>)";
return file_name;
}

}
2 changes: 2 additions & 0 deletions src/lpython/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ bool path_exists(std::string path);
// Decodes the exit status code of the process (in Unix)
int32_t get_exit_status(int32_t err);

std::string generate_visualize_html(std::string &astr_data_json);

} // LFortran

#endif // LFORTRAN_UTILS_H
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"basename": "asr-array_01_decl-f955627",
"cmd": "lpython --show-asr --indent --no-color {infile} -o {outfile}",
"basename": "asr-array_01_decl-39cf894",
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
"infile": "tests/../integration_tests/array_01_decl.py",
"infile_hash": "3dff59bab7475d254ce0470065c11e797e52a5b2a3d7546acc0e6705",
"outfile": null,
"outfile_hash": null,
"stdout": "asr-array_01_decl-f955627.stdout",
"stdout": "asr-array_01_decl-39cf894.stdout",
"stdout_hash": "ae255051c7b45506791318e252b42358043c41c3f3c13848d11e91b8",
"stderr": null,
"stderr_hash": null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"basename": "asr-array_02_decl-8860c8a",
"cmd": "lpython --show-asr --indent --no-color {infile} -o {outfile}",
"basename": "asr-array_02_decl-e8f6874",
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}",
"infile": "tests/../integration_tests/array_02_decl.py",
"infile_hash": "8daa77dd2d5fe6c6f5f3ce867746c5e13290305ef7e1723ac9669285",
"outfile": null,
"outfile_hash": null,
"stdout": "asr-array_02_decl-8860c8a.stdout",
"stdout": "asr-array_02_decl-e8f6874.stdout",
"stdout_hash": "c836b9c2ff4199c1e0f360b2587181ea9999ee1ff9bbf42750a7094e",
"stderr": null,
"stderr_hash": null,
Expand Down
Loading