summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Paul <Torsten.Paul@gmx.de>2013-11-10 01:42:58 (GMT)
committerTorsten Paul <Torsten.Paul@gmx.de>2013-11-11 00:31:44 (GMT)
commit00a329f0bd4ab940c1063106ee6ba7db7811a090 (patch)
tree26872901c9cbc603e7a62b14975e306b0e146885
parente5d535e900ae5bc5923fc76c80fb3b3f9153a80e (diff)
Add support for handling negative step values in ranges (fixes #500).
-rw-r--r--src/control.cc24
-rw-r--r--src/expr.cc17
-rw-r--r--src/parser.y2
-rw-r--r--src/value.cc102
-rw-r--r--src/value.h75
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 &range;
+ 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;
contact: Jan Huwald // Impressum