使用括号()标明优先级,最重要的还是语法图。
先回顾上一个语法图
js
programAst : termAst ((PLUS|MINUS) termAst)*
termAst : factorAst ((MUL|DIV) factorAst)*
factorAst: INTEGER
我们明确了一个规律,语法图越往下优先级越高,带括号优先级最高,所以在termAst和programAst之下。 和数字相比,数字本身就明确展示无运算,所以是同一个优先级。
修改语法图
js
programAst : termAst ((PLUS|MINUS) termAst)*
termAst : factorAst ((MUL|DIV) factorAst)*
factorAst: INTEGER | LBRACKET programAst RBRACKET
修改词法解析器
java
// 词性枚举
public enum TokenType {
INTEGER // 数字
, PLUS // 加法运算符
, EOF // 程序结束
, MINUS // 减法运算符
, MUL // 乘法运算符
, DIV // 除法运算符
, LBRACKET // 左括号
, RBRACKET // 右括号
}
// 词法解析器
public class Lexer {
private String text; // 输入的程序
private Integer position; // 记录扫描的位置
private Character currentChar; // 记录当前扫描的字符
public Token getNextToken(){ // 获取词法单元
while(this.currentChar != null){
if(Character.isDigit(this.currentChar)){
return this.integer();
}else if(Character.isWhitespace(currentChar)){
this.skipWhiteSpace();
}else if(this.currentChar == '+'){
Token token = new Token(TokenType.PLUS , "+");
this.advance();
return token;
}else if(this.currentChar == '-'){
Token token = new Token(TokenType.MINUS , "-");
this.advance();
return token;
}else if(this.currentChar == '*'){
Token token = new Token(TokenType.MUL , "*");
this.advance();
return token;
}else if(this.currentChar == '/'){
Token token = new Token(TokenType.DIV , "/");
this.advance();
return token;
}else if(this.currentChar == '/'){
Token token = new Token(TokenType.DIV , "/");
this.advance();
return token;
}else if(this.currentChar == '('){
Token token = new Token(TokenType.LBRACKET , "(");
this.advance();
return token;
}else if(this.currentChar == ')'){
Token token = new Token(TokenType.RBRACKET , ")");
this.advance();
return token;
}else {
this.error("未知的词法");
}
}
return new Token(TokenType.EOF);
}
public Token integer(){ // 识别多个数字
String result = "";
while(this.currentChar != null && Character.isDigit(this.currentChar)){
result += this.currentChar;
this.advance();
}
return new Token(TokenType.INTEGER ,Integer.valueOf(result));
}
private void skipWhiteSpace(){ // 空格跳过
while(currentChar != null && Character.isWhitespace(currentChar)){
this.advance();
}
}
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);
}
}
修改语法解析器
java
// 语法解析器
public class Parser {
private Lexer lexer ; // 词法解析器
private Token currentToken; // 当前的词法单元
public Parser(Lexer lexer) {
this.lexer = lexer;
this.currentToken = this.lexer.getNextToken();
}
public Ast programAst(){ // 程序节点
// programAst : termAst ((PLUS|MINUS) termAst)*
Ast node = this.termAst();
while(Arrays.asList(TokenType.PLUS,TokenType.MINUS).contains(this.currentToken.getType())){
Token op = this.currentToken;
if(op.getType() == TokenType.PLUS){
this.eat(TokenType.PLUS);
}else if(op.getType() == TokenType.MINUS){
this.eat(TokenType.MINUS);
}
node = new ProgramAst(node ,op.getType(),this.termAst());
}
return node;
}
public Ast termAst(){
// termAst : factorAst ((MUL|DIV) factorAst)*
Ast node = this.factorAst();
while(Arrays.asList(TokenType.MUL,TokenType.DIV).contains(this.currentToken.getType())){
Token op = this.currentToken;
if(op.getType() == TokenType.MUL){
this.eat(TokenType.MUL);
}else if(op.getType() == TokenType.DIV){
this.eat(TokenType.DIV);
}
node = new TermAst(node ,op.getType(),this.factorAst());
}
return node;
}
public Ast factorAst(){
// INTEGER | LBRACKET programAst RBRACKET
Token left = this.currentToken;
if(left.getType() == TokenType.INTEGER){
this.eat(TokenType.INTEGER);
return new FactorAst((Integer)left.getValue());
}else if(left.getType() == TokenType.LBRACKET){
this.eat(TokenType.LBRACKET);
Ast ast = this.programAst();
this.eat(TokenType.RBRACKET);
return ast;
}
this.error("语法错误");
return null;
}
public void eat(TokenType tokenType){ // 确认当前的词性是否正确
if(tokenType == this.currentToken.getType()){
this.currentToken = this.lexer.getNextToken();
}else{
this.error("语法错误");
}
}
public void error(String msg){ // 报错函数
throw new RuntimeException(msg);
}
public Ast parse(){ // 获取语法树
return this.programAst();
}
}
解释器不修改
执行测试
java
private static void testInterpreter() {
Lexer lexer = new Lexer(" (2 + 1) * 4");
Parser parser = new Parser(lexer);
Interpreter interpreter = new Interpreter(parser);
Integer result = interpreter.expr();
System.out.println("计算结果:" + result);
}
打印结果 计算结果:12