Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add isalpha, istitle, and title APIs in str #2357

Merged
merged 4 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix conflicts
  • Loading branch information
Agent-Hellboy committed Oct 9, 2023
commit 229832fa23f86abb78a6de251bb7edf4e1df7710
41 changes: 40 additions & 1 deletion integration_tests/test_str_01.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,43 @@ 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"
res: bool = a.isalpha()
res2: bool = b.isalpha()
res3: bool = c.isalpha()
assert res == True
assert res2 == False
Agent-Hellboy marked this conversation as resolved.
Show resolved Hide resolved
assert res3 == 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"
Agent-Hellboy marked this conversation as resolved.
Show resolved Hide resolved
assert res3 == "Hello World"
assert res4 == "{Hel1O}World"

def test_str_istitle():
a: str = "Hello World"
b: str = "Hj'kl"
c: str = "hELlo wOrlD"
res: bool = a.istitle()
res2: bool = b.istitle()
res3: bool = c.istitle()
assert res == True
assert res2 == False
Copy link
Collaborator

@Thirumalai-Shaktivel Thirumalai-Shaktivel Oct 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add test for empty string.
Also add " Hello", "HeLlo" or something similar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this was an edge case
CPython istitle and title is not clear in documentation
updated the code according to the response from cpython

>>> l="\tHello"
>>> l.istitle()
True
>>> l="\thello"
>>> l.istitle()
False

assert res3 == False

def test_str_repeat():
a: str
a = "Xyz"
Expand Down Expand Up @@ -88,5 +125,7 @@ def check():
test_str_join_empty_str()
test_str_join_empty_list()
test_constant_str_subscript()

test_str_title()
Agent-Hellboy marked this conversation as resolved.
Show resolved Hide resolved
test_str_istitle()
test_str_isalpha()
Agent-Hellboy marked this conversation as resolved.
Show resolved Hide resolved
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
85 changes: 85 additions & 0 deletions src/runtime/lpython_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,91 @@ 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

for ch in s:
if (ch == ' ' or ch == '\t' or ch == '\n') and word_start:
return False # Found a space character at the start of a word
<<<<<<< HEAD
<<<<<<< HEAD
elif ch.isalpha() and (ord('A') <= ord(ch) and ord(ch) <= ord('Z')):
=======
elif ch.isalpha() and ch.isupper():
>>>>>>> 5c5bcc620 (Fix merge conflict)
=======
elif ch.isalpha() and (ord('A') <= ord(ch) and ord(ch) <= ord('Z')):
>>>>>>> f7fcb9835 (Fix test references)
if word_start:
word_start = False
else:
return False # Found an uppercase character in the middle of a word
<<<<<<< HEAD
<<<<<<< HEAD
elif ch.isalpha() and (ord('a') <= ord(ch) and ord(ch) <= ord('z')):
=======
elif ch.isalpha() and ch.islower():
>>>>>>> 5c5bcc620 (Fix merge conflict)
=======
elif ch.isalpha() and (ord('a') <= ord(ch) and ord(ch) <= ord('z')):
>>>>>>> f7fcb9835 (Fix test references)
if word_start:
return False # Found a lowercase character in the middle of a word
word_start = False
else:
word_start = True

return True


<<<<<<< HEAD
<<<<<<< HEAD
=======

>>>>>>> 5c5bcc620 (Fix merge conflict)
=======
>>>>>>> f7fcb9835 (Fix test references)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove merge conflict tags

Suggested change
<<<<<<< HEAD
<<<<<<< HEAD
elif ch.isalpha() and (ord('A') <= ord(ch) and ord(ch) <= ord('Z')):
=======
elif ch.isalpha() and ch.isupper():
>>>>>>> 5c5bcc620 (Fix merge conflict)
=======
elif ch.isalpha() and (ord('A') <= ord(ch) and ord(ch) <= ord('Z')):
>>>>>>> f7fcb9835 (Fix test references)
if word_start:
word_start = False
else:
return False # Found an uppercase character in the middle of a word
<<<<<<< HEAD
<<<<<<< HEAD
elif ch.isalpha() and (ord('a') <= ord(ch) and ord(ch) <= ord('z')):
=======
elif ch.isalpha() and ch.islower():
>>>>>>> 5c5bcc620 (Fix merge conflict)
=======
elif ch.isalpha() and (ord('a') <= ord(ch) and ord(ch) <= ord('z')):
>>>>>>> f7fcb9835 (Fix test references)
if word_start:
return False # Found a lowercase character in the middle of a word
word_start = False
else:
word_start = True
return True
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 5c5bcc620 (Fix merge conflict)
=======
>>>>>>> f7fcb9835 (Fix test references)
elif ch.isalpha() and (ord('A') <= ord(ch) and ord(ch) <= ord('Z')):
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')):
if word_start:
return False # Found a lowercase character in the middle of a word
word_start = False
else:
word_start = True
return True

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

@overload
def _lpython_str_find(s: str, sub: str) -> i32:
s_len :i32; sub_len :i32; flag: bool; _len: i32;
Expand Down
Loading
Loading