package org.overture.interpreter.eval; import org.overture.ast.analysis.AnalysisException; import org.overture.ast.expressions.AAndBooleanBinaryExp; import org.overture.ast.expressions.ACompBinaryExp; import org.overture.ast.expressions.ADivNumericBinaryExp; import org.overture.ast.expressions.ADivideNumericBinaryExp; import org.overture.ast.expressions.ADomainResByBinaryExp; import org.overture.ast.expressions.ADomainResToBinaryExp; import org.overture.ast.expressions.AEqualsBinaryExp; import org.overture.ast.expressions.AEquivalentBooleanBinaryExp; import org.overture.ast.expressions.AGreaterEqualNumericBinaryExp; import org.overture.ast.expressions.AGreaterNumericBinaryExp; import org.overture.ast.expressions.AImpliesBooleanBinaryExp; import org.overture.ast.expressions.AInSetBinaryExp; import org.overture.ast.expressions.ALessEqualNumericBinaryExp; import org.overture.ast.expressions.ALessNumericBinaryExp; import org.overture.ast.expressions.AMapUnionBinaryExp; import org.overture.ast.expressions.AModNumericBinaryExp; import org.overture.ast.expressions.ANotEqualBinaryExp; import org.overture.ast.expressions.ANotInSetBinaryExp; import org.overture.ast.expressions.AOrBooleanBinaryExp; import org.overture.ast.expressions.APlusNumericBinaryExp; import org.overture.ast.expressions.APlusPlusBinaryExp; import org.overture.ast.expressions.AProperSubsetBinaryExp; import org.overture.ast.expressions.ARangeResByBinaryExp; import org.overture.ast.expressions.ARangeResToBinaryExp; import org.overture.ast.expressions.ARemNumericBinaryExp; import org.overture.ast.expressions.ASeqConcatBinaryExp; import org.overture.ast.expressions.ASetDifferenceBinaryExp; import org.overture.ast.expressions.ASetIntersectBinaryExp; import org.overture.ast.expressions.ASetUnionBinaryExp; import org.overture.ast.expressions.AStarStarBinaryExp; import org.overture.ast.expressions.ASubsetBinaryExp; import org.overture.ast.expressions.ASubtractNumericBinaryExp; import org.overture.ast.expressions.ATimesNumericBinaryExp; import org.overture.interpreter.runtime.Context; import org.overture.interpreter.runtime.ValueException; import org.overture.interpreter.runtime.VdmRuntime; import org.overture.interpreter.runtime.VdmRuntimeError; import org.overture.interpreter.values.BooleanValue; import org.overture.interpreter.values.CompFunctionValue; import org.overture.interpreter.values.FunctionValue; import org.overture.interpreter.values.IterFunctionValue; import org.overture.interpreter.values.MapValue; import org.overture.interpreter.values.NumericValue; import org.overture.interpreter.values.SeqValue; import org.overture.interpreter.values.SetValue; import org.overture.interpreter.values.UndefinedValue; import org.overture.interpreter.values.Value; import org.overture.interpreter.values.ValueList; import org.overture.interpreter.values.ValueMap; import org.overture.interpreter.values.ValueSet; public class BinaryExpressionEvaluator extends UnaryExpressionEvaluator { /* * Boolean */ @Override public Value caseAAndBooleanBinaryExp(AAndBooleanBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (lv.isUndefined()) { return lv; } boolean lb = lv.boolValue(ctxt); if (!lb) { return lv; // Stop after LHS } Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (lb) { return rv; } return new BooleanValue(false); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAEquivalentBooleanBinaryExp( AEquivalentBooleanBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (lv.isUndefined() || rv.isUndefined()) { return new UndefinedValue(); } return new BooleanValue(lv.boolValue(ctxt) == rv.boolValue(ctxt)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAImpliesBooleanBinaryExp(AImpliesBooleanBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (lv.isUndefined()) { return lv; } boolean lb = lv.boolValue(ctxt); if (lb) { return node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); } return new BooleanValue(true); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAOrBooleanBinaryExp(AOrBooleanBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (lv.isUndefined()) { return lv; } else { boolean lb = lv.boolValue(ctxt); if (lb) { return lv; // Stop after LHS } Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (lb) { return new BooleanValue(true); } else { return rv; } } } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } /* * end boolean */ @Override public Value caseACompBinaryExp(ACompBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).deref(); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).deref(); if (lv instanceof MapValue) { ValueMap lm = null; ValueMap rm = null; try { lm = lv.mapValue(ctxt); rm = rv.mapValue(ctxt); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } ValueMap result = new ValueMap(); for (Value v : rm.keySet()) { Value rng = lm.get(rm.get(v)); if (rng == null) { VdmRuntimeError.abort(node.getLocation(), 4162, "The RHS range is not a subset of the LHS domain", ctxt); } Value old = result.put(v, rng); if (old != null && !old.equals(rng)) { VdmRuntimeError.abort(node.getLocation(), 4005, "Duplicate map keys have different values", ctxt); } } return new MapValue(result); } try { FunctionValue f1 = lv.functionValue(ctxt); FunctionValue f2 = rv.functionValue(ctxt); return new CompFunctionValue(f1, f2); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseADomainResByBinaryExp(ADomainResByBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { ValueSet set = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt); ValueMap map = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).mapValue(ctxt); ValueMap modified = new ValueMap(map); for (Value k : map.keySet()) { if (set.contains(k)) { modified.remove(k); } } return new MapValue(modified); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseADomainResToBinaryExp(ADomainResToBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { ValueSet set = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt); ValueMap map = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).mapValue(ctxt); ValueMap modified = new ValueMap(map); for (Value k : map.keySet()) { if (!set.contains(k)) { modified.remove(k); } } return new MapValue(modified); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAEqualsBinaryExp(AEqualsBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (lv.isUndefined()) { return lv; } Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (rv.isUndefined()) { return rv; } return new BooleanValue(lv.equals(rv)); } @Override public Value caseAInSetBinaryExp(AInSetBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered Value elem = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value set = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); try { return new BooleanValue(set.setValue(ctxt).contains(elem)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAMapUnionBinaryExp(AMapUnionBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered ValueMap lm = null; ValueMap rm = null; try { lm = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).mapValue(ctxt); rm = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).mapValue(ctxt); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } ValueMap result = new ValueMap(); result.putAll(lm); for (Value k : rm.keySet()) { Value rng = rm.get(k); Value old = result.put(k, rng); if (old != null && !old.equals(rng)) { VdmRuntimeError.abort(node.getLocation(), 4021, "Duplicate map keys have different values: " + k, ctxt); } } return new MapValue(result); } @Override public Value caseANotEqualBinaryExp(ANotEqualBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); return new BooleanValue(!lv.equals(rv)); } @Override public Value caseANotInSetBinaryExp(ANotInSetBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered Value elem = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value set = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); try { return new BooleanValue(!set.setValue(ctxt).contains(elem)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } /* * Numeric */ @Override public Value caseADivNumericBinaryExp(ADivNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { double lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).intValue(ctxt); double rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).intValue(ctxt); if (rv == 0) { throw new ValueException(4134, "Infinite or NaN trouble", ctxt); } return NumericValue.valueOf(div(lv, rv), ctxt); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseADivideNumericBinaryExp(ADivideNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { double lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).realValue(ctxt); double rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).realValue(ctxt); return NumericValue.valueOf(lv / rv, ctxt); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAGreaterEqualNumericBinaryExp( AGreaterEqualNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); try { return new BooleanValue(lv.realValue(ctxt) >= rv.realValue(ctxt)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAGreaterNumericBinaryExp(AGreaterNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); try { return new BooleanValue(lv.realValue(ctxt) > rv.realValue(ctxt)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseALessEqualNumericBinaryExp( ALessEqualNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); try { return new BooleanValue(lv.realValue(ctxt) <= rv.realValue(ctxt)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseALessNumericBinaryExp(ALessNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); try { return new BooleanValue(lv.realValue(ctxt) < rv.realValue(ctxt)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAModNumericBinaryExp(AModNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { /* * Remainder x rem y and modulus x mod y are the same if the signs of x and y are the same, otherwise they * differ and rem takes the sign of x and mod takes the sign of y. The formulas for remainder and modulus * are: x rem y = x - y * (x div y) x mod y = x - y * floor(x/y) Hence, -14 rem 3 equals -2 and -14 mod 3 * equals 1. One can view these results by walking the real axis, starting at -14 and making jumps of 3. The * remainder will be the last negative number one visits, because the first argument corresponding to x is * negative, while the modulus will be the first positive number one visit, because the second argument * corresponding to y is positive. */ double lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).intValue(ctxt); double rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).intValue(ctxt); if (rv == 0) { throw new ValueException(4134, "Infinite or NaN trouble", ctxt); } return NumericValue.valueOf(lv - rv * (long) Math.floor(lv / rv), ctxt); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAPlusNumericBinaryExp(APlusNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value l = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value r = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (NumericValue.areIntegers(l, r)) { long lv = l.intValue(ctxt); long rv = r.intValue(ctxt); long sum = addExact(lv, rv, ctxt); return NumericValue.valueOf(sum, ctxt); } else { double lv = l.realValue(ctxt); double rv = r.realValue(ctxt); return NumericValue.valueOf(lv + rv, ctxt); } } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseARemNumericBinaryExp(ARemNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { /* * Remainder x rem y and modulus x mod y are the same if the signs of x and y are the same, otherwise they * differ and rem takes the sign of x and mod takes the sign of y. The formulas for remainder and modulus * are: x rem y = x - y * (x div y) x mod y = x - y * floor(x/y) Hence, -14 rem 3 equals -2 and -14 mod 3 * equals 1. One can view these results by walking the real axis, starting at -14 and making jumps of 3. The * remainder will be the last negative number one visits, because the first argument corresponding to x is * negative, while the modulus will be the first positive number one visit, because the second argument * corresponding to y is positive. */ double lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).intValue(ctxt); double rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).intValue(ctxt); if (rv == 0) { throw new ValueException(4134, "Infinite or NaN trouble", ctxt); } return NumericValue.valueOf(lv - rv * div(lv, rv), ctxt); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseASubtractNumericBinaryExp(ASubtractNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value l = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value r = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (NumericValue.areIntegers(l, r)) { long lv = l.intValue(ctxt); long rv = r.intValue(ctxt); long diff = subtractExact(lv, rv, ctxt); return NumericValue.valueOf(diff, ctxt); } else { double lv = l.realValue(ctxt); double rv = r.realValue(ctxt); return NumericValue.valueOf(lv - rv, ctxt); } } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseATimesNumericBinaryExp(ATimesNumericBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value l = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value r = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (NumericValue.areIntegers(l, r)) { long lv = l.intValue(ctxt); long rv = r.intValue(ctxt); long mult = multiplyExact(lv, rv, ctxt); return NumericValue.valueOf(mult, ctxt); } else { double lv = l.realValue(ctxt); double rv = r.realValue(ctxt); return NumericValue.valueOf(lv * rv, ctxt); } } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } /* * end numeric */ @Override public Value caseAPlusPlusBinaryExp(APlusPlusBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).deref(); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (lv instanceof MapValue) { ValueMap lm = new ValueMap(lv.mapValue(ctxt)); ValueMap rm = rv.mapValue(ctxt); for (Value k : rm.keySet()) { lm.put(k, rm.get(k)); } return new MapValue(lm); } else { ValueList seq = lv.seqValue(ctxt); ValueMap map = rv.mapValue(ctxt); ValueList result = new ValueList(seq); for (Value k : map.keySet()) { int iv = (int) k.intValue(ctxt); if (iv < 1 || iv > seq.size()) { VdmRuntimeError.abort(node.getLocation(), 4025, "Map key not within sequence index range: " + k, ctxt); } result.set(iv - 1, map.get(k)); } return new SeqValue(result); } } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAProperSubsetBinaryExp(AProperSubsetBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { ValueSet set1 = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt); ValueSet set2 = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt); return new BooleanValue(set1.size() < set2.size() && set2.containsAll(set1)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseARangeResByBinaryExp(ARangeResByBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered ValueSet set = null; ValueMap map = null; try { set = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt); map = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).mapValue(ctxt); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } ValueMap modified = new ValueMap(map); for (Value k : map.keySet()) { if (set.contains(map.get(k))) { modified.remove(k); } } return new MapValue(modified); } @Override public Value caseARangeResToBinaryExp(ARangeResToBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered ValueSet set = null; ValueMap map = null; try { set = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt); map = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).mapValue(ctxt); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } ValueMap modified = new ValueMap(map); for (Value k : map.keySet()) { if (!set.contains(map.get(k))) { modified.remove(k); } } return new MapValue(modified); } @Override public Value caseASeqConcatBinaryExp(ASeqConcatBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); ValueList result = new ValueList(); result.addAll(lv.seqValue(ctxt)); result.addAll(rv.seqValue(ctxt)); return new SeqValue(result); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseASetDifferenceBinaryExp(ASetDifferenceBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered ValueSet result = new ValueSet(); ValueSet togo = null; try { togo = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt); result.addAll(node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } for (Value r : togo) { result.remove(r); } return new SetValue(result); } @Override public Value caseASetIntersectBinaryExp(ASetIntersectBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { ValueSet result = new ValueSet(); result.addAll(node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt)); result.retainAll(node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt)); return new SetValue(result); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseASetUnionBinaryExp(ASetUnionBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { ValueSet result = new ValueSet(); result.addAll(node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt)); result.addAll(node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt)); return new SetValue(result); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseAStarStarBinaryExp(AStarStarBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { Value lv = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).deref(); Value rv = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt); if (lv instanceof MapValue) { ValueMap map = lv.mapValue(ctxt); long n = rv.intValue(ctxt); ValueMap result = new ValueMap(); for (Value k : map.keySet()) { Value r = k; for (int i = 0; i < n; i++) { r = map.get(r); } if (r == null) { VdmRuntimeError.abort(node.getLocation(), 4133, "Map range is not a subset of its domain: " + k, ctxt); } Value old = result.put(k, r); if (old != null && !old.equals(r)) { VdmRuntimeError.abort(node.getLocation(), 4030, "Duplicate map keys have different values: " + k, ctxt); } } return new MapValue(result); } else if (lv instanceof FunctionValue) { return new IterFunctionValue(lv.functionValue(ctxt), rv.intValue(ctxt)); } else if (lv instanceof NumericValue) { double ld = lv.realValue(ctxt); double rd = rv.realValue(ctxt); return NumericValue.valueOf(Math.pow(ld, rd), ctxt); } return VdmRuntimeError.abort(node.getLocation(), 4031, "First arg of '**' must be a map, function or number", ctxt); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } @Override public Value caseASubsetBinaryExp(ASubsetBinaryExp node, Context ctxt) throws AnalysisException { // breakpoint.check(location, ctxt); node.getLocation().hit(); // Mark as covered try { ValueSet set1 = node.getLeft().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt); ValueSet set2 = node.getRight().apply(VdmRuntime.getExpressionEvaluator(), ctxt).setValue(ctxt); return new BooleanValue(set2.containsAll(set1)); } catch (ValueException e) { return VdmRuntimeError.abort(node.getLocation(), e); } } /* * Utility methods */ static public long div(double lv, double rv) { /* * There is often confusion on how integer division, remainder and modulus work on negative numbers. In fact, * there are two valid answers to -14 div 3: either (the intuitive) -4 as in the Toolbox, or -5 as in e.g. * Standard ML [Paulson91]. It is therefore appropriate to explain these operations in some detail. Integer * division is defined using floor and real number division: x/y < 0: x div y = -floor(abs(-x/y)) x/y >= 0: x * div y = floor(abs(x/y)) Note that the order of floor and abs on the right-hand side makes a difference, the * above example would yield -5 if we changed the order. This is because floor always yields a smaller (or * equal) integer, e.g. floor (14/3) is 4 while floor (-14/3) is -5. */ if (lv / rv < 0) { return (long) -Math.floor(Math.abs(lv / rv)); } else { return (long) Math.floor(Math.abs(-lv / rv)); } } private long addExact(long x, long y, Context ctxt) throws ValueException { try { return Math.addExact(x, y); } catch (ArithmeticException e) { throw new ValueException(4169, "Arithmetic overflow", ctxt); } } private long subtractExact(long x, long y, Context ctxt) throws ValueException { try { return Math.subtractExact(x, y); } catch (ArithmeticException e) { throw new ValueException(4169, "Arithmetic overflow", ctxt); } } private long multiplyExact(long x, long y, Context ctxt) throws ValueException { try { return Math.multiplyExact(x, y); } catch (ArithmeticException e) { throw new ValueException(4169, "Arithmetic overflow", ctxt); } } }