From f6245bb57877f7caf61629fbf98842f7e4a707c4 Mon Sep 17 00:00:00 2001 From: David Benn Date: Fri, 12 Jul 2024 22:21:17 +0930 Subject: [PATCH] #440: ensured that converted operand is returned and fixed UT results; added conversion of bool, int, real to string function params --- .../tools/vstar/vela/FunctionExecutor.java | 3 - src/org/aavso/tools/vstar/vela/Operand.java | 924 +++++++++--------- src/org/aavso/tools/vstar/vela/Type.java | 4 +- test/org/aavso/tools/vstar/vela/VeLaTest.java | 4 +- 4 files changed, 467 insertions(+), 468 deletions(-) diff --git a/src/org/aavso/tools/vstar/vela/FunctionExecutor.java b/src/org/aavso/tools/vstar/vela/FunctionExecutor.java index ec96758a3..8332d50fa 100644 --- a/src/org/aavso/tools/vstar/vela/FunctionExecutor.java +++ b/src/org/aavso/tools/vstar/vela/FunctionExecutor.java @@ -132,9 +132,6 @@ public FunctionExecutor(Optional funcName, Optional returnType) { public boolean conforms(List actualParameters) { boolean result = true; - actualParameters = actualParameters.stream().map(op -> op.copy()) - .collect(Collectors.toList()); - if (parameterTypes == ANY_FORMALS) { result = true; } else if (actualParameters.size() != parameterTypes.size()) { diff --git a/src/org/aavso/tools/vstar/vela/Operand.java b/src/org/aavso/tools/vstar/vela/Operand.java index 3ca92cb2b..7f6a9cec2 100644 --- a/src/org/aavso/tools/vstar/vela/Operand.java +++ b/src/org/aavso/tools/vstar/vela/Operand.java @@ -32,466 +32,468 @@ */ public class Operand { - private Type type; - private long intVal; - private double doubleVal; - private String stringVal; - private boolean booleanVal; - private List listVal; - private FunctionExecutor functionVal; - - public static Operand EMPTY_LIST = new Operand(Type.LIST, Collections.emptyList()); - - public static Operand NO_VALUE = new Operand(Type.NONE, false); - - public Operand(Type type, long value) { - this.type = type; - intVal = value; - } - - public Operand(Type type, double value) { - this.type = type; - doubleVal = value; - } - - public Operand(Type type, String value) { - this.type = type; - stringVal = value; - } - - public Operand(Type type, boolean value) { - this.type = type; - booleanVal = value; - } - - public Operand(Type type, List value) { - this.type = type; - listVal = value; - } - - public Operand(Type type, FunctionExecutor value) { - this.type = type; - functionVal = value; - } - - // For object copy - private Operand() { - } - - // TODO: add a Property to Operand method - - /** - * Given a VeLa type and a Java object, return an Operand instance. - * - * @param type The VeLa type. - * @param obj The Java object. - * @return A corresponding Operand instance. - */ - public static Operand object2Operand(Type type, Object obj) { - Operand operand = null; - - switch (type) { - case INTEGER: - operand = new Operand(Type.INTEGER, (int) obj); - break; - case REAL: - operand = new Operand(Type.REAL, (double) obj); - break; - case STRING: - operand = new Operand(Type.STRING, (String) obj); - break; - case BOOLEAN: - operand = new Operand(Type.BOOLEAN, (boolean) obj); - break; - case LIST: - if (obj.getClass() == Type.DBL_ARR.getClass()) { - List arr = new ArrayList(); - for (double n : (double[]) obj) { - arr.add(new Operand(Type.REAL, n)); - } - obj = arr; - } else if (obj.getClass() == Type.DBL_CLASS_ARR.getClass()) { - List arr = new ArrayList(); - for (Double n : (Double[]) obj) { - arr.add(new Operand(Type.REAL, n)); - } - obj = arr; - } - operand = new Operand(Type.LIST, (List) obj); - break; - case FUNCTION: - operand = new Operand(Type.FUNCTION, (FunctionExecutor) obj); - break; - case NONE: - operand = NO_VALUE; - break; - case OBJECT: - // TODO + private Type type; + private long intVal; + private double doubleVal; + private String stringVal; + private boolean booleanVal; + private List listVal; + private FunctionExecutor functionVal; + + public static Operand EMPTY_LIST = new Operand(Type.LIST, Collections.emptyList()); + + public static Operand NO_VALUE = new Operand(Type.NONE, false); + + public Operand(Type type, long value) { + this.type = type; + intVal = value; + } + + public Operand(Type type, double value) { + this.type = type; + doubleVal = value; + } + + public Operand(Type type, String value) { + this.type = type; + stringVal = value; + } + + public Operand(Type type, boolean value) { + this.type = type; + booleanVal = value; + } + + public Operand(Type type, List value) { + this.type = type; + listVal = value; + } + + public Operand(Type type, FunctionExecutor value) { + this.type = type; + functionVal = value; + } + + // For object copy + private Operand() { + } + + // TODO: add a Property to Operand method + + /** + * Given a VeLa type and a Java object, return an Operand instance. + * + * @param type The VeLa type. + * @param obj The Java object. + * @return A corresponding Operand instance. + */ + public static Operand object2Operand(Type type, Object obj) { + Operand operand = null; + + switch (type) { + case INTEGER: + operand = new Operand(Type.INTEGER, (int) obj); + break; + case REAL: + operand = new Operand(Type.REAL, (double) obj); + break; + case STRING: + operand = new Operand(Type.STRING, (String) obj); + break; + case BOOLEAN: + operand = new Operand(Type.BOOLEAN, (boolean) obj); + break; + case LIST: + if (obj.getClass() == Type.DBL_ARR.getClass()) { + List arr = new ArrayList(); + for (double n : (double[]) obj) { + arr.add(new Operand(Type.REAL, n)); + } + obj = arr; + } else if (obj.getClass() == Type.DBL_CLASS_ARR.getClass()) { + List arr = new ArrayList(); + for (Double n : (Double[]) obj) { + arr.add(new Operand(Type.REAL, n)); + } + obj = arr; + } + operand = new Operand(Type.LIST, (List) obj); + break; + case FUNCTION: + operand = new Operand(Type.FUNCTION, (FunctionExecutor) obj); + break; + case NONE: + operand = NO_VALUE; + break; + case OBJECT: + // TODO // operand = new Operand(Type.OBJECT, obj); - } - - return operand; - } - - /** - * Return a Java object corresponding to this Operand instance. - * - * @return A corresponding Java object. - */ - public Object toObject() { - Object obj = null; - - switch (type) { - case INTEGER: - obj = intVal; - break; - case REAL: - obj = doubleVal; - break; - case STRING: - obj = stringVal; - break; - case BOOLEAN: - obj = booleanVal; - break; - case LIST: - for (Type type : Type.values()) { - if (listVal.stream().allMatch(op -> op.type == type)) { - int i = 0; - try { - switch (type) { - case INTEGER: - int[] ints = new int[listVal.size()]; - for (Operand op : listVal) { - ints[i++] = (int) op.intVal; - } - obj = ints; - break; - case REAL: - double[] reals = new double[listVal.size()]; - for (Operand op : listVal) { - reals[i++] = (double) op.doubleVal; - } - obj = reals; - break; - case STRING: - String[] strings = new String[listVal.size()]; - for (Operand op : listVal) { - strings[i++] = (String) op.stringVal; - } - obj = strings; - break; - case BOOLEAN: - boolean[] booleans = new boolean[listVal.size()]; - for (Operand op : listVal) { - booleans[i++] = (boolean) op.booleanVal; - } - obj = booleans; - break; - default: - throw new VeLaEvalError(""); - } - } catch (Throwable t) { - throw new VeLaEvalError("Cannot construct array from VeLa list"); - } - } - } - break; - case FUNCTION: - // TODO - break; - case NONE: - // TODO - break; - case OBJECT: - // TODO - break; - } - - return obj; - } - - /** - * Convert this operand to the required type, if possible. - * - * @param operand The operand to be converted. - * @param requiredType The required type. - * @return The converted type; will be unchanged if it matches the required type - * or can't be converted; TODO: consider returning Optional; if - * empty, then the type can't be converted - */ - public Type convert(Type requiredType) { - if (type != requiredType) { - // Integer to double - if (type == Type.INTEGER && requiredType == Type.REAL) { - setType(Type.REAL); - setDoubleVal((double) intVal); - } - } - - return type; - } - - /** - * Convert this operand's type to string. - */ - public void convertToString() { - assert type == Type.INTEGER || type == Type.REAL || type == Type.BOOLEAN; - - switch (type) { - case INTEGER: - setStringVal(Long.toString(intVal)); - setType(Type.STRING); - break; - case REAL: - setStringVal(NumericPrecisionPrefs.formatOther(doubleVal)); - setType(Type.STRING); - break; - case BOOLEAN: - setStringVal(Boolean.toString(booleanVal)); - setType(Type.STRING); - break; - default: - break; - } - } - - /** - * @return the type - */ - public Type getType() { - return type; - } - - /** - * @param type the type to set - */ - public void setType(Type type) { - this.type = type; - } - - /** - * @param intVal the intVal to set - */ - public void setIntegerVal(long intVal) { - this.intVal = intVal; - } - - /** - * @param doubleVal the doubleVal to set - */ - public void setDoubleVal(double doubleVal) { - this.doubleVal = doubleVal; - } - - /** - * @return the intVal - */ - public long intVal() { - return intVal; - } - - /** - * @return the doubleVal - */ - public double doubleVal() { - return doubleVal; - } - - /** - * @param stringVal the stringVal to set - */ - public void setStringVal(String stringVal) { - this.stringVal = stringVal; - } - - /** - * @return the stringVal - */ - public String stringVal() { - return stringVal; - } - - /** - * @param booleanVal the booleanVal to set - */ - public void setBooleanVal(boolean booleanVal) { - this.booleanVal = booleanVal; - } - - /** - * @return the booleanVal - */ - public boolean booleanVal() { - return booleanVal; - } - - /** - * @return the listVal - */ - public List listVal() { - return listVal; - } - - /** - * @param listVal the listVal to set - */ - public void setListVal(List listVal) { - this.listVal = listVal; - } - - /** - * @return the functionVal - */ - public FunctionExecutor functionVal() { - return functionVal; - } - - /** - * @param functionVal the functionVal to set - */ - public void setFunctionVal(FunctionExecutor functionVal) { - this.functionVal = functionVal; - } - - public String toHumanReadableString() { - String str = ""; - - switch (type) { - case INTEGER: - str = Long.toString(intVal); - break; - case REAL: - str = NumericPrecisionPrefs.formatOther(doubleVal); - break; - case BOOLEAN: - str = booleanVal ? "True" : "False"; - break; - case STRING: - str = stringVal; - break; - case LIST: - str = listVal.toString().replace(",", ""); - break; - case FUNCTION: - str = functionVal.toString(); - break; - } - - return str; - } - - @Override - public String toString() { - String str = ""; - - switch (type) { - case INTEGER: - str = Long.toString(intVal); - break; - case REAL: - str = NumericPrecisionPrefs.formatOther(doubleVal); - break; - case BOOLEAN: - str = booleanVal ? "True" : "False"; - break; - case STRING: - str = "\"" + stringVal + "\""; - break; - case LIST: - str = listVal.toString().replace(",", "").replace("[", "'(").replace("]", ")"); - break; - case FUNCTION: - str = functionVal.toString(); - break; - } - - // str += " (" + type + ")"; - - return str; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (booleanVal ? 1231 : 1237); - long temp; - temp = Double.doubleToLongBits(doubleVal); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + ((functionVal == null) ? 0 : functionVal.hashCode()); - result = prime * result + (int) (intVal ^ (intVal >>> 32)); - result = prime * result + ((listVal == null) ? 0 : listVal.hashCode()); - result = prime * result + ((stringVal == null) ? 0 : stringVal.hashCode()); - result = prime * result + ((type == null) ? 0 : type.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Operand other = (Operand) obj; - if (booleanVal != other.booleanVal) - return false; - if (Double.doubleToLongBits(doubleVal) != Double.doubleToLongBits(other.doubleVal)) - return false; - if (functionVal == null) { - if (other.functionVal != null) - return false; - } else if (!functionVal.equals(other.functionVal)) - return false; - if (intVal != other.intVal) - return false; - if (listVal == null) { - if (other.listVal != null) - return false; - } else if (!listVal.equals(other.listVal)) - return false; - if (stringVal == null) { - if (other.stringVal != null) - return false; - } else if (!stringVal.equals(other.stringVal)) - return false; - if (type != other.type) - return false; - return true; - } - - public Operand copy() { - Operand operand = new Operand(); - - operand.type = type; - - switch (type) { - case INTEGER: - operand.intVal = intVal; - break; - case REAL: - operand.doubleVal = doubleVal; - break; - case BOOLEAN: - operand.booleanVal = booleanVal; - break; - case STRING: - operand.stringVal = stringVal; - break; - case LIST: - List list = new ArrayList(); - for (Operand op : listVal) { - list.add(op.copy()); - } - operand.listVal = list; - break; - case FUNCTION: - operand.functionVal = functionVal; - break; - } - - return operand; - } + } + + return operand; + } + + /** + * Return a Java object corresponding to this Operand instance. + * + * @return A corresponding Java object. + */ + public Object toObject() { + Object obj = null; + + switch (type) { + case INTEGER: + obj = intVal; + break; + case REAL: + obj = doubleVal; + break; + case STRING: + obj = stringVal; + break; + case BOOLEAN: + obj = booleanVal; + break; + case LIST: + for (Type type : Type.values()) { + if (listVal.stream().allMatch(op -> op.type == type)) { + int i = 0; + try { + switch (type) { + case INTEGER: + int[] ints = new int[listVal.size()]; + for (Operand op : listVal) { + ints[i++] = (int) op.intVal; + } + obj = ints; + break; + case REAL: + double[] reals = new double[listVal.size()]; + for (Operand op : listVal) { + reals[i++] = (double) op.doubleVal; + } + obj = reals; + break; + case STRING: + String[] strings = new String[listVal.size()]; + for (Operand op : listVal) { + strings[i++] = (String) op.stringVal; + } + obj = strings; + break; + case BOOLEAN: + boolean[] booleans = new boolean[listVal.size()]; + for (Operand op : listVal) { + booleans[i++] = (boolean) op.booleanVal; + } + obj = booleans; + break; + default: + throw new VeLaEvalError(""); + } + } catch (Throwable t) { + throw new VeLaEvalError("Cannot construct array from VeLa list"); + } + } + } + break; + case FUNCTION: + // TODO + break; + case NONE: + // TODO + break; + case OBJECT: + // TODO + break; + } + + return obj; + } + + /** + * Convert this operand to the required type, if possible. + * + * @param requiredType The required type. + * @return The converted type; will be unchanged if it matches the required + * type. + * @throws VeLaEvalError if the type cannot be converted. + */ + public Type convert(Type requiredType) { + if (!type.isComposite()) { + if (type != requiredType) { + if (type == Type.INTEGER && requiredType == Type.REAL) { + setType(Type.REAL); + setDoubleVal((double) intVal); + } else if (type.oneOf(Type.INTEGER, Type.REAL, Type.BOOLEAN) && requiredType == Type.STRING) { + convertToString(); + } + } + } + + return type; + } + + /** + * Convert this operand's type to string. + */ + public void convertToString() { + assert type == Type.INTEGER || type == Type.REAL || type == Type.BOOLEAN; + + switch (type) { + case INTEGER: + setStringVal(Long.toString(intVal)); + setType(Type.STRING); + break; + case REAL: + setStringVal(NumericPrecisionPrefs.formatOther(doubleVal)); + setType(Type.STRING); + break; + case BOOLEAN: + setStringVal(Boolean.toString(booleanVal)); + setType(Type.STRING); + break; + default: + break; + } + } + + /** + * @return the type + */ + public Type getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(Type type) { + this.type = type; + } + + /** + * @param intVal the intVal to set + */ + public void setIntegerVal(long intVal) { + this.intVal = intVal; + } + + /** + * @param doubleVal the doubleVal to set + */ + public void setDoubleVal(double doubleVal) { + this.doubleVal = doubleVal; + } + + /** + * @return the intVal + */ + public long intVal() { + return intVal; + } + + /** + * @return the doubleVal + */ + public double doubleVal() { + return doubleVal; + } + + /** + * @param stringVal the stringVal to set + */ + public void setStringVal(String stringVal) { + this.stringVal = stringVal; + } + + /** + * @return the stringVal + */ + public String stringVal() { + return stringVal; + } + + /** + * @param booleanVal the booleanVal to set + */ + public void setBooleanVal(boolean booleanVal) { + this.booleanVal = booleanVal; + } + + /** + * @return the booleanVal + */ + public boolean booleanVal() { + return booleanVal; + } + + /** + * @return the listVal + */ + public List listVal() { + return listVal; + } + + /** + * @param listVal the listVal to set + */ + public void setListVal(List listVal) { + this.listVal = listVal; + } + + /** + * @return the functionVal + */ + public FunctionExecutor functionVal() { + return functionVal; + } + + /** + * @param functionVal the functionVal to set + */ + public void setFunctionVal(FunctionExecutor functionVal) { + this.functionVal = functionVal; + } + + public String toHumanReadableString() { + String str = ""; + + switch (type) { + case INTEGER: + str = Long.toString(intVal); + break; + case REAL: + str = NumericPrecisionPrefs.formatOther(doubleVal); + break; + case BOOLEAN: + str = booleanVal ? "True" : "False"; + break; + case STRING: + str = stringVal; + break; + case LIST: + str = listVal.toString().replace(",", ""); + break; + case FUNCTION: + str = functionVal.toString(); + break; + } + + return str; + } + + @Override + public String toString() { + String str = ""; + + switch (type) { + case INTEGER: + str = Long.toString(intVal); + break; + case REAL: + str = NumericPrecisionPrefs.formatOther(doubleVal); + break; + case BOOLEAN: + str = booleanVal ? "True" : "False"; + break; + case STRING: + str = "\"" + stringVal + "\""; + break; + case LIST: + str = listVal.toString().replace(",", "").replace("[", "'(").replace("]", ")"); + break; + case FUNCTION: + str = functionVal.toString(); + break; + } + + // str += " (" + type + ")"; + + return str; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (booleanVal ? 1231 : 1237); + long temp; + temp = Double.doubleToLongBits(doubleVal); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + ((functionVal == null) ? 0 : functionVal.hashCode()); + result = prime * result + (int) (intVal ^ (intVal >>> 32)); + result = prime * result + ((listVal == null) ? 0 : listVal.hashCode()); + result = prime * result + ((stringVal == null) ? 0 : stringVal.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Operand other = (Operand) obj; + if (booleanVal != other.booleanVal) + return false; + if (Double.doubleToLongBits(doubleVal) != Double.doubleToLongBits(other.doubleVal)) + return false; + if (functionVal == null) { + if (other.functionVal != null) + return false; + } else if (!functionVal.equals(other.functionVal)) + return false; + if (intVal != other.intVal) + return false; + if (listVal == null) { + if (other.listVal != null) + return false; + } else if (!listVal.equals(other.listVal)) + return false; + if (stringVal == null) { + if (other.stringVal != null) + return false; + } else if (!stringVal.equals(other.stringVal)) + return false; + if (type != other.type) + return false; + return true; + } + + public Operand copy() { + Operand operand = new Operand(); + + operand.type = type; + + switch (type) { + case INTEGER: + operand.intVal = intVal; + break; + case REAL: + operand.doubleVal = doubleVal; + break; + case BOOLEAN: + operand.booleanVal = booleanVal; + break; + case STRING: + operand.stringVal = stringVal; + break; + case LIST: + List list = new ArrayList(); + for (Operand op : listVal) { + list.add(op.copy()); + } + operand.listVal = list; + break; + case FUNCTION: + operand.functionVal = functionVal; + break; + } + + return operand; + } } diff --git a/src/org/aavso/tools/vstar/vela/Type.java b/src/org/aavso/tools/vstar/vela/Type.java index 3cc26635f..cb1f46661 100644 --- a/src/org/aavso/tools/vstar/vela/Type.java +++ b/src/org/aavso/tools/vstar/vela/Type.java @@ -28,7 +28,7 @@ */ public enum Type { - INTEGER, REAL, STRING, BOOLEAN, LIST, FUNCTION, OBJECT, NONE; + INTEGER, REAL, BOOLEAN, STRING, LIST, FUNCTION, OBJECT, NONE; public final static int[] INT_ARR = new int[0]; public final static double[] DBL_ARR = new double[0]; @@ -154,7 +154,7 @@ public static Type propertyToVela(Property prop) { } public boolean isComposite() { - return this == LIST || this == FUNCTION; + return this == LIST || this == FUNCTION || this == OBJECT; } public boolean oneOf(Type... types) { diff --git a/test/org/aavso/tools/vstar/vela/VeLaTest.java b/test/org/aavso/tools/vstar/vela/VeLaTest.java index c5db04bd7..848ec1f82 100644 --- a/test/org/aavso/tools/vstar/vela/VeLaTest.java +++ b/test/org/aavso/tools/vstar/vela/VeLaTest.java @@ -1820,7 +1820,7 @@ public void testUserCode1() { File code = new File("test/org/aavso/tools/vstar/vela/code/sqr.vela"); Optional result = vela.program(code); assertTrue(result.isPresent()); - assertEquals(144, result.get().intVal()); + assertEquals(144.0, result.get().doubleVal()); } public void testUserCode2() { @@ -1830,7 +1830,7 @@ public void testUserCode2() { VeLaInterpreter vela = new VeLaInterpreter(VERBOSE, ADD_VSTAR_API, dirs); Optional result = vela.program("cube(2)"); assertTrue(result.isPresent()); - assertEquals(8, result.get().intVal()); + assertEquals(8.0, result.get().doubleVal()); } // Standard Library Functions: VeLa and intrinsic (Java)