java · 2022-09-30 0

antlr4 使用

1.maven 依赖

<dependencies>
    <dependency>
        <groupId>org.antlr</groupId>
        <artifactId>antlr4</artifactId>
        <version>4.7.2</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.antlr</groupId>
            <artifactId>antlr4-maven-plugin</artifactId>
            <version>4.7.2</version>
            <executions>
                <execution>
                    <configuration>
                        <listener>true</listener>
                        <visitor>true</visitor>
                    </configuration>
                    <goals>
                        <goal>antlr4</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <version>3.0.0</version>
            <executions>
                <execution>
                    <id>package</id>
                    <phase>generate-sources</phase>
                    <goals>
                        <goal>add-source</goal>
                    </goals>
                    <configuration>
                        <sources>
                            <source>${basedir}/target/generated-sources</source>
                        </sources>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

2.g4文件

创建目录 src/main/antlr4/org/example/parser
创建文件 Calculator.g4

// CalculatorParser.g4
// 语法文件
grammar Calculator;

// 引入词法文件,会根据 CalculatorLex.g4 生成 java 类,而是根据 Calculator.g4 生成文件
// import CalculatorLex;

// 指定词法规则文件,会根据 CalculatorLex.g4 生成 java 类
//options {
//    tokenVocab = CalculatorLex;
//}

start:
    expr EOF
    ;

expr: expr op=('*'|'/') expr  # MulDiv
    | expr op=('+'|'-') expr  # AddSub
    | INT                     # int
    | '(' expr ')'            # parens
    ;

INT     : [0-9]+ ;
MUL     : '*' ;
DIV     : '/' ;
ADD     : '+' ;
SUB     : '-' ;

执行 mvn package 编译,会在 target/generated-sources/antlr4 目录下,看到编译出的 java 文件

3.java 文件

CalculatorVisitorImpl.java

package org.example;

import org.example.parser.CalculatorBaseVisitor;
import org.example.parser.CalculatorParser;

public class CalculatorVisitorImpl extends CalculatorBaseVisitor<Integer> {

    @Override
    public Integer visitStart(CalculatorParser.StartContext ctx) {
        if (ctx.expr() != null) {
            return visit(ctx.expr());
        }
        return null;
    }

    @Override
    public Integer visitMulDiv(CalculatorParser.MulDivContext ctx) {
        int left = visit(ctx.expr(0));
        int right = visit(ctx.expr(1));
        if (ctx.op.getType() == CalculatorParser.MUL) {
            return left * right;
        }
        return left / right;
    }

    @Override
    public Integer visitAddSub(CalculatorParser.AddSubContext ctx) {
        int left = visit(ctx.expr(0));
        int right = visit(ctx.expr(1));
        if (ctx.op.getType() == CalculatorParser.ADD) {
            return left + right;
        }
        return left - right;
    }

    @Override
    public Integer visitInt(CalculatorParser.IntContext ctx) {
        return Integer.valueOf(ctx.INT().getText());
    }

    @Override
    public Integer visitParens(CalculatorParser.ParensContext ctx) {
        return visit(ctx.expr());
    }
}

CalculatorListenerImpl.java

package org.example;

import org.example.parser.CalculatorBaseListener;
import org.example.parser.CalculatorParser;

import java.util.Deque;
import java.util.LinkedList;

public class CalculatorListenerImpl extends CalculatorBaseListener {

    private final Deque<Integer> stack = new LinkedList<>();

    public Integer getResult() {
        return this.stack.peek();
    }

    @Override
    public void exitMulDiv(CalculatorParser.MulDivContext ctx) {
        int right = this.stack.pop();
        int left = this.stack.pop();
        if (ctx.op.getType() == CalculatorParser.MUL) {
            this.stack.push(left * right);
        } else {
            this.stack.push(left / right);
        }
    }

    @Override
    public void exitAddSub(CalculatorParser.AddSubContext ctx) {
        int right = this.stack.pop();
        int left = this.stack.pop();
        if (ctx.op.getType() == CalculatorParser.ADD) {
            this.stack.push(left + right);
        } else {
            this.stack.push(left - right);
        }
    }

    @Override
    public void exitInt(CalculatorParser.IntContext ctx) {
        Integer number = Integer.parseInt(ctx.INT().getText());
        this.stack.push(number);
    }
}

4.测试

@Test
public void testVisitor() {
    // 将输入字符串化为输入流(也可以是 ANTLRInputStream )然后传入词法分析器
    CalculatorLexer lexer = new CalculatorLexer(CharStreams.fromString("(1+2)-(3+4)"));
    // 转换成 Token 流传入语法分析器
    CalculatorParser parser = new CalculatorParser(new CommonTokenStream(lexer));

    // 因为词法文件里最外层定义了 start 规则,这里就能从 parser.start() 解析并获取规则树
    CalculatorParser.StartContext context = parser.start();
    ParseTree parseTree = context;

    // 计算
    CalculatorVisitorImpl visitor = new CalculatorVisitorImpl();

    Integer result = visitor.visit(parseTree);
    // Integer result = parseTree.accept(visitor);

    System.out.println(result);
}

@Test
public void testListener() {
    // 将输入字符串化为输入流(也可以是 ANTLRInputStream )然后传入词法分析器
    CalculatorLexer lexer = new CalculatorLexer(CharStreams.fromString("(1+2)-(3+4)"));
    // 转换成 Token 流传入语法分析器
    CalculatorParser parser = new CalculatorParser(new CommonTokenStream(lexer));

    // 因为词法文件里最外层定义了 start 规则,这里就能从 parser.start() 解析并获取规则树
    CalculatorParser.StartContext context = parser.start();
    ParseTree parseTree = context;

    // 计算
    CalculatorListenerImpl listener = new CalculatorListenerImpl();

    ParseTreeWalker walker = new ParseTreeWalker();
    walker.walk(listener, parseTree);
    Integer result = listener.getResult();

    System.out.println(result);
}