Метод рекурсивного спуска. Java Math Parser

Класс предназначен для интерпретирования арифметических выражений методом рекурсивного спуска.
Поддерживаемые операции:
Побитовое И            &
Побитовое ИЛИ          |
Побитовое НЕ           ~
Сложение               +
Вычитание              -
Умножение              *
Деление                /
Остаток от деления     %
Целочисленное деление  \
Возведение в степень   ^


Поддерживаемые функции:
sin(x)   - синус
cos(x)   - косинус
tan(x)   - тангенс
ctg(x)   - котангенс
sinh(x)  - гиперболический синус
cosh(x)  - гиперболический косинус
tanh(x)  - гиперболический тангенс
sec(x)   - секанс
cosec(x) - косеканс
abs(x)   - модуль числа
sqrt(x)  - квадратный корень
ln(x)    - натуральный логарифм
lg(x)    - десятичный логарифм
log(x,y) - логарифм x по основанию y
xor(x,y) - побитовое исключающее ИЛИ




MathParser.java

import java.util.HashMap;

/**
 *
 * Класс модифицирован автором ShamaN.
 * Метод рекурсивного спуска для интерпретирования математических выражений.
 * Поддерживаются тригонометрические функции с одним/двумя параметрами.
 * @author shurik
 * @version 1.1
 */
public class MathParser {

    private static HashMap<String, Double> var;

    public MathParser() {
            var = new HashMap<>();
            setVariable("pi",Math.PI);
            setVariable("e",Math.E);
    }

    
    /**
     * Вставить новую переменную
     * @param varName имя переменной
     * @param varValue значение переменной
     */
    public static void setVariable(String varName, Double varValue) {
            var.put(varName, varValue);
    }
    
    /**
     * Заменяет значение существующей переменной
     * @param varName имя переменной
     * @param varValue значение переменной
     */
    public void replaceVariable(String varName, Double varValue) {       
            var.replace(varName, varValue);
    }

    /**
     * 
     * @param varName
     * @return Возвращает значение переменной varName
     * @throws Exception ругаемся на отсутствие переменной
     */
    public Double getVariable(String varName) throws Exception {
        if(!var.containsKey(varName)) {
            throw new Exception("Error:Try get unexists "+
                                               "variable '"+varName+"'" );
        }
        return var.get(varName);
    }

    /**
     * Парсим математическое выражение
     * @param s математическое выражение
     * @return результат
     * @throws Exception 
     */
    public double Parse(String s) throws Exception {
        if(s.isEmpty())
            throw new Exception("Empty expression");
        Result result = binaryFunc(s);
        if (!result.rest.isEmpty())
            throw new Exception("Error: can't full parse \n "+
                                              "rest: " + result.rest);
        return result.acc;
    }
    

    private Result binaryFunc(String s) throws Exception{
        
        Result cur;
        
        if(s.charAt(0) == '~'){
            cur = plusMinus(s.substring(1));
            
            cur.acc = ~ (int)cur.acc;
            return cur;
        }
        
        cur = plusMinus(s);
        double acc = cur.acc;
        
        cur.rest = skipSpaces(cur.rest);
        
        while(cur.rest.length() > 0){
            if(!(cur.rest.charAt(0) == '&' ||
                 cur.rest.charAt(0) == '|' || 
                 cur.rest.charAt(0) == '~')) break;
            
            char sign = cur.rest.charAt(0);
            String next = cur.rest.substring(1);
            cur = plusMinus(next);
            

            if(sign == '&')
                    acc = (int)acc & (int)cur.acc;
            else
                    acc = (int)acc | (int)cur.acc;
        }
        
        return new Result(acc,cur.rest);
        
    }

    private Result plusMinus(String s) throws Exception {

        Result cur = mulDiv(s);
        double acc = cur.acc;
        
        cur.rest = skipSpaces(cur.rest);

        while(cur.rest.length() > 0){
            if(!(cur.rest.charAt(0) == '+' || cur.rest.charAt(0) == '-'))
                    break;
            
            char sign = cur.rest.charAt(0);
            String next = cur.rest.substring(1);

            cur = binaryFunc(next);

            if(sign == '+')
                    acc+=cur.acc;
            else
                    acc-=cur.acc;
        }
        return new Result(acc,cur.rest);
    }

    


    private Result mulDiv(String s) throws Exception{
        Result cur = exponentiation(s);
        double acc = cur.acc;

        cur.rest = skipSpaces(cur.rest);
        
        
        while(true){
            if(cur.rest.length() == 0)
                    return cur;
                
            char sign = cur.rest.charAt(0);
            if(sign != '*' && sign != '/' && sign != '%' && sign != '\\') 
                return cur;

            String next = cur.rest.substring(1);
            Result right = exponentiation(next);
            switch(sign){
                case '*': 
                    acc*=right.acc; 
                    break;
                case '/': 
                    acc/=right.acc; 
                    break;
                case '%': // остаток от деления
                    acc%=right.acc; 
                    break; 
                case '\\': // целочисленное деление
                    acc = (acc - acc % right.acc)/right.acc; 
                    break;
            }
            cur = new Result(acc,right.rest);
        }
    }


    private Result exponentiation(String s) throws Exception{
        Result cur = bracket(s);
        double acc = cur.acc;
        
        cur.rest = skipSpaces(cur.rest);

        while(true){
            
            if(cur.rest.length() == 0) return cur;
            if(cur.rest.charAt(0) !='^') break;

            String next = cur.rest.substring(1);
            cur = bracket(next);
            cur.acc = Math.pow(acc,cur.acc);
        } 
        return cur;
    }


    private Result bracket(String s) throws Exception{
        
        s = skipSpaces(s);
        char zeroChar = s.charAt(0);
        if (zeroChar == '(') {
            Result r = binaryFunc(s.substring(1));
            if (!r.rest.isEmpty()) {
                r.rest = r.rest.substring(1);
            } else {
                throw new Exception("Expected closing bracket");
            }
            return r;
        }
        return functionVariable(s);
    }

    private Result functionVariable(String s) throws Exception{
        String f = "";
        int i = 0;
        // ищем название функции или переменной
        // имя обязательно должна начинаться с буквы
        while (i < s.length() && (Character.isLetter(s.charAt(i)) || 
              ( Character.isDigit(s.charAt(i)) && i > 0 ) )) {
            f += s.charAt(i);
            i++;
        }
        if (!f.isEmpty()) { // если что-нибудь нашли
            if ( s.length() > i && s.charAt( i ) == '(') { 
            // и следующий символ скобка значит - это функция
                Result r = binaryFunc(s.substring(f.length()+1));
                
                if(!r.rest.isEmpty() && r.rest.charAt(0) == ','){
                // если функция с двумя параметрами
                    double acc = r.acc;
                    Result r2 = binaryFunc(r.rest.substring(1));
                    
                    r2 = closeBracket(r2);
                    return processFunction(f, acc,r2);

                } else {
                    r = closeBracket(r);
                    return processFunction(f, r);
                }
            } else { // иначе - это переменная
                return new Result(getVariable(f), s.substring(f.length()));
            }
        }
        return num(s);
    }
    private Result closeBracket(Result r) throws Exception{
        if(!r.rest.isEmpty() && r.rest.charAt(0) ==')'){
            r.rest = r.rest.substring(1);
        } else
            throw new Exception("Expected closing bracket");
        return r;
    }

    private Result num(String s) throws Exception{
        int i = 0;
        int dot_cnt = 0;
        boolean negative = false;
        // число также может начинаться с минуса
        if( s.charAt(0) == '-' ){
            negative = true;
            s = s.substring( 1 );
        }
        // разрешаем только цифры и точку
        while (i < s.length() && 
              (Character.isDigit(s.charAt(i)) || s.charAt(i) == '.')) {
            // но также проверяем, что в числе может быть только одна точка!
            if (s.charAt(i) == '.' && ++dot_cnt > 1) {
                throw new Exception("not valid number '" 
                                    + s.substring(0, i + 1) + "'");
            }
            i++;
        }
        if( i == 0 ){ // что-либо похожее на число мы не нашли
            throw new Exception("can't get valid number in '" + s + "'" );
        }

        double dPart = Double.parseDouble(s.substring(0, i));
        if(negative) dPart = -dPart;
        String restPart = s.substring(i);

        return new Result(dPart, restPart);
    }

    private Result processFunction(String func, Result r) throws Exception{
        switch (func) {
            case "sin":
                return new Result(Math.sin(r.acc), r.rest);
            case "sinh": // гиперболический синус
                return new Result(Math.sinh(r.acc), r.rest);
            case "cos": 
                return new Result(Math.cos(r.acc), r.rest);
            case "cosh": // гиперболический косинус
                return new Result(Math.cosh(r.acc), r.rest);
            case "tan":
                return new Result(Math.tan(r.acc), r.rest);
            case "tanh": // гиперболический тангенс
                return new Result(Math.tanh(r.acc), r.rest);
            case "ctg":
                return new Result(1/Math.tan(r.acc), r.rest);
            case "sec": // секанс
                return new Result(1/Math.cos(r.acc), r.rest);
            case "cosec": // косеканс
                return new Result(1/Math.sin(r.acc), r.rest);   
            case "abs":
                return new Result(Math.abs(r.acc), r.rest);
            case "ln":
                return new Result(Math.log(r.acc), r.rest);
            case "lg": // десятичный логарифм
                return new Result(Math.log10(r.acc), r.rest);
            case "sqrt":
                return new Result(Math.sqrt(r.acc), r.rest);
            default:
                throw new Exception("function '" + func + "' is not       
                                                              defined");    
        }
    }
    private Result processFunction(String func,
                                   double acc, 
                                   Result r) throws Exception{
        switch(func){
            case "log": // логарифм x по основанию y
                return new Result(Math.log(acc)/Math.log(r.acc),
                                  r.rest);
            case "xor": // исключающее или 
                return new Result((int)acc ^ (int)r.acc,r.rest);
            default:
                throw new Exception("function '" + func + 
                                    "' is not defined");
        }
    }
    
    private String skipSpaces(String s){
        return s.trim();
    }
    

    private class Result {
        public double acc; // Аккумулятор
        public String rest; // остаток строки, которую мы еще не обработали
        public Result(double v, String r) {
            this.acc = v;
            this.rest = r;
        }
    }
}


Пример использования:
Main.java
public class Main{
    public static void main(String[] args){
        MathParser parser = new MathParser();
        String[] expressions = {"2+4*3-8^3",
                                "4*sin(0.5)+2*cos(2*0.5)",
                                "log(64,2)*lg(100)",
                                "xor(65,83)",
                                "63 & 95",
                                "2*(5-7",
                                "x*5",
                                "",
                                "2*pi*50",
                                "e^2+1",
                                "6.78.-7"};
        
        for(String expression:expressions){
            System.out.print(expression+"  ");
            try{
                System.out.print(parser.Parse(expression)+"\n");
            } catch(Exception e){
                System.out.println(e.getMessage());
            }
        }  
    }
}


Результат тестирования:
2+4*3-8^3 = -498.0
4*sin(0.5)+2*cos(2*0.5) = 2.9983067661530916
log(64,2)*lg(100) = 12.0
xor(65,83) = 18.0
63 & 95 = 31.0
2*(5-7 = Expected closing bracket
x*5 = Error:Try get unexists variable 'x'
2^6+8 = 72.0
2*pi*50 = 314.1592653589793
e^2+1 = 8.389056098930649
6.78.-7 = not valid number '6.78.'
Теги:
Добавлено: 18 Мая 2015 21:20:03 Добавил: Василий Рыбин Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...