解決中綴表達式中的單目運算符的處理

編譯程序或者腳本解釋程序中經常用到表達式的處理,其中單目運算符需要特殊處理:正號+、負號-、按位取反~、邏輯非!;

尤其是單目運算符與二元運算符混雜的情況下,例如正號+、負號-與二元的加+、減-容易混淆;


實際上仔細觀察中綴表達式的字符流時可以發現也是有規律可循的,單目運算符出現無非兩種情況:

(1)表達式開始的位置,或者括號()內部子表達式開始的位置,出現了+或者-,可以判定爲是單目的正號、負號;舉例:

    -30+a*b            //一開始就出現符號

3+(-20+b*c)      //(後一開始就出現符號

!a && b

(2)上一個字符已經是運算符,這時接着又來了一個運算符,第二個運算符可以判定爲單目運算符;舉例:

   30*-20+10        //乘號*後原本期望是個值,結果來了個-,這個-可以判定爲是負號

 

(3) 前一個字符已經是值(例如變量、字面常量、帶括號的值),接下來如果是運算符,這個運算符肯定不是單目運算符;舉例:

(a+b)-c           //(a+b)表示一個完整值,之後的-肯定不是單目運算符

a*b-c              //b是一個值,之後的-肯定不是單目運算符

 

根據以上原則,在掃描中綴表達式時,就能夠識別出哪些是單目運算符了,以下給出支持加減乘除、負號及左右括號的表達式處理代碼:


import java.util.Stack;
public class Expression {
    interface IToken {
    }
    static class Literal implements IToken {
        final int value;
        Literal(int input) {
            value = input;
        }
    }
    interface Operator extends IToken {
        int getLevel();
        Literal calculate(Literal... literals);
    }
    static Operator POSITIVE   = new Operator() {
        public int getLevel() {
            return 3;
        }
        public Literal calculate(Literal... literals) {
            return literals[0];
        }
    };
    static Operator NEGATIVE   = new Operator() {
        public int getLevel() {
            return 3;
        }
        public Literal calculate(Literal... literals) {
            return new Literal(-literals[0].value);
        }
    };
    static Operator MUL        = new Operator() {
        public int getLevel() {
            return 2;
        }
        public Literal calculate(Literal... literals) {
            return new Literal(literals[0].value * literals[1].value);
        }
    };
    static Operator DIV        = new Operator() {
        public int getLevel() {
            return 2;
        }
        public Literal calculate(Literal... literals) {
            return new Literal(literals[0].value / literals[1].value);
        }
    };
    static Operator ADD        = new Operator() {
        public int getLevel() {
            return 1;
        }
        public Literal calculate(Literal... literals) {
            return new Literal(literals[0].value + literals[1].value);
        }
    };
    static Operator SUB        = new Operator() {
        public int getLevel() {
            return 1;
        }
        public Literal calculate(Literal... literals) {
            return new Literal(literals[0].value - literals[1].value);
        }
    };
    static Operator LEFT_WRAP  = new Operator() {
        public int getLevel() {
            return -1;
        }
        public Literal calculate(Literal... literals) {
            return null;
        }
    };
    static Operator RIGHT_WRAP = new Operator() {
        public int getLevel() {
            return -1;
        }
        public Literal calculate(Literal... literals) {
            return null;
        }
    };
    enum State {
        IDLE, AFTER_LITERAL, AFTER_OPERATOR
    }
    static class Lexer {
        final   int    len;
        final   String text;
        private int    pos;
        State state;
        private Stack<IToken>   stack;
        private Stack<Operator> opStack;
        Lexer(String input) {
            text = input;
            pos = 0;
            len = text.length();
            state = State.IDLE;
            stack = new Stack<IToken>();
            opStack = new Stack<Operator>();
            IToken token = null;
            while ((token = next()) != null) {
                if (token instanceof Literal) {
                    if (state == State.IDLE || state == State.AFTER_OPERATOR) {
                        stack.push(token);
                        state = State.AFTER_LITERAL;
                        continue;
                    } else {
                        throw new IllegalArgumentException("Unexpected token Literal");
                    }
                } else {
                    Operator op = (Operator) token;
                    if (op == LEFT_WRAP) {
                        opStack.push(LEFT_WRAP);
                        state = State.IDLE;
                    } else if (op == RIGHT_WRAP) {
                        popUtilLeftWrap();
                        state = State.AFTER_LITERAL;
                    } else {
                        if (op == ADD) {
                            if (state == State.IDLE || state == State.AFTER_OPERATOR) {
                                op = POSITIVE;
                            }
                            state = State.AFTER_OPERATOR;
                        } else if (op == SUB) {
                            if (state == State.IDLE || state == State.AFTER_OPERATOR) {
                                op = NEGATIVE;
                            }
                            state = State.AFTER_OPERATOR;
                        } else {
                            if (state == State.AFTER_LITERAL) {
                                state = State.AFTER_OPERATOR;
                            } else {
                                throw new IllegalArgumentException("Unexpected token MUL/DIV");
                            }
                        }
                        popUtilLevel(op.getLevel());
                        opStack.push(op);
                    }
                }
            }
            popUtilLevel(-2);
        }
        private void popUtilLevel(int level) {
            while (!opStack.empty() && opStack.peek().getLevel() >= level) {
                stack.push(opStack.pop());
            }
        }
        private void popUtilLeftWrap(){
            while (!opStack.empty() && opStack.peek()!= LEFT_WRAP) {
                stack.push(opStack.pop());
            }
            if(opStack.peek() == LEFT_WRAP){
                opStack.pop();
            }else{
                throw new IllegalArgumentException("Unmatched RIGHT_WRAP");
            }
        }
        private IToken next() {
            for (; pos < len; pos++) {
                if (!Character.isWhitespace(text.charAt(pos))) {
                    break;
                }
            }
            if (pos == len) {
                return null;
            }
            char ch = text.charAt(pos);
            if (Character.isDigit(ch)) {
                int start = pos;
                for (; pos < len; pos++) {
                    if (!Character.isDigit(text.charAt(pos))) {
                        break;
                    }
                }
                return new Literal(Integer.valueOf(text.substring(start, pos)));
            }
            pos++;
            switch (ch) {
                case '+': {
                    return ADD;
                }
                case '-': {
                    return SUB;
                }
                case '*': {
                    return MUL;
                }
                case '/': {
                    return DIV;
                }
                case '(': {
                    return LEFT_WRAP;
                }
                case ')': {
                    return RIGHT_WRAP;
                }
                default: {
                    throw new IllegalArgumentException("Unacceptable char: " + ch);
                }
            }
        }
        int calculate() {
            int size = stack.size();
            IToken[] tokens = stack.toArray(new IToken[size]);
            Stack<Literal> result = new Stack<Literal>();
            for(int i = 0; i < size; i++){
                IToken token = tokens[i];
                if(token instanceof Literal){
                    result.push((Literal)token);
                }else{
                    Operator op = (Operator)token;
                    if(op == POSITIVE || op == NEGATIVE){
                        if(!result.empty()){
                            Literal literal = result.pop();
                            result.push(op.calculate(literal));
                        }else{
                            throw new IllegalArgumentException("Unexpected OP1");
                        }
                    }else{
                        if(result.size() >= 2){
                            Literal right = result.pop();
                            Literal left = result.pop();
                            result.push(op.calculate(left,right));
                        }else {
                            throw new IllegalArgumentException("Unexpected OP2");
                        }
                    }
                }
            }
            if(result.size() == 1){
                return result.pop().value;
            }else{
                throw new IllegalArgumentException("Unexpected result");
            }
        }
    }
    public static void main(String[] args){
        String[] examples = new String[]{
                "(1+2)*-3",
                "((-2+3)*(2+4-3/2)+5*6)-7",
                "-1*9+5*6/3"
        };
        for(String example :examples){
            try{
                System.out.println("" + example + "=" + (new Lexer(example).calculate()));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章