章节9:支持连续的加减法

支持连续的加减法,想要实现这一个功能,最重要的是编写语法图

1+1的语法图是 INTEGER PLUS INTEGER

1+2-3+4-5的语法图是 INTEGER ((PLUS|MINUS) INTEGER)*, *代表0个或多个

再简单点用括号标上 1 (+ 2) (- 3) (+ 4) (- 5),提取公共的部分得出连续加减法的语法图,我们用factorAst代表INTEGER,来容纳更多的可能性

词法解析器不变

修改语法解析

java 复制代码
@Data
public class FactorAst extends Ast{
    private Integer value;

    public FactorAst(Integer value) {
        this.value = value;
    }    
}
@Data
public class ProgramAst extends Ast{
    private Ast leftValue;
    private TokenType op;
    private Ast rightValue;
    public ProgramAst(Ast leftValue,TokenType op, Ast rightValue) {
        this.leftValue = leftValue;
        this.op = op;
        this.rightValue = rightValue;
    }
}

// 语法解析器
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 : factorAst ((PLUS|MINUS) factorAst)*
        // factorAst: INTEGER
        Ast node = this.factorAst();
        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.factorAst());
        }
        return node;
    }
    public Ast factorAst(){
        Token left = this.currentToken;
        this.eat(TokenType.INTEGER);
        return new FactorAst((Integer)left.getValue());
    }
    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 复制代码
// 目标执行器
public class Interpreter {
    private Parser parser; // 语法解析器
    public Interpreter(Parser parser) {
        this.parser = parser;
    }
    public Object visitProgramAst(Ast ast){ // 访问programAst节点
        ProgramAst programAst = (ProgramAst) ast; 
        if(programAst.getOp() == TokenType.PLUS){
            return (Integer)this.visit(programAst.getLeftValue()) + (Integer)this.visit(programAst.getRightValue()); // 加法计算
        }else if(programAst.getOp() == TokenType.MINUS){
            return (Integer)this.visit(programAst.getLeftValue()) - (Integer)this.visit(programAst.getRightValue()); // 减法计算
        }
        return null;
    }
    public Object visitFactorAst(Ast ast){
        FactorAst factorAst = (FactorAst) ast;
        return factorAst.getValue();
    }
    public Object visit(Ast ast){ // 使用反射通过类名调用对应的函数
        String methodName = "visit" + ast.getClass().getSimpleName();
        try {
            Method method = this.getClass().getDeclaredMethod(methodName , Ast.class );
            return method.invoke(this, ast);
        } catch (Exception e) {
            e.printStackTrace();
        } 
        return null;
    }
    public Integer expr() {
        Ast ast = parser.parse(); // 获取语法树
        Integer result = (Integer)this.visit(ast); // 遍历获取结果
        return result;
    }
}

执行测试

java 复制代码
private static void testInterpreter() {
    Lexer lexer = new Lexer(" 11 + 14 - 22 ");
    Parser parser = new Parser(lexer);
    Interpreter interpreter = new Interpreter(parser);
    Integer result = interpreter.expr();
    System.out.println("计算结果:" + result);
}

执行打印   计算结果:3
相关推荐
小信丶1 小时前
@EnableTransactionManagement注解介绍、应用场景和示例代码
java·spring boot·后端
To Be Clean Coder1 小时前
【Spring源码】createBean如何寻找构造器(四)——类型转换与匹配权重
java·后端·spring
-孤存-1 小时前
SpringBoot核心注解与配置详解
java·spring boot·后端
2301_818732062 小时前
项目启动报错,错误指向xml 已解决
xml·java·数据库·后端·springboot
小王不爱笑1323 小时前
SpringBoot 整合 Ollama + 本地 DeepSeek 模型
java·spring boot·后端
短剑重铸之日4 小时前
《设计模式》第七篇:适配器模式
java·后端·设计模式·适配器模式
树码小子6 小时前
SpringIoC & DI (1):IOC介绍 & Spring IoC使用 & DI
java·后端·spring
墨染青竹梦悠然7 小时前
基于Django+vue的图书借阅管理系统
前端·vue.js·后端·python·django·毕业设计·毕设
怪兽毕设7 小时前
基于Django的洗衣服务平台设计与实现
后端·python·django·洗衣服务平台
程序员泠零澪回家种桔子7 小时前
微服务日志治理:ELK 栈实战指南
后端·elk·微服务·云原生·架构