Skip to content

Commit

Permalink
Underscore: _ variable in REPL (#2755)
Browse files Browse the repository at this point in the history
* support to print lists in REPL

* `_` variable in REPL

* fix

* update according to code review suggestions

* fix reference test (CI fail)
  • Loading branch information
Vipul-Cariappa authored Jul 9, 2024
1 parent ba07d7b commit 8f3393d
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 6 deletions.
28 changes: 26 additions & 2 deletions src/libasr/codegen/asr_to_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
std::map<llvm::Value*, llvm::Value*> strings_to_be_allocated; // (array, size)
Vec<llvm::Value*> 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},
Expand All @@ -231,7 +233,8 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
set_api_sc(std::make_unique<LLVMSetSeparateChaining>(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();
Expand Down Expand Up @@ -2709,11 +2712,20 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
}

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
Expand Down Expand Up @@ -6966,6 +6978,12 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>

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];
Expand Down Expand Up @@ -8472,6 +8490,11 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
if (ASR::is_a<ASR::Variable_t>(*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) ) {
Expand Down Expand Up @@ -10085,7 +10108,7 @@ Result<std::unique_ptr<LLVMModule>> 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
Expand All @@ -10102,6 +10125,7 @@ Result<std::unique_ptr<LLVMModule>> 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;
Expand Down
1 change: 1 addition & 0 deletions src/libasr/codegen/asr_to_llvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/libasr/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 22 additions & 4 deletions src/lpython/python_evaluator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ PythonCompiler::PythonCompiler(CompilerOptions compiler_options)
e{std::make_unique<LLVMEvaluator>()},
#endif
eval_count{1},
symbol_table{nullptr}
symbol_table{nullptr},
global_underscore_name{""}
{
}

Expand Down Expand Up @@ -122,13 +123,18 @@ Result<PythonCompiler::EvalResult> 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") {
call_run_fn = true;
}

ASR::symbol_t *global_underscore_sym = symbol_table->get_symbol("_" + run_fn);
ASR::symbol_t *global_underscore_sym = symbol_table->get_symbol(global_underscore_name);
if ((return_type == "struct") && (global_underscore_sym)) {
// we compute the offsets of the struct's attribute here
// we will be using it later in aggregate_type_to_string to print the struct
Expand Down Expand Up @@ -233,7 +239,7 @@ Result<PythonCompiler::EvalResult> PythonCompiler::evaluate(
} else if (return_type == "struct") {
e->execfn<void>(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;
Expand All @@ -252,6 +258,18 @@ Result<PythonCompiler::EvalResult> PythonCompiler::evaluate(
ASR::down_cast<ASR::Module_t>(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<ASR::Variable_t>(global_underscore_symbol);
ASR::Variable_t *b = al.make_new<ASR::Variable_t>();
*b = *a;
Str s;
s.from_str(al, "_");
b->m_name = s.c_str(al);
symbol_table->add_symbol("_", ASR::down_cast<ASR::symbol_t>((ASR::asr_t*)b));
}

eval_count++;
return result;
Expand Down Expand Up @@ -429,7 +447,7 @@ Result<std::unique_ptr<LLVMModule>> PythonCompiler::get_llvm3(
Result<std::unique_ptr<LCompilers::LLVMModule>> 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 {
Expand Down
1 change: 1 addition & 0 deletions src/lpython/python_evaluator.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ class PythonCompiler
int eval_count;
SymbolTable *symbol_table;
std::string run_fn;
std::string global_underscore_name;
};

} // namespace LCompilers
Expand Down
146 changes: 146 additions & 0 deletions src/lpython/tests/test_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<PythonCompiler::EvalResult>

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<PythonCompiler::EvalResult>

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<PythonCompiler::EvalResult>

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<PythonCompiler::EvalResult>

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;
Expand Down

0 comments on commit 8f3393d

Please sign in to comment.