解析器的范式
用程序完成”1+1“还不简单吗?
直接用字符串分割把两个数字加起来就得到了结果。如果是这样的话,往后扩展延伸将会奇难无比。
实际上任何事物都存在一个范式,俗称规律。解释语言也有它的范式,词法解析->语法解析->执行目标,如果不理解不用去纠结,随着学习的深入将会有深刻的体会。
我们拿到了字符串String str = "1+1" , 第一步要做的事情就是词法解析。
词法解析
学过英语的朋友知道,一个句子是由多个单词组成,比如"i eat a fresh apple",每一个单词都有对应的含义,含义之外还有词的性质,eat是动词,fresh是形容词,apple是名词。分析词的性质就是词法解析。
对应的分析计算表达式"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;
}
}
- 定义词法解析的类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);
}
}
- 遍历字符串,辨别词性构造词法单元返回。
// 词法解析器
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);
}
}
- 写个测试函数测一下, 打印结果和预想一样。
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)