diff --git a/src/main/java/com/vectron/fcl/types/Dic.java b/src/main/java/com/vectron/fcl/types/Dic.java index a28f8f1..69389f0 100644 --- a/src/main/java/com/vectron/fcl/types/Dic.java +++ b/src/main/java/com/vectron/fcl/types/Dic.java @@ -9,7 +9,7 @@ import java.util.Map; import static com.vectron.fcl.Fcl.STRICT; -public class Dic implements Obj { +public class Dic implements Obj, Iterable { private final Map value = new LinkedHashMap<>(); public static Dic empty() { @@ -70,6 +70,7 @@ public class Dic implements Obj { return Bool.TRUE; } + @Override public Iterator iterator() { return new Iterator() { private Iterator> it = value.entrySet().iterator(); diff --git a/src/main/java/com/vectron/fcl/types/Lst.java b/src/main/java/com/vectron/fcl/types/Lst.java index e278add..c6002e1 100644 --- a/src/main/java/com/vectron/fcl/types/Lst.java +++ b/src/main/java/com/vectron/fcl/types/Lst.java @@ -9,7 +9,7 @@ import java.util.List; import static com.vectron.fcl.Fcl.STRICT; -public class Lst implements Obj, ArithmeticOperand { +public class Lst implements Obj, ArithmeticOperand, Iterable { private final List value = new ArrayList<>(); public static Lst empty() { @@ -76,10 +76,6 @@ public class Lst implements Obj, ArithmeticOperand { return value.get(index.intValue()); } - private Obj atIfAbsent(int index, Obj defaultValue) { - return index < size() ? value.get(index) : defaultValue; - } - public int indexOf(Obj item) { return value.indexOf(item); } @@ -114,6 +110,7 @@ public class Lst implements Obj, ArithmeticOperand { return Bool.TRUE; } + @Override public Iterator iterator() { return value.iterator(); } @@ -166,11 +163,13 @@ public class Lst implements Obj, ArithmeticOperand { for (Obj each : value) result.append(Fcl.aOp(each).add(other)); return result; - } else if (other instanceof Lst) { + } else if (other.iterable().boolValue()) { Lst result = Lst.empty(); - for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { - Obj a = atIfAbsent(i, Num.ZERO); - Obj b = ((Lst)other).atIfAbsent(i, Num.ZERO); + Iterator it1 = this.iterator(); + Iterator it2 = ((Iterable) other).iterator(); + while (it1.hasNext() || it2.hasNext()) { + Obj a = it1.hasNext() ? it1.next() : Num.ZERO; + Obj b = it2.hasNext() ? it2.next() : Num.ZERO; result.append(Fcl.aOp(a).add(b)); } return result; @@ -186,11 +185,13 @@ public class Lst implements Obj, ArithmeticOperand { for (Obj each : value) result.append(Fcl.aOp(each).sub(other)); return result; - } else if (other instanceof Lst) { + } else if (other.iterable().boolValue()) { Lst result = Lst.empty(); - for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { - Obj a = atIfAbsent(i, Num.ZERO); - Obj b = ((Lst)other).atIfAbsent(i, Num.ZERO); + Iterator it1 = this.iterator(); + Iterator it2 = ((Iterable) other).iterator(); + while (it1.hasNext() || it2.hasNext()) { + Obj a = it1.hasNext() ? it1.next() : Num.ZERO; + Obj b = it2.hasNext() ? it2.next() : Num.ZERO; result.append(Fcl.aOp(a).sub(b)); } return result; @@ -206,11 +207,13 @@ public class Lst implements Obj, ArithmeticOperand { for (Obj each : value) result.append(Fcl.aOp(each).mul(other)); return result; - } else if (other instanceof Lst) { + } else if (other.iterable().boolValue()) { Lst result = Lst.empty(); - for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { - Obj a = atIfAbsent(i, Num.ONE); - Obj b = ((Lst)other).atIfAbsent(i, Num.ONE); + Iterator it1 = this.iterator(); + Iterator it2 = ((Iterable) other).iterator(); + while (it1.hasNext() || it2.hasNext()) { + Obj a = it1.hasNext() ? it1.next() : Num.ONE; + Obj b = it2.hasNext() ? it2.next() : Num.ONE; result.append(Fcl.aOp(a).mul(b)); } return result; @@ -226,12 +229,14 @@ public class Lst implements Obj, ArithmeticOperand { for (Obj each : value) result.append(Fcl.aOp(each).div(other)); return result; - } else if (other instanceof Lst) { + } else if (other.iterable().boolValue()) { Lst result = Lst.empty(); - for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { - Obj a = atIfAbsent(i, Num.ZERO); - Obj b = ((Lst)other).atIfAbsent(i, Num.ONE); - result.append((Fcl.aOp(a)).div(b)); + Iterator it1 = this.iterator(); + Iterator it2 = ((Iterable) other).iterator(); + while (it1.hasNext() || it2.hasNext()) { + Obj a = it1.hasNext() ? it1.next() : Num.ZERO; + Obj b = it2.hasNext() ? it2.next() : Num.ONE; + result.append(Fcl.aOp(a).div(b)); } return result; } else { @@ -246,11 +251,13 @@ public class Lst implements Obj, ArithmeticOperand { for (Obj each : value) result.append(Fcl.aOp(each).pow(other)); return result; - } else if (other instanceof Lst) { + } else if (other.iterable().boolValue()) { Lst result = Lst.empty(); - for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { - Obj a = atIfAbsent(i, Num.ONE); - Obj b = ((Lst)other).atIfAbsent(i, Num.ONE); + Iterator it1 = this.iterator(); + Iterator it2 = ((Iterable) other).iterator(); + while (it1.hasNext() || it2.hasNext()) { + Obj a = it1.hasNext() ? it1.next() : Num.ONE; + Obj b = it2.hasNext() ? it2.next() : Num.ONE; result.append(Fcl.aOp(a).pow(b)); } return result; diff --git a/src/main/java/com/vectron/fcl/types/Num.java b/src/main/java/com/vectron/fcl/types/Num.java index 0bd3f7e..5fae6b3 100644 --- a/src/main/java/com/vectron/fcl/types/Num.java +++ b/src/main/java/com/vectron/fcl/types/Num.java @@ -5,6 +5,7 @@ import com.vectron.fcl.exceptions.TypeMismatched; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.util.Iterator; import java.util.Locale; import java.util.Objects; @@ -76,8 +77,8 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand { return new Num((Double) value + (Long)other.value()); else if (value instanceof Double && other.value() instanceof Double) return new Num((Double) value + (Double) other.value()); - else if (other instanceof Lst) - return ((Lst) other).add(this); + else if (other instanceof ArithmeticOperand && !(other instanceof Num)) + return ((ArithmeticOperand) other).add(this); else if (STRICT) throw new TypeMismatched("+", this, other); return Num.NAN; @@ -93,8 +94,8 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand { return new Num((Double) value - (Long)other.value()); else if (value instanceof Double && other.value() instanceof Double) return new Num((Double) value - (Double) other.value()); - else if (other instanceof Lst) - return ((Lst) other).mul(Num.MINUS_ONE).add(this); + else if (other instanceof ArithmeticOperand && !(other instanceof Num)) + return ((ArithmeticOperand)((ArithmeticOperand) other).mul(Num.MINUS_ONE)).add(this); else if (STRICT) throw new TypeMismatched("-", this, other); return Num.NAN; @@ -110,10 +111,8 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand { return new Num((Double) value * (Long)other.value()); else if (value instanceof Double && other.value() instanceof Double) return new Num((Double) value * (Double) other.value()); - else if (other instanceof Lst) - return ((Lst) other).mul(this); - else if (other instanceof Str) - return ((Str) other).mul(this); + else if (other instanceof ArithmeticOperand && !(other instanceof Num)) + return ((ArithmeticOperand) other).mul(this); else if (STRICT) throw new TypeMismatched("*", this, other); return Num.NAN; @@ -129,8 +128,8 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand { return new Num((Double) value / (Long)other.value()); else if (value instanceof Double && other.value() instanceof Double) return new Num((Double) value / (Double) other.value()); - else if (other instanceof Lst) - return ((ArithmeticOperand) ((Lst) other).pow(Num.MINUS_ONE)).mul(this); + else if (other instanceof ArithmeticOperand && !(other instanceof Num)) + return ((ArithmeticOperand) ((ArithmeticOperand) other).pow(Num.MINUS_ONE)).mul(this); else if (STRICT) throw new TypeMismatched("/", this, other); return Num.NAN; @@ -140,10 +139,11 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand { public Obj pow(Obj other) { if (other instanceof Num) { return new Num(Math.pow(doubleValue(), other.doubleValue())); - } else if (other instanceof Lst) { + } else if (other.iterable().boolValue()) { Lst result = Lst.empty(); - for (Obj each : ((Lst) other).value()) - result.append(this.pow(each)); + Iterator it = ((Iterable)other).iterator(); + while (it.hasNext()) + result.append(this.pow(it.next())); return result; } else if (STRICT) { throw new TypeMismatched("pow", this, other); diff --git a/src/main/java/com/vectron/fcl/types/Range.java b/src/main/java/com/vectron/fcl/types/Range.java index 8e80b52..61a1291 100644 --- a/src/main/java/com/vectron/fcl/types/Range.java +++ b/src/main/java/com/vectron/fcl/types/Range.java @@ -7,12 +7,10 @@ import java.util.Iterator; import static com.vectron.fcl.Fcl.STRICT; -public class Range implements Obj { - private RangeIterator iterator; +public class Range implements Obj, Iterable, ArithmeticOperand { private final Num from; private final Num to; private final Num by; - private Num current; public static Range create(Num by, Num to, Num from) { return new Range(from, to, by); @@ -24,7 +22,6 @@ public class Range implements Obj { this.from = from; this.to = to; this.by = by; - this.current = from; } @Override @@ -63,22 +60,21 @@ public class Range implements Obj { return Bool.TRUE; } + @Override public Iterator iterator() { - if (iterator == null) - iterator = new RangeIterator(); - return iterator; + return new RangeIterator(from, to, by); } @Override public String toString() { return by.doubleValue() == 1 - ? String.format("%s..%s (%s)", from, to, current) - : String.format("%s...%s (%s) by %s", from, to, current, by); + ? String.format("%s..%s", from, to) + : String.format("%s..%s by %s", from, to, by); } @Override public Object value() { - return iterator; + return new RangeIterator(from, to, by); } @Override @@ -91,7 +87,50 @@ public class Range implements Obj { return -1; } - public class RangeIterator implements Iterator { + @Override + public Obj add(Obj other) { + return toLst().add(other); + } + + @Override + public Obj sub(Obj other) { + return toLst().sub(other); + } + + @Override + public Obj mul(Obj other) { + return toLst().mul(other); + } + + @Override + public Obj div(Obj other) { + return toLst().div(other); + } + + @Override + public Obj pow(Obj other) { + return toLst().pow(other); + } + + private Lst toLst() { + Lst result = Lst.empty(); + Iterator it = iterator(); + while (it.hasNext()) + result.append(it.next()); + return result; + } + + public static class RangeIterator implements Iterator { + private Num current; + private final Num to; + private final Num by; + + public RangeIterator(Num from, Num to, Num by) { + this.current = from; + this.to = to; + this.by = by; + } + @Override public boolean hasNext() { return by.doubleValue() > 0 diff --git a/src/main/java/com/vectron/fcl/types/Str.java b/src/main/java/com/vectron/fcl/types/Str.java index 74f1107..cc1bc59 100644 --- a/src/main/java/com/vectron/fcl/types/Str.java +++ b/src/main/java/com/vectron/fcl/types/Str.java @@ -8,7 +8,7 @@ import java.util.Objects; import static com.vectron.fcl.Fcl.STRICT; -public class Str implements Obj, ArithmeticOperand { +public class Str implements Obj, ArithmeticOperand, Iterable { private final String value; public Str(String value) { @@ -113,8 +113,9 @@ public class Str implements Obj, ArithmeticOperand { return Bool.TRUE; } - public Iterator iterator() { - return new Iterator() { + @Override + public Iterator iterator() { + return new Iterator() { private int index = 0; @Override public boolean hasNext() { @@ -122,7 +123,7 @@ public class Str implements Obj, ArithmeticOperand { } @Override - public Str next() { + public Chr next() { return new Chr(value.charAt(index++)); } }; diff --git a/src/test/java/com/vectron/fcl/FclTest.java b/src/test/java/com/vectron/fcl/FclTest.java index cdc7ff9..444069f 100644 --- a/src/test/java/com/vectron/fcl/FclTest.java +++ b/src/test/java/com/vectron/fcl/FclTest.java @@ -1069,6 +1069,49 @@ public class FclTest { } } + @Test + public void testRangeArithmetic() { + assertEquals("[ 1 2 3 ]", evalPop("1 3 .. round").toString()); + assertEquals("[ 16 16 17 17 17 ]", evalPop("256 300 10 ... sqrt round").toString()); + + assertEquals("[ 11 12 13 ]", evalPop("10 1 3 .. +").toString()); + assertEquals("[ 9 8 7 ]", evalPop("10 1 3 .. -").toString()); + assertEquals("[ 10 20 30 ]", evalPop("10 1 3 .. *").toString()); + assertEquals("[ 6.0 3.0 2.0 ]", evalPop("6 1 3 .. /").toString()); + + assertEquals("[ 11 12 13 ]", evalPop("1 3 .. 10 +").toString()); + assertEquals("[ -9 -8 -7 ]", evalPop("1 3 .. 10 -").toString()); + assertEquals("[ 10 20 30 ]", evalPop("1 3 .. 10 *").toString()); + assertEquals("[ 0.5 1.0 1.5 ]", evalPop("1 3 .. 2 /").toString()); + + assertEquals("[ 1.0 4.0 9.0 ]", evalPop("1 3 .. 2 pow").toString()); + assertEquals("[ 2.0 4.0 8.0 ]", evalPop("2 1 3 .. pow").toString()); + + assertEquals("[ 11 13 15 ]", evalPop("[ 10 11 12 ] 1 3 .. +").toString()); + assertEquals("[ 9 7 5 ]", evalPop("[ 10 9 8 ] 1 3 .. -").toString()); + assertEquals("[ 10 22 36 ]", evalPop("[ 10 11 12 ] 1 3 .. *").toString()); + assertEquals("[ 10.0 6.0 10.0 ]", evalPop("[ 10 12 30 ] 1 3 .. /").toString()); + assertEquals("[ 2.0 9.0 64.0 ]", evalPop("[ 2 3 4 ] 1 3 .. pow").toString()); + + assertEquals("[ 11 13 15 ]", evalPop("1 3 .. [ 10 11 12 ] +").toString()); + assertEquals("[ -9 1 -1 ]", evalPop("1 3 .. [ 10 1 4 ] -").toString()); + assertEquals("[ 2 6 12 ]", evalPop("1 3 .. [ 2 3 4 ] *").toString()); + assertEquals("[ 0.5 0.25 1.0 ]", evalPop("1 3 .. [ 2 8 3 ] /").toString()); + assertEquals("[ 1.0 8.0 27.0 ]", evalPop("1 3 .. [ 2 3 3 ] pow").toString()); + + assertEquals("[ 11 13 15 ]", evalPop("1 3 .. 10 12 .. +").toString()); + assertEquals("[ 9 8 7 ]", evalPop("10 12 .. 1 5 2 ... -").toString()); // [1 3 5] + assertEquals("[ 10 22 36 ]", evalPop("1 3 .. 10 12 .. *").toString()); + assertEquals("[ 11.0 4.0 2.6 ]", evalPop("11 13 .. 1 5 2 ... /").toString()); // [1 3 5] + assertEquals("[ 1.0 8.0 243.0 ]", evalPop("1 3 .. 1 5 2 ... pow").toString()); // [1 3 5] + + assertEquals("[ 2 4 3 ]", evalPop("1 3 .. 1 2 .. +").toString()); + assertEquals("[ 9 8 12 ]", evalPop("10 12 .. 1 3 2 ... -").toString()); // [1 3] + assertEquals("[ 10 22 3 ]", evalPop("1 3 .. 10 11 .. *").toString()); + assertEquals("[ 11.0 4.0 13.0 ]", evalPop("11 13 .. 1 3 2 ... /").toString()); // [1 3] + assertEquals("[ 1.0 8.0 3.0 ]", evalPop("1 3 .. 1 3 2 ... pow").toString()); // [1 3] + } + @Test public void testStrArithmetic() throws Exception { assertEquals("'ababab'", evalPop("3 'ab' *").toString()); diff --git a/src/test/java/com/vectron/fcl/JsonSerializerTest.java b/src/test/java/com/vectron/fcl/JsonSerializerTest.java new file mode 100644 index 0000000..f7a4e76 --- /dev/null +++ b/src/test/java/com/vectron/fcl/JsonSerializerTest.java @@ -0,0 +1,44 @@ +package com.vectron.fcl; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.vectron.fcl.types.Lst; +import com.vectron.fcl.types.Num; +import com.vectron.fcl.types.Obj; +import com.vectron.fcl.types.Range; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class JsonSerializerTest { + static FclTypeAdapter typeAdapter = new FclTypeAdapter(); + static Gson gson = new GsonBuilder() + .registerTypeAdapter(Obj.class, typeAdapter) + .setLenient() + .serializeSpecialFloatingPointValues() + .create(); + + static { + typeAdapter.setGSon(gson); + } + + @Test + public void testSerializeList() { + Lst lst = Lst.empty(); + lst.append(new Num(1)); + lst.append(new Num(2)); + assertEquals( + "{\"value\":[{\"value\":1,\"__type\":\"num\"},{\"value\":2,\"__type\":\"num\"}],\"__type\":\"lst\"}", + gson.toJsonTree(lst, Obj.class).toString()); + } + + @Test + public void testSerializeRange() { + Range rng = Range.create(new Num(1), new Num(3), new Num(1)); + assertEquals( + "{\"from\":{\"value\":1\"__type\":\"num\"},\"to\":{\"value\":3\"__type\":\"num\"},\"by\":{\"value\":1\"__type\":\"num\"},\"__type\":\"rng\"}", + gson.toJsonTree(rng, Obj.class).toString()); + } +} \ No newline at end of file