Skip to content
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
18 changes: 18 additions & 0 deletions integration_tests/test_str_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ def strip():
assert " AASAsaSas " .lstrip() == "AASAsaSas "
assert " AASAsaSas " .strip() == "AASAsaSas"

def startswith():
s: str
s = " empty"
assert s.startswith(" ") == True
assert " @" .startswith(" ") == True
assert " emptyAaaaAABBbbbbBB" .startswith(s) == True
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's try giving something in the middle portion of the string such as AABB in startswith and see if it returns False. Also give something like emptyAaaX as input to startswith.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i added them and edit those corner cases in functions, i think it's ready now.

assert " emptyAaaaAABBbbbbBB" .startswith("AABB") == False
assert " emptyAaaaAABBbbbbBB" .startswith("emptyAaxX") == False

assert "". startswith("sd") == False
assert "empty" .startswith("") == True
assert "".startswith("") == True
assert s.startswith("") == True
s = ""
assert s.startswith("") == True
assert s.startswith("sdd") == False

capitalize()
lower()
strip()
startswith()
69 changes: 69 additions & 0 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4554,6 +4554,30 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
args.push_back(al, arg);
tmp = make_call_helper(al, fn_div, current_scope, args, "_lpython_str_strip", x.base.base.loc);
return;
} else if (std::string(at->m_attr) == std::string("startswith")) {
if(args.size() != 1) {
throw SemanticError("str.startwith() takes one argument",
x.base.base.loc);
}
ASR::expr_t *arg_sub = args[0].m_value;
ASR::ttype_t *arg_sub_type = ASRUtils::expr_type(arg_sub);
if(!ASRUtils::is_character(*arg_sub_type)) {
throw SemanticError("str.startwith() takes one argument of type: str",
x.base.base.loc);
}
ASR::symbol_t *fn_div = resolve_intrinsic_function(x.base.base.loc, "_lpython_str_startswith");
Vec<ASR::call_arg_t> function_args;
function_args.reserve(al, 1);
ASR::call_arg_t str;
str.loc = x.base.base.loc;
str.m_value = se;
ASR::call_arg_t sub;
sub.loc = x.base.base.loc;
sub.m_value = args[0].m_value;
function_args.push_back(al, str);
function_args.push_back(al, sub);
tmp = make_call_helper(al, fn_div, current_scope, function_args, "_lpython_str_startswith", x.base.base.loc);
return;
}
}
handle_attribute(se, at->m_attr, x.base.base.loc, eles);
Expand Down Expand Up @@ -4668,6 +4692,51 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
1, 1, nullptr, nullptr , 0));
tmp = ASR::make_StringConstant_t(al, x.base.base.loc, s2c(al, res), str_type);
return;
} else if (std::string(at->m_attr) == std::string("startswith")) {
if (args.size() != 1) {
throw SemanticError("str.startswith() takes one arguments",
x.base.base.loc);
}
ASR::expr_t *arg = args[0].m_value;
ASR::ttype_t *type = ASRUtils::expr_type(arg);
if (ASRUtils::is_character(*type)) {
AST::ConstantStr_t* str_str_con = AST::down_cast<AST::ConstantStr_t>(at->m_value);
std::string str = str_str_con->m_value;
if (ASRUtils::expr_value(arg) != nullptr) {
ASR::StringConstant_t* sub_str_con = ASR::down_cast<ASR::StringConstant_t>(arg);
std::string sub = sub_str_con->m_s;
size_t ind1 = 0, ind2 = 0;
bool res = !(str.size() == 0 && sub.size());
while ((ind1 < str.size()) && (ind2 < sub.size()) && res) {
res &= str[ind1] == sub[ind2];
ind1++;
ind2++;
}
res &= res? (ind2 == sub.size()): true;
tmp = ASR::make_LogicalConstant_t(al, x.base.base.loc, res, ASRUtils::TYPE(ASR::make_Logical_t(al, x.base.base.loc,
4, nullptr, 0)));
} else {
ASR::symbol_t *fn_div = resolve_intrinsic_function(x.base.base.loc, "_lpython_str_startswith");
Vec<ASR::call_arg_t> args;
args.reserve(al, 1);
ASR::call_arg_t str_arg;
str_arg.loc = x.base.base.loc;
ASR::ttype_t *str_type = ASRUtils::TYPE(ASR::make_Character_t(al, x.base.base.loc,
1, 0, nullptr, nullptr, 0));
str_arg.m_value = ASRUtils::EXPR(
ASR::make_StringConstant_t(al, x.base.base.loc, s2c(al, str), str_type));
ASR::call_arg_t sub_arg;
sub_arg.loc = x.base.base.loc;
sub_arg.m_value = arg;
args.push_back(al, str_arg);
args.push_back(al, sub_arg);
tmp = make_call_helper(al, fn_div, current_scope, args, "_lpython_str_startswith", x.base.base.loc);
}
} else {
throw SemanticError("str.startwith() takes one arguments of type: str",
arg->base.loc);
}
return;
} else {
throw SemanticError("'str' object has no attribute '" + std::string(at->m_attr) + "'",
x.base.base.loc);
Expand Down
11 changes: 10 additions & 1 deletion src/lpython/semantics/python_comptime_eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ struct PythonIntrinsicProcedures {
{"_lpython_str_lower", {m_builtin, &eval__lpython_str_lower}},
{"_lpython_str_rstrip", {m_builtin, &eval__lpython_str_rstrip}},
{"_lpython_str_lstrip", {m_builtin, &eval__lpython_str_lstrip}},
{"_lpython_str_strip", {m_builtin, &eval__lpython_str_strip}}
{"_lpython_str_strip", {m_builtin, &eval__lpython_str_strip}},
{"_lpython_str_startswith", {m_builtin, &eval__lpython_str_startswith}}
};
}

Expand Down Expand Up @@ -740,6 +741,14 @@ struct PythonIntrinsicProcedures {
ASR::ttype_t *res_type = ASRUtils::TYPE(ASR::make_StringConstant_t(al, loc, s2c(al, ""), type));
return ASR::down_cast<ASR::expr_t>(ASR::make_StringConstant_t(al, loc, s2c(al, res), res_type));
}

static ASR::expr_t *eval__lpython_str_startswith(Allocator &al, const Location &loc, Vec<ASR::expr_t *> &/*args*/) {
// compile time action implemented on ast->asr
ASR::ttype_t* res_type = ASRUtils::TYPE(ASR::make_Logical_t(al, loc,
4, nullptr, 0));
return ASR::down_cast<ASR::expr_t>(ASR::make_LogicalConstant_t(al, loc, 0, res_type));
}

}; // ComptimeEval

} // namespace LFortran
Expand Down
13 changes: 13 additions & 0 deletions src/runtime/lpython_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -659,3 +659,16 @@ def _lpython_str_strip(x: str) -> str:
res = _lpython_str_lstrip(x)
res = _lpython_str_rstrip(res)
return res

@overload
def _lpython_str_startswith(s: str ,sub: str) -> bool:
res :bool
res = not (len(s) == 0 and len(sub) > 0)
i: i32; j: i32
i = 0; j = 0
while (i < len(s)) and ((j < len(sub)) and res):
res = res and (s[i] == sub[j])
i += 1; j+=1
if res:
res = res and (j == len(sub))
return res
2 changes: 1 addition & 1 deletion tests/reference/asr-complex1-f26c460.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "asr-complex1-f26c460.stdout",
"stdout_hash": "18b20d566cba3464ada1eca0f7570f65ab4ab77e0a957ea2285a5a0b",
"stdout_hash": "533e7e69cb0732f89d58e6ee1d45d246cda1466bad59e039353ec7d3",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
Loading