Skip to content

Commit

Permalink
A possible solution for #3810, allowing float expressions in loops
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexisDrogoul committed May 28, 2023
1 parent 4897374 commit 0c3cc33
Showing 1 changed file with 78 additions and 26 deletions.
104 changes: 78 additions & 26 deletions msi.gama.core/src/msi/gaml/statements/LoopStatement.java
@@ -1,11 +1,11 @@
/*******************************************************************************************************
*
* LoopStatement.java, in msi.gama.core, is part of the source code of the GAMA modeling and simulation platform
* (v.1.9.2).
* (v.2.0.0).
*
* (c) 2007-2023 UMI 209 UMMISCO IRD/SU & Partners (IRIT, MIAT, TLU, CTU)
*
* Visit https://github.com/gama-platform/gama for license information and contacts.
* Visit https://github.com/gama-platform/gama2 for license information and contacts.
*
********************************************************************************************************/
package msi.gaml.statements;
Expand Down Expand Up @@ -66,6 +66,10 @@
* The Class LoopStatement.
*/

/**
* The Class LoopStatement.
*/

/**
* The Class LoopStatement.
*/
Expand All @@ -79,19 +83,19 @@
@facets (
value = { @facet (
name = IKeyword.FROM,
type = IType.INT,
type = { IType.INT, IType.FLOAT },
optional = true,
doc = @doc ("an int expression")),
doc = @doc ("an int or float expression that represents the lower bound of the loop")),
@facet (
name = IKeyword.TO,
type = IType.INT,
type = { IType.INT, IType.FLOAT },
optional = true,
doc = @doc ("an int expression")),
doc = @doc ("an int or float expression that represents the higher bound of the loop")),
@facet (
name = IKeyword.STEP,
type = IType.INT,
type = { IType.INT, IType.FLOAT },
optional = true,
doc = @doc ("an int expression")),
doc = @doc ("an int or float expression that represents the incrementation of the loop")),
@facet (
name = IKeyword.NAME,
type = IType.NEW_TEMP_ID,
Expand All @@ -116,7 +120,7 @@
@inside (
kinds = { ISymbolKind.BEHAVIOR, ISymbolKind.SEQUENCE_STATEMENT, ISymbolKind.LAYER })
@doc (
value = "Allows the agent to perform the same set of statements either a fixed number of times, or while a condition is true, or by progressing in a collection of elements or along an interval of integers. Be aware that there are no prevention of infinite loops. As a consequence, open loops should be used with caution, as one agent may block the execution of the whole model.",
value = "Allows the agent to perform the same set of statements either a fixed number of times, or while a condition is true, or by progressing in a collection of elements or along an interval of numbers. Be aware that there are no prevention of infinite loops. As a consequence, open loops should be used with caution, as one agent may block the execution of the whole model.",
usages = { @usage (
value = "The basic syntax for repeating a fixed number of times a set of statements is:",
examples = { @example (
Expand Down Expand Up @@ -217,7 +221,7 @@
equals = "60",
isTestOnly = true) }),
@usage (
value = "The second (quite common) case of the loop syntax allows one to use an interval of integers. The from and to facets take an integer expression as arguments, with the first (resp. the last) specifying the beginning (resp. end) of the inclusive interval (i.e. [to, from]). If the step is not defined, it is assumed to be equal to 1 or -1, depending on the direction of the range. If it is defined, its sign will be respected, so that a positive step will never allow the loop to enter a loop from i to j where i is greater than j",
value = "The second (quite common) case of the loop syntax allows one to use an interval of integers or floats. The from and to facets take an int or float expression as arguments, with the first (resp. the last) specifying the beginning (resp. end) of the inclusive interval (i.e. [to, from]). If the step is not defined, it is assumed to be equal to 1 or -1, depending on the direction of the range. If it is defined, its sign will be respected, so that a positive step will never allow the loop to enter a loop from i to j where i is greater than j",
examples = { @example (
value = "list the_list <-list (species_of (self));"),
@example (
Expand Down Expand Up @@ -490,17 +494,20 @@ class Bounded implements LoopExecuter {
private final IExpression step = getFacet(IKeyword.STEP);

/** The constant step. */
private Integer constantFrom;
private Number constantFrom;

/** The constant to. */
private Integer constantTo;
private Number constantTo;

/** The constant step. */
private Integer constantStep;
private Number constantStep;

/** The step defined. */
private final boolean stepDefined;

/** The is int. */
private final boolean isInt;

/**
* Instantiates a new bounded.
*
Expand All @@ -510,35 +517,80 @@ class Bounded implements LoopExecuter {
Bounded() throws GamaRuntimeException {
final IScope scope = null;
// final IScope scope = GAMA.obtainNewScope();
if (from.isConst()) { constantFrom = Cast.asInt(scope, from.value(scope)); }
if (to.isConst()) { constantTo = Cast.asInt(scope, to.value(scope)); }
isInt = from.getGamlType() == Types.INT && to.getGamlType() == Types.INT
&& (step == null || step.getGamlType() == Types.INT);
if (from.isConst()) { constantFrom = getFromExp(scope, from); }
if (to.isConst()) { constantTo = getFromExp(scope, to); }
if (step == null) {
stepDefined = false;
constantStep = 1;
} else if (step.isConst()) {
stepDefined = true;
constantStep = Cast.asInt(scope, step.value(scope));
constantStep = getFromExp(scope, step);
} else {
stepDefined = true;
}
}

/**
* Gets the from exp.
*
* @param scope
* the scope
* @param exp
* the exp
* @return the from exp
*/
Number getFromExp(final IScope scope, final IExpression exp) {
return isInt ? Cast.asInt(scope, exp.value(scope)) : Cast.asFloat(scope, exp.value(scope));
}

@Override
public Object runIn(final IScope scope) throws GamaRuntimeException {
final Object[] result = new Object[1];
final int f = constantFrom == null ? Cast.asInt(scope, from.value(scope)) : constantFrom;
final int t = constantTo == null ? Cast.asInt(scope, to.value(scope)) : constantTo;
int s = constantStep == null ? Cast.asInt(scope, step.value(scope)) : constantStep;
// if ( f == t ) { return null; }
final Number f = constantFrom == null ? getFromExp(scope, from) : constantFrom;
final Number t = constantTo == null ? getFromExp(scope, to) : constantTo;
Number s = constantStep == null ? getFromExp(scope, step) : constantStep;
boolean shouldBreak = false;
if (f == t) {
if (f.equals(t)) {
loopBody(scope, f, result);
} else if (f - t > 0) {
if (s > 0) {
} else if (f.doubleValue() - t.doubleValue() > 0) {
if (s.doubleValue() > 0) {
if (stepDefined) return null;
s = -s;
if (s instanceof Integer) {
s = -s.intValue();
} else {
s = -s.doubleValue();
}
}
if (isInt) {
for (int i = f.intValue(), n = t.intValue(); i >= n && !shouldBreak; i += s.intValue()) {
FlowStatus status = loopBody(scope, i, result);
switch (status) {
case CONTINUE:
continue;
case BREAK, RETURN, DIE, DISPOSE:
shouldBreak = true;
break;
default:
}
}
} else {
for (double i = f.doubleValue(), n = t.doubleValue(); i >= n && !shouldBreak; i +=
s.doubleValue()) {
FlowStatus status = loopBody(scope, i, result);
switch (status) {
case CONTINUE:
continue;
case BREAK, RETURN, DIE, DISPOSE:
shouldBreak = true;
break;
default:
}
}
}
for (int i = f, n = t - 1; i > n && !shouldBreak; i += s) {
} else if (isInt) {
for (int i = f.intValue(), n = t.intValue(); i <= n && !shouldBreak; i += s.intValue()) {
FlowStatus status = loopBody(scope, i, result);
switch (status) {
case CONTINUE:
Expand All @@ -550,7 +602,7 @@ public Object runIn(final IScope scope) throws GamaRuntimeException {
}
}
} else {
for (int i = f, n = t + 1; i < n && !shouldBreak; i += s) {
for (double i = f.doubleValue(), n = t.doubleValue(); i <= n && !shouldBreak; i += s.doubleValue()) {
FlowStatus status = loopBody(scope, i, result);
switch (status) {
case CONTINUE:
Expand Down

0 comments on commit 0c3cc33

Please sign in to comment.