From 0bcc640606a10e39a27bd9f800894f4b5aa705b5 Mon Sep 17 00:00:00 2001 From: Khushi Agrawal Date: Thu, 7 Mar 2024 11:41:51 +0530 Subject: [PATCH] [asr->python] Initial ASR to Python implementation (#2362) --- run_tests.py | 6 + src/bin/lpython.cpp | 58 +- src/libasr/CMakeLists.txt | 1 + src/libasr/codegen/asr_to_python.cpp | 642 ++++++++++++++++++ src/libasr/codegen/asr_to_python.h | 16 + tests/expr17.py | 9 + tests/reference/python-assert1-192ca6c.json | 13 + tests/reference/python-assert1-192ca6c.stdout | 7 + tests/reference/python-assign1-f87bafa.json | 13 + tests/reference/python-assign1-f87bafa.stdout | 15 + tests/reference/python-expr11-e6681c8.json | 13 + tests/reference/python-expr11-e6681c8.stdout | 10 + tests/reference/python-expr2-6b69018.json | 13 + tests/reference/python-expr2-6b69018.stdout | 13 + tests/reference/python-expr4-161a0ec.json | 13 + tests/reference/python-expr4-161a0ec.stdout | 7 + tests/reference/python-expr5-dee0e5c.json | 13 + tests/reference/python-expr5-dee0e5c.stdout | 7 + tests/reference/python-expr6-1a1d4fb.json | 13 + tests/reference/python-expr6-1a1d4fb.stdout | 9 + tests/tests.toml | 7 + 21 files changed, 897 insertions(+), 1 deletion(-) create mode 100644 src/libasr/codegen/asr_to_python.cpp create mode 100644 src/libasr/codegen/asr_to_python.h create mode 100644 tests/expr17.py create mode 100644 tests/reference/python-assert1-192ca6c.json create mode 100644 tests/reference/python-assert1-192ca6c.stdout create mode 100644 tests/reference/python-assign1-f87bafa.json create mode 100644 tests/reference/python-assign1-f87bafa.stdout create mode 100644 tests/reference/python-expr11-e6681c8.json create mode 100644 tests/reference/python-expr11-e6681c8.stdout create mode 100644 tests/reference/python-expr2-6b69018.json create mode 100644 tests/reference/python-expr2-6b69018.stdout create mode 100644 tests/reference/python-expr4-161a0ec.json create mode 100644 tests/reference/python-expr4-161a0ec.stdout create mode 100644 tests/reference/python-expr5-dee0e5c.json create mode 100644 tests/reference/python-expr5-dee0e5c.stdout create mode 100644 tests/reference/python-expr6-1a1d4fb.json create mode 100644 tests/reference/python-expr6-1a1d4fb.stdout diff --git a/run_tests.py b/run_tests.py index 12a0409856..e6df77d805 100755 --- a/run_tests.py +++ b/run_tests.py @@ -26,6 +26,7 @@ def is_included(backend): llvm_dbg = is_included("llvm_dbg") cpp = is_included("cpp") c = is_included("c") + python = is_included("python") is_cumulative = is_included("cumulative") wat = is_included("wat") run = is_included("run") @@ -133,6 +134,11 @@ def is_included(backend): else: run_test(filename, "c", "lpython --no-color --show-c {infile}", filename, update_reference, extra_args) + + if python: + run_test(filename, "python", "lpython --no-color --show-python {infile}", + filename, update_reference, extra_args) + if wat: run_test(filename, "wat", "lpython --no-color --show-wat {infile}", filename, update_reference, extra_args) diff --git a/src/bin/lpython.cpp b/src/bin/lpython.cpp index d5afe1fb8c..f03081eb1a 100644 --- a/src/bin/lpython.cpp +++ b/src/bin/lpython.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,7 @@ using LCompilers::CompilerOptions; using LCompilers::LPython::parse_python_file; enum class Backend { - llvm, cpp, c, x86, wasm, wasm_x86, wasm_x64 + llvm, cpp, c, x86, wasm, wasm_x86, wasm_x64, python }; @@ -385,6 +386,56 @@ int emit_c_to_file(const std::string &infile, const std::string &outfile, return 0; } +int emit_python(const std::string &infile, + const std::string &runtime_library_dir, + CompilerOptions &compiler_options) +{ + Allocator al(4*1024); + LCompilers::diag::Diagnostics diagnostics; + 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 r = parse_python_file( + al, runtime_library_dir, infile, diagnostics, 0, compiler_options.new_parser); + std::cerr << diagnostics.render(lm, compiler_options); + if (!r.ok) { + return 1; + } + LCompilers::LPython::AST::ast_t* ast = r.result; + + diagnostics.diagnostics.clear(); + + // AST -> ASR + LCompilers::Result + r1 = LCompilers::LPython::python_ast_to_asr(al, lm, nullptr, *ast, diagnostics, compiler_options, true, "__main__", infile); + std::cerr << diagnostics.render(lm, compiler_options); + if (!r1.ok) { + LCOMPILERS_ASSERT(diagnostics.has_error()) + return 2; + } + LCompilers::ASR::TranslationUnit_t* asr = r1.result; + + diagnostics.diagnostics.clear(); + + // ASR -> LPython + bool color = false; + int indent = 0; + LCompilers::Result res = LCompilers::asr_to_python(al, *asr, diagnostics, compiler_options, color, indent); + std::cerr << diagnostics.render(lm, compiler_options); + if (!res.ok) { + LCOMPILERS_ASSERT(diagnostics.has_error()) + return 3; + } + std::cout << res.result; + return 0; +} + int emit_wat(const std::string &infile, const std::string &runtime_library_dir, CompilerOptions &compiler_options) @@ -1490,6 +1541,7 @@ int main(int argc, char *argv[]) bool show_asr = false; bool show_cpp = false; bool show_c = false; + bool show_python = false; bool show_document_symbols = false; bool show_errors = false; bool with_intrinsic_modules = false; @@ -1556,6 +1608,7 @@ int main(int argc, char *argv[]) app.add_flag("--show-llvm", show_llvm, "Show LLVM IR for the given file and exit"); app.add_flag("--show-cpp", show_cpp, "Show C++ translation source for the given python file and exit"); app.add_flag("--show-c", show_c, "Show C translation source for the given python file and exit"); + app.add_flag("--show-python", show_python, "Show Python translation source for the given python file and exit"); app.add_flag("--show-asm", show_asm, "Show assembly for the given file and exit"); app.add_flag("--show-wat", show_wat, "Show WAT (WebAssembly Text Format) and exit"); app.add_flag("--show-stacktrace", compiler_options.show_stacktrace, "Show internal stacktrace on compiler errors"); @@ -1789,6 +1842,9 @@ int main(int argc, char *argv[]) return emit_c(arg_file, runtime_library_dir, lpython_pass_manager, compiler_options); } + if (show_python) { + return emit_python(arg_file, runtime_library_dir, compiler_options); + } if (show_wat) { return emit_wat(arg_file, runtime_library_dir, compiler_options); } diff --git a/src/libasr/CMakeLists.txt b/src/libasr/CMakeLists.txt index 689f6ea86e..9ae6bee701 100644 --- a/src/libasr/CMakeLists.txt +++ b/src/libasr/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRC codegen/asr_to_cpp.cpp codegen/asr_to_c.cpp codegen/asr_to_julia.cpp + codegen/asr_to_python.cpp codegen/asr_to_fortran.cpp codegen/asr_to_py.cpp codegen/x86_assembler.cpp diff --git a/src/libasr/codegen/asr_to_python.cpp b/src/libasr/codegen/asr_to_python.cpp new file mode 100644 index 0000000000..69f7a0bf4d --- /dev/null +++ b/src/libasr/codegen/asr_to_python.cpp @@ -0,0 +1,642 @@ +#include +#include +#include +#include + +using LCompilers::ASR::is_a; +using LCompilers::ASR::down_cast; + +namespace LCompilers { + +enum Precedence { + Or = 4, + And = 5, + Not = 6, + CmpOp = 7, + Add = 12, + Sub = 12, + Mul = 13, + Div = 13, + BitNot = 14, + UnaryMinus = 14, + Exp = 15, + Pow = 15, + Constant = 18, +}; + +class ASRToLpythonVisitor : public ASR::BaseVisitor +{ +public: + Allocator& al; + diag::Diagnostics& diag; + std::string s; + bool use_colors; + int indent_level; + std::string indent; + int indent_spaces; + // Following same order as Python 3.x + // https://docs.python.org/3/reference/expressions.html#expression-lists + int last_expr_precedence; + +public: + ASRToLpythonVisitor(Allocator& al, diag::Diagnostics& diag, CompilerOptions& /*co*/, bool _use_colors, int _indent) + : al{ al }, diag{ diag }, use_colors{_use_colors}, indent_level{0}, + indent_spaces{_indent} + { } + + void inc_indent() { + indent_level++; + indent = std::string(indent_level*indent_spaces, ' '); + } + + void dec_indent() { + indent_level--; + indent = std::string(indent_level*indent_spaces, ' '); + } + + void visit_expr_with_precedence(const ASR::expr_t &x, int current_precedence) { + visit_expr(x); + if (last_expr_precedence == 18 || + last_expr_precedence < current_precedence) { + s = "(" + s + ")"; + } + } + + std::string binop2str(const ASR::binopType type) + { + switch (type) { + case (ASR::binopType::Add) : { + last_expr_precedence = Precedence::Add; + return " + "; + } case (ASR::binopType::Sub) : { + last_expr_precedence = Precedence::Sub; + return " - "; + } case (ASR::binopType::Mul) : { + last_expr_precedence = Precedence::Mul; + return " * "; + } case (ASR::binopType::Div) : { + last_expr_precedence = Precedence::Div; + return " / "; + } case (ASR::binopType::Pow) : { + last_expr_precedence = Precedence::Pow; + return " ** "; + } default : { + throw LCompilersException("Cannot represent the binary operator as a string"); + } + } + } + + std::string cmpop2str(const ASR::cmpopType type) + { + last_expr_precedence = Precedence::CmpOp; + switch (type) { + case (ASR::cmpopType::Eq) : return " == "; + case (ASR::cmpopType::NotEq) : return " != "; + case (ASR::cmpopType::Lt) : return " < "; + case (ASR::cmpopType::LtE) : return " <= "; + case (ASR::cmpopType::Gt) : return " > "; + case (ASR::cmpopType::GtE) : return " >= "; + default : throw LCompilersException("Cannot represent the boolean operator as a string"); + } + } + + std::string logicalbinop2str(const ASR::logicalbinopType type) + { + switch (type) { + case (ASR::logicalbinopType::And) : { + last_expr_precedence = Precedence::And; + return " and "; + } case (ASR::logicalbinopType::Or) : { + last_expr_precedence = Precedence::Or; + return " or "; + } default : { + throw LCompilersException("Cannot represent the boolean operator as a string"); + } + } + } + + template + void visit_body(const T &x, std::string &r, bool apply_indent=true) { + if (apply_indent) { + inc_indent(); + } + for (size_t i = 0; i < x.n_body; i++) { + visit_stmt(*x.m_body[i]); + r += s; + } + if (apply_indent) { + dec_indent(); + } + } + + std::string get_type(const ASR::ttype_t *t) { + std::string r = ""; + switch (t->type) { + case ASR::ttypeType::Integer : { + r += "i"; + r += std::to_string(ASRUtils::extract_kind_from_ttype_t(t)*8); + break; + } case ASR::ttypeType::Real : { + r += "f"; + r += std::to_string(ASRUtils::extract_kind_from_ttype_t(t)*8); + break; + } case ASR::ttypeType::Complex : { + r += "c"; + r += std::to_string(ASRUtils::extract_kind_from_ttype_t(t)*8); + break; + } case ASR::ttypeType::Character : { + r = "str"; + break; + } case ASR::ttypeType::Logical : { + r = "bool"; + break; + } default : { + throw LCompilersException("The type `" + + ASRUtils::type_to_str_python(t) + "` is not handled yet"); + } + } + return r; + } + + void visit_TranslationUnit(const ASR::TranslationUnit_t &x) { + std::string r = ""; + + for (auto &item : x.m_symtab->get_scope()) { + if (is_a(*item.second)) { + visit_symbol(*item.second); + r += s; + r += "\n"; + } + } + + for (auto &item : x.m_symtab->get_scope()) { + if (is_a(*item.second)) { + visit_symbol(*item.second); + r += s; + r += "\n"; + } + } + + // Main program + for (auto &item : x.m_symtab->get_scope()) { + if (is_a(*item.second)) { + visit_symbol(*item.second); + r += s; + } + } + s = r; + } + + void visit_Module(const ASR::Module_t &x) { + std::string r; + + for (auto &item : x.m_symtab->get_scope()) { + if (is_a(*item.second)) { + visit_symbol(*item.second); + r += s; + } + } + s = r; + } + + void visit_Function(const ASR::Function_t &x) { + // Generate code for the lpython function + std::string r; + r = "def"; + r += " "; + r.append(x.m_name); + r += "("; + for (size_t i = 0; i < x.n_args; i++) { + visit_expr(*x.m_args[i]); + r += s; + // TODO: Specify the datatype of the argument here + if (i < x.n_args - 1) { + r += ", "; + } + } + r += "):"; + r += "\n"; + + inc_indent(); + for (auto &item : x.m_symtab->get_scope()) { + if (is_a(*item.second)) { + visit_symbol(*item.second); + r += s; + } + } + dec_indent(); + + visit_body(x, r, true); + + s = r; + } + + void visit_Program(const ASR::Program_t &x) { + std::string r; + + for (auto &item : x.m_symtab->get_scope()) { + if (is_a(*item.second)) { + visit_symbol(*item.second); + r += s; + } + } + + r += "\n"; + s = r; + } + + void visit_Variable(const ASR::Variable_t &x) { + std::string r = indent; + r += x.m_name; + r += ": "; + r += get_type(x.m_type); + r += "\n"; + s = r; + } + + void visit_Print(const ASR::Print_t &x) { + std::string r = indent; + r += "print("; + for (size_t i = 0; i < x.n_values; i++) { + visit_expr(*x.m_values[i]); + r += s; + if (i < x.n_values-1) + r += ", "; + } + r += ")"; + r += "\n"; + s = r; + } + + void visit_Assignment(const ASR::Assignment_t &x) { + std::string r = indent; + visit_expr(*x.m_target); + r += s; + r += " = "; + visit_expr(*x.m_value); + r += s; + r += "\n"; + s = r; + } + + void visit_Return(const ASR::Return_t /*&x*/) { + // TODO: Handle cases for returning an expression/value + s = indent + "return" + "\n"; + } + + void visit_SubroutineCall(const ASR::SubroutineCall_t &x) { + std::string r = indent; + r += ASRUtils::symbol_name(x.m_name); + r += "("; + for (size_t i = 0; i < x.n_args; i++) { + visit_expr(*x.m_args[i].m_value); + r += s; + if (i < x.n_args - 1) + r += ", "; + } + r += ")\n"; + s = r; + } + + void visit_FunctionCall(const ASR::FunctionCall_t &x) { + std::string r = ""; + if (x.m_original_name) { + r += ASRUtils::symbol_name(x.m_original_name); + } else { + r += ASRUtils::symbol_name(x.m_name); + } + + r += "("; + for (size_t i = 0; i < x.n_args; i++) { + visit_expr(*x.m_args[i].m_value); + r += s; + if (i < x.n_args - 1) + r += ", "; + } + r += ")"; + s = r; + } + + void visit_Cast(const ASR::Cast_t &x) { + // TODO + visit_expr(*x.m_arg); + } + + void visit_Var(const ASR::Var_t &x) { + s = ASRUtils::symbol_name(x.m_v); + } + + void visit_If(const ASR::If_t &x) { + std::string r = indent; + r += "if "; + visit_expr(*x.m_test); + r += s; + r += ":\n"; + inc_indent(); + for (size_t i = 0; i < x.n_body; i++) { + visit_stmt(*x.m_body[i]); + r += s; + } + dec_indent(); + if (x.n_orelse == 0) { + r += "\n"; + } else { + for (size_t i = 0; i < x.n_orelse; i++) { + r += indent + "else:\n"; + inc_indent(); + visit_stmt(*x.m_orelse[i]); + r += s; + dec_indent(); + r += "\n"; + } + } + s = r; + } + + void visit_WhileLoop(const ASR::WhileLoop_t &x) { + std::string r = indent; + r += "while "; + visit_expr(*x.m_test); + r += s; + r += ":\n"; + visit_body(x, r); + s = r; + } + + void visit_NamedExpr(const ASR::NamedExpr_t &x) { + this->visit_expr(*x.m_target); + std::string t = std::move(s); + this->visit_expr(*x.m_value); + std::string v = std::move(s); + s = "(" + t + " := " + v + ")"; + } + + void visit_ExplicitDeallocate(const ASR::ExplicitDeallocate_t &x) { + std::string r = indent; + r += "del "; + for (size_t i = 0; i < x.n_vars; i++) { + if (i > 0) { + r += ", "; + } + visit_expr(*x.m_vars[i]); + r += s; + } + s = r; + } + + void visit_IntrinsicScalarFunction(const ASR::IntrinsicScalarFunction_t &x) { + std::string out; + switch (x.m_intrinsic_id) { + SET_INTRINSIC_NAME(Abs, "abs"); + default : { + throw LCompilersException("IntrinsicScalarFunction: `" + + ASRUtils::get_intrinsic_name(x.m_intrinsic_id) + + "` is not implemented"); + } + } + LCOMPILERS_ASSERT(x.n_args == 1); + visit_expr(*x.m_args[0]); + out += "(" + s + ")"; + s = out; + } + + void visit_StringCompare(const ASR::StringCompare_t &x) { + std::string r; + int current_precedence = last_expr_precedence; + visit_expr_with_precedence(*x.m_left, current_precedence); + r += s; + r += cmpop2str(x.m_op); + visit_expr_with_precedence(*x.m_right, current_precedence); + r += s; + last_expr_precedence = current_precedence; + s = r; + } + + void visit_StringConstant(const ASR::StringConstant_t &x) { + s = "\""; + s.append(x.m_s); + s += "\""; + last_expr_precedence = Precedence::Constant; + } + + void visit_StringChr(const ASR::StringChr_t &x) { + visit_expr(*x.m_arg); + s = "chr(" + s + ")"; + } + + void visit_IntegerBinOp(const ASR::IntegerBinOp_t &x) { + std::string r; + int current_precedence = last_expr_precedence; + visit_expr_with_precedence(*x.m_left, current_precedence); + r += s; + r += binop2str(x.m_op); + visit_expr_with_precedence(*x.m_right, current_precedence); + r += s; + last_expr_precedence = current_precedence; + s = r; + } + + void visit_IntegerCompare(const ASR::IntegerCompare_t &x) { + std::string r; + int current_precedence = last_expr_precedence; + visit_expr_with_precedence(*x.m_left, current_precedence); + r += s; + r += cmpop2str(x.m_op); + visit_expr_with_precedence(*x.m_right, current_precedence); + r += s; + last_expr_precedence = current_precedence; + s = r; + } + + void visit_IntegerConstant(const ASR::IntegerConstant_t &x) { + s = std::to_string(x.m_n); + last_expr_precedence = Precedence::Constant; + } + + void visit_IntegerUnaryMinus(const ASR::IntegerUnaryMinus_t &x) { + visit_expr_with_precedence(*x.m_arg, 14); + s = "-" + s; + last_expr_precedence = Precedence::UnaryMinus; + } + + void visit_IntegerBitNot(const ASR::IntegerBitNot_t &x) { + visit_expr_with_precedence(*x.m_arg, 14); + s = "~" + s; + last_expr_precedence = Precedence::BitNot; + } + + void visit_RealConstant(const ASR::RealConstant_t &x) { + s = std::to_string(x.m_r); + last_expr_precedence = Precedence::Constant; + } + + void visit_RealCompare(const ASR::RealCompare_t &x) { + std::string r; + int current_precedence = last_expr_precedence; + visit_expr_with_precedence(*x.m_left, current_precedence); + r += s; + r += cmpop2str(x.m_op); + visit_expr_with_precedence(*x.m_right, current_precedence); + r += s; + last_expr_precedence = current_precedence; + s = r; + } + + void visit_RealUnaryMinus(const ASR::RealUnaryMinus_t &x) { + visit_expr_with_precedence(*x.m_arg, 14); + s = "-" + s; + last_expr_precedence = Precedence::UnaryMinus; + } + + void visit_RealBinOp(const ASR::RealBinOp_t &x) { + std::string r; + std::string m_op = binop2str(x.m_op); + int current_precedence = last_expr_precedence; + visit_expr_with_precedence(*x.m_left, current_precedence); + r += s; + r += m_op; + visit_expr_with_precedence(*x.m_right, current_precedence); + r += s; + last_expr_precedence = current_precedence; + s = r; + } + + void visit_LogicalConstant(const ASR::LogicalConstant_t &x) { + std::string r; + if (x.m_value) { + r += "True"; + } else { + r += "False"; + } + s = r; + last_expr_precedence = Precedence::Constant; + } + + void visit_LogicalBinOp(const ASR::LogicalBinOp_t &x) { + std::string r; + std::string m_op = logicalbinop2str(x.m_op); + int current_precedence = last_expr_precedence; + visit_expr_with_precedence(*x.m_left, current_precedence); + r += s; + r += m_op; + visit_expr_with_precedence(*x.m_right, current_precedence); + r += s; + last_expr_precedence = current_precedence; + s = r; + } + + void visit_LogicalCompare(const ASR::LogicalCompare_t &x) { + std::string r; + int current_precedence = last_expr_precedence; + visit_expr_with_precedence(*x.m_left, current_precedence); + r += s; + r += cmpop2str(x.m_op); + visit_expr_with_precedence(*x.m_right, current_precedence); + r += s; + last_expr_precedence = current_precedence; + s = r; + } + + void visit_LogicalNot(const ASR::LogicalNot_t &x) { + visit_expr_with_precedence(*x.m_arg, 6); + s = "not " + s; + last_expr_precedence = Precedence::Not; + } + + void visit_StringConcat(const ASR::StringConcat_t &x) { + this->visit_expr(*x.m_left); + std::string left = std::move(s); + this->visit_expr(*x.m_right); + std::string right = std::move(s); + s = left + " + " + right; + } + + void visit_StringRepeat(const ASR::StringRepeat_t &x) { + this->visit_expr(*x.m_left); + std::string left = std::move(s); + this->visit_expr(*x.m_right); + std::string right = std::move(s); + s = left + " * " + right; + } + + void visit_StringOrd(const ASR::StringOrd_t &x) { + std::string r; + r = "ord("; + visit_expr(*x.m_arg); + r += s; + r += ")"; + s = r; + } + + void visit_StringLen(const ASR::StringLen_t &x) { + visit_expr(*x.m_arg); + s += "len(" + s + ")"; + } + + void visit_IfExp(const ASR::IfExp_t &x) { + std::string r; + visit_expr(*x.m_body); + r += s; + r += " if "; + visit_expr(*x.m_test); + r += s; + r += " else "; + visit_expr(*x.m_orelse); + r += s; + s = r; + } + + void visit_ComplexConstant(const ASR::ComplexConstant_t &x) { + std::string re = std::to_string(x.m_re); + std::string im = std::to_string(x.m_im); + s = "complex(" + re + ", " + im + ")"; + } + + void visit_ComplexUnaryMinus(const ASR::ComplexUnaryMinus_t &x) { + visit_expr_with_precedence(*x.m_arg, 14); + s = "-" + s; + last_expr_precedence = Precedence::UnaryMinus; + } + + void visit_ComplexCompare(const ASR::ComplexCompare_t &x) { + std::string r; + int current_precedence = last_expr_precedence; + visit_expr_with_precedence(*x.m_left, current_precedence); + r += s; + r += cmpop2str(x.m_op); + visit_expr_with_precedence(*x.m_right, current_precedence); + r += s; + last_expr_precedence = current_precedence; + s = r; + } + + void visit_Assert(const ASR::Assert_t &x) { + std::string r = indent; + r += "assert "; + visit_expr(*x.m_test); + r += s; + if (x.m_msg) { + r += ", "; + visit_expr(*x.m_msg); + r += s; + } + r += "\n"; + s = r; + } + +}; + +Result asr_to_python(Allocator& al, ASR::TranslationUnit_t &asr, + diag::Diagnostics& diagnostics, CompilerOptions& co, + bool color, int indent) { + ASRToLpythonVisitor v(al, diagnostics, co, color, indent=4); + try { + v.visit_TranslationUnit(asr); + } catch (const CodeGenError &e) { + diagnostics.diagnostics.push_back(e.d); + return Error(); + } + return v.s; +} + +} // namespace LCompilers diff --git a/src/libasr/codegen/asr_to_python.h b/src/libasr/codegen/asr_to_python.h new file mode 100644 index 0000000000..fa812a7fe3 --- /dev/null +++ b/src/libasr/codegen/asr_to_python.h @@ -0,0 +1,16 @@ +#ifndef LPYTHON_ASR_TO_PYTHON_H +#define LPYTHON_ASR_TO_PYTHON_H + +#include +#include + +namespace LCompilers { + + // Convert ASR to Python source code + Result asr_to_python(Allocator &al, ASR::TranslationUnit_t &asr, + diag::Diagnostics &diagnostics, CompilerOptions &co, + bool color, int indent); + +} // namespace LCompilers + +#endif // LPYTHON_ASR_TO_PYTHON_H diff --git a/tests/expr17.py b/tests/expr17.py new file mode 100644 index 0000000000..17b61150ea --- /dev/null +++ b/tests/expr17.py @@ -0,0 +1,9 @@ +def if_check(): + a: i32 + a = 4 + if a < 0: + print("negative value") + elif a > 0: + print("positive value") + else: + print("zero") diff --git a/tests/reference/python-assert1-192ca6c.json b/tests/reference/python-assert1-192ca6c.json new file mode 100644 index 0000000000..ed9457882f --- /dev/null +++ b/tests/reference/python-assert1-192ca6c.json @@ -0,0 +1,13 @@ +{ + "basename": "python-assert1-192ca6c", + "cmd": "lpython --no-color --show-python {infile}", + "infile": "tests/assert1.py", + "infile_hash": "0ff84ea5ccd3d0815cbb66e1c3d3acd61dee7257c98aa1a27ceeef3b", + "outfile": null, + "outfile_hash": null, + "stdout": "python-assert1-192ca6c.stdout", + "stdout_hash": "2120f9dd9a8d41e4d67cced7ceb14c4ded9d56335058f66b24cfe12e", + "stderr": null, + "stderr_hash": null, + "returncode": 0 +} \ No newline at end of file diff --git a/tests/reference/python-assert1-192ca6c.stdout b/tests/reference/python-assert1-192ca6c.stdout new file mode 100644 index 0000000000..77016e73f3 --- /dev/null +++ b/tests/reference/python-assert1-192ca6c.stdout @@ -0,0 +1,7 @@ +def test_assert(): + a: i32 + a = 5 + assert (a) == (5), "a is not 5" + assert (a) != (10) + + diff --git a/tests/reference/python-assign1-f87bafa.json b/tests/reference/python-assign1-f87bafa.json new file mode 100644 index 0000000000..f4ce6903f8 --- /dev/null +++ b/tests/reference/python-assign1-f87bafa.json @@ -0,0 +1,13 @@ +{ + "basename": "python-assign1-f87bafa", + "cmd": "lpython --no-color --show-python {infile}", + "infile": "tests/assign1.py", + "infile_hash": "3b82a73e457bd65e85828b72d56596ca927e7c661e333691f154912b", + "outfile": null, + "outfile_hash": null, + "stdout": "python-assign1-f87bafa.stdout", + "stdout_hash": "6fa5f5e1492cac718631d3c05d27ab2ddda15dd2e56cc37a1ce188b9", + "stderr": null, + "stderr_hash": null, + "returncode": 0 +} \ No newline at end of file diff --git a/tests/reference/python-assign1-f87bafa.stdout b/tests/reference/python-assign1-f87bafa.stdout new file mode 100644 index 0000000000..caa7aaf2a6 --- /dev/null +++ b/tests/reference/python-assign1-f87bafa.stdout @@ -0,0 +1,15 @@ +def test_augassign(): + a: str + r: i32 + s: i32 + r = 0 + r = (r) + (4) + s = 5 + r = (r) * (s) + r = (r) - (2) + s = 10 + r = r / s + a = "" + a = a + "test" + + diff --git a/tests/reference/python-expr11-e6681c8.json b/tests/reference/python-expr11-e6681c8.json new file mode 100644 index 0000000000..067b2c4518 --- /dev/null +++ b/tests/reference/python-expr11-e6681c8.json @@ -0,0 +1,13 @@ +{ + "basename": "python-expr11-e6681c8", + "cmd": "lpython --no-color --show-python {infile}", + "infile": "tests/expr11.py", + "infile_hash": "940f2d32759315dfb8d54ea50819f2bfef9737e486615703609fd47a", + "outfile": null, + "outfile_hash": null, + "stdout": "python-expr11-e6681c8.stdout", + "stdout_hash": "6ae1afc8767434e703cfd38bbdd22275c73effa90a5e86c5c09bab5b", + "stderr": null, + "stderr_hash": null, + "returncode": 0 +} \ No newline at end of file diff --git a/tests/reference/python-expr11-e6681c8.stdout b/tests/reference/python-expr11-e6681c8.stdout new file mode 100644 index 0000000000..da61c57b5a --- /dev/null +++ b/tests/reference/python-expr11-e6681c8.stdout @@ -0,0 +1,10 @@ +def test_StrOp_repeat(): + s: str + s = "a" * 2 + s = "a" * -(1) + s = "test" * 5 + s = "bb" * 4 + s = "bb" * -(40) + s = "a" * 3 * 3 + + diff --git a/tests/reference/python-expr2-6b69018.json b/tests/reference/python-expr2-6b69018.json new file mode 100644 index 0000000000..784e5f3e6f --- /dev/null +++ b/tests/reference/python-expr2-6b69018.json @@ -0,0 +1,13 @@ +{ + "basename": "python-expr2-6b69018", + "cmd": "lpython --no-color --show-python {infile}", + "infile": "tests/expr2.py", + "infile_hash": "52d7d4d33553138f2cf55b9900047e5310c54d62e54b3ca1fa394024", + "outfile": null, + "outfile_hash": null, + "stdout": "python-expr2-6b69018.stdout", + "stdout_hash": "849450cb39ead48c1935431e54d989370ecece8c711f34e4ca38fd2b", + "stderr": null, + "stderr_hash": null, + "returncode": 0 +} \ No newline at end of file diff --git a/tests/reference/python-expr2-6b69018.stdout b/tests/reference/python-expr2-6b69018.stdout new file mode 100644 index 0000000000..45bd2f5ccc --- /dev/null +++ b/tests/reference/python-expr2-6b69018.stdout @@ -0,0 +1,13 @@ +def test_boolOp(): + a: bool + b: bool + a = False + b = True + a = a and b + b = a or (True) + a = a or b + a = a and b == b + a = a and b != b + a = b or b + + diff --git a/tests/reference/python-expr4-161a0ec.json b/tests/reference/python-expr4-161a0ec.json new file mode 100644 index 0000000000..9823faecf8 --- /dev/null +++ b/tests/reference/python-expr4-161a0ec.json @@ -0,0 +1,13 @@ +{ + "basename": "python-expr4-161a0ec", + "cmd": "lpython --no-color --show-python {infile}", + "infile": "tests/expr4.py", + "infile_hash": "5cba7a5d589f54fc31463e48903d5b46604fb64e3e64ba215339047c", + "outfile": null, + "outfile_hash": null, + "stdout": "python-expr4-161a0ec.stdout", + "stdout_hash": "1378e1d01da96646ba0d45dbc6f5d30f7956e1c874c558df95a9f733", + "stderr": null, + "stderr_hash": null, + "returncode": 0 +} \ No newline at end of file diff --git a/tests/reference/python-expr4-161a0ec.stdout b/tests/reference/python-expr4-161a0ec.stdout new file mode 100644 index 0000000000..a6a301dd99 --- /dev/null +++ b/tests/reference/python-expr4-161a0ec.stdout @@ -0,0 +1,7 @@ +def test_del(): + a: i32 + b: i32 + a = 4 + b = 20 + del a, b + diff --git a/tests/reference/python-expr5-dee0e5c.json b/tests/reference/python-expr5-dee0e5c.json new file mode 100644 index 0000000000..5cf281a38d --- /dev/null +++ b/tests/reference/python-expr5-dee0e5c.json @@ -0,0 +1,13 @@ +{ + "basename": "python-expr5-dee0e5c", + "cmd": "lpython --no-color --show-python {infile}", + "infile": "tests/expr5.py", + "infile_hash": "7bbb5f9dacb13556f99de8f2969f9089235fea372fc2f43fc9c4bb18", + "outfile": null, + "outfile_hash": null, + "stdout": "python-expr5-dee0e5c.stdout", + "stdout_hash": "cef56dbae8b4efd362ea96bcbdc080667fd46930c1d7cfbca92bbe8e", + "stderr": null, + "stderr_hash": null, + "returncode": 0 +} \ No newline at end of file diff --git a/tests/reference/python-expr5-dee0e5c.stdout b/tests/reference/python-expr5-dee0e5c.stdout new file mode 100644 index 0000000000..b63878db76 --- /dev/null +++ b/tests/reference/python-expr5-dee0e5c.stdout @@ -0,0 +1,7 @@ +def test_StrOp_concat(): + s: str + s = "3" + "4" + s = "a " + "test" + s = "test" + "test" + "test" + + diff --git a/tests/reference/python-expr6-1a1d4fb.json b/tests/reference/python-expr6-1a1d4fb.json new file mode 100644 index 0000000000..f1eb23d77b --- /dev/null +++ b/tests/reference/python-expr6-1a1d4fb.json @@ -0,0 +1,13 @@ +{ + "basename": "python-expr6-1a1d4fb", + "cmd": "lpython --no-color --show-python {infile}", + "infile": "tests/expr6.py", + "infile_hash": "1f3b5a7d997851264e679d58353346835eb450c608f6da7d2f5e5cd2", + "outfile": null, + "outfile_hash": null, + "stdout": "python-expr6-1a1d4fb.stdout", + "stdout_hash": "f7e339254436fdf45988c26786265c1d00bfcc23c0c5419fc07c0e6c", + "stderr": null, + "stderr_hash": null, + "returncode": 0 +} \ No newline at end of file diff --git a/tests/reference/python-expr6-1a1d4fb.stdout b/tests/reference/python-expr6-1a1d4fb.stdout new file mode 100644 index 0000000000..ccb26aa61f --- /dev/null +++ b/tests/reference/python-expr6-1a1d4fb.stdout @@ -0,0 +1,9 @@ +def test_ifexp(): + a: i32 + b: i32 + c: bool + a = 2 + b = 6 if (a) == (2) else 8 + c = True if (b) > (5) else False + + diff --git a/tests/tests.toml b/tests/tests.toml index 4f048e910b..9be71acb27 100644 --- a/tests/tests.toml +++ b/tests/tests.toml @@ -62,23 +62,27 @@ ast = true asr = true cpp = true wat = true +python = true [[test]] filename = "expr4.py" ast = true asr = true +python = true [[test]] filename = "expr5.py" ast = true asr = true cpp = true +python = true [[test]] filename = "expr6.py" ast = true asr = true cpp = true +python = true [[test]] filename = "expr7.py" @@ -110,6 +114,7 @@ asr = true filename = "expr11.py" ast = true asr = true +python = true [[test]] filename = "expr12.py" @@ -250,11 +255,13 @@ ast = true asr = true cpp = true llvm = true +python = true [[test]] filename = "assign1.py" ast = true asr = true +python = true [[test]] filename = "assign2.py"