Add support for Python-like negative indexing

Negative indexing is a useful feature in Python, especially when combined
with array slicing. Array slicing will hopefully be implemented later, but
negative indexing is useful in its own right.

A negative index is indexing from the end of an array,
"array[-1] == array[array.size()-1]", using a negative index
larger/smaller than the length of the array is still an error.

While primarily useful for arrays and strings, support is also added to
"array like" structures like Vector3 and Color. This is done just
to be consistent; vector3[2] is much clearer than vector3[-1], but disallowing
it while allowing it for an array with 3 elements seems confusing.
This commit is contained in:
Dennis Brakhane 2016-05-28 16:12:10 +02:00
parent 842057e56f
commit 1e068d34f4
2 changed files with 34 additions and 6 deletions

View file

@ -861,7 +861,6 @@ void Variant::evaluate(const Operator& p_op, const Variant& p_a, const Variant&
} break; } break;
//logic //logic
case OP_AND: { case OP_AND: {
bool l = p_a.booleanize(r_valid); bool l = p_a.booleanize(r_valid);
if (!r_valid) if (!r_valid)
return; return;
@ -978,6 +977,8 @@ Variant Variant::get_named(const StringName& p_index, bool *r_valid) const {
int index = p_index;\ int index = p_index;\
m_type *arr=reinterpret_cast<m_type* >(_data._mem);\ m_type *arr=reinterpret_cast<m_type* >(_data._mem);\
\ \
if (index<0)\
index += arr->size();\
if (index>=0 && index<arr->size()) {\ if (index>=0 && index<arr->size()) {\
valid=true;\ valid=true;\
cmd;\ cmd;\
@ -1011,7 +1012,10 @@ void Variant::set(const Variant& p_index, const Variant& p_value, bool *r_valid)
int idx=p_index; int idx=p_index;
String *str=reinterpret_cast<String*>(_data._mem); String *str=reinterpret_cast<String*>(_data._mem);
if (idx <0 || idx>=str->length()) int len = str->length();
if (idx<0)
idx += len;
if (idx<0 || idx>=len)
return; return;
String chr; String chr;
@ -1025,7 +1029,7 @@ void Variant::set(const Variant& p_index, const Variant& p_value, bool *r_valid)
return; return;
} }
*str = str->substr(0,idx)+chr+str->substr(idx+1,str->length()); *str = str->substr(0,idx)+chr+str->substr(idx+1, len);
valid=true; valid=true;
return; return;
@ -1040,6 +1044,8 @@ void Variant::set(const Variant& p_index, const Variant& p_value, bool *r_valid)
// scalar index // scalar index
int idx=p_index; int idx=p_index;
if (idx<0)
idx += 2;
if (idx>=0 && idx<2) { if (idx>=0 && idx<2) {
Vector2 *v=reinterpret_cast<Vector2*>(_data._mem); Vector2 *v=reinterpret_cast<Vector2*>(_data._mem);
@ -1098,6 +1104,8 @@ void Variant::set(const Variant& p_index, const Variant& p_value, bool *r_valid)
int index = p_index; int index = p_index;
if (index<0)
index += 3;
if (index>=0 && index<3) { if (index>=0 && index<3) {
Matrix32 *v=_data._matrix32; Matrix32 *v=_data._matrix32;
@ -1134,6 +1142,8 @@ void Variant::set(const Variant& p_index, const Variant& p_value, bool *r_valid)
if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) { if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) {
//scalar index //scalar index
int idx=p_index; int idx=p_index;
if (idx<0)
idx += 3;
if (idx>=0 && idx<3) { if (idx>=0 && idx<3) {
Vector3 *v=reinterpret_cast<Vector3*>(_data._mem); Vector3 *v=reinterpret_cast<Vector3*>(_data._mem);
@ -1268,6 +1278,8 @@ void Variant::set(const Variant& p_index, const Variant& p_value, bool *r_valid)
int index = p_index; int index = p_index;
if (index<0)
index += 3;
if (index>=0 && index<3) { if (index>=0 && index<3) {
Matrix3 *v=_data._matrix3; Matrix3 *v=_data._matrix3;
@ -1306,6 +1318,8 @@ void Variant::set(const Variant& p_index, const Variant& p_value, bool *r_valid)
int index = p_index; int index = p_index;
if (index<0)
index += 4;
if (index>=0 && index<4) { if (index>=0 && index<4) {
Transform *v=_data._transform; Transform *v=_data._transform;
valid=true; valid=true;
@ -1394,6 +1408,8 @@ void Variant::set(const Variant& p_index, const Variant& p_value, bool *r_valid)
} else if (p_index.get_type()==Variant::INT) { } else if (p_index.get_type()==Variant::INT) {
int idx = p_index; int idx = p_index;
if (idx<0)
idx += 4;
if (idx>=0 || idx<4) { if (idx>=0 || idx<4) {
Color *v=reinterpret_cast<Color*>(_data._mem); Color *v=reinterpret_cast<Color*>(_data._mem);
(*v)[idx]=p_value; (*v)[idx]=p_value;
@ -1841,6 +1857,8 @@ Variant Variant::get(const Variant& p_index, bool *r_valid) const {
int idx=p_index; int idx=p_index;
const String *str=reinterpret_cast<const String*>(_data._mem); const String *str=reinterpret_cast<const String*>(_data._mem);
if (idx<0)
idx += str->length();
if (idx >=0 && idx<str->length()) { if (idx >=0 && idx<str->length()) {
valid=true; valid=true;
@ -1854,6 +1872,8 @@ Variant Variant::get(const Variant& p_index, bool *r_valid) const {
if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) { if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) {
// scalar index // scalar index
int idx=p_index; int idx=p_index;
if (idx<0)
idx += 2;
if (idx>=0 && idx<2) { if (idx>=0 && idx<2) {
const Vector2 *v=reinterpret_cast<const Vector2*>(_data._mem); const Vector2 *v=reinterpret_cast<const Vector2*>(_data._mem);
@ -1899,6 +1919,8 @@ Variant Variant::get(const Variant& p_index, bool *r_valid) const {
if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) { if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) {
//scalar index //scalar index
int idx=p_index; int idx=p_index;
if (idx<0)
idx += 3;
if (idx>=0 && idx<3) { if (idx>=0 && idx<3) {
const Vector3 *v=reinterpret_cast<const Vector3*>(_data._mem); const Vector3 *v=reinterpret_cast<const Vector3*>(_data._mem);
@ -1929,6 +1951,8 @@ Variant Variant::get(const Variant& p_index, bool *r_valid) const {
int index = p_index; int index = p_index;
if (index<0)
index += 3;
if (index>=0 && index<3) { if (index>=0 && index<3) {
const Matrix32 *v=_data._matrix32; const Matrix32 *v=_data._matrix32;
@ -2024,7 +2048,8 @@ Variant Variant::get(const Variant& p_index, bool *r_valid) const {
if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) { if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) {
int index = p_index; int index = p_index;
if (index<0)
index += 3;
if (index>=0 && index<3) { if (index>=0 && index<3) {
const Matrix3 *v=_data._matrix3; const Matrix3 *v=_data._matrix3;
@ -2054,7 +2079,8 @@ Variant Variant::get(const Variant& p_index, bool *r_valid) const {
if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) { if (p_index.get_type()==Variant::INT || p_index.get_type()==Variant::REAL) {
int index = p_index; int index = p_index;
if (index<0)
index += 4;
if (index>=0 && index<4) { if (index>=0 && index<4) {
const Transform *v=_data._transform; const Transform *v=_data._transform;
valid=true; valid=true;
@ -2118,6 +2144,8 @@ Variant Variant::get(const Variant& p_index, bool *r_valid) const {
} else if (p_index.get_type()==Variant::INT) { } else if (p_index.get_type()==Variant::INT) {
int idx = p_index; int idx = p_index;
if (idx<0)
idx += 4;
if (idx>=0 || idx<4) { if (idx>=0 || idx<4) {
const Color *v=reinterpret_cast<const Color*>(_data._mem); const Color *v=reinterpret_cast<const Color*>(_data._mem);
valid=true; valid=true;

View file

@ -4325,7 +4325,7 @@
Generic array datatype. Generic array datatype.
</brief_description> </brief_description>
<description> <description>
Generic array, contains several elements of any type, accessible by numerical index starting at 0. Arrays are always passed by reference. Generic array, contains several elements of any type, accessible by numerical index starting at 0. Negative indices can be used to count from the right, like in Python. Arrays are always passed by reference.
</description> </description>
<methods> <methods>
<method name="append"> <method name="append">