语法生成树会生成歧义,这会影响我们程序解析的正确性。因此消除歧义十分重要。 我们将使用递归向下解析法来消除歧义。
语法的歧义性,
优先级: 优先级高的运算符会先于优先级低的运算符
关联性:在一串优先级相同的运算符中,左关联指的是从左到右进行运算,右关联指的是从右到左进行运算。
这里定义优先级,就是先计算乘除,再计算加减。
不同的编译器,语法的优先级规则也是不同的,这个根据你的语言的需求考虑,我们这里为了简化编译器的设计,我们考虑的是遵循和c语言一样的语法优先级
名字 操作符 关联性
优先级从上向下,依次递减。
文法的解析法有很多种,如LL文法,LR文法,LALR文法等等。 我们这里采用的是递归向下解析法,这是所有文法中最简单的一种。虽然他很简单,但是可以支持复杂的错误处理,在很多语言的解析器里面,它都被使用的很频繁。
遇到终结符, 我们的解析就会去匹配词素
遇到非终结符, 我们的解析将继续。
遇到| 我们的解析就要进行判断
遇到*或者+ 我们的解析就要进行循环
遇到? 我们的解析进行判断
写代码开始
import java.util.List;
import static com.craftinginterpreters.lox.TokenType.*;
class Parser {
private final List<Token> tokens;
private int current = 0;
Parser(List<Token> tokens) {
this.tokens = tokens;
}
}
创建一个解析器 生成一个创造器, 声明词素,和一个intcurrent
我们要实现如下图的功能
第一个expression ---------->equality
private Expr expression() {
return equality();
}
然后是
equality → comparison ( ( "!=" | "==" ) comparison )* ;
实现方法是这样的
private Expr equality() {
Expr expr = comparison();
while (match(BANG_EQUAL, EQUAL_EQUAL)) {
Token operator = previous();
Expr right = comparison();
expr = new Expr.Binary(expr, operator, right);
}
return expr;
}
我们的equality方法中,包含了一个match方法
private boolean match(TokenType... types) {
for (TokenType type : types) {
if (check(type)) {
advance();
return true;
}
}
return false;
}
如果匹配成功,我们的解析器就前进,如果失败就退回去
private Token advance() {
if (!isAtEnd()) current++;
return previous();
}
然后我们还需要几个辅助函数
private boolean isAtEnd() {
return peek().type == EOF;
}
private Token peek() {
return tokens.get(current);
}
private Token previous() {
return tokens.get(current - 1);
}
我们继续
comparison → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ;
翻译成java就是
private Expr equality() {
Expr expr = comparison();
while (match(BANG_EQUAL, EQUAL_EQUAL)) {
Token operator = previous();
Expr right = comparison();
expr = new Expr.Binary(expr, operator, right);
}
return expr;
}
然后我们
private boolean match(TokenType... types) {
for (TokenType type : types) {
if (check(type)) {
advance();
return true;
}
}
return false;
}
接下来
private boolean check(TokenType type) {
if (isAtEnd()) return false;
return peek().type == type;
}
private Token advance() {
if (!isAtEnd()) current++;
return previous();
}
private boolean isAtEnd() {
return peek().type == EOF;
}
private Token peek() {
return tokens.get(current);
}
private Token previous() {
return tokens.get(current - 1);
}
接下来继续
comparison → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ;
然后
private Expr comparison() {
Expr expr = addition();
while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {
Token operator = previous();
Expr right = addition();
expr = new Expr.Binary(expr, operator, right);
}
return expr;
}
private Expr addition() {
Expr expr = multiplication();
while (match(MINUS, PLUS)) {
Token operator = previous();
Expr right = multiplication();
expr = new Expr.Binary(expr, operator, right);
}
return expr;
}
private Expr multiplication() {
Expr expr = unary();
while (match(SLASH, STAR)) {
Token operator = previous();
Expr right = unary();
expr = new Expr.Binary(expr, operator, right);
}
return expr;
}
不得不说实现一个编译器是真的难, 今天有点累,下次继续。