Skip to content

Commit 7fea711

Browse files
authored
Merge pull request #152 from namannimmo10/builtin2
Implement `str` and `bool` runtime functions
2 parents bfb1bfd + 9417792 commit 7fea711

25 files changed

+219
-175
lines changed

integration_tests/run_tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"test_math.py",
1616
#"test_builtin.py",
1717
"test_builtin_abs.py",
18+
"test_builtin_bool.py",
1819
"test_math1.py"
1920
]
2021

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
def test_bool():
2+
a: i32
3+
a = 34
4+
assert bool(a) == True
5+
a = 0
6+
assert bool(a) == False
7+
assert bool(-1) == True
8+
assert bool(0) == False

integration_tests/test_builtin_str.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
def test_str_int():
2+
s: str
3+
s = str(356)
4+
assert s == "356"
5+
s = str(-567)
6+
assert s == "-567"
7+
assert str(4) == "4"
8+
assert str(-5) == "-5"

src/libasr/codegen/asr_to_llvm.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2613,8 +2613,24 @@ class ASRToLLVMVisitor : public ASR::BaseVisitor<ASRToLLVMVisitor>
26132613
x.base.base.loc);
26142614
}
26152615
}
2616+
} else if (optype == ASR::ttypeType::Logical) {
2617+
switch (x.m_op) {
2618+
case (ASR::cmpopType::Eq) : {
2619+
tmp = builder->CreateICmpEQ(left, right);
2620+
break;
2621+
}
2622+
case (ASR::cmpopType::NotEq) : {
2623+
tmp = builder->CreateICmpNE(left, right);
2624+
break;
2625+
}
2626+
default : {
2627+
throw CodeGenError("Comparison operator not implemented.",
2628+
x.base.base.loc);
2629+
}
2630+
}
26162631
} else {
2617-
throw CodeGenError("Only Integer, Real, Complex, Character implemented in Compare");
2632+
throw CodeGenError("Only Integer, Real, Complex, Character, and Logical"
2633+
" types are supported for comparison.", x.base.base.loc);
26182634
}
26192635
}
26202636

src/lpython/semantics/python_ast_to_asr.cpp

Lines changed: 25 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,9 +1708,11 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
17081708
right_type->type != ASR::ttypeType::Complex) &&
17091709
x.m_ops != AST::cmpopType::Eq && x.m_ops != AST::cmpopType::NotEq) &&
17101710
(left_type->type != ASR::ttypeType::Character ||
1711-
right_type->type != ASR::ttypeType::Character))) {
1711+
right_type->type != ASR::ttypeType::Character)) &&
1712+
(left_type->type != ASR::ttypeType::Logical ||
1713+
right_type->type != ASR::ttypeType::Logical)) {
17121714
throw SemanticError(
1713-
"Compare: only Integer, Real, or String can be on the LHS and RHS."
1715+
"Compare: only Integer, Real, Logical, or String can be on the LHS and RHS."
17141716
"If operator is Eq or NotEq then Complex type is also acceptable",
17151717
x.base.base.loc);
17161718
}
@@ -1810,6 +1812,27 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
18101812
value = ASR::down_cast<ASR::expr_t>(ASR::make_ConstantLogical_t(
18111813
al, x.base.base.loc, result, type));
18121814

1815+
} else if (ASRUtils::is_logical(*source_type)) {
1816+
bool left_value = ASR::down_cast<ASR::ConstantLogical_t>(
1817+
ASRUtils::expr_value(left))->m_value;
1818+
bool right_value = ASR::down_cast<ASR::ConstantLogical_t>(
1819+
ASRUtils::expr_value(right))->m_value;
1820+
bool result;
1821+
switch (asr_op) {
1822+
case (ASR::cmpopType::Eq): { result = left_value == right_value; break; }
1823+
case (ASR::cmpopType::Gt): { result = left_value > right_value; break; }
1824+
case (ASR::cmpopType::GtE): { result = left_value >= right_value; break; }
1825+
case (ASR::cmpopType::Lt): { result = left_value < right_value; break; }
1826+
case (ASR::cmpopType::LtE): { result = left_value <= right_value; break; }
1827+
case (ASR::cmpopType::NotEq): { result = left_value != right_value; break; }
1828+
default: {
1829+
throw SemanticError("Comparison operator not implemented",
1830+
x.base.base.loc);
1831+
}
1832+
}
1833+
value = ASR::down_cast<ASR::expr_t>(ASR::make_ConstantLogical_t(
1834+
al, x.base.base.loc, result, type));
1835+
18131836
} else if (ASRUtils::is_character(*source_type)) {
18141837
char* left_value = ASR::down_cast<ASR::ConstantString_t>(
18151838
ASRUtils::expr_value(left))->m_s;
@@ -2271,130 +2294,6 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
22712294
throw SemanticError(call_name + "() must have one integer argument",
22722295
x.base.base.loc);
22732296
}
2274-
} else if (call_name == "abs") {
2275-
if (args.size() != 1) {
2276-
throw SemanticError(call_name + "() takes exactly one argument (" +
2277-
std::to_string(args.size()) + " given)", x.base.base.loc);
2278-
}
2279-
std::string rl_path = get_runtime_library_dir();
2280-
SymbolTable *st = current_scope;
2281-
while (st->parent != nullptr) {
2282-
st = st->parent;
2283-
}
2284-
bool ltypes;
2285-
std::string msym = "lpython_builtin";
2286-
ASR::symbol_t *t = (ASR::symbol_t*)(load_module(al, st,
2287-
msym, x.base.base.loc, true, rl_path, ltypes,
2288-
[&](const std::string &msg, const Location &loc) { throw SemanticError(msg, loc); }
2289-
));
2290-
LFORTRAN_ASSERT(!ltypes)
2291-
if (!t) {
2292-
throw SemanticError("The module '" + msym + "' cannot be loaded",
2293-
x.base.base.loc);
2294-
}
2295-
2296-
ASR::Module_t *m = ASR::down_cast<ASR::Module_t>(t);
2297-
2298-
std::string local_sym = "abs";
2299-
t = m->m_symtab->resolve_symbol(local_sym);
2300-
if (!t) {
2301-
throw SemanticError("ICE: The symbol '" + local_sym + "' not found in the module '" + msym + "'",
2302-
x.base.base.loc);
2303-
}
2304-
if (ASR::is_a<ASR::Function_t>(*t)) {
2305-
if (current_scope->scope.find(local_sym) != current_scope->scope.end()) {
2306-
throw SemanticError("Function already defined",
2307-
x.base.base.loc);
2308-
}
2309-
ASR::Function_t *mfn = ASR::down_cast<ASR::Function_t>(t);
2310-
// `mfn` is the Function in a module. Now we construct
2311-
// an ExternalSymbol that points to it.
2312-
Str name;
2313-
name.from_str(al, local_sym);
2314-
char *cname = name.c_str(al);
2315-
ASR::asr_t *fn = ASR::make_ExternalSymbol_t(
2316-
al, mfn->base.base.loc,
2317-
/* a_symtab */ current_scope,
2318-
/* a_name */ cname,
2319-
(ASR::symbol_t*)mfn,
2320-
m->m_name, nullptr, 0, mfn->m_name,
2321-
ASR::accessType::Public
2322-
);
2323-
current_scope->scope[local_sym] = ASR::down_cast<ASR::symbol_t>(fn);
2324-
2325-
ASR::ttype_t *a_type = ASRUtils::TYPE(ASR::make_Real_t(al,
2326-
x.base.base.loc, 8, nullptr, 0));
2327-
tmp = ASR::make_FunctionCall_t(al, x.base.base.loc, ASR::down_cast<ASR::symbol_t>(fn),
2328-
nullptr, args.p, args.size(), nullptr, 0, a_type, nullptr, nullptr);
2329-
return;
2330-
} else {
2331-
throw SemanticError("ICE: Abs expected to be a function", x.base.base.loc);
2332-
}
2333-
// Compile time value implementation:
2334-
/*
2335-
ASR::expr_t* arg = ASRUtils::expr_value(args[0]);
2336-
ASR::ttype_t* t = ASRUtils::expr_type(arg);
2337-
ASR::ttype_t* real_type = ASRUtils::TYPE(ASR::make_Real_t(al,
2338-
x.base.base.loc, 8, nullptr, 0));
2339-
ASR::ttype_t *int_type = ASRUtils::TYPE(ASR::make_Integer_t(al,
2340-
x.base.base.loc, 4, nullptr, 0));
2341-
if (ASRUtils::is_real(*t)) {
2342-
double rv = ASR::down_cast<ASR::ConstantReal_t>(arg)->m_r;
2343-
double val = std::abs(rv);
2344-
tmp = ASR::make_ConstantReal_t(al, x.base.base.loc, val, t);
2345-
} else if (ASRUtils::is_integer(*t)) {
2346-
int64_t rv = ASR::down_cast<ASR::ConstantInteger_t>(arg)->m_n;
2347-
int64_t val = std::abs(rv);
2348-
tmp = ASR::make_ConstantInteger_t(al, x.base.base.loc, val, t);
2349-
} else if (ASRUtils::is_complex(*t)) {
2350-
double re = ASR::down_cast<ASR::ConstantComplex_t>(arg)->m_re;
2351-
double im = ASR::down_cast<ASR::ConstantComplex_t>(arg)->m_im;
2352-
std::complex<double> c(re, im);
2353-
double result = std::abs(c);
2354-
tmp = ASR::make_ConstantReal_t(al, x.base.base.loc, result, real_type);
2355-
} else if (ASRUtils::is_logical(*t)) {
2356-
bool rv = ASR::down_cast<ASR::ConstantLogical_t>(arg)->m_value;
2357-
int8_t val = rv ? 1 : 0;
2358-
tmp = ASR::make_ConstantInteger_t(al, x.base.base.loc, val, int_type);
2359-
} else {
2360-
throw SemanticError(call_name + "() must have one real, integer, complex, or logical argument",
2361-
x.base.base.loc);
2362-
}
2363-
return;
2364-
*/
2365-
} else if (call_name == "bool") {
2366-
if (args.size() != 1) {
2367-
throw SemanticError(call_name + "() takes exactly one argument (" +
2368-
std::to_string(args.size()) + " given)", x.base.base.loc);
2369-
}
2370-
ASR::ttype_t *type = ASRUtils::TYPE(ASR::make_Logical_t(al, x.base.base.loc,
2371-
1, nullptr, 0));
2372-
ASR::expr_t* arg = ASRUtils::expr_value(args[0]);
2373-
ASR::ttype_t* t = ASRUtils::expr_type(arg);
2374-
bool result;
2375-
if (ASRUtils::is_real(*t)) {
2376-
double rv = ASR::down_cast<ASR::ConstantReal_t>(arg)->m_r;
2377-
result = rv ? true : false;
2378-
} else if (ASRUtils::is_integer(*t)) {
2379-
int64_t rv = ASR::down_cast<ASR::ConstantInteger_t>(arg)->m_n;
2380-
result = rv ? true : false;
2381-
} else if (ASRUtils::is_complex(*t)) {
2382-
double re = ASR::down_cast<ASR::ConstantComplex_t>(arg)->m_re;
2383-
double im = ASR::down_cast<ASR::ConstantComplex_t>(arg)->m_im;
2384-
std::complex<double> c(re, im);
2385-
result = (re || im) ? true : false;
2386-
} else if (ASRUtils::is_logical(*t)) {
2387-
bool rv = ASR::down_cast<ASR::ConstantLogical_t>(arg)->m_value;
2388-
result = rv;
2389-
} else if (ASRUtils::is_character(*t)) {
2390-
char* c = ASR::down_cast<ASR::ConstantString_t>(ASRUtils::expr_value(arg))->m_s;
2391-
result = strlen(s2c(al, std::string(c))) ? true : false;
2392-
} else {
2393-
throw SemanticError(call_name + "() must have one real, integer, character,"
2394-
" complex, or logical argument", x.base.base.loc);
2395-
}
2396-
tmp = ASR::make_ConstantLogical_t(al, x.base.base.loc, result, type);
2397-
return;
23982297
} else if (call_name == "callable") {
23992298
if (args.size() != 1) {
24002299
throw SemanticError(call_name + "() takes exactly one argument (" +
@@ -2482,40 +2381,6 @@ class BodyVisitor : public CommonVisitor<BodyVisitor> {
24822381
ASRUtils::type_to_str(float_type) + "'", x.base.base.loc);
24832382
}
24842383
return;
2485-
} else if (call_name == "str") {
2486-
ASR::ttype_t* str_type = ASRUtils::TYPE(ASR::make_Character_t(al,
2487-
x.base.base.loc, 1, 1, nullptr, nullptr, 0));
2488-
if (args.size() == 0) { // create an empty string
2489-
tmp = ASR::make_ConstantString_t(al, x.base.base.loc, s2c(al, ""), str_type);
2490-
return;
2491-
}
2492-
ASR::expr_t* arg = ASRUtils::expr_value(args[0]);
2493-
ASR::ttype_t* arg_type = ASRUtils::expr_type(arg);
2494-
if (arg == nullptr) {
2495-
throw SemanticError("runtime str(x) is not supported, only compile time for now",
2496-
x.base.base.loc);
2497-
}
2498-
if (ASRUtils::is_integer(*arg_type)) {
2499-
int64_t ival = ASR::down_cast<ASR::ConstantInteger_t>(arg)->m_n;
2500-
std::string s = std::to_string(ival);
2501-
tmp = ASR::make_ConstantString_t(al, x.base.base.loc, s2c(al, s), str_type);
2502-
} else if (ASRUtils::is_real(*arg_type)) {
2503-
double rval = ASR::down_cast<ASR::ConstantReal_t>(arg)->m_r;
2504-
std::string s = std::to_string(rval);
2505-
tmp = ASR::make_ConstantString_t(al, x.base.base.loc, s2c(al, s), str_type);
2506-
} else if (ASRUtils::is_logical(*arg_type)) {
2507-
bool rv = ASR::down_cast<ASR::ConstantLogical_t>(arg)->m_value;
2508-
std::string s = rv ? "True" : "False";
2509-
tmp = ASR::make_ConstantString_t(al, x.base.base.loc, s2c(al, s), str_type);
2510-
} else if (ASRUtils::is_character(*arg_type)) {
2511-
char* c = ASR::down_cast<ASR::ConstantString_t>(arg)->m_s;
2512-
std::string s = std::string(c);
2513-
tmp = ASR::make_ConstantString_t(al, x.base.base.loc, s2c(al, s), str_type);
2514-
} else {
2515-
throw SemanticError("str() argument must be real, integer, logical, or a string, not '" +
2516-
ASRUtils::type_to_str(arg_type) + "'", x.base.base.loc);
2517-
}
2518-
return;
25192384
} else if (call_name == "divmod") {
25202385
if (args.size() != 2) {
25212386
throw SemanticError(call_name + "() takes exactly two arguments (" +

src/lpython/semantics/python_comptime_eval.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#define LPYTHON_SEMANTICS_COMPTIME_EVAL_H
33

44
#include <complex>
5+
#include <string>
6+
#include <cstring>
57

68
#include <libasr/asr.h>
79
#include <lpython/ast.h>
@@ -26,6 +28,8 @@ struct PythonIntrinsicProcedures {
2628
PythonIntrinsicProcedures() {
2729
comptime_eval_map = {
2830
{"abs", {m_builtin, &eval_abs}},
31+
{"str", {m_builtin, &eval_str}},
32+
{"bool", {m_builtin, &eval_bool}},
2933
};
3034
}
3135

@@ -100,6 +104,70 @@ struct PythonIntrinsicProcedures {
100104
}
101105
}
102106

107+
static ASR::expr_t *eval_str(Allocator &al, const Location &loc, Vec<ASR::expr_t*> &args) {
108+
ASR::ttype_t* str_type = ASRUtils::TYPE(ASR::make_Character_t(al,
109+
loc, 1, 1, nullptr, nullptr, 0));
110+
if (args.size() == 0) { // create an empty string
111+
return ASR::down_cast<ASR::expr_t>(ASR::make_ConstantString_t(al, loc, s2c(al, ""), str_type));
112+
}
113+
ASR::expr_t* arg = ASRUtils::expr_value(args[0]);
114+
ASR::ttype_t* arg_type = ASRUtils::expr_type(arg);
115+
if (ASRUtils::is_integer(*arg_type)) {
116+
int64_t ival = ASR::down_cast<ASR::ConstantInteger_t>(arg)->m_n;
117+
std::string s = std::to_string(ival);
118+
return ASR::down_cast<ASR::expr_t>(ASR::make_ConstantString_t(al, loc, s2c(al, s), str_type));
119+
} else if (ASRUtils::is_real(*arg_type)) {
120+
double rval = ASR::down_cast<ASR::ConstantReal_t>(arg)->m_r;
121+
std::string s = std::to_string(rval);
122+
return ASR::down_cast<ASR::expr_t>(ASR::make_ConstantString_t(al, loc, s2c(al, s), str_type));
123+
} else if (ASRUtils::is_logical(*arg_type)) {
124+
bool rv = ASR::down_cast<ASR::ConstantLogical_t>(arg)->m_value;
125+
std::string s = rv ? "True" : "False";
126+
return ASR::down_cast<ASR::expr_t>(ASR::make_ConstantString_t(al, loc, s2c(al, s), str_type));
127+
} else if (ASRUtils::is_character(*arg_type)) {
128+
char* c = ASR::down_cast<ASR::ConstantString_t>(arg)->m_s;
129+
std::string s = std::string(c);
130+
return ASR::down_cast<ASR::expr_t>(ASR::make_ConstantString_t(al, loc, s2c(al, s), str_type));
131+
} else {
132+
throw SemanticError("str() argument must be real, integer, logical, or a string, not '" +
133+
ASRUtils::type_to_str(arg_type) + "'", loc);
134+
}
135+
}
136+
137+
static ASR::expr_t *eval_bool(Allocator &al, const Location &loc, Vec<ASR::expr_t*> &args) {
138+
if (args.size() != 1) {
139+
throw SemanticError("bool() takes exactly one argument (" +
140+
std::to_string(args.size()) + " given)", loc);
141+
}
142+
ASR::ttype_t *type = ASRUtils::TYPE(ASR::make_Logical_t(al, loc,
143+
1, nullptr, 0));
144+
ASR::expr_t* arg = ASRUtils::expr_value(args[0]);
145+
ASR::ttype_t* t = ASRUtils::expr_type(arg);
146+
bool result;
147+
if (ASRUtils::is_real(*t)) {
148+
double rv = ASR::down_cast<ASR::ConstantReal_t>(arg)->m_r;
149+
result = rv ? true : false;
150+
} else if (ASRUtils::is_integer(*t)) {
151+
int64_t rv = ASR::down_cast<ASR::ConstantInteger_t>(arg)->m_n;
152+
result = rv ? true : false;
153+
} else if (ASRUtils::is_complex(*t)) {
154+
double re = ASR::down_cast<ASR::ConstantComplex_t>(arg)->m_re;
155+
double im = ASR::down_cast<ASR::ConstantComplex_t>(arg)->m_im;
156+
std::complex<double> c(re, im);
157+
result = (re || im) ? true : false;
158+
} else if (ASRUtils::is_logical(*t)) {
159+
bool rv = ASR::down_cast<ASR::ConstantLogical_t>(arg)->m_value;
160+
result = rv;
161+
} else if (ASRUtils::is_character(*t)) {
162+
char* c = ASR::down_cast<ASR::ConstantString_t>(ASRUtils::expr_value(arg))->m_s;
163+
result = strlen(s2c(al, std::string(c))) ? true : false;
164+
} else {
165+
throw SemanticError("bool() must have one real, integer, character,"
166+
" complex, or logical argument, not '" + ASRUtils::type_to_str(t) + "'", loc);
167+
}
168+
return ASR::down_cast<ASR::expr_t>(make_ConstantLogical_t(al, loc, result, type));
169+
}
170+
103171
}; // ComptimeEval
104172

105173
} // namespace LFortran

src/runtime/lpython_builtin.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,40 @@ def abs(x: f64) -> f64:
3838
return x
3939
else:
4040
return -x
41+
42+
43+
def str(x: i32) -> str:
44+
"""
45+
Return the string representation of an integer `x`.
46+
"""
47+
if x == 0:
48+
return '0'
49+
result: str
50+
result = ''
51+
if x < 0:
52+
result += '-'
53+
x = -x
54+
rev_result: str
55+
rev_result = ''
56+
rev_result_len: i32
57+
rev_result_len = 0
58+
pos_to_str: list[str]
59+
pos_to_str = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
60+
while x > 0:
61+
rev_result += pos_to_str[x - (x//10)*10]
62+
rev_result_len += 1
63+
x = x//10
64+
pos: i32
65+
for pos in range(rev_result_len - 1, -1, -1):
66+
result += rev_result[pos]
67+
return result
68+
69+
70+
def bool(x: i32) -> bool:
71+
"""
72+
Return False when the argument `x` is 0, True otherwise.
73+
"""
74+
if x == 0:
75+
return False
76+
else:
77+
return True

0 commit comments

Comments
 (0)