Skip to content

Commit ffd9b05

Browse files
authored
Merge pull request #1012 from Abdelrahman-Kh-Fouad/str@find
Strings methods: `find()`
2 parents d75d00b + bff8d77 commit ffd9b05

File tree

52 files changed

+222
-49
lines changed

Some content is hidden

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

52 files changed

+222
-49
lines changed

integration_tests/test_str_attributes.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,26 @@ def strip():
2525
assert " AASAsaSas " .lstrip() == "AASAsaSas "
2626
assert " AASAsaSas " .strip() == "AASAsaSas"
2727

28+
def find():
29+
s: str
30+
sub: str
31+
s = "AaaaAABBbbbbBB!@12223BN"
32+
sub = "@"
33+
assert s.find(sub) == 15
34+
assert s.find('B') == 6
35+
assert "empty strings" .find("string") == 6
36+
s2: str
37+
s2 = "Well copying a string from a website makes us prone to copyright claims. Can you just write something of your own? Like just take this review comment and put it as a string?"
38+
assert s2.find("of") == 102
39+
assert s2.find("own") == 110
40+
assert s2.find("this") == 130
41+
assert s2.find("") == 0
42+
assert "".find("dd") == -1
43+
assert "".find("") == 0
44+
s2 = ""
45+
assert s2.find("") == 0
46+
assert s2.find("we") == -1
47+
2848
def startswith():
2949
s: str
3050
s = " empty"
@@ -45,4 +65,5 @@ def startswith():
4565
capitalize()
4666
lower()
4767
strip()
68+
find()
4869
startswith()

src/lpython/semantics/python_ast_to_asr.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4512,6 +4512,30 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
45124512
args.push_back(al, arg);
45134513
tmp = make_call_helper(al, fn_div, current_scope, args, "_lpython_str_lower", x.base.base.loc);
45144514
return;
4515+
} else if (std::string(at->m_attr) == std::string("find")) {
4516+
if(args.size() != 1) {
4517+
throw SemanticError("str.find() takes one argument",
4518+
x.base.base.loc);
4519+
}
4520+
ASR::expr_t *arg_sub = args[0].m_value;
4521+
ASR::ttype_t *arg_sub_type = ASRUtils::expr_type(arg_sub);
4522+
if(!ASRUtils::is_character(*arg_sub_type)) {
4523+
throw SemanticError("str.find() takes one argument of type: str",
4524+
x.base.base.loc);
4525+
}
4526+
ASR::symbol_t *fn_div = resolve_intrinsic_function(x.base.base.loc, "_lpython_str_find");
4527+
Vec<ASR::call_arg_t> function_args;
4528+
function_args.reserve(al, 1);
4529+
ASR::call_arg_t str;
4530+
str.loc = x.base.base.loc;
4531+
str.m_value = se;
4532+
ASR::call_arg_t sub;
4533+
sub.loc = x.base.base.loc;
4534+
sub.m_value = args[0].m_value;
4535+
function_args.push_back(al, str);
4536+
function_args.push_back(al, sub);
4537+
tmp = make_call_helper(al, fn_div, current_scope, function_args, "_lpython_str_find", x.base.base.loc);
4538+
return;
45154539
} else if (std::string(at->m_attr) == std::string("rstrip")) {
45164540
if(args.size() != 0) {
45174541
throw SemanticError("str.srtrip() takes no arguments",
@@ -4642,6 +4666,80 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
46424666
1, 1, nullptr, nullptr , 0));
46434667
tmp = ASR::make_StringConstant_t(al, x.base.base.loc, s2c(al, res), str_type);
46444668
return;
4669+
} else if (std::string(at->m_attr) == std::string("find")) {
4670+
if (args.size() != 1) {
4671+
throw SemanticError("str.find() takes one arguments",
4672+
x.base.base.loc);
4673+
}
4674+
ASR::expr_t *arg = args[0].m_value;
4675+
ASR::ttype_t *type = ASRUtils::expr_type(arg);
4676+
if (ASRUtils::is_character(*type)) {
4677+
AST::ConstantStr_t* str_str_con = AST::down_cast<AST::ConstantStr_t>(at->m_value);
4678+
std::string str = str_str_con->m_value;
4679+
if (ASRUtils::expr_value(arg) != nullptr) {
4680+
ASR::StringConstant_t* sub_str_con = ASR::down_cast<ASR::StringConstant_t>(arg);
4681+
std::string sub = sub_str_con->m_s;
4682+
//KMP matching
4683+
int str_len = str.size();
4684+
int sub_len = sub.size();
4685+
bool flag = 0;
4686+
int res = -1;
4687+
std::vector<int>lps(sub_len, 0);
4688+
if (str_len == 0 || sub_len == 0) {
4689+
res = (!sub_len || (sub_len == str_len))? 0: -1;
4690+
} else {
4691+
for(int i = 1, len = 0; i < sub_len;) {
4692+
if (sub[i] == sub[len]) {
4693+
lps[i++] = ++len;
4694+
} else {
4695+
if (len != 0) {
4696+
len = lps[len - 1];
4697+
} else {
4698+
lps[i++] = 0;
4699+
}
4700+
}
4701+
}
4702+
for (int i = 0, j = 0; (str_len - i) >= (sub_len - j) && !flag;) {
4703+
if (sub[j] == str[i]) {
4704+
j++, i++;
4705+
}
4706+
if (j == sub_len) {
4707+
res = i - j;
4708+
flag = 1;
4709+
j = lps[j - 1];
4710+
} else if (i < str_len && sub[j] != str[i]) {
4711+
if (j != 0) {
4712+
j = lps[j - 1];
4713+
} else {
4714+
i = i + 1;
4715+
}
4716+
}
4717+
}
4718+
}
4719+
tmp = ASR::make_IntegerConstant_t(al, x.base.base.loc, res, ASRUtils::TYPE(ASR::make_Integer_t(al, x.base.base.loc,
4720+
4, nullptr, 0)));
4721+
} else {
4722+
ASR::symbol_t *fn_div = resolve_intrinsic_function(x.base.base.loc, "_lpython_str_find");
4723+
Vec<ASR::call_arg_t> args;
4724+
args.reserve(al, 1);
4725+
ASR::call_arg_t str_arg;
4726+
str_arg.loc = x.base.base.loc;
4727+
ASR::ttype_t *str_type = ASRUtils::TYPE(ASR::make_Character_t(al, x.base.base.loc,
4728+
1, 0, nullptr, nullptr, 0));
4729+
str_arg.m_value = ASRUtils::EXPR(
4730+
ASR::make_StringConstant_t(al, x.base.base.loc, s2c(al, str), str_type));
4731+
ASR::call_arg_t sub_arg;
4732+
sub_arg.loc = x.base.base.loc;
4733+
sub_arg.m_value = arg;
4734+
args.push_back(al, str_arg);
4735+
args.push_back(al, sub_arg);
4736+
tmp = make_call_helper(al, fn_div, current_scope, args, "_lpython_str_find", x.base.base.loc);
4737+
}
4738+
} else {
4739+
throw SemanticError("str.find() takes one arguments of type: str",
4740+
arg->base.loc);
4741+
}
4742+
return;
46454743
} else if (std::string(at->m_attr) == std::string("rstrip")) {
46464744
if(args.size() != 0) {
46474745
throw SemanticError("str.rstrip() takes no arguments",

src/lpython/semantics/python_comptime_eval.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct PythonIntrinsicProcedures {
6464
{"min" , {m_builtin , &eval_min}},
6565
{"_lpython_str_capitalize", {m_builtin, &eval__lpython_str_capitalize}},
6666
{"_lpython_str_lower", {m_builtin, &eval__lpython_str_lower}},
67+
{"_lpython_str_find", {m_builtin, &eval_lpython_str_find}},
6768
{"_lpython_str_rstrip", {m_builtin, &eval__lpython_str_rstrip}},
6869
{"_lpython_str_lstrip", {m_builtin, &eval__lpython_str_lstrip}},
6970
{"_lpython_str_strip", {m_builtin, &eval__lpython_str_strip}},
@@ -741,7 +742,13 @@ struct PythonIntrinsicProcedures {
741742
ASR::ttype_t *res_type = ASRUtils::TYPE(ASR::make_StringConstant_t(al, loc, s2c(al, ""), type));
742743
return ASR::down_cast<ASR::expr_t>(ASR::make_StringConstant_t(al, loc, s2c(al, res), res_type));
743744
}
744-
745+
746+
static ASR::expr_t *eval_lpython_str_find(Allocator &al, const Location &loc, Vec<ASR::expr_t *> &/*args*/) {
747+
// compile time action implemented on ast->asr
748+
ASR::ttype_t *int_type = ASRUtils::TYPE(ASR::make_Integer_t(al, loc, 4, nullptr, 0));
749+
return ASR::down_cast<ASR::expr_t>(ASR::make_IntegerConstant_t(al, loc, -1, int_type));
750+
}
751+
745752
static ASR::expr_t *eval__lpython_str_startswith(Allocator &al, const Location &loc, Vec<ASR::expr_t *> &/*args*/) {
746753
// compile time action implemented on ast->asr
747754
ASR::ttype_t* res_type = ASRUtils::TYPE(ASR::make_Logical_t(al, loc,

src/runtime/lpython_builtin.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,53 @@ def _lpython_str_lower(x: str) -> str:
638638
return res
639639

640640
@overload
641+
def _lpython_str_find(s: str, sub: str) -> i32:
642+
s_len :i32; sub_len :i32; flag: bool; _len: i32;
643+
res: i32; i: i32;
644+
lps: list[i32]
645+
s_len = len(s)
646+
sub_len = len(sub)
647+
flag = False
648+
res = -1
649+
if s_len == 0 or sub_len == 0:
650+
return 0 if sub_len == 0 or (sub_len == s_len) else -1
651+
652+
for i in range(sub_len):
653+
lps.append(0)
654+
655+
i = 1
656+
_len = 0
657+
while i < sub_len:
658+
if sub[i] == sub[_len]:
659+
_len += 1
660+
lps[i] = _len
661+
i += 1
662+
else:
663+
if _len != 0:
664+
_len = lps[_len - 1]
665+
else:
666+
lps[i] = 0
667+
i += 1
668+
669+
j: i32
670+
j = 0
671+
i = 0
672+
while (s_len - i) >= (sub_len - j) and not flag:
673+
if sub[j] == s[i]:
674+
i += 1
675+
j += 1
676+
if j == sub_len:
677+
res = i- j
678+
flag = True
679+
j = lps[j - 1]
680+
elif i < s_len and sub[j] != s[i]:
681+
if j != 0:
682+
j = lps[j - 1]
683+
else:
684+
i = i + 1
685+
686+
return res
687+
641688
def _lpython_str_rstrip(x: str) -> str:
642689
ind: i32
643690
ind = len(x) - 1

tests/reference/asr-complex1-f26c460.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-complex1-f26c460.stdout",
9-
"stdout_hash": "533e7e69cb0732f89d58e6ee1d45d246cda1466bad59e039353ec7d3",
9+
"stdout_hash": "ec30511e10451c64a896595e1470c429df7ec2bb31dd60b299a15d2b",
1010
"stderr": null,
1111
"stderr_hash": null,
1212
"returncode": 0

0 commit comments

Comments
 (0)