This commit is contained in:
zeroflag 2022-12-08 10:37:09 +01:00
parent bb5e19461b
commit 43b718d1a7
7 changed files with 190 additions and 55 deletions

View file

@ -9,7 +9,7 @@ import java.util.Map;
import static com.vectron.fcl.Fcl.STRICT; import static com.vectron.fcl.Fcl.STRICT;
public class Dic implements Obj { public class Dic implements Obj, Iterable<Lst> {
private final Map<Obj, Obj> value = new LinkedHashMap<>(); private final Map<Obj, Obj> value = new LinkedHashMap<>();
public static Dic empty() { public static Dic empty() {
@ -70,6 +70,7 @@ public class Dic implements Obj {
return Bool.TRUE; return Bool.TRUE;
} }
@Override
public Iterator<Lst> iterator() { public Iterator<Lst> iterator() {
return new Iterator<Lst>() { return new Iterator<Lst>() {
private Iterator<Map.Entry<Obj,Obj>> it = value.entrySet().iterator(); private Iterator<Map.Entry<Obj,Obj>> it = value.entrySet().iterator();

View file

@ -9,7 +9,7 @@ import java.util.List;
import static com.vectron.fcl.Fcl.STRICT; import static com.vectron.fcl.Fcl.STRICT;
public class Lst implements Obj, ArithmeticOperand { public class Lst implements Obj, ArithmeticOperand, Iterable<Obj> {
private final List<Obj> value = new ArrayList<>(); private final List<Obj> value = new ArrayList<>();
public static Lst empty() { public static Lst empty() {
@ -76,10 +76,6 @@ public class Lst implements Obj, ArithmeticOperand {
return value.get(index.intValue()); return value.get(index.intValue());
} }
private Obj atIfAbsent(int index, Obj defaultValue) {
return index < size() ? value.get(index) : defaultValue;
}
public int indexOf(Obj item) { public int indexOf(Obj item) {
return value.indexOf(item); return value.indexOf(item);
} }
@ -114,6 +110,7 @@ public class Lst implements Obj, ArithmeticOperand {
return Bool.TRUE; return Bool.TRUE;
} }
@Override
public Iterator<Obj> iterator() { public Iterator<Obj> iterator() {
return value.iterator(); return value.iterator();
} }
@ -166,11 +163,13 @@ public class Lst implements Obj, ArithmeticOperand {
for (Obj each : value) for (Obj each : value)
result.append(Fcl.aOp(each).add(other)); result.append(Fcl.aOp(each).add(other));
return result; return result;
} else if (other instanceof Lst) { } else if (other.iterable().boolValue()) {
Lst result = Lst.empty(); Lst result = Lst.empty();
for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { Iterator<Obj> it1 = this.iterator();
Obj a = atIfAbsent(i, Num.ZERO); Iterator<Obj> it2 = ((Iterable<Obj>) other).iterator();
Obj b = ((Lst)other).atIfAbsent(i, Num.ZERO); 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)); result.append(Fcl.aOp(a).add(b));
} }
return result; return result;
@ -186,11 +185,13 @@ public class Lst implements Obj, ArithmeticOperand {
for (Obj each : value) for (Obj each : value)
result.append(Fcl.aOp(each).sub(other)); result.append(Fcl.aOp(each).sub(other));
return result; return result;
} else if (other instanceof Lst) { } else if (other.iterable().boolValue()) {
Lst result = Lst.empty(); Lst result = Lst.empty();
for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { Iterator<Obj> it1 = this.iterator();
Obj a = atIfAbsent(i, Num.ZERO); Iterator<Obj> it2 = ((Iterable<Obj>) other).iterator();
Obj b = ((Lst)other).atIfAbsent(i, Num.ZERO); 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)); result.append(Fcl.aOp(a).sub(b));
} }
return result; return result;
@ -206,11 +207,13 @@ public class Lst implements Obj, ArithmeticOperand {
for (Obj each : value) for (Obj each : value)
result.append(Fcl.aOp(each).mul(other)); result.append(Fcl.aOp(each).mul(other));
return result; return result;
} else if (other instanceof Lst) { } else if (other.iterable().boolValue()) {
Lst result = Lst.empty(); Lst result = Lst.empty();
for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { Iterator<Obj> it1 = this.iterator();
Obj a = atIfAbsent(i, Num.ONE); Iterator<Obj> it2 = ((Iterable<Obj>) other).iterator();
Obj b = ((Lst)other).atIfAbsent(i, Num.ONE); 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)); result.append(Fcl.aOp(a).mul(b));
} }
return result; return result;
@ -226,12 +229,14 @@ public class Lst implements Obj, ArithmeticOperand {
for (Obj each : value) for (Obj each : value)
result.append(Fcl.aOp(each).div(other)); result.append(Fcl.aOp(each).div(other));
return result; return result;
} else if (other instanceof Lst) { } else if (other.iterable().boolValue()) {
Lst result = Lst.empty(); Lst result = Lst.empty();
for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { Iterator<Obj> it1 = this.iterator();
Obj a = atIfAbsent(i, Num.ZERO); Iterator<Obj> it2 = ((Iterable<Obj>) other).iterator();
Obj b = ((Lst)other).atIfAbsent(i, Num.ONE); while (it1.hasNext() || it2.hasNext()) {
result.append((Fcl.aOp(a)).div(b)); 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; return result;
} else { } else {
@ -246,11 +251,13 @@ public class Lst implements Obj, ArithmeticOperand {
for (Obj each : value) for (Obj each : value)
result.append(Fcl.aOp(each).pow(other)); result.append(Fcl.aOp(each).pow(other));
return result; return result;
} else if (other instanceof Lst) { } else if (other.iterable().boolValue()) {
Lst result = Lst.empty(); Lst result = Lst.empty();
for (int i = 0; i < Math.max(size(), ((Lst) other).size()); i++) { Iterator<Obj> it1 = this.iterator();
Obj a = atIfAbsent(i, Num.ONE); Iterator<Obj> it2 = ((Iterable<Obj>) other).iterator();
Obj b = ((Lst)other).atIfAbsent(i, Num.ONE); 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)); result.append(Fcl.aOp(a).pow(b));
} }
return result; return result;

View file

@ -5,6 +5,7 @@ import com.vectron.fcl.exceptions.TypeMismatched;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols; import java.text.DecimalFormatSymbols;
import java.util.Iterator;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
@ -76,8 +77,8 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand {
return new Num((Double) value + (Long)other.value()); return new Num((Double) value + (Long)other.value());
else if (value instanceof Double && other.value() instanceof Double) else if (value instanceof Double && other.value() instanceof Double)
return new Num((Double) value + (Double) other.value()); return new Num((Double) value + (Double) other.value());
else if (other instanceof Lst) else if (other instanceof ArithmeticOperand && !(other instanceof Num))
return ((Lst) other).add(this); return ((ArithmeticOperand) other).add(this);
else if (STRICT) else if (STRICT)
throw new TypeMismatched("+", this, other); throw new TypeMismatched("+", this, other);
return Num.NAN; return Num.NAN;
@ -93,8 +94,8 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand {
return new Num((Double) value - (Long)other.value()); return new Num((Double) value - (Long)other.value());
else if (value instanceof Double && other.value() instanceof Double) else if (value instanceof Double && other.value() instanceof Double)
return new Num((Double) value - (Double) other.value()); return new Num((Double) value - (Double) other.value());
else if (other instanceof Lst) else if (other instanceof ArithmeticOperand && !(other instanceof Num))
return ((Lst) other).mul(Num.MINUS_ONE).add(this); return ((ArithmeticOperand)((ArithmeticOperand) other).mul(Num.MINUS_ONE)).add(this);
else if (STRICT) else if (STRICT)
throw new TypeMismatched("-", this, other); throw new TypeMismatched("-", this, other);
return Num.NAN; return Num.NAN;
@ -110,10 +111,8 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand {
return new Num((Double) value * (Long)other.value()); return new Num((Double) value * (Long)other.value());
else if (value instanceof Double && other.value() instanceof Double) else if (value instanceof Double && other.value() instanceof Double)
return new Num((Double) value * (Double) other.value()); return new Num((Double) value * (Double) other.value());
else if (other instanceof Lst) else if (other instanceof ArithmeticOperand && !(other instanceof Num))
return ((Lst) other).mul(this); return ((ArithmeticOperand) other).mul(this);
else if (other instanceof Str)
return ((Str) other).mul(this);
else if (STRICT) else if (STRICT)
throw new TypeMismatched("*", this, other); throw new TypeMismatched("*", this, other);
return Num.NAN; return Num.NAN;
@ -129,8 +128,8 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand {
return new Num((Double) value / (Long)other.value()); return new Num((Double) value / (Long)other.value());
else if (value instanceof Double && other.value() instanceof Double) else if (value instanceof Double && other.value() instanceof Double)
return new Num((Double) value / (Double) other.value()); return new Num((Double) value / (Double) other.value());
else if (other instanceof Lst) else if (other instanceof ArithmeticOperand && !(other instanceof Num))
return ((ArithmeticOperand) ((Lst) other).pow(Num.MINUS_ONE)).mul(this); return ((ArithmeticOperand) ((ArithmeticOperand) other).pow(Num.MINUS_ONE)).mul(this);
else if (STRICT) else if (STRICT)
throw new TypeMismatched("/", this, other); throw new TypeMismatched("/", this, other);
return Num.NAN; return Num.NAN;
@ -140,10 +139,11 @@ public class Num implements Obj, LogicOperand, ArithmeticOperand {
public Obj pow(Obj other) { public Obj pow(Obj other) {
if (other instanceof Num) { if (other instanceof Num) {
return new Num(Math.pow(doubleValue(), other.doubleValue())); return new Num(Math.pow(doubleValue(), other.doubleValue()));
} else if (other instanceof Lst) { } else if (other.iterable().boolValue()) {
Lst result = Lst.empty(); Lst result = Lst.empty();
for (Obj each : ((Lst) other).value()) Iterator<Obj> it = ((Iterable)other).iterator();
result.append(this.pow(each)); while (it.hasNext())
result.append(this.pow(it.next()));
return result; return result;
} else if (STRICT) { } else if (STRICT) {
throw new TypeMismatched("pow", this, other); throw new TypeMismatched("pow", this, other);

View file

@ -7,12 +7,10 @@ import java.util.Iterator;
import static com.vectron.fcl.Fcl.STRICT; import static com.vectron.fcl.Fcl.STRICT;
public class Range implements Obj { public class Range implements Obj, Iterable<Obj>, ArithmeticOperand {
private RangeIterator iterator;
private final Num from; private final Num from;
private final Num to; private final Num to;
private final Num by; private final Num by;
private Num current;
public static Range create(Num by, Num to, Num from) { public static Range create(Num by, Num to, Num from) {
return new Range(from, to, by); return new Range(from, to, by);
@ -24,7 +22,6 @@ public class Range implements Obj {
this.from = from; this.from = from;
this.to = to; this.to = to;
this.by = by; this.by = by;
this.current = from;
} }
@Override @Override
@ -63,22 +60,21 @@ public class Range implements Obj {
return Bool.TRUE; return Bool.TRUE;
} }
@Override
public Iterator<Obj> iterator() { public Iterator<Obj> iterator() {
if (iterator == null) return new RangeIterator(from, to, by);
iterator = new RangeIterator();
return iterator;
} }
@Override @Override
public String toString() { public String toString() {
return by.doubleValue() == 1 return by.doubleValue() == 1
? String.format("%s..%s (%s)", from, to, current) ? String.format("%s..%s", from, to)
: String.format("%s...%s (%s) by %s", from, to, current, by); : String.format("%s..%s by %s", from, to, by);
} }
@Override @Override
public Object value() { public Object value() {
return iterator; return new RangeIterator(from, to, by);
} }
@Override @Override
@ -91,7 +87,50 @@ public class Range implements Obj {
return -1; return -1;
} }
public class RangeIterator implements Iterator<Obj> { @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<Obj> it = iterator();
while (it.hasNext())
result.append(it.next());
return result;
}
public static class RangeIterator implements Iterator<Obj> {
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 @Override
public boolean hasNext() { public boolean hasNext() {
return by.doubleValue() > 0 return by.doubleValue() > 0

View file

@ -8,7 +8,7 @@ import java.util.Objects;
import static com.vectron.fcl.Fcl.STRICT; import static com.vectron.fcl.Fcl.STRICT;
public class Str implements Obj, ArithmeticOperand { public class Str implements Obj, ArithmeticOperand, Iterable<Chr> {
private final String value; private final String value;
public Str(String value) { public Str(String value) {
@ -113,8 +113,9 @@ public class Str implements Obj, ArithmeticOperand {
return Bool.TRUE; return Bool.TRUE;
} }
public Iterator<Str> iterator() { @Override
return new Iterator<Str>() { public Iterator<Chr> iterator() {
return new Iterator<Chr>() {
private int index = 0; private int index = 0;
@Override @Override
public boolean hasNext() { public boolean hasNext() {
@ -122,7 +123,7 @@ public class Str implements Obj, ArithmeticOperand {
} }
@Override @Override
public Str next() { public Chr next() {
return new Chr(value.charAt(index++)); return new Chr(value.charAt(index++));
} }
}; };

View file

@ -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 @Test
public void testStrArithmetic() throws Exception { public void testStrArithmetic() throws Exception {
assertEquals("'ababab'", evalPop("3 'ab' *").toString()); assertEquals("'ababab'", evalPop("3 'ab' *").toString());

View file

@ -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());
}
}