/* * JaamSim Discrete Event Simulation * Copyright (C) 2014 Ausenco Engineering Canada Inc. * Copyright (C) 2015 JaamSim Software Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jaamsim.input; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.HashMap; import org.junit.Test; import com.jaamsim.basicsim.Entity; import com.jaamsim.input.ExpParser.Assigner; import com.jaamsim.input.ExpParser.EvalContext; import com.jaamsim.input.ExpParser.OutputResolver; import com.jaamsim.input.ExpParser.UnitData; import com.jaamsim.units.AreaUnit; import com.jaamsim.units.DimensionlessUnit; import com.jaamsim.units.DistanceUnit; import com.jaamsim.units.SpeedUnit; import com.jaamsim.units.TimeUnit; import com.jaamsim.units.Unit; public class TestExpParser { private static class DummyResolver implements ExpParser.OutputResolver { private final String name; public DummyResolver(String name) { this.name = name; } @Override public ExpResult resolve(EvalContext ec, ExpResult ent) throws ExpError { if (name.equals("foo")) return ExpResult.makeNumResult(4, DimensionlessUnit.class); if (name.equals("bar")) return ExpResult.makeNumResult(3, DimensionlessUnit.class); return ExpResult.makeNumResult(1, DimensionlessUnit.class); } @Override public ExpValResult validate(ExpValResult entValRes) { return ExpValResult.makeValidRes(ExpResType.NUMBER, DimensionlessUnit.class); } } private static class PC implements ExpParser.ParseContext { @Override public UnitData getUnitByName(String name) { return null; } @Override public Class<? extends Unit> multUnitTypes(Class<? extends Unit> a, Class<? extends Unit> b) { return DimensionlessUnit.class; } @Override public Class<? extends Unit> divUnitTypes(Class<? extends Unit> num, Class<? extends Unit> denom) { return DimensionlessUnit.class; } @Override public ExpResult getValFromName(String name, String source, int pos) throws ExpError { return ExpResult.makeNumResult(1, DimensionlessUnit.class); } @Override public OutputResolver getOutputResolver(String name) throws ExpError { return new DummyResolver(name); } @Override public OutputResolver getConstOutputResolver(ExpResult constEnt, String name) throws ExpError { return new DummyResolver(name); } @Override public Assigner getAssigner(String attribName) throws ExpError { throw new ExpError(null, 0, "Assign not supported"); } @Override public Assigner getConstAssigner(ExpResult constEnt, String attribName) throws ExpError { throw new ExpError(null, 0, "Assign not supported"); } } static Entity mapEnt = new Entity(); static Entity arrayEnt = new Entity(); static Entity dummyEnt = new Entity(); static HashMap<Double, Double> map0 = new HashMap<>(); static HashMap<String, Double> map1 = new HashMap<>(); static HashMap<String, Double> distanceMap = new HashMap<>(); static HashMap<Entity, Entity> entMap = new HashMap<>(); static Entity[] entArray = { dummyEnt }; static double[] doubleArray = { 1.0, 2.0, 3.0, 42.0 }; static int[] intArray = { 1, 2, 3, 42 }; static String[] stringArray = { "foo", "bar" }; { map0.put(1.0, 1.0); map0.put(2.0, 42.0); map1.put("one", 1.0); map1.put("two", 2.0); map1.put("everything", 42.0); distanceMap.put("near", 1.0); distanceMap.put("far", 42000.0); distanceMap.put("middling", 4.0); entMap.put(dummyEnt, dummyEnt); } static PC pc = new PC(); private static class EC implements ExpParser.EvalContext { } static EC ec = new EC(); private static void testToken(ExpTokenizer.Token tok, int type, String val) { assertTrue(tok.type == type); assertTrue(tok.value.equals(val)); } @Test public void testTokenize() throws ExpError { ArrayList<ExpTokenizer.Token> tokens = ExpTokenizer.tokenize(" a b c 1 2 3 + -"); assertTrue(tokens.size() == 8); testToken(tokens.get(0), ExpTokenizer.VAR_TYPE, "a"); testToken(tokens.get(1), ExpTokenizer.VAR_TYPE, "b"); testToken(tokens.get(2), ExpTokenizer.VAR_TYPE, "c"); testToken(tokens.get(3), ExpTokenizer.NUM_TYPE, "1"); testToken(tokens.get(4), ExpTokenizer.NUM_TYPE, "2"); testToken(tokens.get(5), ExpTokenizer.NUM_TYPE, "3"); testToken(tokens.get(6), ExpTokenizer.SYM_TYPE, "+"); testToken(tokens.get(7), ExpTokenizer.SYM_TYPE, "-"); tokens = ExpTokenizer.tokenize("foo bar blarg123"); assertTrue(tokens.size() == 3); testToken(tokens.get(0), ExpTokenizer.VAR_TYPE, "foo"); testToken(tokens.get(1), ExpTokenizer.VAR_TYPE, "bar"); testToken(tokens.get(2), ExpTokenizer.VAR_TYPE, "blarg123"); tokens = ExpTokenizer.tokenize("bar.frump ( -12.3)"); assertTrue(tokens.size() == 7); testToken(tokens.get(0), ExpTokenizer.VAR_TYPE, "bar"); testToken(tokens.get(1), ExpTokenizer.SYM_TYPE, "."); testToken(tokens.get(2), ExpTokenizer.VAR_TYPE, "frump"); testToken(tokens.get(3), ExpTokenizer.SYM_TYPE, "("); testToken(tokens.get(4), ExpTokenizer.SYM_TYPE, "-"); testToken(tokens.get(5), ExpTokenizer.NUM_TYPE, "12.3"); testToken(tokens.get(6), ExpTokenizer.SYM_TYPE, ")"); tokens = ExpTokenizer.tokenize("-12.3e6 ... ---"); assertTrue(tokens.size() == 8); testToken(tokens.get(0), ExpTokenizer.SYM_TYPE, "-"); testToken(tokens.get(1), ExpTokenizer.NUM_TYPE, "12.3e6"); testToken(tokens.get(2), ExpTokenizer.SYM_TYPE, "."); testToken(tokens.get(3), ExpTokenizer.SYM_TYPE, "."); testToken(tokens.get(4), ExpTokenizer.SYM_TYPE, "."); testToken(tokens.get(5), ExpTokenizer.SYM_TYPE, "-"); testToken(tokens.get(6), ExpTokenizer.SYM_TYPE, "-"); testToken(tokens.get(7), ExpTokenizer.SYM_TYPE, "-"); tokens = ExpTokenizer.tokenize("-42.3E-6"); assertTrue(tokens.size() == 2); testToken(tokens.get(0), ExpTokenizer.SYM_TYPE, "-"); testToken(tokens.get(1), ExpTokenizer.NUM_TYPE, "42.3E-6"); tokens = ExpTokenizer.tokenize("[123][abc] [+- 2]"); assertTrue(tokens.size() == 3); testToken(tokens.get(0), ExpTokenizer.SQ_TYPE, "123"); testToken(tokens.get(1), ExpTokenizer.SQ_TYPE, "abc"); testToken(tokens.get(2), ExpTokenizer.SQ_TYPE, "+- 2"); // Test long symbol parsing tokens = ExpTokenizer.tokenize("&&||==<==&|"); assertTrue(tokens.size() == 7); testToken(tokens.get(0), ExpTokenizer.SYM_TYPE, "&&"); testToken(tokens.get(1), ExpTokenizer.SYM_TYPE, "||"); testToken(tokens.get(2), ExpTokenizer.SYM_TYPE, "=="); testToken(tokens.get(3), ExpTokenizer.SYM_TYPE, "<="); testToken(tokens.get(4), ExpTokenizer.SYM_TYPE, "="); testToken(tokens.get(5), ExpTokenizer.SYM_TYPE, "&"); testToken(tokens.get(6), ExpTokenizer.SYM_TYPE, "|"); tokens = ExpTokenizer.tokenize("[[blarg]][foo][[bar]] 42"); assertTrue(tokens.size() == 4); testToken(tokens.get(0), ExpTokenizer.DSQ_TYPE, "blarg"); testToken(tokens.get(1), ExpTokenizer.SQ_TYPE, "foo"); testToken(tokens.get(2), ExpTokenizer.DSQ_TYPE, "bar"); testToken(tokens.get(3), ExpTokenizer.NUM_TYPE, "42"); } @Test public void testParser() throws ExpError { ExpParser.Expression exp = ExpParser.parseExpression(pc, "2*5 + 3*5*(3-1)+2"); double val = exp.evaluate(ec).value; assertTrue(val == 42); exp = ExpParser.parseExpression(pc, "max(3, 42)"); val = exp.evaluate(ec).value; assertTrue(val == 42); exp = ExpParser.parseExpression(pc, "min(3, 42, -5, 602)"); val = exp.evaluate(ec).value; assertTrue(val == -5); exp = ExpParser.parseExpression(pc, "max(3, 42, -5, 602)"); val = exp.evaluate(ec).value; assertTrue(val == 602); exp = ExpParser.parseExpression(pc, "indexOfMin(3, 42, -5, 602)"); val = exp.evaluate(ec).value; assertTrue(val == 3); exp = ExpParser.parseExpression(pc, "indexOfMax(3, 42, -5, 602)"); val = exp.evaluate(ec).value; assertTrue(val == 4); exp = ExpParser.parseExpression(pc, "abs(-42)"); val = exp.evaluate(ec).value; assertTrue(val == 42); exp = ExpParser.parseExpression(pc, "abs(+42)"); val = exp.evaluate(ec).value; assertTrue(val == 42); exp = ExpParser.parseExpression(pc, "[foo].foo*[bar].bar"); val = exp.evaluate(ec).value; assertTrue(val == 12); exp = ExpParser.parseExpression(pc, "50/2/5"); // left associative val = exp.evaluate(ec).value; assertTrue(val == 5); exp = ExpParser.parseExpression(pc, "2^2^3"); // right associative val = exp.evaluate(ec).value; assertTrue(val == 256); exp = ExpParser.parseExpression(pc, "1 + 2^2*4 + 2*[foo].foo"); val = exp.evaluate(ec).value; assertTrue(val == 25); exp = ExpParser.parseExpression(pc, "1 + 2^(2*4) + 2"); val = exp.evaluate(ec).value; assertTrue(val == 259); exp = ExpParser.parseExpression(pc, "2----2"); // A quadruple negative val = exp.evaluate(ec).value; assertTrue(val == 4); exp = ExpParser.parseExpression(pc, "2---+-2"); // Still a quadruple negative val = exp.evaluate(ec).value; assertTrue(val == 4); exp = ExpParser.parseExpression(pc, "-1+1"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "(((((1+1)))*5))"); val = exp.evaluate(ec).value; assertTrue(val == 10); exp = ExpParser.parseExpression(pc, "!42"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "!0"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "42 == 42"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "42 == 41"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "42 != 42"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "42 != 41"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "42 || 0"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "0 || 42"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "0 || 0"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "42 && 0"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "0 && 42"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "1 && 2"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "!(1&&42)"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "!!(1&&42)"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "42<41"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "41<42"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "42>41"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "41>42"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "42<=41"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "41<=42"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "42>=41"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "41>=42"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "42>=42"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "42>=42"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(pc, "42>42"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "42>42"); val = exp.evaluate(ec).value; assertTrue(val == 0); exp = ExpParser.parseExpression(pc, "1==0?42:24"); val = exp.evaluate(ec).value; assertTrue(val == 24); exp = ExpParser.parseExpression(pc, "1==1?42:24"); val = exp.evaluate(ec).value; assertTrue(val == 42); } private static class TestVariableResolver implements ExpParser.OutputResolver { private final String name; public TestVariableResolver(String n) { this.name = n; } @Override public ExpResult resolve(EvalContext ec, ExpResult ent) throws ExpError { if (ent.type == ExpResType.ENTITY && ent.entVal == mapEnt) { if (name.equals("map0")) { return ExpCollections.getCollection(map0, DimensionlessUnit.class); } if (name.equals("map1")) { return ExpCollections.getCollection(map1, DimensionlessUnit.class); } if (name.equals("entMap")) { return ExpCollections.getCollection(entMap, DimensionlessUnit.class); } if (name.equals("distances")) { return ExpCollections.getCollection(distanceMap, DistanceUnit.class); } } if (ent.type == ExpResType.ENTITY && ent.entVal == arrayEnt) { if (name.equals("intArray")) { return ExpCollections.getCollection(intArray, DimensionlessUnit.class); } if (name.equals("doubleArray")) { return ExpCollections.getCollection(doubleArray, DimensionlessUnit.class); } if (name.equals("entArray")) { return ExpCollections.getCollection(entArray, DimensionlessUnit.class); } if (name.equals("stringArray")) { return ExpCollections.getCollection(stringArray, DimensionlessUnit.class); } } return ExpResult.makeNumResult(1, DimensionlessUnit.class); } @Override public ExpValResult validate(ExpValResult entValRes) { return ExpValResult.makeUndecidableRes(); } } private static class VariableTestPC extends PC { @Override public OutputResolver getOutputResolver(String name) throws ExpError { return new TestVariableResolver(name); } @Override public OutputResolver getConstOutputResolver(ExpResult constEnt, String name) throws ExpError { return new TestVariableResolver(name); } @Override public ExpResult getValFromName(String name, String source, int pos) throws ExpError { if (name.equals("Maps")) { return ExpResult.makeEntityResult(mapEnt); } if (name.equals("Arrays")) { return ExpResult.makeEntityResult(arrayEnt); } if (name.equals("Dummy")) { return ExpResult.makeEntityResult(dummyEnt); } return ExpResult.makeNumResult(0, DimensionlessUnit.class); } } @Test public void testVariables() throws ExpError { VariableTestPC vtpc = new VariableTestPC(); ExpParser.Expression exp = ExpParser.parseExpression(vtpc, "[Maps].map0(1)"); double val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(vtpc, "[Maps].map0(2)"); val = exp.evaluate(ec).value; assertTrue(val == 42); exp = ExpParser.parseExpression(vtpc, "[Maps].map1([[two]])"); val = exp.evaluate(ec).value; assertTrue(val == 2); exp = ExpParser.parseExpression(vtpc, "[Maps].map1([[everything]])"); val = exp.evaluate(ec).value; assertTrue(val == 42); exp = ExpParser.parseExpression(vtpc, "[Maps].entMap([Dummy])"); ExpResult res = exp.evaluate(ec); assertTrue(res.type == ExpResType.ENTITY && res.entVal == dummyEnt); exp = ExpParser.parseExpression(vtpc, "[Arrays].doubleArray(4)"); val = exp.evaluate(ec).value; assertTrue(val == 42); exp = ExpParser.parseExpression(vtpc, "maxCol([Arrays].doubleArray)"); val = exp.evaluate(ec).value; assertTrue(val == 42); exp = ExpParser.parseExpression(vtpc, "indexOfMaxCol([Arrays].doubleArray)"); val = exp.evaluate(ec).value; assertTrue(val == 4); exp = ExpParser.parseExpression(vtpc, "minCol([Arrays].intArray)"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(vtpc, "indexOfMinCol([Arrays].intArray)"); val = exp.evaluate(ec).value; assertTrue(val == 1); exp = ExpParser.parseExpression(vtpc, "[Arrays].intArray(4)"); val = exp.evaluate(ec).value; assertTrue(val == 42); exp = ExpParser.parseExpression(vtpc, "[Arrays].entArray(1)"); res = exp.evaluate(ec); assertTrue(res.type == ExpResType.ENTITY && res.entVal == dummyEnt); exp = ExpParser.parseExpression(vtpc, "[Arrays].stringArray(2)"); res = exp.evaluate(ec); assertTrue(res.type == ExpResType.STRING && res.stringVal.equals("bar")); exp = ExpParser.parseExpression(vtpc, "maxCol([Maps].distances)"); res = exp.evaluate(ec); assertTrue(res.type == ExpResType.NUMBER); assertTrue(res.unitType == DistanceUnit.class); assertTrue(res.value == 42000.0); exp = ExpParser.parseExpression(vtpc, "indexOfMaxCol([Maps].distances)"); res = exp.evaluate(ec); assertTrue(res.type == ExpResType.STRING); assertTrue(res.stringVal.equals("far")); exp = ExpParser.parseExpression(vtpc, "size([Arrays].doubleArray)"); val = exp.evaluate(ec).value; assertTrue(val == 4); } @Test public void testArray() throws ExpError { ExpParser.Expression exp = ExpParser.parseExpression(pc, "{1, 2, 3, 4}(2)"); ExpResult res = exp.evaluate(ec); assertTrue(res.type == ExpResType.NUMBER); assertTrue(res.value == 2); exp = ExpParser.parseExpression(pc, "{[[foo]], [[bar]], [[baz]]}(3)"); res = exp.evaluate(ec); assertTrue(res.type == ExpResType.STRING); assertTrue(res.stringVal.equals("baz")); } @Test public void testString() throws ExpError { ExpParser.Expression exp = ExpParser.parseExpression(pc, "[[stringly]]"); ExpResult res = exp.evaluate(ec); assertTrue(res.type == ExpResType.STRING); assertTrue(res.stringVal.equals("stringly")); exp = ExpParser.parseExpression(pc, "[[stri]] + [[ngly]]"); res = exp.evaluate(ec); assertTrue(res.type == ExpResType.STRING); assertTrue(res.stringVal.equals("stringly")); } @Test public void testUnits() throws ExpError { class ErrorResolver implements ExpParser.OutputResolver { private final ExpError error = new ExpError(null, 0, "Variables not supported in test"); @Override public ExpResult resolve(EvalContext ec, ExpResult ent) throws ExpError { throw error; } @Override public ExpValResult validate(ExpValResult entValRes) { return ExpValResult.makeErrorRes(error); } } class UnitPC implements ExpParser.ParseContext { @Override public UnitData getUnitByName(String name) { UnitData ret = new UnitData(); if (name.equals("s")) { ret.scaleFactor = 1; ret.unitType = TimeUnit.class; return ret; } if (name.equals("min")) { ret.scaleFactor = 60; ret.unitType = TimeUnit.class; return ret; } if (name.equals("hr")) { ret.scaleFactor = 3600; ret.unitType = TimeUnit.class; return ret; } if (name.equals("m")) { ret.scaleFactor = 1; ret.unitType = DistanceUnit.class; return ret; } if (name.equals("km")) { ret.scaleFactor = 1000; ret.unitType = DistanceUnit.class; return ret; } return null; } @Override public Class<? extends Unit> multUnitTypes(Class<? extends Unit> a, Class<? extends Unit> b) { return Unit.getMultUnitType(a, b); } @Override public Class<? extends Unit> divUnitTypes(Class<? extends Unit> num, Class<? extends Unit> denom) { return Unit.getDivUnitType(num, denom); } @Override public OutputResolver getOutputResolver(String name) throws ExpError { return new ErrorResolver(); } @Override public Assigner getAssigner(String attribName) throws ExpError { throw new ExpError(null, 0, "Assign not supported"); } @Override public Assigner getConstAssigner(ExpResult constEnt, String attribName) throws ExpError { throw new ExpError(null, 0, "Assign not supported"); } @Override public ExpResult getValFromName(String name, String source, int pos) throws ExpError { return null; } @Override public OutputResolver getConstOutputResolver(ExpResult constEnt, String name) throws ExpError { return new ErrorResolver(); } } UnitPC upc = new UnitPC(); ExpParser.Expression exp = ExpParser.parseExpression(upc, "1[km] + 1[m]"); ExpResult res = exp.evaluate(ec); assertTrue(res.value == 1001); assertTrue(res.unitType == DistanceUnit.class); exp = ExpParser.parseExpression(upc, "1[hr] + 1[min] + 5[s]"); res = exp.evaluate(ec); assertTrue(res.value == 3665); assertTrue(res.unitType == TimeUnit.class); boolean threw = false; try { exp = ExpParser.parseExpression(upc, "1[hr] + 1[m]"); res = exp.evaluate(ec); assertTrue(false); } catch (ExpError ex) { threw = true; } assertTrue(threw); exp = ExpParser.parseExpression(upc, "max(1[hr], 1[s])"); res = exp.evaluate(ec); assertTrue(res.value == 3600); assertTrue(res.unitType == TimeUnit.class); exp = ExpParser.parseExpression(upc, "6*1[km]"); res = exp.evaluate(ec); assertTrue(res.value == 6000); assertTrue(res.unitType == DistanceUnit.class); threw = false; try { exp = ExpParser.parseExpression(upc, "1[parsec]"); } catch(ExpError ex) { threw = true; } assertTrue(threw); exp = ExpParser.parseExpression(upc, "1[m]/1[s]"); res = exp.evaluate(ec); assertTrue(res.value == 1); assertTrue(res.unitType == SpeedUnit.class); exp = ExpParser.parseExpression(upc, "5[m]*1[km]"); res = exp.evaluate(ec); assertTrue(res.value == 5000); assertTrue(res.unitType == AreaUnit.class); exp = ExpParser.parseExpression(upc, "5[m]/1[m]"); res = exp.evaluate(ec); assertTrue(res.value == 5); assertTrue(res.unitType == DimensionlessUnit.class); } private static class AssignContainer { public ExpResult res; public ExpResult.Collection col; public String lastAttribName; } private static class TestAssigner implements ExpParser.Assigner { AssignContainer cont; String attribName; TestAssigner(AssignContainer cont, String attribName) { this.cont = cont; this.attribName = attribName; } @Override public void assign(ExpResult ent, ExpResult index, ExpResult val) throws ExpError { if (index == null) { cont.res = val; } else { cont.col.assign(index, val); } cont.lastAttribName = attribName; } } private static class AssignPC extends PC { AssignContainer cont; AssignPC(AssignContainer cont) { this.cont = cont; } @Override public Assigner getAssigner(String attribName) throws ExpError { return new TestAssigner(cont, attribName); } @Override public Assigner getConstAssigner(ExpResult constEnt, String attribName) throws ExpError { return new TestAssigner(cont, attribName); } } @Test public void testAssignment() throws ExpError { AssignContainer cont = new AssignContainer(); ArrayList<ExpResult> initialRes = new ArrayList<>(); initialRes.add(ExpResult.makeNumResult(42, DimensionlessUnit.class)); cont.col = ExpCollections.makeExpressionCollection(initialRes).colVal; AssignPC apc = new AssignPC(cont); ExpParser.Assignment assign = ExpParser.parseAssignment(apc, "[foo].arg = 40 + 2"); ExpResult res = assign.evaluate(ec); assert(res.value == 42.0); assert(cont.res.type == ExpResType.NUMBER); assert(cont.res.value == 42.0); assert(cont.lastAttribName.equals("arg")); assign = ExpParser.parseAssignment(apc, "[foo].blarg(42) = 40 + 5"); res = assign.evaluate(ec); ExpResult contained = cont.col.index(ExpResult.makeNumResult(42, DimensionlessUnit.class)); assert(res.value == 45.0); assert(contained.type == ExpResType.NUMBER); assert(contained.value == 45.0); assert(cont.lastAttribName.equals("blarg")); } }