mirror of
				https://github.com/DanilaFe/abacus
				synced 2025-11-04 02:43:41 -08:00 
			
		
		
		
	Merge pull request #37 from DanilaFe/more-exceptions
Replace some more cases where null is used with Exceptions.
This commit is contained in:
		
						commit
						e0ccb67ad3
					
				@ -0,0 +1,24 @@
 | 
			
		||||
package org.nwapw.abacus.exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exception thrown by the Context in cases where lookup fails
 | 
			
		||||
 * where it should not.
 | 
			
		||||
 */
 | 
			
		||||
public class ContextException extends AbacusException {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new ContextException without an extra message.
 | 
			
		||||
     */
 | 
			
		||||
    public ContextException() {
 | 
			
		||||
        this("");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a ContextException with the given message.
 | 
			
		||||
     * @param message the message to use.
 | 
			
		||||
     */
 | 
			
		||||
    public ContextException(String message){
 | 
			
		||||
        super("Context exception", message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,25 @@
 | 
			
		||||
package org.nwapw.abacus.exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exception thrown by the NumberReducer if something goes wrong when
 | 
			
		||||
 * transforming a parse tree into a single value.
 | 
			
		||||
 */
 | 
			
		||||
public class NumberReducerException extends AbacusException {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new NumberReducerException with
 | 
			
		||||
     * no additional message.
 | 
			
		||||
     */
 | 
			
		||||
    public NumberReducerException() {
 | 
			
		||||
        this("");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new NumberReducerException with the given message.
 | 
			
		||||
     * @param message the message.
 | 
			
		||||
     */
 | 
			
		||||
    public NumberReducerException(String message) {
 | 
			
		||||
        super("Error evaluating expression", message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
package org.nwapw.abacus.exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exception thrown by parsers.
 | 
			
		||||
 */
 | 
			
		||||
public class ParseException extends AbacusException {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new ParseException with no additional message.
 | 
			
		||||
     */
 | 
			
		||||
    public ParseException(){
 | 
			
		||||
        this("");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new ParseException with the given additional message.
 | 
			
		||||
     * @param message the message.
 | 
			
		||||
     */
 | 
			
		||||
    public ParseException(String message){
 | 
			
		||||
        super("Failed to parse string", message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,16 +1,14 @@
 | 
			
		||||
package org.nwapw.abacus.exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An exception thrown primarily from Tree Value operators and functions,
 | 
			
		||||
 * which have to deal with the result of a Reducer as well as the results
 | 
			
		||||
 * of Applicable.
 | 
			
		||||
 * An exception thrown from TreeReducers.
 | 
			
		||||
 */
 | 
			
		||||
public class EvaluationException extends AbacusException {
 | 
			
		||||
public class ReductionException extends AbacusException {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new EvaluationException with the default string.
 | 
			
		||||
     */
 | 
			
		||||
    public EvaluationException() {
 | 
			
		||||
    public ReductionException() {
 | 
			
		||||
        this("");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -18,7 +16,7 @@ public class EvaluationException extends AbacusException {
 | 
			
		||||
     * Creates a new EvaluationError with the given message string.
 | 
			
		||||
     * @param message the message string.
 | 
			
		||||
     */
 | 
			
		||||
    public EvaluationException(String message) {
 | 
			
		||||
    public ReductionException(String message) {
 | 
			
		||||
        super("Evaluation error", message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
package org.nwapw.abacus.exception;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exception thrown by Lexers when they are unable to tokenize the input string.
 | 
			
		||||
 */
 | 
			
		||||
public class TokenizeException extends AbacusException {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new tokenize exception with no additional data.
 | 
			
		||||
     */
 | 
			
		||||
    public TokenizeException() {
 | 
			
		||||
        this("");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new tokenize exception with the given message.
 | 
			
		||||
     * @param message the message to use.
 | 
			
		||||
     */
 | 
			
		||||
    public TokenizeException(String message){
 | 
			
		||||
        super("Failed to tokenize string", message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package org.nwapw.abacus.parsing;
 | 
			
		||||
 | 
			
		||||
import org.nwapw.abacus.exception.TokenizeException;
 | 
			
		||||
import org.nwapw.abacus.lexing.Lexer;
 | 
			
		||||
import org.nwapw.abacus.lexing.pattern.Match;
 | 
			
		||||
import org.nwapw.abacus.lexing.pattern.Pattern;
 | 
			
		||||
@ -42,7 +43,9 @@ public class LexerTokenizer implements Tokenizer<Match<TokenType>>, PluginListen
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<Match<TokenType>> tokenizeString(String string) {
 | 
			
		||||
        return lexer.lexAll(string, 0, TOKEN_SORTER);
 | 
			
		||||
        List<Match<TokenType>> tokens = lexer.lexAll(string, 0, TOKEN_SORTER);
 | 
			
		||||
        if(tokens == null) throw new TokenizeException();
 | 
			
		||||
        return tokens;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package org.nwapw.abacus.parsing;
 | 
			
		||||
 | 
			
		||||
import org.nwapw.abacus.exception.ParseException;
 | 
			
		||||
import org.nwapw.abacus.function.Operator;
 | 
			
		||||
import org.nwapw.abacus.function.OperatorAssociativity;
 | 
			
		||||
import org.nwapw.abacus.function.OperatorType;
 | 
			
		||||
@ -99,7 +100,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
 | 
			
		||||
                while (!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH) {
 | 
			
		||||
                    output.add(tokenStack.pop());
 | 
			
		||||
                }
 | 
			
		||||
                if (tokenStack.empty()) return null;
 | 
			
		||||
                if (tokenStack.empty()) throw new ParseException("mismatched parentheses");
 | 
			
		||||
                if (matchType == TokenType.CLOSE_PARENTH) {
 | 
			
		||||
                    tokenStack.pop();
 | 
			
		||||
                }
 | 
			
		||||
@ -111,7 +112,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
 | 
			
		||||
            if (!(newMatchType == TokenType.OP ||
 | 
			
		||||
                    newMatchType == TokenType.TREE_VALUE_OP ||
 | 
			
		||||
                    newMatchType == TokenType.FUNCTION ||
 | 
			
		||||
                    newMatchType == TokenType.TREE_VALUE_FUNCTION)) return null;
 | 
			
		||||
                    newMatchType == TokenType.TREE_VALUE_FUNCTION)) throw new ParseException("mismatched parentheses");
 | 
			
		||||
            output.add(tokenStack.pop());
 | 
			
		||||
        }
 | 
			
		||||
        return output;
 | 
			
		||||
@ -124,7 +125,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
 | 
			
		||||
     * @return the construct tree expression.
 | 
			
		||||
     */
 | 
			
		||||
    public TreeNode constructRecursive(List<Match<TokenType>> matches) {
 | 
			
		||||
        if (matches.size() == 0) return null;
 | 
			
		||||
        if (matches.size() == 0) throw new ParseException("no tokens left in input");
 | 
			
		||||
        Match<TokenType> match = matches.remove(0);
 | 
			
		||||
        TokenType matchType = match.getType();
 | 
			
		||||
        if (matchType == TokenType.OP || matchType == TokenType.TREE_VALUE_OP) {
 | 
			
		||||
@ -133,7 +134,6 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
 | 
			
		||||
            if (type == OperatorType.BINARY_INFIX) {
 | 
			
		||||
                TreeNode right = constructRecursive(matches);
 | 
			
		||||
                TreeNode left = constructRecursive(matches);
 | 
			
		||||
                if (left == null || right == null) return null;
 | 
			
		||||
                if (matchType == TokenType.OP) {
 | 
			
		||||
                    return new NumberBinaryNode(operator, left, right);
 | 
			
		||||
                } else {
 | 
			
		||||
@ -141,7 +141,6 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                TreeNode applyTo = constructRecursive(matches);
 | 
			
		||||
                if (applyTo == null) return null;
 | 
			
		||||
                if (matchType == TokenType.OP) {
 | 
			
		||||
                    return new NumberUnaryNode(operator, applyTo);
 | 
			
		||||
                } else {
 | 
			
		||||
@ -157,10 +156,9 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
 | 
			
		||||
            List<TreeNode> children = new ArrayList<>();
 | 
			
		||||
            while (!matches.isEmpty() && matches.get(0).getType() != TokenType.INTERNAL_FUNCTION_END) {
 | 
			
		||||
                TreeNode argument = constructRecursive(matches);
 | 
			
		||||
                if (argument == null) return null;
 | 
			
		||||
                children.add(0, argument);
 | 
			
		||||
            }
 | 
			
		||||
            if (matches.isEmpty()) return null;
 | 
			
		||||
            if (matches.isEmpty()) throw new ParseException("incorrectly formatted function call");
 | 
			
		||||
            matches.remove(0);
 | 
			
		||||
            CallNode node;
 | 
			
		||||
            if (matchType == TokenType.FUNCTION) {
 | 
			
		||||
@ -170,16 +168,17 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
 | 
			
		||||
            }
 | 
			
		||||
            return node;
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
        throw new ParseException("unrecognized token");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TreeNode constructTree(List<Match<TokenType>> tokens) {
 | 
			
		||||
        if (tokens.isEmpty()) throw new ParseException("no input tokens");
 | 
			
		||||
        tokens = intoPostfix(new ArrayList<>(tokens));
 | 
			
		||||
        if (tokens == null) return null;
 | 
			
		||||
        Collections.reverse(tokens);
 | 
			
		||||
        TreeNode constructedTree = constructRecursive(tokens);
 | 
			
		||||
        return tokens.size() == 0 ? constructedTree : null;
 | 
			
		||||
        if(tokens.size() == 0) return constructedTree;
 | 
			
		||||
        throw new ParseException("could not parse all input");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
@ -42,9 +42,7 @@ public class TreeBuilder<T> {
 | 
			
		||||
     * @return the resulting tree.
 | 
			
		||||
     */
 | 
			
		||||
    public TreeNode fromString(String input) {
 | 
			
		||||
        List<T> tokens = tokenizer.tokenizeString(input);
 | 
			
		||||
        if (tokens == null) return null;
 | 
			
		||||
        return parser.constructTree(tokens);
 | 
			
		||||
        return parser.constructTree(tokenizer.tokenizeString(input));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -332,7 +332,7 @@ public class StandardPlugin extends Plugin {
 | 
			
		||||
            //We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
 | 
			
		||||
            //In the following, a=1/3^n, b=1/4^n, c = 1/n.
 | 
			
		||||
            //a is also an error bound.
 | 
			
		||||
            NumberInterface a = implementation.instanceForString("1"), b = a, c = a;
 | 
			
		||||
            NumberInterface a = implementation.instanceForString("1"), b = a, c;
 | 
			
		||||
            NumberInterface sum = implementation.instanceForString("0");
 | 
			
		||||
            NumberInterface one = implementation.instanceForString("1");
 | 
			
		||||
            int n = 0;
 | 
			
		||||
@ -715,7 +715,7 @@ public class StandardPlugin extends Plugin {
 | 
			
		||||
     * @return the value of the series
 | 
			
		||||
     */
 | 
			
		||||
    private static NumberInterface sinTaylor(MutableEvaluationContext context, NumberInterface x) {
 | 
			
		||||
        NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x;
 | 
			
		||||
        NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm, sum = x;
 | 
			
		||||
        NumberInterface maxError = x.getMaxError();
 | 
			
		||||
        int n = 1;
 | 
			
		||||
        do {
 | 
			
		||||
 | 
			
		||||
@ -88,7 +88,7 @@ class Abacus(val configuration: Configuration) {
 | 
			
		||||
     * @param input the input string to parse
 | 
			
		||||
     * @return the resulting tree, null if the tree builder or the produced tree are null.
 | 
			
		||||
     */
 | 
			
		||||
    fun parseString(input: String): TreeNode? = treeBuilder.fromString(input)
 | 
			
		||||
    fun parseString(input: String): TreeNode = treeBuilder.fromString(input)
 | 
			
		||||
    /**
 | 
			
		||||
     * Evaluates the given tree.
 | 
			
		||||
     *
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
package org.nwapw.abacus.context
 | 
			
		||||
 | 
			
		||||
import org.nwapw.abacus.exception.ContextException
 | 
			
		||||
import kotlin.reflect.KProperty
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -16,14 +17,14 @@ import kotlin.reflect.KProperty
 | 
			
		||||
 */
 | 
			
		||||
class ChainSearchDelegate<out V>(private val valueGetter: EvaluationContext.() -> V?) {
 | 
			
		||||
 | 
			
		||||
    operator fun getValue(selfRef: Any, property: KProperty<*>): V? {
 | 
			
		||||
        var currentRef = selfRef as? EvaluationContext ?: return null
 | 
			
		||||
    operator fun getValue(selfRef: Any, property: KProperty<*>): V {
 | 
			
		||||
        var currentRef = selfRef as EvaluationContext
 | 
			
		||||
        var returnedValue = currentRef.valueGetter()
 | 
			
		||||
        while (returnedValue == null) {
 | 
			
		||||
            currentRef = currentRef.parent ?: break
 | 
			
		||||
            returnedValue = currentRef.valueGetter()
 | 
			
		||||
        }
 | 
			
		||||
        return returnedValue
 | 
			
		||||
        return returnedValue ?: throw ContextException("context chain does not contain value")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -43,13 +43,13 @@ open class EvaluationContext(val parent: EvaluationContext? = null,
 | 
			
		||||
    /**
 | 
			
		||||
     * The implementation inherited from this context's parent.
 | 
			
		||||
     */
 | 
			
		||||
    val inheritedNumberImplementation: NumberImplementation?
 | 
			
		||||
            by ChainSearchDelegate { numberImplementation}
 | 
			
		||||
    val inheritedNumberImplementation: NumberImplementation
 | 
			
		||||
            by ChainSearchDelegate { numberImplementation }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The reducer inherited from this context's parent.
 | 
			
		||||
     */
 | 
			
		||||
    val inheritedReducer: Reducer<NumberInterface>?
 | 
			
		||||
    val inheritedReducer: Reducer<NumberInterface>
 | 
			
		||||
            by ChainSearchDelegate { reducer }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,8 @@ package org.nwapw.abacus.tree
 | 
			
		||||
 | 
			
		||||
import org.nwapw.abacus.Abacus
 | 
			
		||||
import org.nwapw.abacus.context.EvaluationContext
 | 
			
		||||
import org.nwapw.abacus.exception.EvaluationException
 | 
			
		||||
import org.nwapw.abacus.exception.NumberReducerException
 | 
			
		||||
import org.nwapw.abacus.exception.ReductionException
 | 
			
		||||
import org.nwapw.abacus.number.NumberInterface
 | 
			
		||||
 | 
			
		||||
class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer<NumberInterface> {
 | 
			
		||||
@ -17,15 +18,14 @@ class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer<Nu
 | 
			
		||||
        val promotionManager = abacus.promotionManager
 | 
			
		||||
        return when(treeNode){
 | 
			
		||||
            is NumberNode -> {
 | 
			
		||||
                context.inheritedNumberImplementation?.instanceForString(treeNode.number)
 | 
			
		||||
                        ?: throw EvaluationException("no number implementation selected.")
 | 
			
		||||
                context.inheritedNumberImplementation.instanceForString(treeNode.number)
 | 
			
		||||
            }
 | 
			
		||||
            is VariableNode -> {
 | 
			
		||||
                val variable = context.getVariable(treeNode.variable)
 | 
			
		||||
                if(variable != null) return variable
 | 
			
		||||
                val definition = context.getDefinition(treeNode.variable)
 | 
			
		||||
                if(definition != null) return definition.reduce(this)
 | 
			
		||||
                throw EvaluationException("variable is not defined.")
 | 
			
		||||
                throw NumberReducerException("variable is not defined.")
 | 
			
		||||
            }
 | 
			
		||||
            is NumberUnaryNode -> {
 | 
			
		||||
                val child = children[0] as NumberInterface
 | 
			
		||||
@ -58,7 +58,7 @@ class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer<Nu
 | 
			
		||||
                abacus.pluginManager.treeValueFunctionFor(treeNode.callTo)
 | 
			
		||||
                        .apply(context, *treeNode.children.toTypedArray())
 | 
			
		||||
            }
 | 
			
		||||
            else -> throw EvaluationException("unrecognized tree node.")
 | 
			
		||||
            else -> throw ReductionException("unrecognized tree node.")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,6 @@ public class CalculationTests {
 | 
			
		||||
 | 
			
		||||
    private void testOutput(String input, String parseOutput, String output) {
 | 
			
		||||
        TreeNode parsedTree = abacus.parseString(input);
 | 
			
		||||
        Assert.assertNotNull(parsedTree);
 | 
			
		||||
        Assert.assertEquals(parsedTree.toString(), parseOutput);
 | 
			
		||||
        NumberInterface result = abacus.evaluateTree(parsedTree).getValue();
 | 
			
		||||
        Assert.assertNotNull(result);
 | 
			
		||||
@ -31,7 +30,6 @@ public class CalculationTests {
 | 
			
		||||
 | 
			
		||||
    private void testDomainException(String input, String parseOutput) {
 | 
			
		||||
        TreeNode parsedTree = abacus.parseString(input);
 | 
			
		||||
        Assert.assertNotNull(parsedTree);
 | 
			
		||||
        Assert.assertEquals(parsedTree.toString(), parseOutput);
 | 
			
		||||
        try {
 | 
			
		||||
            abacus.evaluateTree(parsedTree);
 | 
			
		||||
 | 
			
		||||
@ -13,11 +13,8 @@ import javafx.util.StringConverter;
 | 
			
		||||
import org.nwapw.abacus.Abacus;
 | 
			
		||||
import org.nwapw.abacus.config.Configuration;
 | 
			
		||||
import org.nwapw.abacus.exception.AbacusException;
 | 
			
		||||
import org.nwapw.abacus.exception.ComputationInterruptedException;
 | 
			
		||||
import org.nwapw.abacus.function.Documentation;
 | 
			
		||||
import org.nwapw.abacus.function.DocumentationType;
 | 
			
		||||
import org.nwapw.abacus.exception.DomainException;
 | 
			
		||||
import org.nwapw.abacus.exception.EvaluationException;
 | 
			
		||||
import org.nwapw.abacus.number.*;
 | 
			
		||||
import org.nwapw.abacus.plugin.ClassFinder;
 | 
			
		||||
import org.nwapw.abacus.plugin.PluginListener;
 | 
			
		||||
@ -145,9 +142,6 @@ public class AbacusController implements PluginListener {
 | 
			
		||||
        private String attemptCalculation() {
 | 
			
		||||
            try {
 | 
			
		||||
                TreeNode constructedTree = abacus.parseString(inputField.getText());
 | 
			
		||||
                if (constructedTree == null) {
 | 
			
		||||
                    return ERR_SYNTAX;
 | 
			
		||||
                }
 | 
			
		||||
                EvaluationResult result = abacus.evaluateTree(constructedTree);
 | 
			
		||||
                NumberInterface evaluatedNumber = result.getValue();
 | 
			
		||||
                String resultingString = evaluatedNumber.toString();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user