学到Part6,用Java重写了解释器。

把这几个类拆开放了,共三个文件:

src
    ├── Interpreter.java
    ├── Lexer.java
    └── Token.java

Token.java

public class Token {

    private String type;
    private String value;


    public Token(String type, String value) {
        this.type = type;
        this.value = value;
    }

    public String getType() {
        return type;
    }

    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "Token{" +
                "type='" + type + '\'' +
                ", value='" + value + '\'' +
                '}';
    }
}

Lexer.java (词法分析器)

import com.sun.xml.internal.ws.api.message.ExceptionHasMessage;

/**
 * 词法解析器:输入字符串->token
 */
public  class Lexer{
    private String text;
    private int pos=0;
    private char current_char;
    public Lexer(String text) {
        this.text = text;
        this.current_char = text.charAt(this.pos);
    }

    private void error() throws InvalidCharException{
        throw new InvalidCharException("Invalid character");
    }

    /**
     * 字符串的解析前进一个字符
     */
    private void advance(){
        this.pos += 1;
        if(this.pos > this.text.length()-1){
            this.current_char = '\0';
        }else{
            this.current_char = this.text.charAt(pos);
        }
    }

    /**
     * 跳过空格
     */
    private void skip_whitespace(){
        if(this.current_char!='\0' && Character.isSpaceChar(this.current_char)){
            advance();
        }
    }

    /**
     * 一/多个字符->整数
     * @return
     */
    private String integer(){
        String result = "";
        while(this.current_char != '\0' && Character.isDigit(this.current_char)){
            result += this.current_char;
            advance();
        }
        return result;
    }

    /**
     * 获取下一个token,并且使得pos加一
     * @return
     */
    public Token get_next_token() throws InvalidCharException {

        while(this.current_char != '\0' ){
            // 判断当前字符是空格
            if(Character.isSpaceChar(this.current_char)){
                //跳过空格
                skip_whitespace();
                //然后继续循环
                continue;
            }else if(Character.isDigit(this.current_char)){ // 如果当前字符是整数
                return new Token(Interpreter.INTEGER,integer());
            }else if(this.current_char == '+'){// 如果当前字符是+
                // 前进一步
                advance();
                //然后返回当前字符对应的token
                return new Token(Interpreter.PLUS,"+");
            }else if(this.current_char == '-'){// 如果当前字符是-
                // 前进一步
                advance();
                //然后返回当前字符对应的token
                return new Token(Interpreter.MINUS,"-");
            }else if(this.current_char == '*'){// 如果当前字符是*
                // 前进一步
                advance();
                //然后返回当前字符对应的token
                return new Token(Interpreter.MUL,"*");
            }else if(this.current_char == '/'){// 如果当前字符是/
                // 前进一步
                advance();
                //然后返回当前字符对应的token
                return new Token(Interpreter.DIV,"/");
            }else if(this.current_char == '('){// 如果当前字符是(
                // 前进一步
                advance();
                //然后返回当前字符对应的token
                return new Token(Interpreter.LPAREN,"(");
            }else if(this.current_char == ')'){// 如果当前字符是)
                // 前进一步
                advance();
                //然后返回当前字符对应的token
                return new Token(Interpreter.RPAREN,")");
            }else{
                error();
            }
        }
        return new Token(Interpreter.EOF,"EOF");

    }

    public class InvalidCharException extends Exception{
        public InvalidCharException(String message) {
            super(message);
        }
    }

}

Interpreter.java(语法分析器和解释器)


import java.util.Scanner;

public class Interpreter {
    // expr : term ((PLUS | MINUS)term)*
    // term : factor ((MUL|DIV)factor)*
    // factor : INTEGER | LPAREN expr RPAREN

    private Lexer lexer;
    private Token current_token;

    public final static String PLUS = "PLUS";
    public final static String MINUS = "MINUS";
    public final static String MUL = "MUL";
    public final static String DIV = "DIV";
    public final static String LPAREN = "(";
    public final static String RPAREN = ")";
    public final static String INTEGER = "INTEGER";
    public final static String EOF = "EOF";



    public Interpreter(Lexer lexer) throws Lexer.InvalidCharException {
        this.lexer = lexer;
        this.current_token = lexer.get_next_token();
    }

    /**
     * 抛出语法异常
     * @throws InvalidSyntaxException
     */
    public void error() throws InvalidSyntaxException {
        throw new InvalidSyntaxException("Invalid syntax");
    }

    public class InvalidSyntaxException extends Exception{
        public InvalidSyntaxException(String message) {
            super(message);
        }
    }

    private void eat(String token_type) throws InvalidSyntaxException, Lexer.InvalidCharException {
        // 如果当前token的type和传入的type确实一样,那么就获取下一个token
        if(this.current_token.getType() == token_type){
            this.current_token = this.lexer.get_next_token();
        }else{
            throw new InvalidSyntaxException("Invalid Syntax");
        }
    }

    /**
     * factor : INTEGER | LPAREN expr RPAREN
     * @return 基本单元
     */
    private int factor() throws InvalidSyntaxException, Lexer.InvalidCharException {
        int result=0;
        if(this.current_token.getType() == INTEGER){
            // 需要先取出当前INTEGER类型的token,否则下面eat之后的return返回的就是INTEGER的下一个token了
            Token token = this.current_token;
            eat(INTEGER);
            result = Integer.valueOf(token.getValue());
        }else if(this.current_token.getType() == LPAREN){
            eat(LPAREN);
            result = expr();
            eat(RPAREN);
        }
        return result;
    }

    /**
     * term : factor ((MUL|DIV)factor)*
     * @return 中间计算结果
     */
    private int term() throws InvalidSyntaxException, Lexer.InvalidCharException {
        // 获取第一个factor
        int result = factor();
        // ()* -> 循环
        while(this.current_token.getType() == MUL || this.current_token.getType() == DIV){
            // | -> if-else
            if(this.current_token.getType() == MUL){
                eat(MUL);
                result *= factor();
            }else if(this.current_token.getType() == DIV){
                eat(DIV);
                result /= factor();
            }
        }
        return result;
    }

    //

    /**
     * expr : term ((PLUS | MINUS)term)*
     *
     * calc> 7 + 3 * (10 / (12 / (3 + 1) - 1))
     * 22
     *
     * @return 最终计算结果
     */
    private int expr() throws InvalidSyntaxException, Lexer.InvalidCharException {
        // 获取第一个term
        int result = term();
        // ()* -> 循环
        while(this.current_token.getType() == PLUS || this.current_token.getType() == MINUS){
            // | -> if-else
            if(this.current_token.getType() == PLUS){
                // 吃掉当前token,设置this.current_token为下一个token
                eat(PLUS);
                // 加上第2个term
                result += term();
            }else if(this.current_token.getType() == MINUS){
                // 吃掉当前token,设置this.current_token为下一个token
                eat(MINUS);
                // 减去第2个term
                result -= term();
            }
        }
        return result;
    }

    public static void main(String[] args) throws Lexer.InvalidCharException, InvalidSyntaxException {
        Scanner sc = new Scanner(System.in);

        while(true){
            String text = "";
            try{
                System.out.print("calc> ");
                text = sc.nextLine();
            }catch (Exception e){
                break;
            }
            if(text.equals("")){
                continue;
            }
            if(text.equals("exit")){
                break;
            }
            // 将输入字符串转换为token
            Lexer lexer = new Lexer(text);
            // 将lexer输入解释器,new一个解释器
            Interpreter interpreter = new Interpreter(lexer);
            //执行解释器的expr,得到结果
            int result = interpreter.expr();
            // 打印结果
            System.out.println(result);
        }
        sc.close();
    }
}

测试


calc> 7 + 3 * (10 / (12 / (3 + 1) - 1))
22
calc> 
calc> 2
2
calc>