/** * * Copyright 2015 Patrick Ahlbrecht * * 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 de.onyxbits.jbee; import java.math.BigDecimal; import java.math.MathContext; import java.util.HashMap; import java.util.List; /** * Default implementation of the {@link MathLib}. In most cases it is best to * subclass this class and call super() on overidden methods instead of * implementing {@link MathLib} from scratch. */ public class DefaultMathLib implements MathLib { private static final BigDecimal HUNDRET = new BigDecimal(100); private MathContext mathContext; private HashMap<String, BigDecimal> constants = new HashMap<String, BigDecimal>(); /** * {@inheritDoc} The default implementation does not provide any functions and * will always throw. */ public BigDecimal onCall(String name, List<BigDecimal> args) throws NotDefinedException { throw new NotDefinedException(name); } /** * {@inheritDoc} */ public MathContext getMathContext() { return mathContext; } /** * Set the {@link MathContext} to use from now on. * * @param m * new {@link MathContext} or null. */ public void setMathContext(MathContext m) { this.mathContext = m; } /** * {@inheritDoc} */ public void map(String name, BigDecimal value) { if (value == null) { constants.remove(name); } else { constants.put(name, value); } } public void clearMappings() { constants.clear(); } /** * {@inheritDoc} */ public BigDecimal onLookup(String name) throws NotDefinedException { return constants.get(name); // Null check is in the parser, don't double it. } /** * {@inheritDoc} * * The default implementation throws an {@link IllegalArgumentException} */ public BigDecimal onEmptyExpression() { throw new IllegalArgumentException("empty expression"); } /** * {@inheritDoc} The default implementation throws an * {@link ArithmeticException} */ public void onSyntaxError(int pos, String token) { throw new ArithmeticException("Syntax error: \'" + token + "\' at position " + pos); } /** * {@inheritDoc} The default implementation throws an * {@link IllegalArgumentException} */ public void onTokenizeError(final char[] input, int offset) { throw new IllegalArgumentException("Illegal character at position " + offset + " in '" + new String(input) + "'"); } /** * {@inheritDoc} */ public BigDecimal onAddition(BigDecimal augend, BigDecimal addend) { MathContext mc = getMathContext(); if (mc == null) { return augend.add(addend); } else { return augend.add(addend, mc); } } /** * {@inheritDoc} */ public BigDecimal onSubtraction(BigDecimal minuend, BigDecimal subtrahend) { MathContext mc = getMathContext(); if (mc == null) { return minuend.subtract(subtrahend); } else { return minuend.subtract(subtrahend, mc); } } /** * {@inheritDoc} */ public BigDecimal onMultiplication(BigDecimal multiplicant, BigDecimal multiplier) { MathContext mc = getMathContext(); if (mc == null) { return multiplicant.multiply(multiplier); } else { return multiplicant.multiply(multiplier, mc); } } /** * {@inheritDoc} */ public BigDecimal onDivision(BigDecimal dividend, BigDecimal divisor) { MathContext mc = getMathContext(); if (mc == null) { // As far as design goes, this is incorrect, but rational numbers such as // 1/3 won't terminate after a finite number of digits and the user // probably doesn't want an ArithmeticException in that case. If the // Exception is truly desired, then this method must be overriden in a // subclass. return dividend.divide(divisor, MathContext.DECIMAL64); } else { return dividend.divide(divisor, mc); } } /** * {@inheritDoc} */ public BigDecimal onExponentiation(BigDecimal base, BigDecimal exponent) { MathContext mc = getMathContext(); int exp = exponent.intValueExact(); if (exp < 0) { exp = -exp; if (mc == null) { return BigDecimal.ONE.divide(base.pow(exp)); } else { return BigDecimal.ONE.divide(base.pow(exp, mc)); } } else { if (mc == null) { return base.pow(exp); } else { return base.pow(exp, mc); } } } /** * {@inheritDoc} */ public BigDecimal onNegation(BigDecimal num) { MathContext mc = getMathContext(); if (mc == null) { return num.negate(); } else { return num.negate(mc); } } /** * {@inheritDoc} */ public BigDecimal onModulation(BigDecimal dividend, BigDecimal moddivisor) { MathContext mc = getMathContext(); if (mc == null) { return dividend.remainder(moddivisor); } else { return dividend.remainder(moddivisor, mc); } } /** * {@inheritDoc} */ public BigDecimal onPercentAddition(BigDecimal base, BigDecimal percent) { MathContext mc = getMathContext(); if (mc == null) { return base.add(base.multiply(percent).divide(HUNDRET)); } else { return base.add(base.multiply(percent, mc).divide(HUNDRET, mc), mc); } } /** * {@inheritDoc} */ public BigDecimal onPercentSubtraction(BigDecimal base, BigDecimal percent) { MathContext mc = getMathContext(); if (mc == null) { return base.subtract(base.multiply(percent).divide(HUNDRET)); } else { return base.subtract(base.multiply(percent, mc).divide(HUNDRET, mc), mc); } } /** * {@inheritDoc} */ public BigDecimal onMovePoint(BigDecimal num, BigDecimal dir) { return num.movePointRight(dir.intValueExact()); } /** * {@inheritDoc} */ public BigDecimal onBitwiseNot(BigDecimal num) throws ArithmeticException { return new BigDecimal(num.toBigIntegerExact().not()); } /** * {@inheritDoc} */ public BigDecimal onBitwiseAnd(BigDecimal lhs, BigDecimal rhs) throws ArithmeticException { return new BigDecimal(lhs.toBigIntegerExact().and(rhs.toBigIntegerExact())); } /** * {@inheritDoc} */ public BigDecimal onBitwiseOr(BigDecimal lhs, BigDecimal rhs) throws ArithmeticException { return new BigDecimal(lhs.toBigIntegerExact().or(rhs.toBigIntegerExact())); } /** * {@inheritDoc} */ public BigDecimal onBitwiseXor(BigDecimal lhs, BigDecimal rhs) throws ArithmeticException { return new BigDecimal(lhs.toBigIntegerExact().xor(rhs.toBigIntegerExact())); } /** * {@inheritDoc} */ public BigDecimal onBitshiftLeft(BigDecimal num, BigDecimal amount) throws ArithmeticException { return new BigDecimal(num.toBigIntegerExact().shiftLeft(amount.toBigIntegerExact().intValue())); } /** * {@inheritDoc} */ public BigDecimal onBitshiftRight(BigDecimal num, BigDecimal amount) throws ArithmeticException { return new BigDecimal(num.toBigIntegerExact().shiftRight(amount.toBigIntegerExact().intValue())); } }