diff --git a/pom.xml b/pom.xml index 35df154..339882a 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ parsington - 3.1.1-SNAPSHOT + 3.2.0-SNAPSHOT Parsington: The SciJava Expression Parser A general-purpose mathematical expression parser, which converts infix expression strings into postfix queues and/or syntax trees. diff --git a/src/main/java/org/scijava/parsington/eval/AbstractEvaluator.java b/src/main/java/org/scijava/parsington/eval/AbstractEvaluator.java index f4099ff..9dc4ae9 100644 --- a/src/main/java/org/scijava/parsington/eval/AbstractEvaluator.java +++ b/src/main/java/org/scijava/parsington/eval/AbstractEvaluator.java @@ -30,6 +30,7 @@ package org.scijava.parsington.eval; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -79,6 +80,11 @@ public Object get(final String name) { return new Unresolved(name); } + @Override + public Map getAll() { + return Collections.unmodifiableMap(vars); + } + @Override public void set(final String name, final Object value) { vars.put(name, value); @@ -89,4 +95,14 @@ public void setAll(final Map map) { vars.putAll(map); } + @Override + public Object remove(final String name) { + return vars.remove(name); + } + + @Override + public void clear() { + vars.clear(); + } + } diff --git a/src/main/java/org/scijava/parsington/eval/Evaluator.java b/src/main/java/org/scijava/parsington/eval/Evaluator.java index 3ebf1e9..1be84d2 100644 --- a/src/main/java/org/scijava/parsington/eval/Evaluator.java +++ b/src/main/java/org/scijava/parsington/eval/Evaluator.java @@ -167,6 +167,13 @@ default Object get(final Variable v) { return get(v.getToken()); } + /** + * Gets a map of all variable names and values. + * + * @return A map from variable names to variable values. + */ + Map getAll(); + /** * Sets the value of a variable. * @@ -184,4 +191,19 @@ default void set(final Variable v, final Object value) { */ void setAll(Map map); + /** + * Removes the named variable. + * + * @param name The name of the variable whose value you want to remove. + * @return The previous variables value associated with name, + * or null if the name did not exist. + */ + Object remove(String name); + + /** + * Clears all the variables. + * + */ + void clear(); + } diff --git a/src/test/java/org/scijava/parsington/eval/AbstractEvaluatorTest.java b/src/test/java/org/scijava/parsington/eval/AbstractEvaluatorTest.java new file mode 100644 index 0000000..62d5fc9 --- /dev/null +++ b/src/test/java/org/scijava/parsington/eval/AbstractEvaluatorTest.java @@ -0,0 +1,118 @@ +/* + * #%L + * Parsington: the SciJava mathematical expression parser. + * %% + * Copyright (C) 2015 - 2026 Board of Regents of the University of + * Wisconsin-Madison. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.parsington.eval; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.scijava.parsington.AbstractTest; + +/** + * Abstract base class for testing {@link Evaluator} implementations. + * + * @author Curtis Rueden + */ +public abstract class AbstractEvaluatorTest extends AbstractTest { + + public abstract Evaluator createEvaluator(); + + /** + * Tests {@link Evaluator#get(String)} and + * {@link Evaluator#set(String, Object)}. + */ + @Test + public void testGetSet() { + final Evaluator e = createEvaluator(); + // In strict mode (the default), getting an unset variable throws. + assertThrows(IllegalArgumentException.class, () -> e.get("x")); + e.set("x", 42); + assertEquals(42, e.get("x")); + e.set("x", "hello"); + assertEquals("hello", e.get("x")); + } + + /** Tests {@link Evaluator#remove(String)}. */ + @Test + public void testRemove() { + final Evaluator e = createEvaluator(); + // Removing an absent variable returns null. + assertNull(e.remove("x")); + e.set("x", 99); + assertEquals(99, e.remove("x")); + // Variable is gone after removal. + assertThrows(IllegalArgumentException.class, () -> e.get("x")); + } + + /** Tests {@link Evaluator#clear()}. */ + @Test + public void testClear() { + final Evaluator e = createEvaluator(); + e.set("a", 1); + e.set("b", 2); + e.clear(); + assertEquals(new HashMap<>(), e.getAll()); + assertThrows(IllegalArgumentException.class, () -> e.get("a")); + assertThrows(IllegalArgumentException.class, () -> e.get("b")); + } + + /** Tests {@link Evaluator#getAll()} and {@link Evaluator#setAll(Map)}. */ + @Test + public void testGetAllSetAll() { + final Evaluator e = createEvaluator(); + assertEquals(new HashMap<>(), e.getAll()); + + final Map vars = new HashMap<>(); + vars.put("a", 1); + vars.put("b", "two"); + vars.put("c", 3.0); + e.setAll(vars); + + assertEquals(vars, e.getAll()); + + // Verify individual get still works after setAll. + assertEquals(1, e.get("a")); + assertEquals("two", e.get("b")); + assertEquals(3.0, e.get("c")); + + // Verify variables created at evaluation are accessible and correct. + e.evaluate("d=a+c"); + assertTrue(e.getAll().containsKey("d")); + final Object dVal = e.get("d"); + assertEquals(4.0, dVal); + assertEquals(dVal, e.getAll().get("d")); + } +} diff --git a/src/test/java/org/scijava/parsington/eval/AbstractStandardEvaluatorTest.java b/src/test/java/org/scijava/parsington/eval/AbstractStandardEvaluatorTest.java index f1a2955..7476c8c 100644 --- a/src/test/java/org/scijava/parsington/eval/AbstractStandardEvaluatorTest.java +++ b/src/test/java/org/scijava/parsington/eval/AbstractStandardEvaluatorTest.java @@ -45,13 +45,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.scijava.parsington.AbstractTest; import org.scijava.parsington.Operators; import org.scijava.parsington.SyntaxTree; import org.scijava.parsington.Variable; /** Abstract base class for testing {@link StandardEvaluator} implementations. */ -public abstract class AbstractStandardEvaluatorTest extends AbstractTest { +public abstract class AbstractStandardEvaluatorTest extends AbstractEvaluatorTest { protected StandardEvaluator e; @@ -60,6 +59,7 @@ public void setUp() { e = createEvaluator(); } + @Override public abstract StandardEvaluator createEvaluator(); /** Tests {@link Evaluator#evaluate(String)}. */