-
Notifications
You must be signed in to change notification settings - Fork 170
Add list.index #1703
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
Add list.index #1703
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from lpython import i32, f64 | ||
|
||
def test_list_index(): | ||
i: i32 | ||
x: list[i32] = [] | ||
y: list[str] = [] | ||
z: list[tuple[i32, str, f64]] = [] | ||
|
||
for i in range(-5, 0): | ||
x.append(i) | ||
assert x.index(i) == len(x)-1 | ||
x.append(i) | ||
assert x.index(i) == len(x)-2 | ||
x.remove(i) | ||
assert x.index(i) == len(x)-1 | ||
|
||
assert x == [-5, -4, -3, -2, -1] | ||
|
||
for i in range(-5, 0): | ||
x.append(i) | ||
assert x.index(i) == 0 | ||
x.remove(i) | ||
assert x.index(i) == len(x)-1 | ||
|
||
# str | ||
y = ['a', 'abc', 'a', 'b', 'abc'] | ||
assert y.index('a') == 0 | ||
assert y.index('abc') == 1 | ||
|
||
# tuple, float | ||
z = [(i32(1), 'a', f64(2.01)), (i32(-1), 'b', f64(2)), (i32(1), 'a', f64(2.02))] | ||
assert z.index((i32(1), 'a', f64(2.01))) == 0 | ||
z.insert(0, (i32(1), 'a', f64(2))) | ||
assert z.index((i32(1), 'a', f64(2.00))) == 0 | ||
z.append((i32(1), 'a', f64(2.00))) | ||
assert z.index((i32(1), 'a', f64(2))) == 0 | ||
z.remove((i32(1), 'a', f64(2))) | ||
assert z.index((i32(1), 'a', f64(2.00))) == 3 | ||
|
||
test_list_index() |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,8 @@ enum class IntrinsicFunctions : int64_t { | |
Gamma, | ||
LogGamma, | ||
Abs, | ||
|
||
ListIndex, | ||
// ... | ||
}; | ||
|
||
|
@@ -453,6 +455,54 @@ namespace Abs { | |
|
||
} // namespace Abs | ||
|
||
namespace ListIndex { | ||
|
||
static inline ASR::expr_t *eval_list_index(Allocator &/*al*/, | ||
const Location &/*loc*/, Vec<ASR::expr_t*>& /*args*/) { | ||
// TODO: To be implemented for ListConstant expression | ||
return nullptr; | ||
} | ||
|
||
static inline ASR::asr_t* create_ListIndex(Allocator& al, const Location& loc, | ||
Vec<ASR::expr_t*>& args, | ||
const std::function<void (const std::string &, const Location &)> err) { | ||
if (args.size() != 2) { | ||
// Support start and end arguments by overloading ListIndex | ||
// intrinsic. We need 3 overload IDs, | ||
// 0 - only list and element | ||
// 1 - list, element and start | ||
// 2 - list, element, start and end | ||
// list, element and end case is not possible as list.index | ||
// doesn't accept keyword arguments | ||
err("For now index() takes exactly one argument", loc); | ||
} | ||
|
||
ASR::expr_t* list_expr = args[0]; | ||
ASR::ttype_t *type = ASRUtils::expr_type(list_expr); | ||
ASR::ttype_t *list_type = ASR::down_cast<ASR::List_t>(type)->m_type; | ||
ASR::ttype_t *ele_type = ASRUtils::expr_type(args[1]); | ||
if (!ASRUtils::check_equal_type(ele_type, list_type)) { | ||
std::string fnd = ASRUtils::get_type_code(ele_type); | ||
std::string org = ASRUtils::get_type_code(list_type); | ||
err( | ||
"Type mismatch in 'index', the types must be compatible " | ||
"(found: '" + fnd + "', expected: '" + org + "')", loc); | ||
} | ||
Vec<ASR::expr_t*> arg_values; | ||
arg_values.reserve(al, args.size()); | ||
for( size_t i = 0; i < args.size(); i++ ) { | ||
arg_values.push_back(al, ASRUtils::expr_value(args[i])); | ||
} | ||
ASR::expr_t* compile_time_value = eval_list_index(al, loc, arg_values); | ||
ASR::ttype_t *to_type = ASRUtils::TYPE(ASR::make_Integer_t(al, loc, | ||
4, nullptr, 0)); | ||
return ASR::make_IntrinsicFunction_t(al, loc, | ||
static_cast<int64_t>(ASRUtils::IntrinsicFunctions::ListIndex), | ||
args.p, args.size(), 0, to_type, compile_time_value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the idea that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. All the error checking related to an intrinsic function resides in one place. Also there is no restriction on using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Later we might need to add some checks to verify() to ensure all arguments to IntrinsicFunctions are correct. For now this is good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can easily do this by registering |
||
} | ||
|
||
} // namespace ListIndex | ||
|
||
|
||
namespace IntrinsicFunctionRegistry { | ||
|
||
|
@@ -468,13 +518,28 @@ namespace IntrinsicFunctionRegistry { | |
&Abs::instantiate_Abs} | ||
}; | ||
|
||
static const std::map<int64_t, std::string>& intrinsic_function_id_to_name = { | ||
{static_cast<int64_t>(ASRUtils::IntrinsicFunctions::LogGamma), | ||
"log_gamma"}, | ||
|
||
{static_cast<int64_t>(ASRUtils::IntrinsicFunctions::Sin), | ||
"sin"}, | ||
{static_cast<int64_t>(ASRUtils::IntrinsicFunctions::Cos), | ||
"cos"}, | ||
{static_cast<int64_t>(ASRUtils::IntrinsicFunctions::Abs), | ||
"abs"}, | ||
{static_cast<int64_t>(ASRUtils::IntrinsicFunctions::ListIndex), | ||
"list.index"} | ||
}; | ||
|
||
static const std::map<std::string, | ||
std::pair<create_intrinsic_function, | ||
eval_intrinsic_function>>& intrinsic_function_by_name_db = { | ||
{"log_gamma", {&LogGamma::create_LogGamma, &LogGamma::eval_log_gamma}}, | ||
{"sin", {&Sin::create_Sin, &Sin::eval_Sin}}, | ||
{"cos", {&Cos::create_Cos, &Cos::eval_Cos}}, | ||
{"abs", {&Abs::create_Abs, &Abs::eval_Abs}}, | ||
{"list.index", {&ListIndex::create_ListIndex, &ListIndex::eval_list_index}}, | ||
}; | ||
|
||
static inline bool is_intrinsic_function(const std::string& name) { | ||
|
@@ -490,9 +555,20 @@ namespace IntrinsicFunctionRegistry { | |
} | ||
|
||
static inline impl_function get_instantiate_function(int64_t id) { | ||
if( intrinsic_function_by_id_db.find(id) == intrinsic_function_by_id_db.end() ) { | ||
return nullptr; | ||
} | ||
return intrinsic_function_by_id_db.at(id); | ||
} | ||
|
||
static inline std::string get_intrinsic_function_name(int64_t id) { | ||
if( intrinsic_function_id_to_name.find(id) == intrinsic_function_id_to_name.end() ) { | ||
throw LCompilersException("IntrinsicFunction with ID " + std::to_string(id) + | ||
" has no name registered for it"); | ||
} | ||
return intrinsic_function_id_to_name.at(id); | ||
} | ||
|
||
} // namespace IntrinsicFunctionRegistry | ||
|
||
#define INTRINSIC_NAME_CASE(X) \ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from lpython import i32 | ||
|
||
def test_list_index_error(): | ||
a: list[i32] | ||
a = [1, 2, 3] | ||
# a.index(1.0) # type mismatch | ||
print(a.index(0)) # no error? | ||
|
||
test_list_index_error() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"basename": "asr-test_list_index-6b8f30b", | ||
"cmd": "lpython --show-asr --no-color {infile} -o {outfile}", | ||
"infile": "tests/errors/test_list_index.py", | ||
"infile_hash": "9c629bc9805dde8f11fc465dc8b35f2c16aef28634c7f105b2e5cfa3", | ||
"outfile": null, | ||
"outfile_hash": null, | ||
"stdout": "asr-test_list_index-6b8f30b.stdout", | ||
"stdout_hash": "d91e269b3ba3053cbecf4e08960e027063d78db443c1bc6440cdf3c6", | ||
"stderr": null, | ||
"stderr_hash": null, | ||
"returncode": 0 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
(TranslationUnit (SymbolTable 1 {main_program: (Program (SymbolTable 3 {}) main_program [] []), test_list_index_error: (Function (SymbolTable 2 {a: (Variable 2 a [] Local () () Default (List (Integer 4 [])) Source Public Required .false.)}) test_list_index_error (FunctionType [] () Source Implementation () .false. .false. .false. .false. .false. [] [] .false.) [] [] [(= (Var 2 a) (ListConstant [(IntegerConstant 1 (Integer 4 [])) (IntegerConstant 2 (Integer 4 [])) (IntegerConstant 3 (Integer 4 []))] (List (Integer 4 []))) ()) (Print () [(ListIndex (Var 2 a) (IntegerConstant 0 (Integer 4 [])) (Integer 4 []) ())] () ())] () Public .false. .false.)}) []) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"basename": "runtime-test_list_index-0483808", | ||
"cmd": "lpython {infile}", | ||
"infile": "tests/errors/test_list_index.py", | ||
"infile_hash": "991dc5eddf2579b7c6b3bc31bb07656cec73bdf91c3196a761985682", | ||
"outfile": null, | ||
"outfile_hash": null, | ||
"stdout": null, | ||
"stdout_hash": null, | ||
"stderr": "runtime-test_list_index-0483808.stderr", | ||
"stderr_hash": "dd3d49b5f2f97ed8f1d27cd73ebca7a8740483660dd4ae702e2048b2", | ||
"returncode": 1 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ValueError: The list does not contain the element: 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit worried that we have a frontend dependency here (
ASRUtils::type_to_str_python
).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! Yes. I would do
get_type_code
here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We now use
get_type_code
since its just an error message so our frontend agnostic type codes should convey the error with same clarity.