From bc87ce62365b71ba5da5afffd8ddf90c6ca4af0a Mon Sep 17 00:00:00 2001 From: sheepandshepherd Date: Sat, 20 Feb 2016 01:13:57 +0100 Subject: [PATCH 1/3] Add RangeIterator class for loops --- core/range_iterator.cpp | 169 +++++++++++++++++++++++++++++++++++ core/range_iterator.h | 72 +++++++++++++++ core/register_core_types.cpp | 3 + 3 files changed, 244 insertions(+) create mode 100644 core/range_iterator.cpp create mode 100644 core/range_iterator.h diff --git a/core/range_iterator.cpp b/core/range_iterator.cpp new file mode 100644 index 00000000000..9534e011d72 --- /dev/null +++ b/core/range_iterator.cpp @@ -0,0 +1,169 @@ +/*************************************************************************/ +/* range_iterator.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "range_iterator.h" +#include "object_type_db.h" + +void RangeIterator::_bind_methods() { + ObjectTypeDB::bind_method(_MD("_iter_init","arg"),&RangeIterator::_iter_init); + ObjectTypeDB::bind_method(_MD("_iter_next","arg"),&RangeIterator::_iter_next); + ObjectTypeDB::bind_method(_MD("_iter_get","arg"),&RangeIterator::_iter_get); + ObjectTypeDB::bind_method(_MD("is_finished"),&RangeIterator::is_finished); + ObjectTypeDB::bind_method(_MD("to_array"),&RangeIterator::to_array); + ObjectTypeDB::bind_method(_MD("set_range","arg1","arg2","arg3"),&RangeIterator::_set_range,DEFVAL(Variant()),DEFVAL(Variant())); +} + +bool RangeIterator::_iter_init(Variant arg) { + return !is_finished(); +} + +bool RangeIterator::_iter_next(Variant arg) { + current += step; + return !is_finished(); +} + +Variant RangeIterator::_iter_get(Variant arg) { + return Variant(current); +} + +bool RangeIterator::is_finished() { + if(step > 0) + { + return current >= stop; + } + else + { + return current <= stop; + } +} + +Array RangeIterator::to_array() { + if (step==0) { + ERR_EXPLAIN("step is zero!"); + ERR_FAIL_V(Array()); + } + + Array arr(true); + if (current >= stop && step > 0) { + return arr; + } + if (current <= stop && step < 0) { + return arr; + } + + //calculate how many + int count=0; + if (step > 0) { + count=((stop-current-1)/step)+1; + } else { + count=((current-stop-1)/-step)+1; + } + + arr.resize(count); + + if (step > 0) { + int idx=0; + for(int i=current;istop;i+=step) { + arr[idx++]=i; + } + } + + return arr; +} + +void RangeIterator::set_range(int stop) { + this->current = 0; + this->stop = stop; + this->step = (stop > 0)?(1):(-1); +} + +void RangeIterator::set_range(int start, int stop) { + this->current = start; + this->stop = stop; + this->step = (stop > start)?(1):(-1); +} + +void RangeIterator::set_range(int start, int stop, int step) { + if(step == 0) + { + ERR_EXPLAIN("step is zero!"); + ERR_FAIL(); + } + + this->current = start; + this->stop = stop; + this->step = step; +} + +Ref RangeIterator::_set_range(Variant arg1, Variant arg2, Variant arg3) +{ + bool valid = true; + if(arg1.get_type() == Variant::INT) + { + if(arg2.get_type() == Variant::INT) + { + if(arg3.get_type() == Variant::INT) set_range((int)arg1, (int)arg2, (int)arg3); // (start, end, step) + else if(arg3.get_type() == Variant::NIL) set_range((int)arg1, (int)arg2); // (start, end) + else valid = false; + } + else if(arg2.get_type() == Variant::NIL) set_range((int)arg1); // (end) + else valid = false; + } + else valid = false; + + if(!valid) + { + ERR_EXPLAIN("Invalid type in function 'set_range' in base 'RangeIterator'. Expected 1, 2, or 3 ints."); + ERR_FAIL_V(Ref()); + } + return Ref(this); +} + +RangeIterator::RangeIterator() { + current = 0; + stop = 0; + step = 0; +} + +RangeIterator::RangeIterator(int stop) { + set_range(stop); +} + +RangeIterator::RangeIterator(int start, int stop) { + set_range(start, stop); +} + +RangeIterator::RangeIterator(int start, int stop, int step) { + set_range(start, stop, step); +} diff --git a/core/range_iterator.h b/core/range_iterator.h new file mode 100644 index 00000000000..c3e38a90949 --- /dev/null +++ b/core/range_iterator.h @@ -0,0 +1,72 @@ +/*************************************************************************/ +/* range_iterator.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef RANGE_ITERATOR_H +#define RANGE_ITERATOR_H + +#include "reference.h" +#include "variant.h" +#include "array.h" + +class RangeIterator : public Reference +{ +protected: + OBJ_TYPE( RangeIterator, Reference ); + + static void _bind_methods(); + +private: + int current; + int stop; + int step; + + bool _iter_init(Variant arg); + bool _iter_next(Variant arg); + Variant _iter_get(Variant arg); + +public: + + bool is_finished(); + + Array to_array(); + + void set_range(int stop); + void set_range(int start, int stop); + void set_range(int start, int stop, int step); + + Ref _set_range(Variant arg1, Variant arg2 = Variant(), Variant arg3 = Variant()); + + void _init(Variant arg1, Variant arg2, Variant arg3); + + RangeIterator(); + RangeIterator(int stop); + RangeIterator(int start, int stop); + RangeIterator(int start, int stop, int step); +}; + +#endif // RANGE_ITERATOR_H diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 54431cf381a..c516059cfb7 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -52,6 +52,7 @@ #include "func_ref.h" #include "input_map.h" #include "undo_redo.h" +#include "range_iterator.h" #ifdef XML_ENABLED static ResourceFormatSaverXML *resource_saver_xml=NULL; @@ -130,6 +131,8 @@ void register_core_types() { ObjectTypeDB::register_type(); ObjectTypeDB::register_type(); ObjectTypeDB::register_type(); + ObjectTypeDB::register_type(); + ObjectTypeDB::register_type(); ObjectTypeDB::register_virtual_type(); From f81153eb6997c16318a5a7df5e982f013ebad37b Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 24 Feb 2016 02:21:56 +0100 Subject: [PATCH 2/3] Add xrange builtin function Also update classes.xml in order to document xrange --- doc/base/classes.xml | 43 ++++++++++++++++ modules/gdscript/gd_functions.cpp | 83 +++++++++++++++++++++++++++++++ modules/gdscript/gd_functions.h | 1 + 3 files changed, 127 insertions(+) diff --git a/doc/base/classes.xml b/doc/base/classes.xml index 258feadf206..12948d4489d 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -556,6 +556,15 @@ Return an array with the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial,final-1,increment). + + + + + + + Return an iterator over the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial,final-1,increment). + + @@ -27597,6 +27606,40 @@ This method controls whether the position between two cached points is interpola + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Raw byte array. diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp index 9b7d8eeac4a..e5689d8865e 100644 --- a/modules/gdscript/gd_functions.cpp +++ b/modules/gdscript/gd_functions.cpp @@ -32,6 +32,7 @@ #include "reference.h" #include "gd_script.h" #include "func_ref.h" +#include "range_iterator.h" #include "os/os.h" #include "variant_parser.h" #include "io/marshalls.h" @@ -98,6 +99,7 @@ const char *GDFunctions::get_func_name(Function p_func) { "var2bytes", "bytes2var", "range", + "xrange", "load", "inst2dict", "dict2inst", @@ -815,6 +817,81 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va } break; } + } break; + case GEN_XRANGE: { + + switch(p_arg_count) { + case 0: { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=1; + } break; + case 1: { + + VALIDATE_ARG_NUM(0); + + int count=*p_args[0]; + + Ref itr = Ref( memnew(RangeIterator) ); + if (!*itr) { + ERR_EXPLAIN("Couldn't allocate iterator!"); + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + ERR_FAIL(); + } + (*itr)->set_range(count); + r_ret=Variant(itr); + return; + } break; + case 2: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + int from=*p_args[0]; + int to=*p_args[1]; + + Ref itr = Ref( memnew(RangeIterator) ); + if (!*itr) { + ERR_EXPLAIN("Couldn't allocate iterator!"); + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + ERR_FAIL(); + } + (*itr)->set_range(from, to); + r_ret=Variant(itr); + return; + } break; + case 3: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + + int from=*p_args[0]; + int to=*p_args[1]; + int incr=*p_args[2]; + + if (incr==0) { + ERR_EXPLAIN("step argument is zero!"); + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + ERR_FAIL(); + } + + Ref itr = Ref( memnew(RangeIterator) ); + if (!*itr) { + ERR_EXPLAIN("Couldn't allocate iterator!"); + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + ERR_FAIL(); + } + (*itr)->set_range(from, to, incr); + r_ret=Variant(itr); + return; + } break; + default: { + + r_error.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_error.argument=3; + } break; + } + } break; case RESOURCE_LOAD: { VALIDATE_ARG_COUNT(1); @@ -1433,6 +1510,12 @@ MethodInfo GDFunctions::get_info(Function p_func) { mi.return_val.type=Variant::ARRAY; return mi; } break; + case GEN_XRANGE: { + + MethodInfo mi("xrange",PropertyInfo(Variant::NIL,"...")); + mi.return_val.type=Variant::OBJECT; + return mi; + } break; case RESOURCE_LOAD: { MethodInfo mi("load",PropertyInfo(Variant::STRING,"path")); diff --git a/modules/gdscript/gd_functions.h b/modules/gdscript/gd_functions.h index 8c884725672..3a993cc0383 100644 --- a/modules/gdscript/gd_functions.h +++ b/modules/gdscript/gd_functions.h @@ -92,6 +92,7 @@ public: VAR_TO_BYTES, BYTES_TO_VAR, GEN_RANGE, + GEN_XRANGE, RESOURCE_LOAD, INST2DICT, DICT2INST, From 5f66692395744712244f19e66eaa89790590a019 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 24 Feb 2016 02:46:36 +0100 Subject: [PATCH 3/3] Use xrange for common "for i in range(...)" use case Make the parser eliminate a wasteful allocation and initialisation of a possibly large array. --- modules/gdscript/gd_parser.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 4f572b7b6ed..4b0164d8a26 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -1779,6 +1779,20 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) { return; } + // Little optimisation for common usage "for i in range(...):": + // don't create and initialize a possibly huge array as range() + // would do, but instead create an iterator using xrange() + if (container->type == Node::TYPE_OPERATOR) { + OperatorNode *op = static_cast(container); + if (op->arguments.size() > 0 && + op->arguments[0]->type == Node::TYPE_BUILT_IN_FUNCTION) { + BuiltInFunctionNode *c = static_cast(op->arguments[0]); + if (c->function == GDFunctions::GEN_RANGE) { + c->function = GDFunctions::GEN_XRANGE; + } + } + } + ControlFlowNode *cf_for = alloc_node(); cf_for->cf_type=ControlFlowNode::CF_FOR;