Skip to content

Fix isspace edge case #2489

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

Merged
merged 4 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
50 changes: 32 additions & 18 deletions integration_tests/test_str_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ def lower():
assert "DDd12Vv" .lower() == "ddd12vv"
assert "".lower() == ""


def upper():
s: str
s = "AaaaAABBbbbbBB!@12223BN"
assert s.upper() == "AAAAAABBBBBBBB!@12223BN"
assert "DDd12Vv".upper() == "DDD12VV"
assert "".upper() == ""


def strip():
s: str
s = " AASAsaSas "
Expand Down Expand Up @@ -88,14 +90,15 @@ def startswith():
assert s.startswith("sdd") == False
assert "".startswith("ok") == False


def endswith():

# The following test suite fulfils the control flow graph coverage
# in terms of Statement Coverage and Branch Coverage associated with endwith() functionality.

# Case 1: When string is constant and suffix is also constant
# Case 1: When string is constant and suffix is also constant
assert "".endswith("") == True
assert "".endswith(" ") == False
assert "".endswith(" ") == False
assert "".endswith("%") == False
assert "".endswith("a1234PT#$") == False
assert "".endswith("blah blah") == False
Expand All @@ -105,13 +108,12 @@ def endswith():
assert " rendezvous 5:30 ".endswith("apple") == False
assert "two plus".endswith("longer than string") == False


# Case 2: When string is constant and suffix is variable
suffix: str
suffix = ""
assert "".endswith(suffix) == True
suffix = " "
assert "".endswith(suffix) == False
assert "".endswith(suffix) == False
suffix = "5:30 "
assert " rendezvous 5:30 ".endswith(suffix) == True
suffix = ""
Expand All @@ -138,13 +140,14 @@ def endswith():
suffix = "apple"
assert s.endswith(suffix) == False


def partition():
# Note: Both string or seperator cannot be empty
# Case 1: When string is constant and seperator is also constant
assert " ".partition(" ") == (""," "," ")
assert "apple mango".partition(" ") == ("apple"," ","mango")
assert "applemango".partition("afdnjkfsn") == ("applemango","","")

# Note: Both string or seperator cannot be empty
# Case 1: When string is constant and seperator is also constant
assert " ".partition(" ") == ("", " ", " ")
assert "apple mango".partition(" ") == ("apple", " ", "mango")
assert "applemango".partition("afdnjkfsn") == ("applemango", "", "")
assert "applemango".partition("an") == ("applem", "an", "go")
assert "applemango".partition("mango") == ("apple", "mango", "")
assert "applemango".partition("applemango") == ("", "applemango", "")
Expand All @@ -154,15 +157,17 @@ def partition():
# Case 2: When string is constant and seperator is variable
seperator: str
seperator = " "
assert " ".partition(seperator) == (""," "," ")
assert " ".partition(seperator) == ("", " ", " ")
seperator = " "
assert "apple mango".partition(seperator) == ("apple"," ","mango")
assert "apple mango".partition(seperator) == ("apple", " ", "mango")
seperator = "5:30 "
assert " rendezvous 5:30 ".partition(seperator) == (" rendezvous ", "5:30 ", "")
assert " rendezvous 5:30 ".partition(
seperator) == (" rendezvous ", "5:30 ", "")
seperator = "^&"
assert "@#$%^&*()#!".partition(seperator) == ("@#$%", "^&", "*()#!")
seperator = "daddada "
assert " rendezvous 5:30 ".partition(seperator) == (" rendezvous 5:30 ", "", "")
assert " rendezvous 5:30 ".partition(
seperator) == (" rendezvous 5:30 ", "", "")
seperator = "longer than string"
assert "two plus".partition(seperator) == ("two plus", "", "")

Expand All @@ -182,6 +187,7 @@ def partition():
seperator = "apple"
assert s.partition(seperator) == ("rendezvous 5", "", "")


def is_lower():
# Case 1: When constant string is present
assert "".islower() == False
Expand All @@ -204,8 +210,9 @@ def is_lower():
s = "apple is a fruit"
assert s.islower() == True


def is_upper():
# Case 1: When constant string is present
# Case 1: When constant string is present
assert "".isupper() == False
assert "apple".isupper() == False
assert "4432632479".isupper() == False
Expand All @@ -226,6 +233,7 @@ def is_upper():
s = "APPLE IS A FRUIT"
assert s.isupper() == True


def is_decimal():
# Case 1: When constant string is present
assert "".isdecimal() == False
Expand All @@ -251,6 +259,7 @@ def is_decimal():
s = "12 34"
assert s.isdecimal() == False


def is_ascii():
# Case 1: When constant string is present
assert "".isascii() == True
Expand Down Expand Up @@ -280,12 +289,16 @@ def is_ascii():
def is_space():
assert "\n".isspace() == True
assert " ".isspace() == True
assert "\r".isspace() == True
assert "\r".isspace() == True
assert "".isspace() == False

s:str = " "
assert s.isspace() == True
s: str = " "
assert s.isspace() == True
s = "a"
assert s.isspace() == False
s = ""
assert s.isspace() == False


def check():
capitalize()
Expand All @@ -303,4 +316,5 @@ def check():
is_ascii()
is_space()


check()
20 changes: 10 additions & 10 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7107,18 +7107,18 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
Return True if all cased characters in the string are uppercase and there is at least one cased character, False otherwise.
*/
bool is_cased_present = false;
bool is_lower = true;
bool is_upper = true;
for (auto &i : s_var) {
if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z')) {
is_cased_present = true;
if(!(i >= 'A' && i <= 'Z')) {
is_lower = false;
is_upper = false;
break;
}
}
}
is_lower = is_lower && is_cased_present;
tmp = ASR::make_LogicalConstant_t(al, loc, is_lower,
is_upper = is_upper && is_cased_present;
tmp = ASR::make_LogicalConstant_t(al, loc, is_upper,
ASRUtils::TYPE(ASR::make_Logical_t(al, loc, 4)));
return;
} else if(attr_name == "isdecimal") {
Expand Down Expand Up @@ -7160,13 +7160,13 @@ characters, as defined by CPython. Return false otherwise. For now we use the
std::isspace function, but if we later discover that it differs from CPython,
we will have to use something else.
*/
bool is_space = true;
for (char i : s_var) {
if (!std::isspace(static_cast<unsigned char>(i))) {
is_space = false;
break;
}
bool is_space = (s_var.size() != 0);
for (char i : s_var) {
if (!std::isspace(static_cast<unsigned char>(i))) {
is_space = false;
break;
}
}
tmp = ASR::make_LogicalConstant_t(al, loc, is_space,
ASRUtils::TYPE(ASR::make_Logical_t(al, loc, 4)));
return;
Expand Down
4 changes: 3 additions & 1 deletion src/runtime/lpython_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ def _lpython_str_isdecimal(s: str) -> bool:

@overload
def _lpython_str_isascii(s: str) -> bool:
if(len(s) == 0):
if len(s) == 0:
return True
i: str
for i in s:
Expand All @@ -964,6 +964,8 @@ def _lpython_str_isascii(s: str) -> bool:
return True

def _lpython_str_isspace(s:str) -> bool:
if len(s) == 0:
return False
ch: str
for ch in s:
if ch != ' ' and ch != '\t' and ch != '\n' and ch != '\r' and ch != '\f' and ch != '\v':
Expand Down