Skip to content

Commit ac65227

Browse files
Add isalpha, istitle, and title APIs in str (#2357)
1 parent 013df0c commit ac65227

File tree

62 files changed

+1377
-1229
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1377
-1229
lines changed

integration_tests/test_str_01.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,52 @@ def test_str_slice():
3737
# TODO:
3838
# assert a[0:5:-1] == ""
3939

40+
def test_str_isalpha():
41+
a: str = "helloworld"
42+
b: str = "hj kl"
43+
c: str = "a12(){}A"
44+
d: str = " "
45+
res: bool = a.isalpha()
46+
res2: bool = b.isalpha()
47+
res3: bool = c.isalpha()
48+
res4: bool = d.isalpha()
49+
assert res == True
50+
assert res2 == False
51+
assert res3 == False
52+
assert res4 == False
53+
54+
55+
def test_str_title():
56+
a: str = "hello world"
57+
b: str = "hj'kl"
58+
c: str = "hELlo wOrlD"
59+
d: str = "{Hel1o}world"
60+
res: str = a.title()
61+
res2: str = b.title()
62+
res3: str = c.title()
63+
res4: str = d.title()
64+
assert res == "Hello World"
65+
assert res2 == "Hj'Kl"
66+
assert res3 == "Hello World"
67+
assert res4 == "{Hel1O}World"
68+
69+
def test_str_istitle():
70+
a: str = "Hello World"
71+
b: str = "Hj'kl"
72+
c: str = "hELlo wOrlD"
73+
d: str = " Hello"
74+
e: str = " "
75+
res: bool = a.istitle()
76+
res2: bool = b.istitle()
77+
res3: bool = c.istitle()
78+
res4: bool = d.istitle()
79+
res5: bool = e.istitle()
80+
assert res == True
81+
assert res2 == False
82+
assert res3 == False
83+
assert res4 == True
84+
assert res5 == False
85+
4086
def test_str_repeat():
4187
a: str
4288
a = "Xyz"
@@ -88,5 +134,8 @@ def check():
88134
test_str_join_empty_str()
89135
test_str_join_empty_list()
90136
test_constant_str_subscript()
137+
test_str_title()
138+
test_str_istitle()
139+
test_str_isalpha()
91140

92141
check()

src/lpython/semantics/python_ast_to_asr.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6468,6 +6468,36 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
64686468
arg.loc = loc;
64696469
arg.m_value = s_var;
64706470
fn_args.push_back(al, arg);
6471+
} else if (attr_name == "isalpha") {
6472+
if (args.size() != 0) {
6473+
throw SemanticError("str.isalpha() takes no arguments",
6474+
loc);
6475+
}
6476+
fn_call_name = "_lpython_str_isalpha";
6477+
ASR::call_arg_t arg;
6478+
arg.loc = loc;
6479+
arg.m_value = s_var;
6480+
fn_args.push_back(al, arg);
6481+
} else if (attr_name == "istitle") {
6482+
if (args.size() != 0) {
6483+
throw SemanticError("str.istitle() takes no arguments",
6484+
loc);
6485+
}
6486+
fn_call_name = "_lpython_str_istitle";
6487+
ASR::call_arg_t arg;
6488+
arg.loc = loc;
6489+
arg.m_value = s_var;
6490+
fn_args.push_back(al, arg);
6491+
} else if (attr_name == "title") {
6492+
if (args.size() != 0) {
6493+
throw SemanticError("str.title() takes no arguments",
6494+
loc);
6495+
}
6496+
fn_call_name = "_lpython_str_title";
6497+
ASR::call_arg_t arg;
6498+
arg.loc = loc;
6499+
arg.m_value = s_var;
6500+
fn_args.push_back(al, arg);
64716501
} else if (attr_name == "upper") {
64726502
if (args.size() != 0) {
64736503
throw SemanticError("str.upper() takes no arguments",

src/lpython/semantics/python_comptime_eval.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ struct PythonIntrinsicProcedures {
8282
{"_lpython_str_upper", {m_builtin, &not_implemented}},
8383
{"_lpython_str_join", {m_builtin, &not_implemented}},
8484
{"_lpython_str_find", {m_builtin, &not_implemented}},
85+
{"_lpython_str_isalpha", {m_builtin, &not_implemented}},
86+
{"_lpython_str_title", {m_builtin, &not_implemented}},
87+
{"_lpython_str_istitle", {m_builtin, &not_implemented}},
8588
{"_lpython_str_rstrip", {m_builtin, &not_implemented}},
8689
{"_lpython_str_lstrip", {m_builtin, &not_implemented}},
8790
{"_lpython_str_strip", {m_builtin, &not_implemented}},

src/runtime/lpython_builtin.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,72 @@ def _lpython_str_join(s:str, lis:list[str]) -> str:
685685
res += s + lis[i]
686686
return res
687687

688+
def _lpython_str_isalpha(s: str) -> bool:
689+
ch: str
690+
for ch in s:
691+
ch_ord: i32 = ord(ch)
692+
if 65 <= ch_ord and ch_ord <= 90:
693+
continue
694+
if 97 <= ch_ord and ch_ord <= 122:
695+
continue
696+
return False
697+
return True
698+
699+
def _lpython_str_title(s: str) -> str:
700+
result: str = ""
701+
capitalize_next: bool = True
702+
ch: str
703+
for ch in s:
704+
ch_ord: i32 = ord(ch)
705+
if ch_ord >= 97 and ch_ord <= 122:
706+
if capitalize_next:
707+
result += chr(ord(ch) - ord('a') + ord('A'))
708+
capitalize_next = False
709+
else:
710+
result += ch
711+
elif ch_ord >= 65 and ch_ord <= 90:
712+
if capitalize_next:
713+
result += ch
714+
capitalize_next = False
715+
else:
716+
result += chr(ord(ch) + ord('a') - ord('A'))
717+
else:
718+
result += ch
719+
capitalize_next = True
720+
721+
return result
722+
723+
def _lpython_str_istitle(s: str) -> bool:
724+
length: i32 = len(s)
725+
726+
if length == 0:
727+
return False # Empty string is not in title case
728+
729+
word_start: bool = True # Flag to track the start of a word
730+
ch: str
731+
only_whitespace: bool = True
732+
for ch in s:
733+
if (ch == ' ' or ch == '\t' or ch == '\n') and word_start:
734+
continue # Found a space character at the start of a word
735+
elif ch.isalpha() and (ord('A') <= ord(ch) and ord(ch) <= ord('Z')):
736+
only_whitespace = False
737+
if word_start:
738+
word_start = False
739+
else:
740+
return False # Found an uppercase character in the middle of a word
741+
742+
elif ch.isalpha() and (ord('a') <= ord(ch) and ord(ch) <= ord('z')):
743+
only_whitespace = False
744+
if word_start:
745+
return False # Found a lowercase character in the middle of a word
746+
word_start = False
747+
else:
748+
word_start = True
749+
750+
return True if not only_whitespace else False
751+
752+
753+
688754
@overload
689755
def _lpython_str_find(s: str, sub: str) -> i32:
690756
s_len :i32; sub_len :i32; flag: bool; _len: i32;

tests/reference/asr-array_01_decl-39cf894.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"outfile": null,
77
"outfile_hash": null,
88
"stdout": "asr-array_01_decl-39cf894.stdout",
9-
"stdout_hash": "337d67c221f17230293b36428d0f59e687b3a1d577e9b361298e1257",
9+
"stdout_hash": "1faebd012adf52a67e912023e64747cd86287bca16bcd3551011da5a",
1010
"stderr": null,
1111
"stderr_hash": null,
1212
"returncode": 0

0 commit comments

Comments
 (0)