From 6786844239fc7d2caeb6b9ff334e714a4a9d7a8f Mon Sep 17 00:00:00 2001 From: Petr Pytelka Date: Mon, 3 Feb 2020 16:13:39 +0100 Subject: [PATCH] Support for empty value / uninitialized variable --- .../operators/AbstractCompareOperator.java | 4 ++++ .../executors/rightvalues/BasicBooleanValue.java | 2 +- .../executors/rightvalues/BasicDoubleValue.java | 3 +++ .../executors/rightvalues/BasicEmptyValue.java | 16 ++++++++++++++++ .../executors/rightvalues/BasicLongValue.java | 3 +++ .../executors/rightvalues/BasicStringValue.java | 3 +++ .../executors/rightvalues/EmptyValue.java | 14 ++++++++++++++ .../executors/rightvalues/VariableAccess.java | 3 +++ .../scriptbasic/utility/RightValueUtility.java | 2 ++ src/test/java/com/scriptbasic/TestEngine.java | 7 ++++--- .../executors/operators/TestOperators.java | 12 ++++++++++++ .../scriptbasic/testprograms/TestPrograms.java | 6 +++--- 12 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/scriptbasic/executors/rightvalues/BasicEmptyValue.java create mode 100644 src/main/java/com/scriptbasic/executors/rightvalues/EmptyValue.java diff --git a/src/main/java/com/scriptbasic/executors/operators/AbstractCompareOperator.java b/src/main/java/com/scriptbasic/executors/operators/AbstractCompareOperator.java index 63a3e643..8e568ba1 100644 --- a/src/main/java/com/scriptbasic/executors/operators/AbstractCompareOperator.java +++ b/src/main/java/com/scriptbasic/executors/operators/AbstractCompareOperator.java @@ -68,6 +68,10 @@ protected RightValue evaluateOn(final RightValue leftOperand, } } } + if (leftOperand == BasicEmptyValue.EMPTY_VALUE && rightOperand == BasicEmptyValue.EMPTY_VALUE) { + return new BasicBooleanValue(compareTo(BasicEmptyValue.EMPTY_VALUE.getNumericValue(), + BasicEmptyValue.EMPTY_VALUE.getNumericValue())); + } throw new BasicRuntimeException("Type mismatch, left operand: " + leftOperand + ", right operand: " + rightOperand); } diff --git a/src/main/java/com/scriptbasic/executors/rightvalues/BasicBooleanValue.java b/src/main/java/com/scriptbasic/executors/rightvalues/BasicBooleanValue.java index 382aecec..b0bb68ba 100644 --- a/src/main/java/com/scriptbasic/executors/rightvalues/BasicBooleanValue.java +++ b/src/main/java/com/scriptbasic/executors/rightvalues/BasicBooleanValue.java @@ -27,7 +27,7 @@ private static Boolean convertNumeric( public static Boolean asBoolean(final RightValue originalValue) throws BasicRuntimeException { final Boolean convertedValue; - if (originalValue == null) { + if (originalValue == null || originalValue == BasicEmptyValue.EMPTY_VALUE) { convertedValue = Boolean.FALSE; } else if (originalValue instanceof AbstractNumericRightValue) { convertedValue = convertNumeric((AbstractNumericRightValue) originalValue); diff --git a/src/main/java/com/scriptbasic/executors/rightvalues/BasicDoubleValue.java b/src/main/java/com/scriptbasic/executors/rightvalues/BasicDoubleValue.java index 3f6ce23e..8f23dde7 100644 --- a/src/main/java/com/scriptbasic/executors/rightvalues/BasicDoubleValue.java +++ b/src/main/java/com/scriptbasic/executors/rightvalues/BasicDoubleValue.java @@ -52,6 +52,9 @@ public static Double asDouble(final RightValue rv) // TODO elaborate the conversion with other object classes, like // Long, String... } + if (rv == BasicEmptyValue.EMPTY_VALUE) { + return 0.0; + } throw new BasicRuntimeException("Can not convert value to double"); } diff --git a/src/main/java/com/scriptbasic/executors/rightvalues/BasicEmptyValue.java b/src/main/java/com/scriptbasic/executors/rightvalues/BasicEmptyValue.java new file mode 100644 index 00000000..36841edf --- /dev/null +++ b/src/main/java/com/scriptbasic/executors/rightvalues/BasicEmptyValue.java @@ -0,0 +1,16 @@ +package com.scriptbasic.executors.rightvalues; + +public class BasicEmptyValue extends AbstractNumericRightValue { + + public static final BasicEmptyValue EMPTY_VALUE = new BasicEmptyValue(); + + private BasicEmptyValue() { + setValue(EmptyValue.EMPTY_VALUE); + } + + @Override + public Long getNumericValue() { + return 0L; + } + +} diff --git a/src/main/java/com/scriptbasic/executors/rightvalues/BasicLongValue.java b/src/main/java/com/scriptbasic/executors/rightvalues/BasicLongValue.java index af1fd61c..5d640c9a 100644 --- a/src/main/java/com/scriptbasic/executors/rightvalues/BasicLongValue.java +++ b/src/main/java/com/scriptbasic/executors/rightvalues/BasicLongValue.java @@ -48,6 +48,9 @@ public static Long asLong(final RightValue rv) // TODO elaborate the conversion with other object classes, like // Double, String... } + if (rv == BasicEmptyValue.EMPTY_VALUE) { + return 0L; + } throw new BasicRuntimeException("Can not convert value to long"); } diff --git a/src/main/java/com/scriptbasic/executors/rightvalues/BasicStringValue.java b/src/main/java/com/scriptbasic/executors/rightvalues/BasicStringValue.java index cd1ee71d..cc968327 100644 --- a/src/main/java/com/scriptbasic/executors/rightvalues/BasicStringValue.java +++ b/src/main/java/com/scriptbasic/executors/rightvalues/BasicStringValue.java @@ -12,6 +12,9 @@ public BasicStringValue(final String s) { public static String asString(final RightValue rv) throws BasicRuntimeException { try { final String resultString; + if (rv == BasicEmptyValue.EMPTY_VALUE) { + return ""; + } if (rv == null || ((AbstractPrimitiveRightValue) rv).getValue() == null) { resultString = "undef"; diff --git a/src/main/java/com/scriptbasic/executors/rightvalues/EmptyValue.java b/src/main/java/com/scriptbasic/executors/rightvalues/EmptyValue.java new file mode 100644 index 00000000..f2889c8c --- /dev/null +++ b/src/main/java/com/scriptbasic/executors/rightvalues/EmptyValue.java @@ -0,0 +1,14 @@ +package com.scriptbasic.executors.rightvalues; + +public class EmptyValue { + + public static EmptyValue EMPTY_VALUE = new EmptyValue(); + + private EmptyValue() { + } + + @Override + public String toString() { + return ""; + } +} diff --git a/src/main/java/com/scriptbasic/executors/rightvalues/VariableAccess.java b/src/main/java/com/scriptbasic/executors/rightvalues/VariableAccess.java index 40245604..0f446002 100644 --- a/src/main/java/com/scriptbasic/executors/rightvalues/VariableAccess.java +++ b/src/main/java/com/scriptbasic/executors/rightvalues/VariableAccess.java @@ -13,6 +13,9 @@ public RightValue evaluate(final Interpreter interpreter) throws ScriptBasicException { final VariableMap variableMap = interpreter.getVariables(); RightValue value = variableMap.getVariableValue(getVariableName()); + if (value == null) { + value = BasicEmptyValue.EMPTY_VALUE; + } value = interpreter.getHook().variableRead(getVariableName(), value); return value; } diff --git a/src/main/java/com/scriptbasic/utility/RightValueUtility.java b/src/main/java/com/scriptbasic/utility/RightValueUtility.java index 54a615ed..ec2ff4ac 100644 --- a/src/main/java/com/scriptbasic/utility/RightValueUtility.java +++ b/src/main/java/com/scriptbasic/utility/RightValueUtility.java @@ -80,6 +80,8 @@ public static RightValue createRightValue(final Object value) { new char[]{(Character) value})); } else if (value instanceof Boolean) { rightValue = new BasicBooleanValue((Boolean) value); + } else if (value == EmptyValue.EMPTY_VALUE) { + rightValue = BasicEmptyValue.EMPTY_VALUE; } else { rightValue = new BasicJavaObjectValue(value); } diff --git a/src/test/java/com/scriptbasic/TestEngine.java b/src/test/java/com/scriptbasic/TestEngine.java index 03694a7a..abbaf07d 100644 --- a/src/test/java/com/scriptbasic/TestEngine.java +++ b/src/test/java/com/scriptbasic/TestEngine.java @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -514,7 +515,7 @@ public void testSubroutineCallWArgumentsWRetval2() throws Exception { final var engine = ScriptBasic.engine(); engine.eval("sub applePie(b,c)\nglobal a\na = c\nreturn 6\nEndSub"); engine.subroutine(Long.class, "applePie").call("hello world"); - assertNull(engine.variable(String.class, "a")); + assertNotNull(engine.variable(Object.class, "a")); } @Test @@ -578,7 +579,7 @@ public void testSubroutineCallWArgumentsWRetval2007() throws Exception { "EndSub"); final var sub = engine.subroutine(null, "applePie"); sub.call("hello world"); - assertGlobalVariableIsNotDefined(engine, "a"); + assertNotNull(engine.variable(Object.class, "a")); } @Test @@ -599,7 +600,7 @@ public void testSubroutineCallWArgumentsWRetval007() throws Exception { } private void assertGlobalVariableIsNotDefined(final ScriptBasic engine, final String name) throws ScriptBasicException { - assertNull(engine.variable(Object.class, "a")); + assertNull(engine.variable(Object.class, name)); } @Test diff --git a/src/test/java/com/scriptbasic/executors/operators/TestOperators.java b/src/test/java/com/scriptbasic/executors/operators/TestOperators.java index 69e035f9..1f89f566 100644 --- a/src/test/java/com/scriptbasic/executors/operators/TestOperators.java +++ b/src/test/java/com/scriptbasic/executors/operators/TestOperators.java @@ -121,6 +121,18 @@ public void testOperators() throws AnalysisException, a("a= 3.0 <> \"a\"", true); a("a= true = \"a\"", false); + // Comparison with uninitialized/empty variable + a("a= b = \"\"", true); + a("a= b = 0", true); + a("a= b = \"x\"", false); + a("a= b = 1", false); + a("a= b = c", true); + a("a= b <> c", false); + a("a= b < c", false); + a("a= b > c", false); + a("a= b <= c", true); + a("a= b >= c", true); + a("a= \"x\" <> \"x\"", false); a("a= \"x\" <> \"y\"", true); a("a= \"x\" < \"y\"", true); diff --git a/src/test/java/com/scriptbasic/testprograms/TestPrograms.java b/src/test/java/com/scriptbasic/testprograms/TestPrograms.java index 85dee7ba..fee8d544 100644 --- a/src/test/java/com/scriptbasic/testprograms/TestPrograms.java +++ b/src/test/java/com/scriptbasic/testprograms/TestPrograms.java @@ -91,7 +91,7 @@ public void testPrograms() throws Exception { } codeTest("TestSub1.bas", "6"); codeTest("TestSub2.bas", "21"); - codeTest("TestSub3.bas", "21undef"); + codeTest("TestSub3.bas", "21"); codeTest("TestSub4.bas", "123\n123\n123\n123\n"); codeTest("TestSub5.bas", "1111"); codeTest("TestWhile1.bas", "89"); @@ -126,7 +126,7 @@ public void testPrograms() throws Exception { codeTest("TestForLoop9.bas", "111213212223313233"); codeTest("TestForLoop10.bas", "12323"); codeTest("TestRuntimeFunction.bas", "1.01.5707963267948966"); - codeTest("TestNullFunction.bas", "undefundef"); + codeTest("TestNullFunction.bas", "undef"); codeTest("TestMethodCall.bas", "" + Math.sin(1.0) + "\n" + Math.sin(1.0)); map = new HashMap<>(); @@ -135,7 +135,7 @@ public void testPrograms() throws Exception { testRuntimeFail("TestBadArray1.bas"); codeTest("TestBadArray2.bas", ""); - codeTest("TestBadArray3.bas", ""); + testRuntimeFail("TestBadArray3.bas"); codeTest("BubbleSort.bas", "-1\n0\n2\n3\n7\n58\n99\n"); codeTest("TestAbs.bas", "131355.377.7"); codeTest("TestChomp.bas", "ttt");