Skip to content

Add builtin functions #2651

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 22 commits into from
Apr 26, 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
27 changes: 27 additions & 0 deletions integration_tests/test_str_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,31 @@ def is_numeric():
assert "".isnumeric() == False
assert "ab2%3".isnumeric() == False

def center():
Copy link
Contributor

@kmr-srbh kmr-srbh Apr 20, 2024

Choose a reason for hiding this comment

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

The current implementation supports runtime strings. Please add tests for them. By runtime strings, I mean a string stored in a variable. You can have a look at how is_upper() is tested.

Copy link
Contributor

Choose a reason for hiding this comment

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

@farah-salama please update the test references also.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please rebuild and update the test references again?

s: str = "test"
assert s.center(8,'*') == "**test**"
assert s.center(11) == " test "
assert s.center(2) == "test"
assert s.center(4) == "test"
assert s.center(9,'/') == "///test//"

def expandtabs():
s: str = '01\t012\t0123\t01234'
assert s.expandtabs() == "01 012 0123 01234"
assert s.expandtabs(4) == "01 012 0123 01234"
assert s.expandtabs(-1) == "01012012301234"
s = '\t'
assert s.expandtabs() == " "
s = ''
assert s.expandtabs() == ""
s = '\tThis\ris\na\ttest'
assert s.expandtabs(4) == " This\ris\na test"
s = '\t\t\t'
assert s.expandtabs(2) == " "
s = 'test\ttest'
assert s.expandtabs(0) == "testtest"
assert s.expandtabs(-5) == "testtest"

def check():
capitalize()
lower()
Expand All @@ -492,6 +517,8 @@ def check():
is_space()
is_alnum()
is_numeric()
center()
expandtabs()


check()
53 changes: 53 additions & 0 deletions src/lpython/semantics/python_ast_to_asr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6880,6 +6880,59 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
value.m_value = args[2].m_value;
fn_args.push_back(al, value);
}
} else if(attr_name == "center") {
if (args.size() != 1 && args.size() != 2) {
throw SemanticError("str.center() takes one or two argument",
loc);
}
ASR::expr_t *arg_value = args[0].m_value;
ASR::ttype_t *arg_value_type = ASRUtils::expr_type(arg_value);
if (!ASRUtils::is_integer(*arg_value_type)) {
throw SemanticError("str.center() argument 1 must be integer", loc);
}

fn_call_name = "_lpython_str_center";
ASR::call_arg_t str;
str.loc = loc;
str.m_value = s_var;

ASR::call_arg_t value;
value.loc = loc;
value.m_value = args[0].m_value;
fn_args.push_back(al, str);
fn_args.push_back(al, value);

if(args.size() == 2){
ASR::expr_t *arg_value = args[1].m_value;
ASR::ttype_t *arg_value_type = ASRUtils::expr_type(arg_value);
if (!ASRUtils::is_character(*arg_value_type)) {
throw SemanticError("str.center() argument 2 must be str", loc);
}
value.m_value = args[1].m_value;
fn_args.push_back(al, value);
}
} else if(attr_name == "expandtabs") {
if(args.size() > 1) {
throw SemanticError("str.expandtabs() takes at most one argument.", loc);
}
fn_call_name = "_lpython_str_expandtabs";
ASR::call_arg_t str;
str.loc = loc;
str.m_value = s_var;
fn_args.push_back(al, str);

if(args.size() == 1){
ASR::expr_t *arg_value = args[0].m_value;
ASR::ttype_t *arg_value_type = ASRUtils::expr_type(arg_value);
if (!ASRUtils::is_integer(*arg_value_type)) {
throw SemanticError("str.expandtabs() argument must be integer", loc);
}

ASR::call_arg_t value;
value.loc = loc;
value.m_value = args[0].m_value;
fn_args.push_back(al, value);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This handles runtime implementation. We also need to support compile-time implementation inside

void handle_constant_string_attributes(std::string &s_var,
.

} else if(attr_name.size() > 2 && attr_name[0] == 'i' && attr_name[1] == 's') {
/*
String Validation Methods i.e all "is" based functions are handled here
Expand Down
4 changes: 3 additions & 1 deletion src/lpython/semantics/python_comptime_eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ struct PythonIntrinsicProcedures {
{"_lpython_str_isupper", {m_builtin, &not_implemented}},
{"_lpython_str_isdecimal", {m_builtin, &not_implemented}},
{"_lpython_str_isascii", {m_builtin, &not_implemented}},
{"_lpython_str_isspace", {m_builtin, &not_implemented}}
{"_lpython_str_isspace", {m_builtin, &not_implemented}},
{"_lpython_str_center", {m_builtin, &not_implemented}},
{"_lpython_str_expandtabs", {m_builtin, &not_implemented}}
};
}

Expand Down
59 changes: 59 additions & 0 deletions src/runtime/lpython_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,65 @@ def _lpython_str_isspace(s: str) -> bool:
return False
return True

@overload
def _lpython_str_center(s: str, width: i32, fillchar: str) -> str:
"""
Return centered in a string of length width.
Padding is done using the specified fillchar (default is an ASCII space).
The original string is returned if width is less than or equal to len(s).
"""
if(len(fillchar) != 1):
raise TypeError("The fill character must be exactly one character long")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Considering #2666, I think this will not print the error message. But I think it is fine for now. The error message should start printing as soon as we fix #2666.

str_len: i32 = len(s)
if width <= str_len:
return s
width -= str_len
result: str = ""
left_padding: i32 = i32(width/2) + _mod(width,2)
i: i32
for i in range(left_padding):
result += fillchar
right_padding: i32 = width - left_padding
result += s
for i in range(right_padding):
result += fillchar
Comment on lines +1103 to +1109
Copy link
Contributor

@kmr-srbh kmr-srbh Apr 20, 2024

Choose a reason for hiding this comment

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

We can utilize the new improvement in string repeat added a few days ago and simplify this to just:

right_padding: i32 = width - left_padding
result += fillchar * left_padding
result += s
result += fillchar * right_padding

#2652 needs to be addressed to achieve it. For now we may keep the current for loop.

return result

@overload
def _lpython_str_center(s: str, width: i32) -> str:
return _lpython_str_center(s, width, ' ')

@overload
def _lpython_str_expandtabs(s: str, tabsize: i32) -> str:
"""
Return a copy of the string where all tab characters are replaced
by one or more spaces, depending on the current column and the given tab size.
"""
if len(s) == 0:
return s
col: i32 = 0
result: str = ""
c: str
for c in s:
if c == '\t':
if tabsize > 0:
i: i32
iterations: i32 = tabsize - _mod(col,tabsize)
for i in range(iterations):
result += ' '
Comment on lines +1132 to +1133
Copy link
Collaborator

Choose a reason for hiding this comment

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

For now this is fine, but we can later refactor this to simply use * operator.

col = 0
elif c == '\n' or c == '\r':
result += c
col = 0
else:
result += c
col += 1
return result

@overload
def _lpython_str_expandtabs(s: str) -> str:
return _lpython_str_expandtabs(s, 8)

def list(s: str) -> list[str]:
l: list[str] = []
i: i32
Expand Down
2 changes: 1 addition & 1 deletion tests/reference/asr-array_01_decl-39cf894.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"outfile": null,
"outfile_hash": null,
"stdout": "asr-array_01_decl-39cf894.stdout",
"stdout_hash": "34c5f9983e43e6b5c65f021792e415f0c2e4fe5135c6435eb5322719",
"stdout_hash": "292194a8fe4110a90c90bbcbf94f66b70f82978e14108ded75104711",
"stderr": null,
"stderr_hash": null,
"returncode": 0
Expand Down
Loading