From 11f7cfa0088e5c4450086e7d206dd9a35de3a51f Mon Sep 17 00:00:00 2001 From: Lubis Date: Sat, 19 Mar 2022 02:29:46 +0900 Subject: [PATCH] Tree printer for Python AST and its ASR --- grammar/asdl_cpp.py | 261 +++++++++++++++++++- src/bin/lpython.cpp | 16 +- src/libasr/utils.h | 1 + src/lpython/pickle.cpp | 24 ++ src/lpython/pickle.h | 4 + src/lpython/semantics/python_ast_to_asr.cpp | 15 ++ src/lpython/semantics/python_ast_to_asr.h | 1 + 7 files changed, 317 insertions(+), 5 deletions(-) diff --git a/grammar/asdl_cpp.py b/grammar/asdl_cpp.py index 5d25621632..ebab87fca2 100644 --- a/grammar/asdl_cpp.py +++ b/grammar/asdl_cpp.py @@ -405,6 +405,264 @@ def visitField(self, field): self.emit( "this->visit_symbol(*a.second);", 3) self.emit("}", 2) +# This class generates a visitor that prints the tree structure of AST/ASR +class TreeVisitorVisitor(ASDLVisitor): + + def visitModule(self, mod): + self.emit("/" + "*"*78 + "/") + self.emit("// Tree Visitor base class") + self.emit("") + self.emit("template ") + self.emit("class TreeBaseVisitor : public BaseVisitor") + self.emit("{") + self.emit("private:") + self.emit( "Derived& self() { return static_cast(*this); }", 1) + self.emit("public:") + self.emit( "std::string s, indtd;", 1) + self.emit( "bool use_colors;", 1) + self.emit( "bool start_line = true;", 1) + self.emit( 'bool last, attached;', 1) + self.emit( "int indent_level = 0, indent_spaces = 2, lvl = 0;", 1) + self.emit("public:") + self.emit( "TreeBaseVisitor() : use_colors(false), last(true), attached(false) { s.reserve(100000); }", 1) + self.emit( "void inc_indent() {", 1) + self.emit( "indent_level++;", 2) + self.emit( 'indtd += " ";', 2) + self.emit( "}", 1) + self.emit( "void inc_lindent() {", 1) + self.emit( "indent_level++;", 2) + self.emit( 'indtd += "| ";', 2) + self.emit( "}", 1) + self.emit( "void dec_indent() {", 1) + self.emit( "indent_level--;", 2) + self.emit( "LFORTRAN_ASSERT(indent_level >= 0);", 2) + self.emit( "indtd = indtd.substr(0, indent_level*indent_spaces);",2) + self.emit( "}", 1) + self.mod = mod + super(TreeVisitorVisitor, self).visitModule(mod) + self.emit("};") + + def visitType(self, tp): + super(TreeVisitorVisitor, self).visitType(tp, tp.name) + + def visitSum(self, sum, *args): + assert isinstance(sum, asdl.Sum) + if is_simple_sum(sum): + name = args[0] + "Type" + self.make_simple_sum_visitor(name, sum.types) + else: + for tp in sum.types: + self.visit(tp, *args) + + def visitProduct(self, prod, name): + self.make_visitor(name, prod.fields, False) + + def visitConstructor(self, cons, _): + self.make_visitor(cons.name, cons.fields, True) + + def make_visitor(self, name, fields, cons): + self.emit("void visit_%s(const %s_t &x) {" % (name, name), 1) + self.emit( 'if(!attached) {', 2) + self.emit( 'if(start_line) {', 3) + self.emit( 'start_line = false;', 4) + self.emit( 's.append(indtd);', 4) + self.emit( '} else {', 3) + self.emit( 's.append("\\n"+indtd);', 4) + self.emit( '}', 3) + self.emit( 'last ? s.append("└-") : s.append("|-");', 3) + self.emit( '}', 2) + self.emit( 'last ? inc_indent() : inc_lindent();', 2) + self.emit( 'attached, last = false;', 2) + if cons: + self.emit( 'if (use_colors) {', 2) + self.emit( 's.append(color(style::bold));', 3) + self.emit( 's.append(color(fg::magenta));', 3) + self.emit( '}', 2) + self.emit( 's.append("%s");' % name, 2) + self.emit( 'if (use_colors) {', 2) + self.emit( 's.append(color(fg::reset));', 3) + self.emit( 's.append(color(style::reset));', 3) + self.emit( '}', 2) + self.used = False + for n, field in enumerate(fields): + self.visitField(field, cons, n == len(fields)-1) + self.emit( 'dec_indent();', 2) + if not self.used: + # Note: a better solution would be to change `&x` to `& /* x */` + # above, but we would need to change emit to return a string. + self.emit("if ((bool&)x) { } // Suppress unused warning", 2) + self.emit("}", 1) + + def make_simple_sum_visitor(self, name, types): + self.emit("void visit_%s(const %s &x) {" % (name, name), 1) + self.emit( 'if (use_colors) {', 2) + self.emit( 's.append(color(style::bold));', 3) + self.emit( 's.append(color(fg::green));', 3) + self.emit( '}', 2) + self.emit( 'switch (x) {', 2) + for tp in types: + self.emit( 'case (%s::%s) : {' % (name, tp.name), 3) + self.emit( 's.append("%s");' % (tp.name), 4) + self.emit( ' break; }',3) + self.emit( '}', 2) + self.emit( 'if (use_colors) {', 2) + self.emit( 's.append(color(fg::reset));', 3) + self.emit( 's.append(color(style::reset));', 3) + self.emit( '}', 2) + self.emit("}", 1) + + def visitField(self, field, cons, last): + arr = '└-' if last else '|-' + if (field.type not in asdl.builtin_types and + field.type not in self.data.simple_types): + self.used = True + level = 2 + if field.type in products: + if field.opt: + template = "self().visit_%s(*x.m_%s);" % (field.type, field.name) + else: + template = "self().visit_%s(x.m_%s);" % (field.type, field.name) + else: + template = "self().visit_%s(*x.m_%s);" % (field.type, field.name) + if field.seq: + self.emit('s.append("\\n" + indtd + "%s" + "%s=\u21a7");' % (arr, field.name), level) + self.emit("for (size_t i=0; iscope) {' % field.name, level) + self.emit( 'i++;', level+1) + self.emit( 'inc_indent();', level+1) + self.emit( 'last = i == x.m_%s->scope.size();' % field.name, level+1) + self.emit( 's.append("\\n" + indtd + (last ? "└-" : "|-") + a.first + ": ");', level+1) + self.emit( 'attached = true;', level+1) + self.emit( 'this->visit_symbol(*a.second);', level+1) + self.emit( 'dec_indent();', level+1) + self.emit('}', level) + self.emit('dec_indent();', level) + elif field.type == "string" and not field.seq: + if field.opt: + self.emit('s.append("\\n" + indtd + "%s" + "%s=");' % (arr, field.name), 2) + self.emit("if (x.m_%s) {" % field.name, 2) + self.emit( 's.append("\\"" + std::string(x.m_%s) + "\\"");' % field.name, 3) + self.emit("} else {", 2) + self.emit( 's.append("()");', 3) + self.emit("}", 2) + else: + self.emit('s.append("\\n" + indtd + "%s" + "%s=");' % (arr, field.name), 2) + self.emit('s.append("\\"" + std::string(x.m_%s) + "\\"");' % field.name, 2) + elif field.type == "int" and not field.seq: + if field.opt: + self.emit('s.append("\\n" + indtd + "%s" + "%s=");' % (arr, field.name), 2) + self.emit("if (x.m_%s) {" % field.name, 2) + self.emit( 's.append(std::to_string(x.m_%s));' % field.name, 3) + self.emit("} else {", 2) + self.emit( 's.append("()");', 3) + self.emit("}", 2) + else: + self.emit('s.append("\\n" + indtd + "%s" + "%s=");' % (arr, field.name), 2) + self.emit('s.append(std::to_string(x.m_%s));' % field.name, 2) + elif field.type == "float" and not field.seq and not field.opt: + self.emit('s.append("\\n" + indtd + "%s" + "%s=");' % (arr, field.name), 2) + self.emit('s.append(std::to_string(x.m_%s));' % field.name, 2) + elif field.type == "bool" and not field.seq and not field.opt: + self.emit('s.append("\\n" + indtd + "%s" + "%s=");' % (arr, field.name), 2) + self.emit("if (x.m_%s) {" % field.name, 2) + self.emit( 's.append(".true.");', 3) + self.emit("} else {", 2) + self.emit( 's.append(".false.");', 3) + self.emit("}", 2) + elif field.type in self.data.simple_types: + if field.opt: + self.emit('s.append("Unimplementedopt");', 2) + else: + self.emit('s.append("\\n" + indtd + "%s" + "%sType=");' % (arr, field.type), 2) + self.emit('visit_%sType(x.m_%s);' \ + % (field.type, field.name), 2) + else: + self.emit('s.append("Unimplemented' + field.type + '");', 2) + class PickleVisitorVisitor(ASDLVisitor): @@ -1257,7 +1515,6 @@ def add_masks(fields, node): return down_cast(t); } - """ FOOT = r"""} // namespace LFortran::%(MOD)s @@ -1267,7 +1524,7 @@ def add_masks(fields, node): visitors = [ASTNodeVisitor0, ASTNodeVisitor1, ASTNodeVisitor, ASTVisitorVisitor1, ASTVisitorVisitor1b, ASTVisitorVisitor2, - ASTWalkVisitorVisitor, PickleVisitorVisitor, + ASTWalkVisitorVisitor, TreeVisitorVisitor, PickleVisitorVisitor, SerializationVisitorVisitor, DeserializationVisitorVisitor] diff --git a/src/bin/lpython.cpp b/src/bin/lpython.cpp index 2c9d4c53b2..c794d96e10 100644 --- a/src/bin/lpython.cpp +++ b/src/bin/lpython.cpp @@ -527,8 +527,12 @@ int emit_ast(const std::string &infile, } LFortran::Python::AST::ast_t* ast = r.result; - std::cout << LFortran::Python::pickle_python(*ast, - compiler_options.use_colors, compiler_options.indent) << std::endl; + if (compiler_options.tree) { + std::cout << LFortran::Python::pickle_tree_python(*ast, compiler_options.use_colors) << std::endl; + } else { + std::cout << LFortran::Python::pickle_python(*ast, + compiler_options.use_colors, compiler_options.indent) << std::endl; + } return 0; } @@ -559,8 +563,13 @@ int emit_asr(const std::string &infile, } LFortran::ASR::TranslationUnit_t* asr = r.result; - std::cout << LFortran::pickle(*asr, compiler_options.use_colors, compiler_options.indent, + if (compiler_options.tree) { + std::cout << LFortran::pickle_tree(*asr, compiler_options.use_colors, with_intrinsic_modules) << std::endl; + } else { + std::cout << LFortran::pickle(*asr, compiler_options.use_colors, compiler_options.indent, + with_intrinsic_modules) << std::endl; + } return 0; } @@ -1285,6 +1294,7 @@ int main(int argc, char *argv[]) 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("--tree", compiler_options.tree, "Tree structure print ASR/AST"); app.add_option("--pass", arg_pass, "Apply the ASR pass and show ASR (implies --show-asr)"); app.add_flag("--symtab-only", compiler_options.symtab_only, "Only create symbol tables in ASR (skip executable stmt)"); app.add_flag("--time-report", time_report, "Show compilation time report"); diff --git a/src/libasr/utils.h b/src/libasr/utils.h index 27079f0742..21ab70d634 100644 --- a/src/libasr/utils.h +++ b/src/libasr/utils.h @@ -25,6 +25,7 @@ struct CompilerOptions { bool show_stacktrace = false; bool use_colors = true; bool indent = false; + bool tree = false; bool fast = false; bool openmp = false; bool no_warnings = false; diff --git a/src/lpython/pickle.cpp b/src/lpython/pickle.cpp index ffcc7731df..5a22d7b262 100644 --- a/src/lpython/pickle.cpp +++ b/src/lpython/pickle.cpp @@ -270,4 +270,28 @@ std::string pickle(LFortran::ASR::TranslationUnit_t &asr, bool colors, bool inde return pickle((ASR::asr_t &)asr, colors, indent, show_intrinsic_modules); } +class ASRTreeVisitor : + public LFortran::ASR::TreeBaseVisitor +{ +public: + bool show_intrinsic_modules; + + std::string get_str() { + return s; + } + +}; + +std::string pickle_tree(LFortran::ASR::asr_t &asr, bool colors, bool show_intrinsic_modules) { + ASRTreeVisitor v; + v.use_colors = colors; + v.show_intrinsic_modules = show_intrinsic_modules; + v.visit_asr(asr); + return v.get_str(); +} + +std::string pickle_tree(LFortran::ASR::TranslationUnit_t &asr, bool colors, bool show_intrinsic_modules) { + return pickle_tree((ASR::asr_t &)asr, colors, show_intrinsic_modules); +} + } diff --git a/src/lpython/pickle.h b/src/lpython/pickle.h index 88e58dba18..8eadb34e11 100644 --- a/src/lpython/pickle.h +++ b/src/lpython/pickle.h @@ -20,6 +20,10 @@ namespace LFortran { std::string pickle(ASR::TranslationUnit_t &asr, bool colors=false, bool indent=false, bool show_intrinsic_modules=false); + // Print the tree structure + std::string pickle_tree(LFortran::ASR::asr_t &asr, bool colors, bool show_intrinsic_modules); + std::string pickle_tree(LFortran::ASR::TranslationUnit_t &asr, bool colors, bool show_intrinsic_modules); + } #endif // LFORTRAN_PICKLE_H diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index fb84e68443..016d243579 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -2433,6 +2433,21 @@ std::string pickle_python(AST::ast_t &ast, bool colors, bool indent) { return v.get_str(); } +class TreeVisitor : public AST::TreeBaseVisitor +{ +public: + std::string get_str() { + return s; + } +}; + +std::string pickle_tree_python(AST::ast_t &ast, bool colors) { + TreeVisitor v; + v.use_colors = colors; + v.visit_ast(ast); + return v.get_str(); +} + Result python_ast_to_asr(Allocator &al, AST::ast_t &ast, diag::Diagnostics &diagnostics, bool main_module, bool symtab_only) diff --git a/src/lpython/semantics/python_ast_to_asr.h b/src/lpython/semantics/python_ast_to_asr.h index 7386fc3177..5f7db2d626 100644 --- a/src/lpython/semantics/python_ast_to_asr.h +++ b/src/lpython/semantics/python_ast_to_asr.h @@ -7,6 +7,7 @@ namespace LFortran::Python { std::string pickle_python(AST::ast_t &ast, bool colors=false, bool indent=false); + std::string pickle_tree_python(AST::ast_t &ast, bool colors=true); Result python_ast_to_asr(Allocator &al, Python::AST::ast_t &ast, diag::Diagnostics &diagnostics, bool main_module, bool symtab_only);