目标1:完成简单的计算”1+1“ (1)

68 阅读3分钟

解析器的范式

用程序完成”1+1“还不简单吗?
直接用字符串分割把两个数字加起来就得到了结果。如果是这样的话,往后扩展延伸将会奇难无比。
实际上任何事物都存在一个范式,俗称规律。解释语言也有它的范式,词法解析->语法解析->执行目标,如果不理解不用去纠结,随着学习的深入将会有深刻的体会。

我们拿到了字符串String str = "1+1" , 第一步要做的事情就是词法解析。

词法解析

学过英语的朋友知道,一个句子是由多个单词组成,比如"i eat a fresh apple",每一个单词都有对应的含义,含义之外还有词的性质,eat是动词,fresh是形容词,apple是名词。分析词的性质就是词法解析。

对应的分析计算表达式"1+1" , "1"是数字 , "+"是加法运算符, 目前词法就这些。词性和词的文本组合在一起取个名字叫词法单元。

词法解析代码

  1. 用枚举(TokenType)定义词性 , 定义词法单元的类Token, 包含词性和值。
// 词性枚举
public enum TokenType {
    INTEGER // 数字
    , PLUS // 加法运算符
}

// 词法单元
@Data
public class Token {
    // 词性
    private TokenType type;
    // 词值
    private Object value;
    // 构造函数
    public Token(TokenType type , Object value){
        this.type = type;
        this.value = value;
    }
    // 构造函数
    public Token(TokenType type){
        this.type = type;
    }
}

  1. 定义词法解析的类Lexer , 输入字符串"1+1", 调用函数getNextToken获取词法单元。
// 词法解析器
public class Lexer {
    // 输入的程序
    private String text;
    // 获取词法单元
    public Token getNextToken(){
        return null;
    }
    // 构造器
    public Lexer(String text) {
        this.text = text;
    }
}

4.解析英语句子的时候,是一个一个单词的去辨别,同理解析表达式的时候也需要逐个字符的扫描。碰到了"1"辨别词性构造词法单元返回,碰到了"+"做同样事情。具体到代码需要增加两个变量position记录扫描到了哪里,currentChar记录当前扫描的字符。

// 词法解析器
public class Lexer {
    private String text; // 输入的程序
    private Integer position; // 记录扫描的位置
    private Character currentChar; // 记录当前扫描的字符
   
    public Token getNextToken(){  // 获取词法单元
        return null;
    }
    public Lexer(String text) {// 构造器
        this.text = text;
        this.position = 0;
        this.currentChar = text.charAt(this.position);
    }
}
  1. 遍历字符串,辨别词性构造词法单元返回。
// 词法解析器
public class Lexer {
    private String text; // 输入的程序
    private Integer position; // 记录扫描的位置
    private Character currentChar; // 记录当前扫描的字符
   
    public Token getNextToken(){  // 获取词法单元
        if(this.currentChar == null){ // 扫描完了结束
            return null; 
        }else if(Character.isDigit(this.currentChar)){
            Token token = new Token(TokenType.INTEGER ,Character.getNumericValue(this.currentChar));
            this.advance();
            return token;
        }else if(this.currentChar == '+'){
            Token token = new Token(TokenType.PLUS , "+");
            this.advance();
            return token; 
        }else {
            this.error("未知的词法");
        }
        return null;
    }
    public void advance(){ // 往后走一步
        this.position += 1;
        if(this.position <= this.text.length() - 1){ // 扫描的位置有效
            this.currentChar = text.charAt(this.position);
        }else{ // 扫描完了
            this.currentChar = null;
        }
        
    }
    public void error(String msg){ // 报错函数
        throw new RuntimeException(msg);
    }
    public Lexer(String text) {// 构造器
        this.text = text;
        this.position = 0;
        this.currentChar = text.charAt(this.position);
    }
}
  1. 写个测试函数测一下, 打印结果和预想一样。
public static void main(String[] args) {
    Lexer lexer = new Lexer("1+1");
    Token token = lexer.getNextToken();
    while (token != null) {
        System.out.println(token);
        token = lexer.getNextToken();
    }
}

Token(type=INTEGER, value=1)
Token(type=PLUS, value=+)
Token(type=INTEGER, value=1)