Skip to content

Commit 5f7ef49

Browse files
farah-salamaubaidskkmr-srbh
authored
Add str.center() and str.expandtabs() builtin functions (#2651)
* add center and expandtabs tests * add _lpython_str_center and _lpython_str_expandtabs * add _lpython_str_center and _lpython_str_expandtabs * fix str.center() * fix typo in line 6924 * update test references * update tests references * edit if condition in str.expandtabs * edit center and expandtabs tests * update test references * remove unnecessary print statements * fix str.expandtabs * Revert "remove unnecessary print statements" This reverts commit 1a9a70c. * edit str.expandtabs * edit str.expandtabs * edit str.center * Formatting changes * Formatting changes * Refactor to use len() once Co-authored-by: Saurabh Kumar <[email protected]> --------- Co-authored-by: Shaikh Ubaid <[email protected]> Co-authored-by: Saurabh Kumar <[email protected]>
1 parent abc5de8 commit 5f7ef49

File tree

60 files changed

+1366
-1225
lines changed

Some content is hidden

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

60 files changed

+1366
-1225
lines changed

integration_tests/test_str_attributes.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,31 @@ def is_numeric():
472472
assert "".isnumeric() == False
473473
assert "ab2%3".isnumeric() == False
474474

475+
def center():
476+
s: str = "test"
477+
assert s.center(8,'*') == "**test**"
478+
assert s.center(11) == " test "
479+
assert s.center(2) == "test"
480+
assert s.center(4) == "test"
481+
assert s.center(9,'/') == "///test//"
482+
483+
def expandtabs():
484+
s: str = '01\t012\t0123\t01234'
485+
assert s.expandtabs() == "01 012 0123 01234"
486+
assert s.expandtabs(4) == "01 012 0123 01234"
487+
assert s.expandtabs(-1) == "01012012301234"
488+
s = '\t'
489+
assert s.expandtabs() == " "
490+
s = ''
491+
assert s.expandtabs() == ""
492+
s = '\tThis\ris\na\ttest'
493+
assert s.expandtabs(4) == " This\ris\na test"
494+
s = '\t\t\t'
495+
assert s.expandtabs(2) == " "
496+
s = 'test\ttest'
497+
assert s.expandtabs(0) == "testtest"
498+
assert s.expandtabs(-5) == "testtest"
499+
475500
def check():
476501
capitalize()
477502
lower()
@@ -492,6 +517,8 @@ def check():
492517
is_space()
493518
is_alnum()
494519
is_numeric()
520+
center()
521+
expandtabs()
495522

496523

497524
check()

src/lpython/semantics/python_ast_to_asr.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6880,6 +6880,59 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
68806880
value.m_value = args[2].m_value;
68816881
fn_args.push_back(al, value);
68826882
}
6883+
} else if(attr_name == "center") {
6884+
if (args.size() != 1 && args.size() != 2) {
6885+
throw SemanticError("str.center() takes one or two argument",
6886+
loc);
6887+
}
6888+
ASR::expr_t *arg_value = args[0].m_value;
6889+
ASR::ttype_t *arg_value_type = ASRUtils::expr_type(arg_value);
6890+
if (!ASRUtils::is_integer(*arg_value_type)) {
6891+
throw SemanticError("str.center() argument 1 must be integer", loc);
6892+
}
6893+
6894+
fn_call_name = "_lpython_str_center";
6895+
ASR::call_arg_t str;
6896+
str.loc = loc;
6897+
str.m_value = s_var;
6898+
6899+
ASR::call_arg_t value;
6900+
value.loc = loc;
6901+
value.m_value = args[0].m_value;
6902+
fn_args.push_back(al, str);
6903+
fn_args.push_back(al, value);
6904+
6905+
if(args.size() == 2){
6906+
ASR::expr_t *arg_value = args[1].m_value;
6907+
ASR::ttype_t *arg_value_type = ASRUtils::expr_type(arg_value);
6908+
if (!ASRUtils::is_character(*arg_value_type)) {
6909+
throw SemanticError("str.center() argument 2 must be str", loc);
6910+
}
6911+
value.m_value = args[1].m_value;
6912+
fn_args.push_back(al, value);
6913+
}
6914+
} else if(attr_name == "expandtabs") {
6915+
if(args.size() > 1) {
6916+
throw SemanticError("str.expandtabs() takes at most one argument.", loc);
6917+
}
6918+
fn_call_name = "_lpython_str_expandtabs";
6919+
ASR::call_arg_t str;
6920+
str.loc = loc;
6921+
str.m_value = s_var;
6922+
fn_args.push_back(al, str);
6923+
6924+
if(args.size() == 1){
6925+
ASR::expr_t *arg_value = args[0].m_value;
6926+
ASR::ttype_t *arg_value_type = ASRUtils::expr_type(arg_value);
6927+
if (!ASRUtils::is_integer(*arg_value_type)) {
6928+
throw SemanticError("str.expandtabs() argument must be integer", loc);
6929+
}
6930+
6931+
ASR::call_arg_t value;
6932+
value.loc = loc;
6933+
value.m_value = args[0].m_value;
6934+
fn_args.push_back(al, value);
6935+
}
68836936
} else if(attr_name.size() > 2 && attr_name[0] == 'i' && attr_name[1] == 's') {
68846937
/*
68856938
String Validation Methods i.e all "is" based functions are handled here

src/lpython/semantics/python_comptime_eval.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ struct PythonIntrinsicProcedures {
100100
{"_lpython_str_isupper", {m_builtin, &not_implemented}},
101101
{"_lpython_str_isdecimal", {m_builtin, &not_implemented}},
102102
{"_lpython_str_isascii", {m_builtin, &not_implemented}},
103-
{"_lpython_str_isspace", {m_builtin, &not_implemented}}
103+
{"_lpython_str_isspace", {m_builtin, &not_implemented}},
104+
{"_lpython_str_center", {m_builtin, &not_implemented}},
105+
{"_lpython_str_expandtabs", {m_builtin, &not_implemented}}
104106
};
105107
}
106108

src/runtime/lpython_builtin.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,65 @@ def _lpython_str_isspace(s: str) -> bool:
10851085
return False
10861086
return True
10871087

1088+
@overload
1089+
def _lpython_str_center(s: str, width: i32, fillchar: str) -> str:
1090+
"""
1091+
Return centered in a string of length width.
1092+
Padding is done using the specified fillchar (default is an ASCII space).
1093+
The original string is returned if width is less than or equal to len(s).
1094+
"""
1095+
if(len(fillchar) != 1):
1096+
raise TypeError("The fill character must be exactly one character long")
1097+
str_len: i32 = len(s)
1098+
if width <= str_len:
1099+
return s
1100+
width -= str_len
1101+
result: str = ""
1102+
left_padding: i32 = i32(width/2) + _mod(width,2)
1103+
i: i32
1104+
for i in range(left_padding):
1105+
result += fillchar
1106+
right_padding: i32 = width - left_padding
1107+
result += s
1108+
for i in range(right_padding):
1109+
result += fillchar
1110+
return result
1111+
1112+
@overload
1113+
def _lpython_str_center(s: str, width: i32) -> str:
1114+
return _lpython_str_center(s, width, ' ')
1115+
1116+
@overload
1117+
def _lpython_str_expandtabs(s: str, tabsize: i32) -> str:
1118+
"""
1119+
Return a copy of the string where all tab characters are replaced
1120+
by one or more spaces, depending on the current column and the given tab size.
1121+
"""
1122+
if len(s) == 0:
1123+
return s
1124+
col: i32 = 0
1125+
result: str = ""
1126+
c: str
1127+
for c in s:
1128+
if c == '\t':
1129+
if tabsize > 0:
1130+
i: i32
1131+
iterations: i32 = tabsize - _mod(col,tabsize)
1132+
for i in range(iterations):
1133+
result += ' '
1134+
col = 0
1135+
elif c == '\n' or c == '\r':
1136+
result += c
1137+
col = 0
1138+
else:
1139+
result += c
1140+
col += 1
1141+
return result
1142+
1143+
@overload
1144+
def _lpython_str_expandtabs(s: str) -> str:
1145+
return _lpython_str_expandtabs(s, 8)
1146+
10881147
def list(s: str) -> list[str]:
10891148
l: list[str] = []
10901149
i: 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": "34c5f9983e43e6b5c65f021792e415f0c2e4fe5135c6435eb5322719",
9+
"stdout_hash": "292194a8fe4110a90c90bbcbf94f66b70f82978e14108ded75104711",
1010
"stderr": null,
1111
"stderr_hash": null,
1212
"returncode": 0

0 commit comments

Comments
 (0)