diff --git a/.gitignore b/.gitignore index 2e1546420b..873a23ee9e 100644 --- a/.gitignore +++ b/.gitignore @@ -234,3 +234,6 @@ integration_tests/array_02_decl integration_tests/array_02_decl.c integration_tests/expr_12 integration_tests/expr_12.c + +# Interactive Shell +/input diff --git a/src/bin/lpython.cpp b/src/bin/lpython.cpp index 4a5da1a3d8..dcf30df425 100644 --- a/src/bin/lpython.cpp +++ b/src/bin/lpython.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -743,6 +744,11 @@ void print_time_report(std::vector> ×, bool #ifdef HAVE_LFORTRAN_LLVM +void section(const std::string &s) +{ + std::cout << color(LCompilers::style::bold) << color(LCompilers::fg::blue) << s << color(LCompilers::style::reset) << color(LCompilers::fg::reset) << std::endl; +} + int emit_llvm(const std::string &infile, const std::string &runtime_library_dir, LCompilers::PassManager& pass_manager, @@ -792,6 +798,157 @@ int emit_llvm(const std::string &infile, return 0; } +int interactive_python_repl( + LCompilers::PassManager& pass_manager, + CompilerOptions &compiler_options, + bool verbose) +{ + Allocator al(4*1024); + compiler_options.interactive = true; + LCompilers::PythonCompiler fe(compiler_options); + LCompilers::diag::Diagnostics diagnostics; + LCompilers::LocationManager lm; + std::vector> times; + LCompilers::PythonCompiler::EvalResult r; + + std::string code_string; + std::cout << ">>> "; + size_t cell_count = 0; + for (std::string input; std::getline(std::cin, input);) { + if (input == "exit" || input == "quit") { + return 0; + } + + if ((input.rfind("def", 0) == 0) || + (input.rfind("for", 0) == 0) || + (input.rfind("if", 0) == 0) || + (input.rfind("else", 0) == 0) || + (input.rfind("elif", 0) == 0) || + (input.rfind("class", 0) == 0) || + (input.rfind('@', 0) == 0) || + (input.rfind(' ', 0) == 0) || + (input.rfind('\t', 0) == 0)) { + // start of a block + code_string += input + "\n"; + std::cout << "... "; + continue; + } + code_string += input + "\n"; + + { + cell_count++; + LCompilers::LocationManager::FileLocations fl; + fl.in_filename = "input"; + std::ofstream out("input"); + out << code_string; + lm.files.push_back(fl); + lm.init_simple(code_string); + lm.file_ends.push_back(code_string.size()); + } + + try { + auto evaluation_start_time = std::chrono::high_resolution_clock::now(); + LCompilers::Result + res = fe.evaluate(code_string, verbose, lm, pass_manager, diagnostics); + if (res.ok) { + r = res.result; + } else { + LCOMPILERS_ASSERT(diagnostics.has_error()) + std::cerr << diagnostics.render(lm, compiler_options); + diagnostics.clear(); + code_string = ""; + std::cout << ">>> "; + continue; + } + + auto evaluation_end_time = std::chrono::high_resolution_clock::now(); + times.push_back(std::make_pair("evalution " + std::to_string(cell_count), std::chrono::duration + (evaluation_start_time - evaluation_end_time).count())); + + } catch (const LCompilers::LCompilersException &e) { + std::cerr << "Internal Compiler Error: Unhandled exception" << std::endl; + std::vector d = e.stacktrace_addresses(); + get_local_addresses(d); + get_local_info(d); + std::cerr << stacktrace2str(d, LCompilers::stacktrace_depth); + std::cerr << e.name() + ": " << e.msg() << std::endl; + + code_string = ""; + std::cout << ">>> "; + continue; + } + + if (verbose) { + section("AST:"); + std::cout << r.ast << std::endl; + section("ASR:"); + std::cout << r.asr << std::endl; + section("LLVM IR:"); + std::cout << r.llvm_ir << std::endl; + } + + switch (r.type) { + case (LCompilers::PythonCompiler::EvalResult::integer4) : { + if (verbose) std::cout << "Return type: integer" << std::endl; + if (verbose) section("Result:"); + std::cout << r.i32 << std::endl; + break; + } + case (LCompilers::PythonCompiler::EvalResult::integer8) : { + if (verbose) std::cout << "Return type: integer(8)" << std::endl; + if (verbose) section("Result:"); + std::cout << r.i64 << std::endl; + break; + } + case (LCompilers::PythonCompiler::EvalResult::real4) : { + if (verbose) std::cout << "Return type: real" << std::endl; + if (verbose) section("Result:"); + std::cout << std::setprecision(8) << r.f32 << std::endl; + break; + } + case (LCompilers::PythonCompiler::EvalResult::real8) : { + if (verbose) std::cout << "Return type: real(8)" << std::endl; + if (verbose) section("Result:"); + std::cout << std::setprecision(17) << r.f64 << std::endl; + break; + } + case (LCompilers::PythonCompiler::EvalResult::complex4) : { + if (verbose) std::cout << "Return type: complex" << std::endl; + if (verbose) section("Result:"); + std::cout << std::setprecision(8) << "(" << r.c32.re << ", " << r.c32.im << ")" << std::endl; + break; + } + case (LCompilers::PythonCompiler::EvalResult::complex8) : { + if (verbose) std::cout << "Return type: complex(8)" << std::endl; + if (verbose) section("Result:"); + std::cout << std::setprecision(17) << "(" << r.c64.re << ", " << r.c64.im << ")" << std::endl; + break; + } + case (LCompilers::PythonCompiler::EvalResult::statement) : { + if (verbose) { + std::cout << "Return type: none" << std::endl; + section("Result:"); + std::cout << "(statement)" << std::endl; + } + break; + } + case (LCompilers::PythonCompiler::EvalResult::none) : { + if (verbose) { + std::cout << "Return type: none" << std::endl; + section("Result:"); + std::cout << "(nothing to execute)" << std::endl; + } + break; + } + default : throw LCompilers::LCompilersException("Return type not supported"); + } + + code_string = ""; + std::cout << ">>> "; + } + return 0; +} + /* Compiles python to object file, if `to_jit` is false otherwise execute python code using llvm JIT @@ -1824,8 +1981,17 @@ int main(int argc, char *argv[]) } if (arg_files.size() == 0) { - std::cerr << "Interactive prompt is not implemented yet in LPython" << std::endl; +#ifdef HAVE_LFORTRAN_LLVM + lpython_pass_manager.parse_pass_arg(arg_pass, skip_pass); + lpython_pass_manager.use_default_passes(); + compiler_options.po.disable_main = true; + compiler_options.emit_debug_line_column = false; + compiler_options.generate_object_code = false; + return interactive_python_repl(lpython_pass_manager, compiler_options, arg_v); +#else + std::cerr << "Interactive prompt requires the LLVM backend to be enabled. Recompile with `WITH_LLVM=yes`." << std::endl; return 1; +#endif } // TODO: for now we ignore the other filenames, only handle diff --git a/src/libasr/diagnostics.h b/src/libasr/diagnostics.h index 63e1d832c3..002e66216f 100644 --- a/src/libasr/diagnostics.h +++ b/src/libasr/diagnostics.h @@ -127,6 +127,10 @@ struct Diagnostics { diagnostics.push_back(d); } + void clear() { + diagnostics.clear(); + } + void message_label(const std::string &message, const std::vector &locations, const std::string &error_label, diff --git a/src/lpython/parser/parser.h b/src/lpython/parser/parser.h index e9f6b92cba..2e1c18eda8 100644 --- a/src/lpython/parser/parser.h +++ b/src/lpython/parser/parser.h @@ -1,6 +1,7 @@ #ifndef LPYTHON_PARSER_PARSER_H #define LPYTHON_PARSER_PARSER_H +#include "lpython/python_ast.h" #include #include #include diff --git a/src/lpython/python_evaluator.cpp b/src/lpython/python_evaluator.cpp index 44075e0a84..f7314fcb32 100644 --- a/src/lpython/python_evaluator.cpp +++ b/src/lpython/python_evaluator.cpp @@ -1,10 +1,16 @@ #include #include +#include #include +#include +#include +#include +#include #include #include #include +#include #ifdef HAVE_LFORTRAN_LLVM #include @@ -26,16 +32,138 @@ PythonCompiler::PythonCompiler(CompilerOptions compiler_options) al{1024*1024}, #ifdef HAVE_LFORTRAN_LLVM e{std::make_unique()}, - eval_count{0}, #endif - compiler_options{compiler_options} -// symbol_table{nullptr} + eval_count{1}, + compiler_options{compiler_options}, + symbol_table{nullptr} { } PythonCompiler::~PythonCompiler() = default; +Result PythonCompiler::evaluate( +#ifdef HAVE_LFORTRAN_LLVM + const std::string &code_orig, bool verbose, LocationManager &lm, + LCompilers::PassManager& pass_manager, diag::Diagnostics &diagnostics +#else + const std::string &/*code_orig*/, bool /*verbose*/, + LocationManager &/*lm*/, LCompilers::PassManager& /*pass_manager*/, + diag::Diagnostics &/*diagnostics*/ +#endif + ) +{ +#ifdef HAVE_LFORTRAN_LLVM + EvalResult result; + result.type = EvalResult::none; + + // Src -> AST + Result res = get_ast2(code_orig, diagnostics); + LCompilers::LPython::AST::ast_t* ast; + if (res.ok) { + ast = res.result; + } else { + return res.error; + } + + if (verbose) { + result.ast = LCompilers::LPython::pickle_python(*ast, true, true); + } + + // AST -> ASR + Result res2 = get_asr3(*ast, diagnostics, lm, true); + ASR::TranslationUnit_t* asr; + if (res2.ok) { + asr = res2.result; + } else { + LCOMPILERS_ASSERT(diagnostics.has_error()) + return res2.error; + } + + if (verbose) { + result.asr = pickle(*asr, true, true, true); + } + + // ASR -> LLVM + std::string module_prefix = "__module___main___"; + std::string module_name = "__main__"; + std::string sym_name = module_name + "global_stmts_" + std::to_string(eval_count) + "__"; + run_fn = module_prefix + sym_name; + + Result> res3 = get_llvm3(*asr, + pass_manager, diagnostics, lm.files.back().in_filename); + std::unique_ptr m; + if (res3.ok) { + m = std::move(res3.result); + } else { + LCOMPILERS_ASSERT(diagnostics.has_error()) + return res3.error; + } + + if (verbose) { + result.llvm_ir = m->str(); + } + + bool call_run_fn = false; + if (m->get_return_type(run_fn) != "none") { + call_run_fn = true; + } + + e->add_module(std::move(m)); + if (call_run_fn) { + e->voidfn(run_fn); + } + + if (call_run_fn) { + ASR::down_cast(symbol_table->resolve_symbol(module_name))->m_symtab + ->erase_symbol(sym_name); + } + + eval_count++; + return result; +#else + throw LCompilersException("LLVM is not enabled"); +#endif +} + +Result PythonCompiler::get_ast2( + const std::string &code_orig, diag::Diagnostics &diagnostics) +{ + // Src -> AST + const std::string *code=&code_orig; + std::string tmp; + Result + res = LCompilers::LPython::parse(al, *code, 0, diagnostics); + if (res.ok) { + return (LCompilers::LPython::AST::ast_t*)res.result; + } else { + LCOMPILERS_ASSERT(diagnostics.has_error()) + return res.error; + } +} + +Result PythonCompiler::get_asr3( + LCompilers::LPython::AST::ast_t &ast, diag::Diagnostics &diagnostics, + LocationManager &lm, bool is_interactive) +{ + ASR::TranslationUnit_t* asr; + // AST -> ASR + if (symbol_table) { + symbol_table->mark_all_variables_external(al); + } + auto res = LCompilers::LPython::python_ast_to_asr(al, lm, symbol_table, ast, diagnostics, + compiler_options, true, "__main__", "", false, is_interactive ? eval_count : 0); + if (res.ok) { + asr = res.result; + } else { + LCOMPILERS_ASSERT(diagnostics.has_error()) + return res.error; + } + if (!symbol_table) symbol_table = asr->m_symtab; + + return asr; +} + Result> PythonCompiler::get_llvm3( #ifdef HAVE_LFORTRAN_LLVM ASR::TranslationUnit_t &asr, LCompilers::PassManager& lpm, @@ -47,9 +175,6 @@ Result> PythonCompiler::get_llvm3( ) { #ifdef HAVE_LFORTRAN_LLVM - eval_count++; - run_fn = "__lfortran_evaluate_" + std::to_string(eval_count); - if (compiler_options.emit_debug_info) { if (!compiler_options.emit_debug_line_column) { diagnostics.add(LCompilers::diag::Diagnostic( diff --git a/src/lpython/python_evaluator.h b/src/lpython/python_evaluator.h index b18b0aaf88..9cba5267ed 100644 --- a/src/lpython/python_evaluator.h +++ b/src/lpython/python_evaluator.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,17 @@ class PythonCompiler std::string llvm_ir; }; + Result evaluate( + const std::string &code_orig, bool verbose, LocationManager &lm, + LCompilers::PassManager& pass_manager, diag::Diagnostics &diagnostics); + + Result get_ast2( + const std::string &code_orig, diag::Diagnostics &diagnostics); + + Result get_asr3( + LCompilers::LPython::AST::ast_t &ast, diag::Diagnostics &diagnostics, + LocationManager &lm, bool is_interactive=false); + Result> get_llvm3(ASR::TranslationUnit_t &asr, LCompilers::PassManager& lpm, diag::Diagnostics &diagnostics, const std::string &infile); @@ -59,10 +71,10 @@ class PythonCompiler Allocator al; #ifdef HAVE_LFORTRAN_LLVM std::unique_ptr e; - int eval_count; #endif + int eval_count; CompilerOptions compiler_options; -// SymbolTable *symbol_table; + SymbolTable *symbol_table; std::string run_fn; }; diff --git a/src/lpython/semantics/python_ast_to_asr.cpp b/src/lpython/semantics/python_ast_to_asr.cpp index b8492e07a0..1538901fe0 100644 --- a/src/lpython/semantics/python_ast_to_asr.cpp +++ b/src/lpython/semantics/python_ast_to_asr.cpp @@ -4086,34 +4086,40 @@ class SymbolTableVisitor : public CommonVisitor { ASR::is_a(*ASR::down_cast(tmp0))); global_scope = current_scope; - ASR::Module_t* module_sym = nullptr; // Every module goes into a Module_t SymbolTable *parent_scope = current_scope; - current_scope = al.make_new(parent_scope); - - ASR::asr_t *tmp1 = ASR::make_Module_t(al, x.base.base.loc, - /* a_symtab */ current_scope, - /* a_name */ s2c(al, module_name), - nullptr, - 0, - false, false); + if (parent_scope->get_scope().find(module_name) == parent_scope->get_scope().end()) { + ASR::Module_t* module_sym = nullptr; + current_scope = al.make_new(parent_scope); + ASR::asr_t *tmp1 = ASR::make_Module_t(al, x.base.base.loc, + /* a_symtab */ current_scope, + /* a_name */ s2c(al, module_name), + nullptr, + 0, + false, false); + module_sym = ASR::down_cast(ASR::down_cast(tmp1)); + parent_scope->add_symbol(module_name, ASR::down_cast(tmp1)); + current_module_dependencies.reserve(al, 1); + for (size_t i=0; iget_scope().find(module_name) != parent_scope->get_scope().end()) { - throw SemanticError("Module '" + module_name + "' already defined", tmp1->loc); - } - module_sym = ASR::down_cast(ASR::down_cast(tmp1)); - parent_scope->add_symbol(module_name, ASR::down_cast(tmp1)); - current_module_dependencies.reserve(al, 1); - for (size_t i=0; im_dependencies = current_module_dependencies.p; + module_sym->n_dependencies = current_module_dependencies.size(); + if (!overload_defs.empty()) { + create_GenericProcedure(x.base.base.loc); + } + } else { + ASR::Module_t* module_sym = + ASR::down_cast(parent_scope->resolve_symbol(module_name)); + LCOMPILERS_ASSERT(module_sym != nullptr); + current_scope = module_sym->m_symtab; + for (size_t i=0; im_dependencies = current_module_dependencies.p; - module_sym->n_dependencies = current_module_dependencies.size(); - if (!overload_defs.empty()) { - create_GenericProcedure(x.base.base.loc); - } global_scope = nullptr; tmp = tmp0; } @@ -4809,12 +4815,13 @@ class BodyVisitor : public CommonVisitor { ASR::asr_t *asr; std::vector do_loop_variables; bool using_func_attr = false; + size_t eval_count; BodyVisitor(Allocator &al, LocationManager &lm, ASR::asr_t *unit, diag::Diagnostics &diagnostics, bool main_module, std::string module_name, std::map &ast_overload, - bool allow_implicit_casting_) + bool allow_implicit_casting_, size_t eval_count=0) : CommonVisitor(al, lm, nullptr, diagnostics, main_module, module_name, ast_overload, "", {}, allow_implicit_casting_), - asr{unit} + asr{unit}, eval_count{eval_count} {} // Transforms statements to a list of ASR statements @@ -4904,6 +4911,10 @@ class BodyVisitor : public CommonVisitor { unit->m_items = items.p; unit->n_items = items.size(); std::string func_name = module_name + "global_stmts"; + if (eval_count > 0) { + // In Interactive Shell. Update the func_name accordingly + func_name += "_" + std::to_string(eval_count) + "__"; + } // Wrap all the global statements into a Function LCompilers::PassOptions pass_options; pass_options.run_fun = func_name; @@ -8301,9 +8312,9 @@ Result body_visitor(Allocator &al, LocationManager &lm, diag::Diagnostics &diagnostics, ASR::asr_t *unit, bool main_module, std::string module_name, std::map &ast_overload, - bool allow_implicit_casting) + bool allow_implicit_casting, size_t eval_count) { - BodyVisitor b(al, lm, unit, diagnostics, main_module, module_name, ast_overload, allow_implicit_casting); + BodyVisitor b(al, lm, unit, diagnostics, main_module, module_name, ast_overload, allow_implicit_casting, eval_count); try { b.visit_Module(ast); } catch (const SemanticError &e) { @@ -8319,6 +8330,9 @@ Result body_visitor(Allocator &al, LocationManager &lm, } std::string get_parent_dir(const std::string &path) { + if (path == "") { + return std::filesystem::current_path().string(); + } int idx = path.size()-1; while (idx >= 0 && path[idx] != '/' && path[idx] != '\\') idx--; if (idx == -1) { @@ -8329,20 +8343,21 @@ std::string get_parent_dir(const std::string &path) { Result python_ast_to_asr(Allocator &al, LocationManager &lm, SymbolTable* symtab, AST::ast_t &ast, diag::Diagnostics &diagnostics, CompilerOptions &compiler_options, - bool main_module, std::string module_name, std::string file_path, bool allow_implicit_casting) + bool main_module, std::string module_name, std::string file_path, bool allow_implicit_casting, size_t eval_count) { std::map ast_overload; std::string parent_dir = get_parent_dir(file_path); - AST::Module_t *ast_m = AST::down_cast2(&ast); ASR::asr_t *unit; - auto res = symbol_table_visitor(al, lm, symtab, *ast_m, diagnostics, main_module, module_name, - ast_overload, parent_dir, compiler_options.import_paths, allow_implicit_casting); + AST::Module_t *ast_m = AST::down_cast2(&ast); + Result res = symbol_table_visitor(al, lm, symtab, *ast_m, diagnostics, main_module, module_name, + ast_overload, parent_dir, compiler_options.import_paths, allow_implicit_casting); if (res.ok) { unit = res.result; } else { return res.error; } + ASR::TranslationUnit_t *tu = ASR::down_cast2(unit); if (compiler_options.po.dump_all_passes) { std::ofstream outfile ("pass_00_initial_asr_01.clj"); @@ -8368,7 +8383,7 @@ Result python_ast_to_asr(Allocator &al, LocationManager if (!compiler_options.symtab_only) { auto res2 = body_visitor(al, lm, *ast_m, diagnostics, unit, main_module, module_name, - ast_overload, allow_implicit_casting); + ast_overload, allow_implicit_casting, eval_count); if (res2.ok) { tu = res2.result; } else { diff --git a/src/lpython/semantics/python_ast_to_asr.h b/src/lpython/semantics/python_ast_to_asr.h index 8270846c32..b53a2137b2 100644 --- a/src/lpython/semantics/python_ast_to_asr.h +++ b/src/lpython/semantics/python_ast_to_asr.h @@ -8,7 +8,7 @@ namespace LCompilers::LPython { Result python_ast_to_asr(Allocator &al, LocationManager &lm, SymbolTable* symtab, LPython::AST::ast_t &ast, diag::Diagnostics &diagnostics, CompilerOptions &compiler_options, - bool main_module, std::string module_name, std::string file_path, bool allow_implicit_casting=false); + bool main_module, std::string module_name, std::string file_path, bool allow_implicit_casting=false, size_t eval_count=0); int save_pyc_files(const ASR::TranslationUnit_t &u, std::string infile);