Skip to content

Commit 589fdbf

Browse files
committed
Возможность итерирования строк, массивов с индексом в for, и functional::foreach
1 parent 2d93c8b commit 589fdbf

File tree

7 files changed

+275
-41
lines changed

7 files changed

+275
-41
lines changed

src/main/java/com/annimon/ownlang/lib/Variables.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ public static void clear() {
4848
scope.variables.put("false", NumberValue.ZERO);
4949
}
5050

51-
static void push() {
51+
public static void push() {
5252
synchronized (lock) {
5353
scope = new Scope(scope);
5454
}
5555
}
5656

57-
static void pop() {
57+
public static void pop() {
5858
synchronized (lock) {
5959
if (scope.parent != null) {
6060
scope = scope.parent;
Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,62 @@
11
package com.annimon.ownlang.modules.functional;
22

33
import com.annimon.ownlang.exceptions.TypeException;
4-
import com.annimon.ownlang.lib.Arguments;
5-
import com.annimon.ownlang.lib.ArrayValue;
6-
import com.annimon.ownlang.lib.Function;
7-
import com.annimon.ownlang.lib.MapValue;
8-
import com.annimon.ownlang.lib.Types;
9-
import com.annimon.ownlang.lib.Value;
10-
import com.annimon.ownlang.lib.ValueUtils;
4+
import com.annimon.ownlang.lib.*;
115
import java.util.Map;
126

137
public final class functional_foreach implements Function {
148

9+
private static final int UNKNOWN = -1;
10+
1511
@Override
1612
public Value execute(Value... args) {
1713
Arguments.check(2, args.length);
1814
final Value container = args[0];
1915
final Function consumer = ValueUtils.consumeFunction(args[1], 1);
20-
if (container.type() == Types.ARRAY) {
21-
final ArrayValue array = (ArrayValue) container;
22-
for (Value element : array) {
23-
consumer.execute(element);
24-
}
25-
return array;
16+
final int argsCount;
17+
if (consumer instanceof UserDefinedFunction) {
18+
argsCount = ((UserDefinedFunction) consumer).getArgsCount();
19+
} else {
20+
argsCount = UNKNOWN;
2621
}
27-
if (container.type() == Types.MAP) {
28-
final MapValue map = (MapValue) container;
29-
for (Map.Entry<Value, Value> element : map) {
30-
consumer.execute(element.getKey(), element.getValue());
31-
}
32-
return map;
22+
23+
switch (container.type()) {
24+
case Types.STRING:
25+
final StringValue string = (StringValue) container;
26+
if (argsCount == 2) {
27+
for (char ch : string.asString().toCharArray()) {
28+
consumer.execute(new StringValue(String.valueOf(ch)), NumberValue.of(ch));
29+
}
30+
} else {
31+
for (char ch : string.asString().toCharArray()) {
32+
consumer.execute(new StringValue(String.valueOf(ch)));
33+
}
34+
}
35+
return string;
36+
37+
case Types.ARRAY:
38+
final ArrayValue array = (ArrayValue) container;
39+
if (argsCount == 2) {
40+
int index = 0;
41+
for (Value element : array) {
42+
consumer.execute(element, NumberValue.of(index++));
43+
}
44+
} else {
45+
for (Value element : array) {
46+
consumer.execute(element);
47+
}
48+
}
49+
return array;
50+
51+
case Types.MAP:
52+
final MapValue map = (MapValue) container;
53+
for (Map.Entry<Value, Value> element : map) {
54+
consumer.execute(element.getKey(), element.getValue());
55+
}
56+
return map;
57+
58+
default:
59+
throw new TypeException("Cannot iterate " + Types.typeToString(container.type()));
3360
}
34-
throw new TypeException("Invalid first argument. Array or map expected");
3561
}
3662
}

src/main/java/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package com.annimon.ownlang.parser.ast;
22

3-
import com.annimon.ownlang.lib.Value;
4-
import com.annimon.ownlang.lib.Variables;
3+
import com.annimon.ownlang.exceptions.TypeException;
4+
import com.annimon.ownlang.lib.*;
5+
import java.util.Map;
56

67
/**
78
*
@@ -23,8 +24,45 @@ public ForeachArrayStatement(String variable, Expression container, Statement bo
2324
public void execute() {
2425
super.interruptionCheck();
2526
final Value previousVariableValue = Variables.isExists(variable) ? Variables.get(variable) : null;
26-
final Iterable<Value> iterator = (Iterable<Value>) container.eval();
27-
for (Value value : iterator) {
27+
28+
final Value containerValue = container.eval();
29+
switch (containerValue.type()) {
30+
case Types.STRING:
31+
iterateString(containerValue.asString());
32+
break;
33+
case Types.ARRAY:
34+
iterateArray((ArrayValue) containerValue);
35+
break;
36+
case Types.MAP:
37+
iterateMap((MapValue) containerValue);
38+
break;
39+
default:
40+
throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()));
41+
}
42+
43+
// Restore variables
44+
if (previousVariableValue != null) {
45+
Variables.set(variable, previousVariableValue);
46+
} else {
47+
Variables.remove(variable);
48+
}
49+
}
50+
51+
private void iterateString(String str) {
52+
for (char ch : str.toCharArray()) {
53+
Variables.set(variable, new StringValue(String.valueOf(ch)));
54+
try {
55+
body.execute();
56+
} catch (BreakStatement bs) {
57+
break;
58+
} catch (ContinueStatement cs) {
59+
// continue;
60+
}
61+
}
62+
}
63+
64+
private void iterateArray(ArrayValue containerValue) {
65+
for (Value value : containerValue) {
2866
Variables.set(variable, value);
2967
try {
3068
body.execute();
@@ -34,9 +72,21 @@ public void execute() {
3472
// continue;
3573
}
3674
}
37-
// Восстанавливаем переменную
38-
if (previousVariableValue != null) {
39-
Variables.set(variable, previousVariableValue);
75+
}
76+
77+
private void iterateMap(MapValue containerValue) {
78+
for (Map.Entry<Value, Value> entry : containerValue) {
79+
Variables.set(variable, new ArrayValue(new Value[] {
80+
entry.getKey(),
81+
entry.getValue()
82+
}));
83+
try {
84+
body.execute();
85+
} catch (BreakStatement bs) {
86+
break;
87+
} catch (ContinueStatement cs) {
88+
// continue;
89+
}
4090
}
4191
}
4292

src/main/java/com/annimon/ownlang/parser/ast/ForeachMapStatement.java

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.annimon.ownlang.parser.ast;
22

3-
import com.annimon.ownlang.lib.Value;
4-
import com.annimon.ownlang.lib.Variables;
3+
import com.annimon.ownlang.exceptions.TypeException;
4+
import com.annimon.ownlang.lib.*;
55
import java.util.Map;
66

77
/**
@@ -26,10 +26,39 @@ public void execute() {
2626
super.interruptionCheck();
2727
final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null;
2828
final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null;
29-
final Iterable<Map.Entry<Value, Value>> iterator = (Iterable<Map.Entry<Value, Value>>) container.eval();
30-
for (Map.Entry<Value, Value> entry : iterator) {
31-
Variables.set(key, entry.getKey());
32-
Variables.set(value, entry.getValue());
29+
30+
final Value containerValue = container.eval();
31+
switch (containerValue.type()) {
32+
case Types.STRING:
33+
iterateString(containerValue.asString());
34+
break;
35+
case Types.ARRAY:
36+
iterateArray((ArrayValue) containerValue);
37+
break;
38+
case Types.MAP:
39+
iterateMap((MapValue) containerValue);
40+
break;
41+
default:
42+
throw new TypeException("Cannot iterate " + Types.typeToString(containerValue.type()) + " as key, value pair");
43+
}
44+
45+
// Restore variables
46+
if (previousVariableValue1 != null) {
47+
Variables.set(key, previousVariableValue1);
48+
} else {
49+
Variables.remove(key);
50+
}
51+
if (previousVariableValue2 != null) {
52+
Variables.set(value, previousVariableValue2);
53+
} else {
54+
Variables.remove(value);
55+
}
56+
}
57+
58+
private void iterateString(String str) {
59+
for (char ch : str.toCharArray()) {
60+
Variables.set(key, new StringValue(String.valueOf(ch)));
61+
Variables.set(value, NumberValue.of(ch));
3362
try {
3463
body.execute();
3564
} catch (BreakStatement bs) {
@@ -38,15 +67,37 @@ public void execute() {
3867
// continue;
3968
}
4069
}
41-
// Восстанавливаем переменные
42-
if (previousVariableValue1 != null) {
43-
Variables.set(key, previousVariableValue1);
70+
}
71+
72+
private void iterateArray(ArrayValue containerValue) {
73+
int index = 0;
74+
for (Value v : containerValue) {
75+
Variables.set(key, v);
76+
Variables.set(value, NumberValue.of(index++));
77+
try {
78+
body.execute();
79+
} catch (BreakStatement bs) {
80+
break;
81+
} catch (ContinueStatement cs) {
82+
// continue;
83+
}
4484
}
45-
if (previousVariableValue2 != null) {
46-
Variables.set(value, previousVariableValue2);
85+
}
86+
87+
private void iterateMap(MapValue containerValue) {
88+
for (Map.Entry<Value, Value> entry : containerValue) {
89+
Variables.set(key, entry.getKey());
90+
Variables.set(value, entry.getValue());
91+
try {
92+
body.execute();
93+
} catch (BreakStatement bs) {
94+
break;
95+
} catch (ContinueStatement cs) {
96+
// continue;
97+
}
4798
}
4899
}
49-
100+
50101
@Override
51102
public void accept(Visitor visitor) {
52103
visitor.visit(this);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
def testArrayIterate() {
2+
sum = 0
3+
for v, i : [1, 2, 3] {
4+
sum += v * i
5+
}
6+
assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum)
7+
}
8+
9+
def testMapIterate() {
10+
map = {12: 1, 13: 2, 14: 3}
11+
sumKey = 0
12+
sumValue = 0
13+
for key, value : map {
14+
sumKey += key
15+
sumValue += value
16+
}
17+
assertEquals(39, sumKey)
18+
assertEquals(6, sumValue)
19+
}
20+
21+
def testStringIterate() {
22+
str = ""
23+
sum = 0
24+
for s, code : "abcd" {
25+
str += s.upper
26+
sum += code
27+
}
28+
assertEquals("ABCD", str)
29+
assertEquals(394/*97 + 98 + 99 + 100*/, sum)
30+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use "std"
2+
3+
def testArrayIterate() {
4+
sum = 0
5+
for v : [1, 2, 3] {
6+
sum += v
7+
}
8+
assertEquals(6, sum)
9+
}
10+
11+
def testMapIterate() {
12+
map = {12: 1, 13: 2, 14: 3}
13+
sumKey = 0
14+
sumValue = 0
15+
for pair : map {
16+
extract(key, value) = pair
17+
sumKey += key
18+
sumValue += value
19+
}
20+
assertEquals(39, sumKey)
21+
assertEquals(6, sumValue)
22+
}
23+
24+
def testStringIterate() {
25+
sum = 0
26+
for s : "abcd" {
27+
sum += s.charAt(0)
28+
}
29+
assertEquals(394/*97 + 98 + 99 + 100*/, sum)
30+
}
31+
32+
def testScope() {
33+
v = 45
34+
sum = 0
35+
for v : [1, 2, 3] {
36+
sum += v
37+
}
38+
assertEquals(6, sum)
39+
assertEquals(45, v)
40+
}
41+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use ["std", "functional"]
2+
3+
def testArrayForeach1Arg() {
4+
sum = 0
5+
foreach([1, 2, 3], def(v) {
6+
sum += v
7+
})
8+
assertEquals(6, sum)
9+
}
10+
11+
def testArrayForeach2Args() {
12+
sum = 0
13+
foreach([1, 2, 3], def(v, index) {
14+
sum += v * index
15+
})
16+
assertEquals(1 * 0 + 2 * 1 + 3 * 2, sum)
17+
}
18+
19+
def testStringForeach1Arg() {
20+
sum = 0
21+
foreach("abcd", def(s) {
22+
sum += s.charAt(0)
23+
})
24+
assertEquals(394/*97 + 98 + 99 + 100*/, sum)
25+
}
26+
27+
def testStringForeach2Args() {
28+
str = ""
29+
sum = 0
30+
foreach("abcd", def(s, code) {
31+
str += s.upper
32+
sum += code
33+
})
34+
assertEquals("ABCD", str)
35+
assertEquals(97 + 98 + 99 + 100, sum)
36+
}

0 commit comments

Comments
 (0)