Skip to content

Commit

Permalink
Add isalpha, istitle, and title APIs in str (lcompilers#2357)
Browse files Browse the repository at this point in the history
  • Loading branch information
Agent-Hellboy committed Oct 9, 2023
1 parent 013df0c commit ac65227
Show file tree
Hide file tree
Showing 62 changed files with 1,377 additions and 1,229 deletions.
49 changes: 49 additions & 0 deletions integration_tests/test_str_01.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,52 @@ def test_str_slice():
# TODO:
# assert a[0:5:-1] == ""

def test_str_isalpha():
a: str = "helloworld"
b: str = "hj kl"
c: str = "a12(){}A"
d: str = " "
res: bool = a.isalpha()
res2: bool = b.isalpha()
res3: bool = c.isalpha()
res4: bool = d.isalpha()
assert res == True
assert res2 == False
assert res3 == False
assert res4 == False


def test_str_title():
a: str = "hello world"
b: str = "hj'kl"
c: str = "hELlo wOrlD"
d: str = "{Hel1o}world"
res: str = a.title()
res2: str = b.title()
res3: str = c.title()
res4: str = d.title()
assert res == "Hello World"
assert res2 == "Hj'Kl"
assert res3 == "Hello World"
assert res4 == "{Hel1O}World"

def test_str_istitle():
a: str = "Hello World"
b: str = "Hj'kl"
c: str = "hELlo wOrlD"
d: str = " Hello"
e: str = " "
res: bool = a.istitle()
res2: bool = b.istitle()
res3: bool = c.istitle()
res4: bool = d.istitle()
res5: bool = e.istitle()
assert res == True
assert res2 == False
assert res3 == False
assert res4 == True
assert res5 == False

def test_str_repeat():
a: str
a = "Xyz"
Expand Down Expand Up @@ -88,5 +134,8 @@ def check():
test_str_join_empty_str()
test_str_join_empty_list()
test_constant_str_subscript()
test_str_title()
test_str_istitle()
test_str_isalpha()

check()
30 changes: 30 additions & 0 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6468,6 +6468,36 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
arg.loc = loc;
arg.m_value = s_var;
fn_args.push_back(al, arg);
} else if (attr_name == "isalpha") {
if (args.size() != 0) {
throw SemanticError("str.isalpha() takes no arguments",
loc);
}
fn_call_name = "_lpython_str_isalpha";
ASR::call_arg_t arg;
arg.loc = loc;
arg.m_value = s_var;
fn_args.push_back(al, arg);
} else if (attr_name == "istitle") {
if (args.size() != 0) {
throw SemanticError("str.istitle() takes no arguments",
loc);
}
fn_call_name = "_lpython_str_istitle";
ASR::call_arg_t arg;
arg.loc = loc;
arg.m_value = s_var;
fn_args.push_back(al, arg);
} else if (attr_name == "title") {
if (args.size() != 0) {
throw SemanticError("str.title() takes no arguments",
loc);
}
fn_call_name = "_lpython_str_title";
ASR::call_arg_t arg;
arg.loc = loc;
arg.m_value = s_var;
fn_args.push_back(al, arg);
} else if (attr_name == "upper") {
if (args.size() != 0) {
throw SemanticError("str.upper() takes no arguments",
Expand Down
3 changes: 3 additions & 0 deletions src/lpython/semantics/python_comptime_eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ struct PythonIntrinsicProcedures {
{"_lpython_str_upper", {m_builtin, &not_implemented}},
{"_lpython_str_join", {m_builtin, &not_implemented}},
{"_lpython_str_find", {m_builtin, &not_implemented}},
{"_lpython_str_isalpha", {m_builtin, &not_implemented}},
{"_lpython_str_title", {m_builtin, &not_implemented}},
{"_lpython_str_istitle", {m_builtin, &not_implemented}},
{"_lpython_str_rstrip", {m_builtin, &not_implemented}},
{"_lpython_str_lstrip", {m_builtin, &not_implemented}},
{"_lpython_str_strip", {m_builtin, &not_implemented}},
Expand Down
66 changes: 66 additions & 0 deletions src/runtime/lpython_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,72 @@ def _lpython_str_join(s:str, lis:list[str]) -> str:
res += s + lis[i]
return res

def _lpython_str_isalpha(s: str) -> bool:
ch: str
for ch in s:
ch_ord: i32 = ord(ch)
if 65 <= ch_ord and ch_ord <= 90:
continue
if 97 <= ch_ord and ch_ord <= 122:
continue
return False
return True

def _lpython_str_title(s: str) -> str:
result: str = ""
capitalize_next: bool = True
ch: str
for ch in s:
ch_ord: i32 = ord(ch)
if ch_ord >= 97 and ch_ord <= 122:
if capitalize_next:
result += chr(ord(ch) - ord('a') + ord('A'))
capitalize_next = False
else:
result += ch
elif ch_ord >= 65 and ch_ord <= 90:
if capitalize_next:
result += ch
capitalize_next = False
else:
result += chr(ord(ch) + ord('a') - ord('A'))
else:
result += ch
capitalize_next = True

return result

def _lpython_str_istitle(s: str) -> bool:
length: i32 = len(s)

if length == 0:
return False # Empty string is not in title case

word_start: bool = True # Flag to track the start of a word
ch: str
only_whitespace: bool = True
for ch in s:
if (ch == ' ' or ch == '\t' or ch == '\n') and word_start:
continue # Found a space character at the start of a word
elif ch.isalpha() and (ord('A') <= ord(ch) and ord(ch) <= ord('Z')):
only_whitespace = False
if word_start:
word_start = False
else:
return False # Found an uppercase character in the middle of a word

elif ch.isalpha() and (ord('a') <= ord(ch) and ord(ch) <= ord('z')):
only_whitespace = False
if word_start:
return False # Found a lowercase character in the middle of a word
word_start = False
else:
word_start = True

return True if not only_whitespace else False



@overload
def _lpython_str_find(s: str, sub: str) -> i32:
s_len :i32; sub_len :i32; flag: bool; _len: i32;
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/asr-array_01_decl-39cf894.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "asr-array_01_decl-39cf894.stdout",
"stdout_hash": "337d67c221f17230293b36428d0f59e687b3a1d577e9b361298e1257",
"stdout_hash": "1faebd012adf52a67e912023e64747cd86287bca16bcd3551011da5a",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
Loading

0 comments on commit ac65227

Please sign in to comment.