Skip to content
Draft
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
3 changes: 3 additions & 0 deletions integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ RUN(NAME test_builtin_len LABELS cpython llvm)
RUN(NAME test_builtin_float LABELS cpython llvm)
RUN(NAME test_builtin_str_02 LABELS cpython llvm)
RUN(NAME test_builtin_round LABELS cpython llvm)
RUN(NAME test_builtin_capitalize LABELS cpython)
RUN(NAME test_builtin_upper LABELS cpython)
RUN(NAME test_builtin_lower LABELS cpython)
RUN(NAME test_math1 LABELS cpython llvm)
RUN(NAME test_math_02 LABELS cpython llvm)
RUN(NAME test_c_interop_01 LABELS cpython llvm c)
Expand Down
12 changes: 12 additions & 0 deletions integration_tests/test_builtin_capitalize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from lpython_builtin import (capitalize)
from ltypes import i32, f64, f32, i64, c32, c64

def test_capitalize():
i: str
i = capitalize("lpyThon")
Copy link
Collaborator

Choose a reason for hiding this comment

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

s: str
s = "lpyThon"
capitalize(s)

assert i == "Lpython"
i: str
i = capitalize("deVeLoPmEnT")
assert i == "Development"

test_capitalize()
9 changes: 9 additions & 0 deletions integration_tests/test_builtin_lower.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from lpython_builtin import (lower)
from ltypes import i32, f64, f32, i64, c32, c64

def test_lower():
i: str
i = lower("CoMpIlEr")
assert i == "compiler"

test_lower()
9 changes: 9 additions & 0 deletions integration_tests/test_builtin_upper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from lpython_builtin import (upper)
from ltypes import i32, f64, f32, i64, c32, c64

def test_upper():
i: str
i = upper("lpython")
assert i == "LPYTHON"

test_upper()
75 changes: 75 additions & 0 deletions src/lpython/semantics/python_comptime_eval.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <sstream>
#include <algorithm>
#include <iterator>
#include <cctype>

#include <libasr/asr.h>
#include <lpython/bigint.h>
Expand All @@ -34,6 +35,9 @@ struct PythonIntrinsicProcedures {
{"str", {m_builtin, &eval_str}},
{"chr", {m_builtin, &eval_chr}},
{"ord", {m_builtin, &eval_ord}},
{"upper", {m_builtin, &eval_upper}},
{"lower", {m_builtin, &eval_lower}},
{"capitalize", {m_builtin, &eval_capitalize}},
// {"len", {m_builtin, &eval_len}},
{"pow", {m_builtin, &eval_pow}},
// {"int", {m_builtin, &eval_int}},
Expand Down Expand Up @@ -163,6 +167,77 @@ struct PythonIntrinsicProcedures {
ASRUtils::type_to_str_python(arg_type) + "'", loc);
}
}
static ASR::expr_t *eval_upper(Allocator &al, const Location &loc, Vec<ASR::expr_t*> &args) {
LFORTRAN_ASSERT(ASRUtils::all_args_evaluated(args));
if (args.size() != 1) {
throw SemanticError("upper() takes exactly one argument (" +
std::to_string(args.size()) + " given)", loc);
}
ASR::ttype_t* str_type = ASRUtils::expr_type(args[0]);
ASR::expr_t *arg = args[0];
ASR::ttype_t* arg_type = ASRUtils::expr_type(arg);
if (ASRUtils::is_character(*arg_type)) {
char* c = ASR::down_cast<ASR::StringConstant_t>(arg)->m_s;
std::string s = std::string(c);
std::transform(s.begin(), s.end(),s.begin(), ::toupper);
return ASR::down_cast<ASR::expr_t>(ASR::make_StringConstant_t(al, loc, s2c(al, s), str_type));
}
else {
throw SemanticError("upper() only works on strings", loc);
}
}

static ASR::expr_t *eval_lower(Allocator &al, const Location &loc, Vec<ASR::expr_t*> &args) {
LFORTRAN_ASSERT(ASRUtils::all_args_evaluated(args));
if (args.size() != 1) {
throw SemanticError("lower() takes exactly one argument (" +
std::to_string(args.size()) + " given)", loc);
}
ASR::ttype_t* str_type = ASRUtils::expr_type(args[0]);
ASR::expr_t *arg = args[0];
ASR::ttype_t* arg_type = ASRUtils::expr_type(arg);

if (ASRUtils::is_character(*arg_type)) {
char* c = ASR::down_cast<ASR::StringConstant_t>(arg)->m_s;
std::string s = std::string(c);
// std::transform(s.begin(), s.end(),s.begin(), ::tolower);
for (auto & x: s) x = tolower(x);
// boost::to_upper(s);
// std::string newstr = boost::to_upper_copy<std::string>("Hello World");


return ASR::down_cast<ASR::expr_t>(ASR::make_StringConstant_t(al, loc, s2c(al, s), str_type));
}
else {
throw SemanticError("lower() only works on strings", loc);
}
}
static ASR::expr_t *eval_capitalize(Allocator &al, const Location &loc, Vec<ASR::expr_t*> &args) {
LFORTRAN_ASSERT(ASRUtils::all_args_evaluated(args));
if (args.size() != 1) {
throw SemanticError("capitalize() takes exactly one argument (" +
std::to_string(args.size()) + " given)", loc);
}
ASR::ttype_t* str_type = ASRUtils::expr_type(args[0]);
ASR::expr_t *arg = args[0];
ASR::ttype_t* arg_type = ASRUtils::expr_type(arg);

if (ASRUtils::is_character(*arg_type)) {
char* c = ASR::down_cast<ASR::StringConstant_t>(arg)->m_s;
std::string s = std::string(c);
s[0] = std::toupper(s[0]);
std::string sub = s.substr(1);
for (auto & x: sub) x = tolower(x);
s = s[0] + sub;

// std::transform(s.begin()+1, s.end(), s.begin()+1, std::tolower);
return ASR::down_cast<ASR::expr_t>(ASR::make_StringConstant_t(al, loc, s2c(al, s), str_type));
}
else {
throw SemanticError("capitalize() only works on strings", loc);
}
}


static ASR::expr_t *eval_chr(Allocator &al, const Location &loc, Vec<ASR::expr_t*> &args) {
LFORTRAN_ASSERT(ASRUtils::all_args_evaluated(args));
Expand Down
88 changes: 88 additions & 0 deletions src/runtime/lpython_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,93 @@ def str(x: i32) -> str:
result += rev_result[pos]
return result


def capitalize(s: str) -> str:
"""
Return a copy of the string with its first character capitalized and the rest lowercased.
"""
result: str
result = upper(s[0]) + lower(s[1:])
return result

def upper(s: str) -> str:
result : str
char : str
i: i64
for i in range(len(s)):
char = s[i]
if ord(char) >= 97 and ord(char) <=122 :
result += chr(ord(char) - 32)
else :
result += char
return result

def lower(s: str) -> str:
result : str
# result = ''
char : str
i: i64
for i in range(len(s)):
char = s[i]
if ord(char) >= 65 and ord(char) <=90 :
result += chr(ord(char) + 32)
else :
result += char
return result


#: bool() as a generic procedure.
#: supported types for argument:
#: i8, i16, i32, i64, f32, f64, bool
@overload
def bool(x: i32) -> bool:
"""
Return False when the argument `x` is 0, True otherwise.
"""
return x != 0

@overload
def bool(x: i64) -> bool:
return x != 0

@overload
def bool(x: i8) -> bool:
return x != 0

@overload
def bool(x: i16) -> bool:
return x != 0

@overload
def bool(f: f32) -> bool:
return f != 0.0

@overload
def bool(f: f64) -> bool:
"""
Return False when the argument `x` is 0.0, True otherwise.
"""
return f != 0.0

@overload
def bool(s: str) -> bool:
"""
Return False when the argument `s` is an empty string, True otherwise.
"""
return len(s) > 0

@overload
def bool(b: bool) -> bool:
return b

@overload
def bool(c: c32) -> bool:
return c.real != 0.0 or _lfortran_caimag(c) != 0.0

@overload
def bool(c: c64) -> bool:
return c.real != 0.0 or _lfortran_zaimag(c) != 0.0

@interface
def len(s: str) -> i32:
"""
Expand Down Expand Up @@ -582,3 +669,4 @@ def min(a: f64, b: f64) -> f64:
return a
else:
return b