章节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
相关推荐
llz_1122 小时前
web-第二次课后作业
前端·后端·web
红尘散仙8 小时前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记9 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
会编程的土豆10 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
喵个咪10 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball61611 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_25183645711 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao11 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
IT_陈寒12 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端
ayqy贾杰13 小时前
基层管理的三板斧,在AI时代行不通了
前端·后端·团队管理