diff options
author | Torsten Paul <Torsten.Paul@gmx.de> | 2013-11-10 01:42:58 (GMT) |
---|---|---|
committer | Torsten Paul <Torsten.Paul@gmx.de> | 2013-11-11 00:31:44 (GMT) |
commit | 00a329f0bd4ab940c1063106ee6ba7db7811a090 (patch) | |
tree | 26872901c9cbc603e7a62b14975e306b0e146885 | |
parent | e5d535e900ae5bc5923fc76c80fb3b3f9153a80e (diff) |
Add support for handling negative step values in ranges (fixes #500).
-rw-r--r-- | src/control.cc | 24 | ||||
-rw-r--r-- | src/expr.cc | 17 | ||||
-rw-r--r-- | src/parser.y | 2 | ||||
-rw-r--r-- | src/value.cc | 102 | ||||
-rw-r--r-- | src/value.h | 75 |
5 files changed, 176 insertions, 44 deletions
diff --git a/src/control.cc b/src/control.cc index 10aadf0..6b10a28 100644 --- a/src/control.cc +++ b/src/control.cc @@ -78,12 +78,14 @@ void ControlModule::for_eval(AbstractNode &node, const ModuleInstantiation &inst Context c(ctx); if (it_values.type() == Value::RANGE) { Value::RangeType range = it_values.toRange(); - range.normalize(); - if (range.nbsteps()<10000) { - for (double i = range.begin; i <= range.end; i += range.step) { - c.set_variable(it_name, Value(i)); - for_eval(node, inst, l+1, &c, evalctx); - } + unsigned long steps = range.nbsteps(); + if (steps >= 10000) { + PRINTB("WARNING: Bad range parameter in for statement: too many elements (%lu).", steps); + } else { + for (Value::RangeType::iterator it = range.begin();it != range.end();it++) { + c.set_variable(it_name, Value(*it)); + for_eval(node, inst, l+1, &c, evalctx); + } } } else if (it_values.type() == Value::VECTOR) { @@ -227,13 +229,13 @@ AbstractNode *ControlModule::instantiate(const Context* /*ctx*/, const ModuleIns else if (value.type() == Value::RANGE) { AbstractNode* node = new AbstractNode(inst); Value::RangeType range = value.toRange(); - range.normalize(); - if (range.nbsteps()>=10000) { - PRINTB("WARNING: Bad range parameter for children: too many elements (%d).", (int)((range.begin-range.end)/range.step)); + unsigned long steps = range.nbsteps(); + if (steps >= 10000) { + PRINTB("WARNING: Bad range parameter for children: too many elements (%lu).", steps); return NULL; } - for (double i = range.begin; i <= range.end; i += range.step) { - AbstractNode* childnode = getChild(Value(i),modulectx); // with error cases + for (Value::RangeType::iterator it = range.begin();it != range.end();it++) { + AbstractNode* childnode = getChild(Value(*it),modulectx); // with error cases if (childnode==NULL) continue; // error node->children.push_back(childnode); } diff --git a/src/expr.cc b/src/expr.cc index 594fccf..51accf3 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -117,11 +117,18 @@ Value Expression::evaluate(const Context *context) const if (this->type == "R") { Value v1 = this->children[0]->evaluate(context); Value v2 = this->children[1]->evaluate(context); - Value v3 = this->children[2]->evaluate(context); - if (v1.type() == Value::NUMBER && v2.type() == Value::NUMBER && v3.type() == Value::NUMBER) { - Value::RangeType range(v1.toDouble(), v2.toDouble(), v3.toDouble()); - return Value(range); - } + if (this->children.size() == 2) { + if (v1.type() == Value::NUMBER && v2.type() == Value::NUMBER) { + Value::RangeType range(v1.toDouble(), v2.toDouble()); + return Value(range); + } + } else { + Value v3 = this->children[2]->evaluate(context); + if (v1.type() == Value::NUMBER && v2.type() == Value::NUMBER && v3.type() == Value::NUMBER) { + Value::RangeType range(v1.toDouble(), v2.toDouble(), v3.toDouble()); + return Value(range); + } + } return Value(); } if (this->type == "V") { diff --git a/src/parser.y b/src/parser.y index 5645104..6446e82 100644 --- a/src/parser.y +++ b/src/parser.y @@ -325,11 +325,9 @@ expr: } | '[' expr ':' expr ']' { - Expression *e_one = new Expression(Value(1.0)); $$ = new Expression(); $$->type = "R"; $$->children.push_back($2); - $$->children.push_back(e_one); $$->children.push_back($4); } | '[' expr ':' expr ':' expr ']' diff --git a/src/value.cc b/src/value.cc index ac33a3b..931c616 100644 --- a/src/value.cc +++ b/src/value.cc @@ -232,7 +232,7 @@ public: } std::string operator()(const Value::RangeType &v) const { - return (boost::format("[%1% : %2% : %3%]") % v.begin % v.step % v.end).str(); + return (boost::format("[%1% : %2% : %3%]") % v.begin_val % v.step_val % v.end_val).str(); } }; @@ -600,9 +600,9 @@ public: Value operator()(const Value::RangeType &range, const double &idx) const { switch(int(idx)) { - case 0: return Value(range.begin); - case 1: return Value(range.step); - case 2: return Value(range.end); + case 0: return Value(range.begin_val); + case 1: return Value(range.step_val); + case 2: return Value(range.end_val); } return Value::undefined; } @@ -617,3 +617,97 @@ Value Value::operator[](const Value &v) { return boost::apply_visitor(bracket_visitor(), this->value, v.value); } + +void Value::RangeType::normalize() { + if ((step_val>0) && (end_val < begin_val)) { + std::swap(begin_val,end_val); + PRINT("DEPRECATED: Using ranges of the form [begin:end] with begin value greater than the end value is deprecated."); + } +} + +unsigned long Value::RangeType::nbsteps() const { + if (begin_val == end_val) { + return 0; + } + + if (step_val == 0) { + return std::numeric_limits<unsigned long>::max(); + } + + double steps; + if (step_val < 0) { + if (begin_val < end_val) { + return 0; + } + steps = (begin_val - end_val) / (-step_val); + } else { + if (begin_val > end_val) { + return 0; + } + steps = (end_val - begin_val) / step_val; + } + + return steps; +} + +Value::RangeType::iterator::iterator(Value::RangeType &range, type_t type) : range(range), val(range.begin_val) +{ + this->type = type; + update_type(); +} + +void Value::RangeType::iterator::update_type() +{ + if (range.step_val == 0) { + type = RANGE_TYPE_END; + } else if (range.step_val < 0) { + if (val < range.end_val) { + type = RANGE_TYPE_END; + } + } else { + if (val > range.end_val) { + type = RANGE_TYPE_END; + } + } +} + +Value::RangeType::iterator::reference Value::RangeType::iterator::operator*() +{ + return val; +} + +Value::RangeType::iterator::pointer Value::RangeType::iterator::operator->() +{ + return &(operator*()); +} + +Value::RangeType::iterator::self_type Value::RangeType::iterator::operator++() +{ + if (type < 0) { + type = RANGE_TYPE_RUNNING; + } + val += range.step_val; + update_type(); + return *this; +} + +Value::RangeType::iterator::self_type Value::RangeType::iterator::operator++(int) +{ + self_type tmp(*this); + operator++(); + return tmp; +} + +bool Value::RangeType::iterator::operator==(const self_type &other) const +{ + if (type == RANGE_TYPE_RUNNING) { + return (type == other.type) && (val == other.val) && (range == other.range); + } else { + return (type == other.type) && (range == other.range); + } +} + +bool Value::RangeType::iterator::operator!=(const self_type &other) const +{ + return !(*this == other); +} diff --git a/src/value.h b/src/value.h index 388b721..4671cdc 100644 --- a/src/value.h +++ b/src/value.h @@ -31,33 +31,64 @@ std::ostream &operator<<(std::ostream &stream, const Filename &filename); class Value { public: - struct RangeType { + class RangeType { + private: + double begin_val; + double step_val; + double end_val; + + /// inverse begin/end if begin is upper than end + void normalize(); + + public: + typedef enum { RANGE_TYPE_BEGIN, RANGE_TYPE_RUNNING, RANGE_TYPE_END } type_t; + + class iterator { + public: + typedef iterator self_type; + typedef double value_type; + typedef double& reference; + typedef double* pointer; + typedef std::forward_iterator_tag iterator_category; + typedef double difference_type; + iterator(RangeType &range, type_t type); + self_type operator++(); + self_type operator++(int junk); + reference operator*(); + pointer operator->(); + bool operator==(const self_type& other) const; + bool operator!=(const self_type& other) const; + private: + RangeType ⦥ + double val; + type_t type; + + void update_type(); + }; + + RangeType(double begin, double end) + : begin_val(begin), step_val(1.0), end_val(end) + { + normalize(); + } + RangeType(double begin, double step, double end) - : begin(begin), step(step), end(end) {} + : begin_val(begin), step_val(step), end_val(end) {} bool operator==(const RangeType &other) const { - return this->begin == other.begin && - this->step == other.step && - this->end == other.end; + return this->begin_val == other.begin_val && + this->step_val == other.step_val && + this->end_val == other.end_val; } - /// inverse begin/end if begin is upper than end - void normalize() { - if ((step>0) && (end < begin)) { - std::swap(begin,end); - } - } - /// return number of steps, max int value if step is null - int nbsteps() const { - if (step<=0) { - return std::numeric_limits<int>::max(); - } - return (int)((begin-end)/step); - } - - double begin; - double step; - double end; + iterator begin() { return iterator(*this, RANGE_TYPE_BEGIN); } + iterator end() { return iterator(*this, RANGE_TYPE_END); } + + /// return number of steps, max int value if step is null + unsigned long nbsteps() const; + + friend class tostring_visitor; + friend class bracket_visitor; }; typedef std::vector<Value> VectorType; |