diff --git a/src/libasr/codegen/asr_to_llvm.cpp b/src/libasr/codegen/asr_to_llvm.cpp index 0d92f7377b..d442898489 100644 --- a/src/libasr/codegen/asr_to_llvm.cpp +++ b/src/libasr/codegen/asr_to_llvm.cpp @@ -205,6 +205,8 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor std::map strings_to_be_allocated; // (array, size) Vec strings_to_be_deallocated; + uint32_t global_underscore_hash; // used in interactive mode + ASRToLLVMVisitor(Allocator &al, llvm::LLVMContext &context, std::string infile, CompilerOptions &compiler_options_, diag::Diagnostics &diagnostics) : diag{diagnostics}, @@ -231,7 +233,8 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor set_api_sc(std::make_unique(context, llvm_utils.get(), builder.get())), arr_descr(LLVMArrUtils::Descriptor::get_descriptor(context, builder.get(), llvm_utils.get(), - LLVMArrUtils::DESCR_TYPE::_SimpleCMODescriptor, compiler_options_, heap_arrays)) + LLVMArrUtils::DESCR_TYPE::_SimpleCMODescriptor, compiler_options_, heap_arrays)), + global_underscore_hash(0) { llvm_utils->tuple_api = tuple_api.get(); llvm_utils->list_api = list_api.get(); @@ -2709,11 +2712,20 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor } void visit_Variable(const ASR::Variable_t &x) { + if ((compiler_options.interactive) && + (std::strcmp(x.m_name, "_") == 0) && + (x.m_abi == ASR::abiType::Interactive)) { + return; + } if (x.m_value && x.m_storage == ASR::storage_typeType::Parameter) { this->visit_expr_wrapper(x.m_value, true); return; } uint32_t h = get_hash((ASR::asr_t*)&x); + if ((compiler_options.interactive) && + (std::strcmp(x.m_name, compiler_options.po.global_underscore.c_str()) == 0)) { + global_underscore_hash = h; + } // This happens at global scope, so the intent can only be either local // (global variable declared/initialized in this translation unit), or // external (global variable not declared/initialized in this @@ -6966,6 +6978,12 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor inline void fetch_val(ASR::Variable_t* x) { uint32_t x_h = get_hash((ASR::asr_t*)x); + if ((compiler_options.interactive) && + (std::strcmp(x->m_name, "_") == 0) && + (x->m_abi == ASR::abiType::Interactive) && + (llvm_symtab.find(x_h) == llvm_symtab.end())) { + x_h = global_underscore_hash; + } llvm::Value* x_v; LCOMPILERS_ASSERT(llvm_symtab.find(x_h) != llvm_symtab.end()); x_v = llvm_symtab[x_h]; @@ -8472,6 +8490,11 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor if (ASR::is_a(*var_sym)) { ASR::Variable_t *arg = EXPR2VAR(x.m_args[i].m_value); uint32_t h = get_hash((ASR::asr_t*)arg); + if ((compiler_options.interactive) && + (std::strcmp(arg->m_name, "_") == 0) && + (arg->m_abi == ASR::abiType::Interactive)) { + h = global_underscore_hash; + } if (llvm_symtab.find(h) != llvm_symtab.end()) { tmp = llvm_symtab[h]; if( !ASRUtils::is_array(arg->m_type) ) { @@ -10085,7 +10108,7 @@ Result> asr_to_llvm(ASR::TranslationUnit_t &asr, diag::Diagnostics &diagnostics, llvm::LLVMContext &context, Allocator &al, LCompilers::PassManager& pass_manager, - CompilerOptions &co, const std::string &run_fn, + CompilerOptions &co, const std::string &run_fn, const std::string &global_underscore, const std::string &infile) { #if LLVM_VERSION_MAJOR >= 15 @@ -10102,6 +10125,7 @@ Result> asr_to_llvm(ASR::TranslationUnit_t &asr, ASRUtils::IntrinsicElementalFunctions::SignFromValue)); co.po.run_fun = run_fn; + co.po.global_underscore = global_underscore; co.po.always_run = false; co.po.skip_optimization_func_instantiation = skip_optimization_func_instantiation; pass_manager.rtlib = co.rtlib; diff --git a/src/libasr/codegen/asr_to_llvm.h b/src/libasr/codegen/asr_to_llvm.h index a1e911e0c2..0b56ec8ceb 100644 --- a/src/libasr/codegen/asr_to_llvm.h +++ b/src/libasr/codegen/asr_to_llvm.h @@ -13,6 +13,7 @@ namespace LCompilers { LCompilers::PassManager& pass_manager, CompilerOptions &compiler_options, const std::string &run_fn, + const std::string &global_underscore, const std::string &infile); } // namespace LCompilers diff --git a/src/libasr/utils.h b/src/libasr/utils.h index 4a51127987..9ff8b0f963 100644 --- a/src/libasr/utils.h +++ b/src/libasr/utils.h @@ -31,6 +31,7 @@ struct PassOptions { int default_integer_kind = 4; std::string run_fun; // for global_stmts pass + std::string global_underscore; // for global_stmts pass // TODO: Convert to std::filesystem::path (also change find_and_load_module()) std::string runtime_library_dir; bool always_run = false; // for unused_functions pass diff --git a/src/lpython/python_evaluator.cpp b/src/lpython/python_evaluator.cpp index a5176db7be..b056d81711 100644 --- a/src/lpython/python_evaluator.cpp +++ b/src/lpython/python_evaluator.cpp @@ -39,7 +39,8 @@ PythonCompiler::PythonCompiler(CompilerOptions compiler_options) e{std::make_unique()}, #endif eval_count{1}, - symbol_table{nullptr} + symbol_table{nullptr}, + global_underscore_name{""} { } @@ -122,6 +123,11 @@ Result PythonCompiler::evaluate( result.llvm_ir = m->str(); } + ASR::symbol_t *global_underscore_symbol = symbol_table->get_symbol("_" + run_fn); + if (global_underscore_symbol) { + global_underscore_name = "_" + run_fn; + } + bool call_run_fn = false; std::string return_type = m->get_return_type(run_fn); if (return_type != "none") { @@ -233,7 +239,7 @@ Result PythonCompiler::evaluate( } else if (return_type == "struct") { e->execfn(run_fn); if (global_underscore_sym) { - void *r = (void*)e->get_symbol_address("_" + run_fn); + void *r = (void*)e->get_symbol_address(global_underscore_name); LCOMPILERS_ASSERT(r) result.structure.structure = r; result.type = EvalResult::struct_type; @@ -252,6 +258,18 @@ Result PythonCompiler::evaluate( ASR::down_cast(symbol_table->resolve_symbol(module_name))->m_symtab ->erase_symbol(run_fn); } + if (global_underscore_symbol) { + if (symbol_table->resolve_symbol("_")) { + symbol_table->erase_symbol("_"); + } + ASR::Variable_t *a = ASR::down_cast(global_underscore_symbol); + ASR::Variable_t *b = al.make_new(); + *b = *a; + Str s; + s.from_str_view("_"); + b->m_name = s.c_str(al); + symbol_table->add_symbol("_", ASR::down_cast((ASR::asr_t*)b)); + } eval_count++; return result; @@ -429,7 +447,7 @@ Result> PythonCompiler::get_llvm3( Result> res = asr_to_llvm(asr, diagnostics, e->get_context(), al, lpm, compiler_options, - run_fn, infile); + run_fn, global_underscore_name, infile); if (res.ok) { m = std::move(res.result); } else { diff --git a/src/lpython/python_evaluator.h b/src/lpython/python_evaluator.h index 60c5c4d7aa..7bf3347fe7 100644 --- a/src/lpython/python_evaluator.h +++ b/src/lpython/python_evaluator.h @@ -137,6 +137,7 @@ class PythonCompiler int eval_count; SymbolTable *symbol_table; std::string run_fn; + std::string global_underscore_name; }; } // namespace LCompilers diff --git a/src/lpython/tests/test_llvm.cpp b/src/lpython/tests/test_llvm.cpp index 3042140d24..22a185bd1d 100644 --- a/src/lpython/tests/test_llvm.cpp +++ b/src/lpython/tests/test_llvm.cpp @@ -1506,6 +1506,152 @@ TEST_CASE("PythonCompiler lists") { CHECK(e.aggregate_type_to_string(r.result) == "[\"lfortran\", \"lpython\", \"lc\"]"); } +TEST_CASE("PythonCompiler underscore 1") { + CompilerOptions cu; + cu.po.disable_main = true; + cu.emit_debug_line_column = false; + cu.generate_object_code = false; + cu.interactive = true; + cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir(); + PythonCompiler e(cu); + LCompilers::Result + + r = e.evaluate2("2"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::integer4); + CHECK(r.result.i32 == 2); + + r = e.evaluate2("_"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::integer4); + CHECK(r.result.i32 == 2); + + r = e.evaluate2("_ + 4"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::integer4); + CHECK(r.result.i32 == 6); + + r = e.evaluate2("_ * 2"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::integer4); + CHECK(r.result.i32 == 12); +} + +TEST_CASE("PythonCompiler underscore 2") { + CompilerOptions cu; + cu.po.disable_main = true; + cu.emit_debug_line_column = false; + cu.generate_object_code = false; + cu.interactive = true; + cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir(); + PythonCompiler e(cu); + LCompilers::Result + + r = e.evaluate2("2"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::integer4); + CHECK(r.result.i32 == 2); + r = e.evaluate2("_"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::integer4); + CHECK(r.result.i32 == 2); + + r = e.evaluate2("2.5"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::real8); + CHECK(r.result.f64 == 2.5); + r = e.evaluate2("_"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::real8); + CHECK(r.result.f64 == 2.5); + + r = e.evaluate2("\"lpython\""); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::string); + CHECK(std::strcmp(r.result.str, "lpython") == 0); + r = e.evaluate2("_"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::string); + CHECK(std::strcmp(r.result.str, "lpython") == 0); + + r = e.evaluate2("[1, 2, 3]"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "list[i32]"); + CHECK(e.aggregate_type_to_string(r.result) == "[1, 2, 3]"); + r = e.evaluate2("_"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "list[i32]"); + CHECK(e.aggregate_type_to_string(r.result) == "[1, 2, 3]"); +} + +TEST_CASE("PythonCompiler underscore 3") { + CompilerOptions cu; + cu.po.disable_main = true; + cu.emit_debug_line_column = false; + cu.generate_object_code = false; + cu.interactive = true; + cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir(); + PythonCompiler e(cu); + LCompilers::Result + + r = e.evaluate2("[1, 2, 3]"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "list[i32]"); + CHECK(e.aggregate_type_to_string(r.result) == "[1, 2, 3]"); + + r = e.evaluate2("_ + [1, 2, 3]"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "list[i32]"); + CHECK(e.aggregate_type_to_string(r.result) == "[1, 2, 3, 1, 2, 3]"); + + r = e.evaluate2(R"( +_.append(5) +x: list[i32] = _ +x +)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "list[i32]"); + CHECK(e.aggregate_type_to_string(r.result) == "[1, 2, 3, 1, 2, 3, 5]"); +} + +TEST_CASE("PythonCompiler underscore 4") { + CompilerOptions cu; + cu.po.disable_main = true; + cu.emit_debug_line_column = false; + cu.generate_object_code = false; + cu.interactive = true; + cu.po.runtime_library_dir = LCompilers::LPython::get_runtime_library_dir(); + PythonCompiler e(cu); + LCompilers::Result + + r = e.evaluate2("[1, 2, 3]"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "list[i32]"); + CHECK(e.aggregate_type_to_string(r.result) == "[1, 2, 3]"); + + r = e.evaluate2("_"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "list[i32]"); + CHECK(e.aggregate_type_to_string(r.result) == "[1, 2, 3]"); + + r = e.evaluate2("f: bool = False"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::none); + + r = e.evaluate2("_"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::struct_type); + CHECK(LCompilers::ASRUtils::get_type_code(r.result.structure.ttype) == "list[i32]"); + CHECK(e.aggregate_type_to_string(r.result) == "[1, 2, 3]"); +} + TEST_CASE("PythonCompiler asr verify 1") { CompilerOptions cu; cu.po.disable_main = true;