Возможность итерирования строк, массивов с индексом в for, и functional::foreach

This commit is contained in:
Victor 2019-04-04 17:53:59 +03:00
parent 2d93c8b9a7
commit 589fdbf0d5
7 changed files with 275 additions and 41 deletions

View File

@ -48,13 +48,13 @@ public final class Variables {
scope.variables.put("false", NumberValue.ZERO); scope.variables.put("false", NumberValue.ZERO);
} }
static void push() { public static void push() {
synchronized (lock) { synchronized (lock) {
scope = new Scope(scope); scope = new Scope(scope);
} }
} }
static void pop() { public static void pop() {
synchronized (lock) { synchronized (lock) {
if (scope.parent != null) { if (scope.parent != null) {
scope = scope.parent; scope = scope.parent;

View File

@ -1,36 +1,62 @@
package com.annimon.ownlang.modules.functional; package com.annimon.ownlang.modules.functional;
import com.annimon.ownlang.exceptions.TypeException; import com.annimon.ownlang.exceptions.TypeException;
import com.annimon.ownlang.lib.Arguments; import com.annimon.ownlang.lib.*;
import com.annimon.ownlang.lib.ArrayValue;
import com.annimon.ownlang.lib.Function;
import com.annimon.ownlang.lib.MapValue;
import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.lib.ValueUtils;
import java.util.Map; import java.util.Map;
public final class functional_foreach implements Function { public final class functional_foreach implements Function {
private static final int UNKNOWN = -1;
@Override @Override
public Value execute(Value... args) { public Value execute(Value... args) {
Arguments.check(2, args.length); Arguments.check(2, args.length);
final Value container = args[0]; final Value container = args[0];
final Function consumer = ValueUtils.consumeFunction(args[1], 1); final Function consumer = ValueUtils.consumeFunction(args[1], 1);
if (container.type() == Types.ARRAY) { final int argsCount;
final ArrayValue array = (ArrayValue) container; if (consumer instanceof UserDefinedFunction) {
for (Value element : array) { argsCount = ((UserDefinedFunction) consumer).getArgsCount();
consumer.execute(element); } else {
} argsCount = UNKNOWN;
return array;
} }
if (container.type() == Types.MAP) {
final MapValue map = (MapValue) container; switch (container.type()) {
for (Map.Entry<Value, Value> element : map) { case Types.STRING:
consumer.execute(element.getKey(), element.getValue()); final StringValue string = (StringValue) container;
} if (argsCount == 2) {
return map; for (char ch : string.asString().toCharArray()) {
consumer.execute(new StringValue(String.valueOf(ch)), NumberValue.of(ch));
}
} else {
for (char ch : string.asString().toCharArray()) {
consumer.execute(new StringValue(String.valueOf(ch)));
}
}
return string;
case Types.ARRAY:
final ArrayValue array = (ArrayValue) container;
if (argsCount == 2) {
int index = 0;
for (Value element : array) {
consumer.execute(element, NumberValue.of(index++));
}
} else {
for (Value element : array) {
consumer.execute(element);
}
}
return array;
case Types.MAP:
final MapValue map = (MapValue) container;
for (Map.Entry<Value, Value> element : map) {
consumer.execute(element.getKey(), element.getValue());
}
return map;
default:
throw new TypeException("Cannot iterate " + Types.typeToString(container.type()));
} }
throw new TypeException("Invalid first argument. Array or map expected");
} }
} }

View File

@ -1,7 +1,8 @@
package com.annimon.ownlang.parser.ast; package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.exceptions.TypeException;
import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.lib.*;
import java.util.Map;
/** /**
* *
@ -23,8 +24,45 @@ public final class ForeachArrayStatement extends InterruptableNode implements St
public void execute() { public void execute() {
super.interruptionCheck(); super.interruptionCheck();
final Value previousVariableValue = Variables.isExists(variable) ? Variables.get(variable) : null; final Value previousVariableValue = Variables.isExists(variable) ? Variables.get(variable) : null;
final Iterable<Value> iterator = (Iterable<Value>) container.eval();
for (Value value : iterator) { final Value containerValue = container.eval();
switch (containerValue.type()) {
case Types.STRING:
iterateString(containerValue.asString());
break;
case Types.ARRAY:
iterateArray((ArrayValue) containerValue);
break;
case Types.MAP:
iterateMap((MapValue) containerValue);
break;
default:
throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()));
}
// Restore variables
if (previousVariableValue != null) {
Variables.set(variable, previousVariableValue);
} else {
Variables.remove(variable);
}
}
private void iterateString(String str) {
for (char ch : str.toCharArray()) {
Variables.set(variable, new StringValue(String.valueOf(ch)));
try {
body.execute();
} catch (BreakStatement bs) {
break;
} catch (ContinueStatement cs) {
// continue;
}
}
}
private void iterateArray(ArrayValue containerValue) {
for (Value value : containerValue) {
Variables.set(variable, value); Variables.set(variable, value);
try { try {
body.execute(); body.execute();
@ -34,9 +72,21 @@ public final class ForeachArrayStatement extends InterruptableNode implements St
// continue; // continue;
} }
} }
// Восстанавливаем переменную }
if (previousVariableValue != null) {
Variables.set(variable, previousVariableValue); private void iterateMap(MapValue containerValue) {
for (Map.Entry<Value, Value> entry : containerValue) {
Variables.set(variable, new ArrayValue(new Value[] {
entry.getKey(),
entry.getValue()
}));
try {
body.execute();
} catch (BreakStatement bs) {
break;
} catch (ContinueStatement cs) {
// continue;
}
} }
} }

View File

@ -1,7 +1,7 @@
package com.annimon.ownlang.parser.ast; package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.exceptions.TypeException;
import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.lib.*;
import java.util.Map; import java.util.Map;
/** /**
@ -26,8 +26,66 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat
super.interruptionCheck(); super.interruptionCheck();
final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null; final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null;
final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null; final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null;
final Iterable<Map.Entry<Value, Value>> iterator = (Iterable<Map.Entry<Value, Value>>) container.eval();
for (Map.Entry<Value, Value> entry : iterator) { final Value containerValue = container.eval();
switch (containerValue.type()) {
case Types.STRING:
iterateString(containerValue.asString());
break;
case Types.ARRAY:
iterateArray((ArrayValue) containerValue);
break;
case Types.MAP:
iterateMap((MapValue) containerValue);
break;
default:
throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()) + " as key, value pair");
}
// Restore variables
if (previousVariableValue1 != null) {
Variables.set(key, previousVariableValue1);
} else {
Variables.remove(key);
}
if (previousVariableValue2 != null) {
Variables.set(value, previousVariableValue2);
} else {
Variables.remove(value);
}
}
private void iterateString(String str) {
for (char ch : str.toCharArray()) {
Variables.set(key, new StringValue(String.valueOf(ch)));
Variables.set(value, NumberValue.of(ch));
try {
body.execute();
} catch (BreakStatement bs) {
break;
} catch (ContinueStatement cs) {
// continue;
}
}
}
private void iterateArray(ArrayValue containerValue) {
int index = 0;
for (Value v : containerValue) {
Variables.set(key, v);
Variables.set(value, NumberValue.of(index++));
try {
body.execute();
} catch (BreakStatement bs) {
break;
} catch (ContinueStatement cs) {
// continue;
}
}
}
private void iterateMap(MapValue containerValue) {
for (Map.Entry<Value, Value> entry : containerValue) {
Variables.set(key, entry.getKey()); Variables.set(key, entry.getKey());
Variables.set(value, entry.getValue()); Variables.set(value, entry.getValue());
try { try {
@ -38,13 +96,6 @@ public final class ForeachMapStatement extends InterruptableNode implements Stat
// continue; // continue;
} }
} }
// Восстанавливаем переменные
if (previousVariableValue1 != null) {
Variables.set(key, previousVariableValue1);
}
if (previousVariableValue2 != null) {
Variables.set(value, previousVariableValue2);
}
} }
@Override @Override

View File

@ -0,0 +1,30 @@
def testArrayIterate() {
sum = 0
for v, i : [1, 2, 3] {
sum += v * i
}
assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum)
}
def testMapIterate() {
map = {12: 1, 13: 2, 14: 3}
sumKey = 0
sumValue = 0
for key, value : map {
sumKey += key
sumValue += value
}
assertEquals(39, sumKey)
assertEquals(6, sumValue)
}
def testStringIterate() {
str = ""
sum = 0
for s, code : "abcd" {
str += s.upper
sum += code
}
assertEquals("ABCD", str)
assertEquals(394/*97 + 98 + 99 + 100*/, sum)
}

View File

@ -0,0 +1,41 @@
use "std"
def testArrayIterate() {
sum = 0
for v : [1, 2, 3] {
sum += v
}
assertEquals(6, sum)
}
def testMapIterate() {
map = {12: 1, 13: 2, 14: 3}
sumKey = 0
sumValue = 0
for pair : map {
extract(key, value) = pair
sumKey += key
sumValue += value
}
assertEquals(39, sumKey)
assertEquals(6, sumValue)
}
def testStringIterate() {
sum = 0
for s : "abcd" {
sum += s.charAt(0)
}
assertEquals(394/*97 + 98 + 99 + 100*/, sum)
}
def testScope() {
v = 45
sum = 0
for v : [1, 2, 3] {
sum += v
}
assertEquals(6, sum)
assertEquals(45, v)
}

View File

@ -0,0 +1,36 @@
use ["std", "functional"]
def testArrayForeach1Arg() {
sum = 0
foreach([1, 2, 3], def(v) {
sum += v
})
assertEquals(6, sum)
}
def testArrayForeach2Args() {
sum = 0
foreach([1, 2, 3], def(v, index) {
sum += v * index
})
assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum)
}
def testStringForeach1Arg() {
sum = 0
foreach("abcd", def(s) {
sum += s.charAt(0)
})
assertEquals(394/*97 + 98 + 99 + 100*/, sum)
}
def testStringForeach2Args() {
str = ""
sum = 0
foreach("abcd", def(s, code) {
str += s.upper
sum += code
})
assertEquals("ABCD", str)
assertEquals(97 + 98 + 99 + 100, sum)
}