diff --git a/integration_tests/CMakeLists.txt b/integration_tests/CMakeLists.txt index 19459b94e8..503676c255 100644 --- a/integration_tests/CMakeLists.txt +++ b/integration_tests/CMakeLists.txt @@ -158,6 +158,9 @@ RUN(NAME test_builtin_len LABELS cpython llvm) RUN(NAME test_builtin_float LABELS cpython llvm) RUN(NAME test_builtin_str_02 LABELS cpython llvm) RUN(NAME test_builtin_round LABELS cpython llvm) +RUN(NAME test_builtin_capitalize LABELS cpython) +RUN(NAME test_builtin_upper LABELS cpython) +RUN(NAME test_builtin_lower LABELS cpython) RUN(NAME test_math1 LABELS cpython llvm) RUN(NAME test_math_02 LABELS cpython llvm) RUN(NAME test_c_interop_01 LABELS cpython llvm c) diff --git a/integration_tests/test_builtin_capitalize.py b/integration_tests/test_builtin_capitalize.py new file mode 100644 index 0000000000..3e729a5ee9 --- /dev/null +++ b/integration_tests/test_builtin_capitalize.py @@ -0,0 +1,12 @@ +from lpython_builtin import (capitalize) +from ltypes import i32, f64, f32, i64, c32, c64 + +def test_capitalize(): + i: str + i = capitalize("lpyThon") + assert i == "Lpython" + i: str + i = capitalize("deVeLoPmEnT") + assert i == "Development" + +test_capitalize() diff --git a/integration_tests/test_builtin_lower.py b/integration_tests/test_builtin_lower.py new file mode 100644 index 0000000000..b36843f4ac --- /dev/null +++ b/integration_tests/test_builtin_lower.py @@ -0,0 +1,9 @@ +from lpython_builtin import (lower) +from ltypes import i32, f64, f32, i64, c32, c64 + +def test_lower(): + i: str + i = lower("CoMpIlEr") + assert i == "compiler" + +test_lower() diff --git a/integration_tests/test_builtin_upper.py b/integration_tests/test_builtin_upper.py new file mode 100644 index 0000000000..542bafb845 --- /dev/null +++ b/integration_tests/test_builtin_upper.py @@ -0,0 +1,9 @@ +from lpython_builtin import (upper) +from ltypes import i32, f64, f32, i64, c32, c64 + +def test_upper(): + i: str + i = upper("lpython") + assert i == "LPYTHON" + +test_upper() diff --git a/src/lpython/semantics/python_comptime_eval.h b/src/lpython/semantics/python_comptime_eval.h index 5cf61ecd5c..3197ec6710 100644 --- a/src/lpython/semantics/python_comptime_eval.h +++ b/src/lpython/semantics/python_comptime_eval.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -34,6 +35,9 @@ struct PythonIntrinsicProcedures { {"str", {m_builtin, &eval_str}}, {"chr", {m_builtin, &eval_chr}}, {"ord", {m_builtin, &eval_ord}}, + {"upper", {m_builtin, &eval_upper}}, + {"lower", {m_builtin, &eval_lower}}, + {"capitalize", {m_builtin, &eval_capitalize}}, // {"len", {m_builtin, &eval_len}}, {"pow", {m_builtin, &eval_pow}}, // {"int", {m_builtin, &eval_int}}, @@ -163,6 +167,77 @@ struct PythonIntrinsicProcedures { ASRUtils::type_to_str_python(arg_type) + "'", loc); } } + static ASR::expr_t *eval_upper(Allocator &al, const Location &loc, Vec &args) { + LFORTRAN_ASSERT(ASRUtils::all_args_evaluated(args)); + if (args.size() != 1) { + throw SemanticError("upper() takes exactly one argument (" + + std::to_string(args.size()) + " given)", loc); + } + ASR::ttype_t* str_type = ASRUtils::expr_type(args[0]); + ASR::expr_t *arg = args[0]; + ASR::ttype_t* arg_type = ASRUtils::expr_type(arg); + if (ASRUtils::is_character(*arg_type)) { + char* c = ASR::down_cast(arg)->m_s; + std::string s = std::string(c); + std::transform(s.begin(), s.end(),s.begin(), ::toupper); + return ASR::down_cast(ASR::make_StringConstant_t(al, loc, s2c(al, s), str_type)); + } + else { + throw SemanticError("upper() only works on strings", loc); + } + } + + static ASR::expr_t *eval_lower(Allocator &al, const Location &loc, Vec &args) { + LFORTRAN_ASSERT(ASRUtils::all_args_evaluated(args)); + if (args.size() != 1) { + throw SemanticError("lower() takes exactly one argument (" + + std::to_string(args.size()) + " given)", loc); + } + ASR::ttype_t* str_type = ASRUtils::expr_type(args[0]); + ASR::expr_t *arg = args[0]; + ASR::ttype_t* arg_type = ASRUtils::expr_type(arg); + + if (ASRUtils::is_character(*arg_type)) { + char* c = ASR::down_cast(arg)->m_s; + std::string s = std::string(c); + // std::transform(s.begin(), s.end(),s.begin(), ::tolower); + for (auto & x: s) x = tolower(x); + // boost::to_upper(s); + // std::string newstr = boost::to_upper_copy("Hello World"); + + + return ASR::down_cast(ASR::make_StringConstant_t(al, loc, s2c(al, s), str_type)); + } + else { + throw SemanticError("lower() only works on strings", loc); + } + } + static ASR::expr_t *eval_capitalize(Allocator &al, const Location &loc, Vec &args) { + LFORTRAN_ASSERT(ASRUtils::all_args_evaluated(args)); + if (args.size() != 1) { + throw SemanticError("capitalize() takes exactly one argument (" + + std::to_string(args.size()) + " given)", loc); + } + ASR::ttype_t* str_type = ASRUtils::expr_type(args[0]); + ASR::expr_t *arg = args[0]; + ASR::ttype_t* arg_type = ASRUtils::expr_type(arg); + + if (ASRUtils::is_character(*arg_type)) { + char* c = ASR::down_cast(arg)->m_s; + std::string s = std::string(c); + s[0] = std::toupper(s[0]); + std::string sub = s.substr(1); + for (auto & x: sub) x = tolower(x); + s = s[0] + sub; + + // std::transform(s.begin()+1, s.end(), s.begin()+1, std::tolower); + return ASR::down_cast(ASR::make_StringConstant_t(al, loc, s2c(al, s), str_type)); + } + else { + throw SemanticError("capitalize() only works on strings", loc); + } + } + static ASR::expr_t *eval_chr(Allocator &al, const Location &loc, Vec &args) { LFORTRAN_ASSERT(ASRUtils::all_args_evaluated(args)); diff --git a/src/runtime/lpython_builtin.py b/src/runtime/lpython_builtin.py index 87a9e1ab37..c171603413 100644 --- a/src/runtime/lpython_builtin.py +++ b/src/runtime/lpython_builtin.py @@ -145,6 +145,93 @@ def str(x: i32) -> str: result += rev_result[pos] return result + +def capitalize(s: str) -> str: + """ + Return a copy of the string with its first character capitalized and the rest lowercased. + """ + result: str + result = upper(s[0]) + lower(s[1:]) + return result + +def upper(s: str) -> str: + result : str + char : str + i: i64 + for i in range(len(s)): + char = s[i] + if ord(char) >= 97 and ord(char) <=122 : + result += chr(ord(char) - 32) + else : + result += char + return result + +def lower(s: str) -> str: + result : str + # result = '' + char : str + i: i64 + for i in range(len(s)): + char = s[i] + if ord(char) >= 65 and ord(char) <=90 : + result += chr(ord(char) + 32) + else : + result += char + return result + + +#: bool() as a generic procedure. +#: supported types for argument: +#: i8, i16, i32, i64, f32, f64, bool +@overload +def bool(x: i32) -> bool: + """ + Return False when the argument `x` is 0, True otherwise. + """ + return x != 0 + +@overload +def bool(x: i64) -> bool: + return x != 0 + +@overload +def bool(x: i8) -> bool: + return x != 0 + +@overload +def bool(x: i16) -> bool: + return x != 0 + +@overload +def bool(f: f32) -> bool: + return f != 0.0 + +@overload +def bool(f: f64) -> bool: + """ + Return False when the argument `x` is 0.0, True otherwise. + """ + return f != 0.0 + +@overload +def bool(s: str) -> bool: + """ + Return False when the argument `s` is an empty string, True otherwise. + """ + return len(s) > 0 + +@overload +def bool(b: bool) -> bool: + return b + +@overload +def bool(c: c32) -> bool: + return c.real != 0.0 or _lfortran_caimag(c) != 0.0 + +@overload +def bool(c: c64) -> bool: + return c.real != 0.0 or _lfortran_zaimag(c) != 0.0 + @interface def len(s: str) -> i32: """ @@ -582,3 +669,4 @@ def min(a: f64, b: f64) -> f64: return a else: return b +