From 8739bcccd14301719b056ed8c28374df217c47ad Mon Sep 17 00:00:00 2001 From: Vipul Cariappa Date: Tue, 4 Jun 2024 07:56:21 +0530 Subject: [PATCH 1/2] REPL `str` support --- src/bin/lpython.cpp | 6 ++ src/libasr/codegen/evaluator.cpp | 8 +++ src/libasr/codegen/evaluator.h | 1 + src/libasr/pass/global_stmts.cpp | 3 +- src/libasr/runtime/lfortran_intrinsics.c | 4 ++ src/lpython/python_evaluator.cpp | 14 +++- src/lpython/python_evaluator.h | 2 + src/lpython/tests/test_llvm.cpp | 88 ++++++++++++++++++++++++ 8 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/bin/lpython.cpp b/src/bin/lpython.cpp index a7241644a1..6e036657c8 100644 --- a/src/bin/lpython.cpp +++ b/src/bin/lpython.cpp @@ -960,6 +960,12 @@ int interactive_python_repl( std::cout << std::setprecision(17) << "(" << r.c64.re << ", " << r.c64.im << ")" << std::endl; break; } + case (LCompilers::PythonCompiler::EvalResult::string) : { + if (verbose) std::cout << "Return type: str" << std::endl; + if (verbose) section("Result:"); + std::cout << (r.str == nullptr ? "" : r.str) << std::endl; + break; + } case (LCompilers::PythonCompiler::EvalResult::statement) : { if (verbose) { std::cout << "Return type: none" << std::endl; diff --git a/src/libasr/codegen/evaluator.cpp b/src/libasr/codegen/evaluator.cpp index 2e00aec308..12f022100a 100644 --- a/src/libasr/codegen/evaluator.cpp +++ b/src/libasr/codegen/evaluator.cpp @@ -110,6 +110,8 @@ std::string LLVMModule::get_return_type(const std::string &fn_name) return "integer4"; } else if (type->isIntegerTy(64)) { return "integer8"; + } else if (type->isPointerTy() && type->getPointerElementType()->isIntegerTy(8)) { + return "integer1ptr"; } else if (type->isStructTy()) { llvm::StructType *st = llvm::cast(type); if (st->hasName()) { @@ -273,6 +275,12 @@ intptr_t LLVMEvaluator::get_symbol_address(const std::string &name) { return (intptr_t)cantFail(std::move(addr0)); } +char *LLVMEvaluator::strfn(const std::string &name) { + intptr_t addr = get_symbol_address(name); + char *(*f)() = (char *(*)())addr; + return f(); +} + int8_t LLVMEvaluator::int8fn(const std::string &name) { intptr_t addr = get_symbol_address(name); int8_t (*f)() = (int8_t (*)())addr; diff --git a/src/libasr/codegen/evaluator.h b/src/libasr/codegen/evaluator.h index 2fc6a50984..65fc053527 100644 --- a/src/libasr/codegen/evaluator.h +++ b/src/libasr/codegen/evaluator.h @@ -52,6 +52,7 @@ class LLVMEvaluator void add_module(std::unique_ptr mod); void add_module(std::unique_ptr m); intptr_t get_symbol_address(const std::string &name); + char *strfn(const std::string &name); int8_t int8fn(const std::string &name); int16_t int16fn(const std::string &name); int32_t int32fn(const std::string &name); diff --git a/src/libasr/pass/global_stmts.cpp b/src/libasr/pass/global_stmts.cpp index f962103293..dd0b91790f 100644 --- a/src/libasr/pass/global_stmts.cpp +++ b/src/libasr/pass/global_stmts.cpp @@ -93,7 +93,8 @@ void pass_wrap_global_stmts(Allocator &al, fn_scope->add_symbol(std::string(var_name), down_cast(return_var)); target = return_var_ref; idx++; - } else if (ASRUtils::expr_type(value)->type == ASR::ttypeType::Complex) { + } else if ((ASRUtils::expr_type(value)->type == ASR::ttypeType::Complex) || + (ASRUtils::expr_type(value)->type == ASR::ttypeType::Character)) { s.from_str(al, fn_name_s + std::to_string(idx)); var_name = s.c_str(al); type = ASRUtils::expr_type(value); diff --git a/src/libasr/runtime/lfortran_intrinsics.c b/src/libasr/runtime/lfortran_intrinsics.c index d6ea899619..7c09965c09 100644 --- a/src/libasr/runtime/lfortran_intrinsics.c +++ b/src/libasr/runtime/lfortran_intrinsics.c @@ -1972,6 +1972,10 @@ LFORTRAN_API void _lfortran_strcpy(char** x, char *y, int8_t free_target) // *x = (char*) malloc((strlen(y) + 1) * sizeof(char)); // _lfortran_string_init(strlen(y) + 1, *x); } + if (y == NULL) { + *x = NULL; + return; + } // if( *x == NULL ) { *x = (char*) malloc((strlen(y) + 1) * sizeof(char)); _lfortran_string_init(strlen(y) + 1, *x); diff --git a/src/lpython/python_evaluator.cpp b/src/lpython/python_evaluator.cpp index a717667fe3..578c196c33 100644 --- a/src/lpython/python_evaluator.cpp +++ b/src/lpython/python_evaluator.cpp @@ -127,7 +127,18 @@ Result PythonCompiler::evaluate( e->add_module(std::move(m)); if (call_run_fn) { - if (return_type == "integer1") { + if (return_type == "integer1ptr") { + ASR::symbol_t *fn = ASR::down_cast(symbol_table->resolve_symbol(module_name)) + ->m_symtab->get_symbol(run_fn); + LCOMPILERS_ASSERT(fn) + if (ASRUtils::get_FunctionType(fn)->m_return_var_type->type == ASR::ttypeType::Character) { + char *r = e->strfn(run_fn); + result.type = EvalResult::string; + result.str = r; + } else { + goto raise_error; + } + } else if (return_type == "integer1") { ASR::symbol_t *fn = ASR::down_cast(symbol_table->resolve_symbol(module_name)) ->m_symtab->get_symbol(run_fn); LCOMPILERS_ASSERT(fn) @@ -203,6 +214,7 @@ Result PythonCompiler::evaluate( } else if (return_type == "none") { result.type = EvalResult::none; } else { +raise_error: throw LCompilersException("FortranEvaluator::evaluate(): Return type not supported"); } } diff --git a/src/lpython/python_evaluator.h b/src/lpython/python_evaluator.h index 05e0e61023..882495813d 100644 --- a/src/lpython/python_evaluator.h +++ b/src/lpython/python_evaluator.h @@ -49,6 +49,7 @@ class PythonCompiler real8, complex4, complex8, + string, statement, none } type; @@ -59,6 +60,7 @@ class PythonCompiler uint64_t u64; float f32; double f64; + char *str; struct {float re, im;} c32; struct {double re, im;} c64; }; diff --git a/src/lpython/tests/test_llvm.cpp b/src/lpython/tests/test_llvm.cpp index 29bb414772..a4e9fa545e 100644 --- a/src/lpython/tests/test_llvm.cpp +++ b/src/lpython/tests/test_llvm.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -1270,3 +1271,90 @@ TEST_CASE("PythonCompiler u16 declaration") { CHECK(r.result.type == PythonCompiler::EvalResult::unsignedInteger2); CHECK(r.result.u32 == 45); } + +TEST_CASE("PythonCompiler string 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("\"My String\""); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::string); + CHECK(std::strcmp(r.result.str, "My String") == 0); + + r = e.evaluate2("\"s1\" + \" \" + \"s2\""); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::string); + CHECK(std::strcmp(r.result.str, "s1 s2") == 0); +} + +TEST_CASE("PythonCompiler string 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("s: str"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::none); + + r = e.evaluate2("s"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::string); + CHECK(r.result.str == nullptr); + + r = e.evaluate2(R"( +s = "" +i: i32 = 0 +for i in range(10): + s += str(i) +)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::statement); + + r = e.evaluate2("s"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::string); + CHECK(std::strcmp(r.result.str, "0123456789") == 0); +} + +TEST_CASE("PythonCompiler string 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(R"( +def my_concat(x: str, y: str) -> str: + return x + " " + y +)"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::none); + + r = e.evaluate2("s: str = \"0123456789\""); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::none); + + r = e.evaluate2("my_concat(s, \"NUM\")"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::string); + CHECK(std::strcmp(r.result.str, "0123456789 NUM") == 0); + + r = e.evaluate2("my_concat(\"Python\", \"REPL\")"); + CHECK(r.ok); + CHECK(r.result.type == PythonCompiler::EvalResult::string); + CHECK(std::strcmp(r.result.str, "Python REPL") == 0); +} From 6dc5851bc098b2160ad78125e3992aade0d58763 Mon Sep 17 00:00:00 2001 From: Vipul Cariappa Date: Thu, 6 Jun 2024 12:01:24 +0530 Subject: [PATCH 2/2] removing goto --- src/lpython/python_evaluator.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lpython/python_evaluator.cpp b/src/lpython/python_evaluator.cpp index 578c196c33..19661c47c9 100644 --- a/src/lpython/python_evaluator.cpp +++ b/src/lpython/python_evaluator.cpp @@ -136,7 +136,7 @@ Result PythonCompiler::evaluate( result.type = EvalResult::string; result.str = r; } else { - goto raise_error; + throw LCompilersException("PythonCompiler::evaluate(): Return type not supported"); } } else if (return_type == "integer1") { ASR::symbol_t *fn = ASR::down_cast(symbol_table->resolve_symbol(module_name)) @@ -214,8 +214,7 @@ Result PythonCompiler::evaluate( } else if (return_type == "none") { result.type = EvalResult::none; } else { -raise_error: - throw LCompilersException("FortranEvaluator::evaluate(): Return type not supported"); + throw LCompilersException("PythonCompiler::evaluate(): Return type not supported"); } }