Java使用ANTLR4解析IDL文件

发布于:2025-04-16 ⋅ 阅读:(17) ⋅ 点赞:(0)

前言

接着上篇:Java使用ANTLR4对Lua脚本语法校验,介绍了什么是ANTLR?/ 举了一个hello world示例 / ANTLR4 的工作流程。

解析IDL文件

准备两个IDL文件

V1.idl为架构的第一版约定文件

#ifndef xxx
#define xxx


module aaa {

    module bbb {

        module ccc {

            struct ddd {

                unsigned short xxx1;

                unsigned long long xxx2;

                octet xxx3;
            };

        };  // module ccc

    };  // module bbb

};  // module aaa

#endif

V2.idl为架构的第二版约定文件

#ifndef xxx
#define xxx

const short aaa = 1;
module bbb {

    struct ccc {

        @default(0) uint8 xxx;
    };

};

module bbb {

    struct ddd {

        @default(255) uint8 xxx;
    };

};
#endif

准备一个IDL Grammar文件

https://github.com/antlr/grammars-v4/tree/master/idl

maven配置

使用JDK8的注意:antlr4最高版本为4.9.3,原因如下:
来源:https://github.com/antlr/antlr4/releases/tag/4.10

Increasing minimum java version
Going forward, we are using Java 11 for the source code and the compiled .class files for the ANTLR tool. The Java runtime target, however, and the associated runtime tests use Java 8 (bumping up from Java 7).

<dependencies>
    <dependency>
        <groupId>org.antlr</groupId>
        <artifactId>antlr4-runtime</artifactId>
        <version>${antlr.version}</version>
    </dependency>
</dependencies>

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

<properties>
    <!-- https://mvnrepository.com/artifact/org.antlr/antlr4-runtime -->
    <!-- Antlr4 4.9.3 is the last version compatible with Java 8 -->
    <antlr.version>4.9.3</antlr.version>
</properties>

生成Lexer Parser Listener Visitor代码

mvn clean compile

新建实体类

不知道咋出来这个界面的,看Java使用ANTLR4对Lua脚本语法校验 > 第一个例子
在这里插入图片描述
由上图,不难看出对象间对应关系:Specification对应多个Definition(Module/TypeDeclaration),TypeDeclaration对应多个Member。

package com.baeldung.antlr.idl;

import java.util.ArrayList;
import java.util.List;

/**
 * 规范
 *
 * @author duhongming
 * @see
 * @since 1.0.0
 */
public class Specification {
    private List<Definition> definitions;

    public Specification() {
        definitions = new ArrayList<>();
    }

    public void setDefinitions(List<Definition> children) {
        definitions = children;
    }

    public List<Definition> getDefinitions() {
        return definitions;
    }

    public void addChild(Definition child) {
        definitions.add(child);
    }
}
package com.baeldung.antlr.idl;

/**
 * 定义
 *
 * @author duhongming
 * @see
 * @since 1.0.0
 */
public interface Definition {
    enum Kind {
        MODULE,
        INTERFACE,
        EXCEPTION,
        TYPE_DECLARATION,
        CONST_DECLARATION,
        ANNOTATION
    }

    boolean isIsModule();

    boolean isIsInterface();

    boolean isIsException();

    boolean isIsTypeDeclaration();

    boolean isIsConstDeclaration();

    boolean isIsAnnotation();
}
package com.baeldung.antlr.idl;

/**
 * 模块
 *
 * @author duhongming
 * @see
 * @since 1.0.0
 */
public class Module implements Definition {
    private String name;
    private Definition child;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Definition getChild() {
        return child;
    }

    public void setChild(Definition child) {
        this.child = child;
    }

    @Override
    public boolean isIsModule() {
        return true;
    }

    @Override
    public boolean isIsInterface() {
        return false;
    }

    @Override
    public boolean isIsException() {
        return false;
    }

    @Override
    public boolean isIsTypeDeclaration() {
        return false;
    }

    @Override
    public boolean isIsConstDeclaration() {
        return false;
    }

    @Override
    public boolean isIsAnnotation() {
        return false;
    }
}

package com.baeldung.antlr.idl;

import java.util.List;

/**
 * 类型声明
 *
 * @author duhongming
 * @see
 * @since 1.0.0
 */
public class TypeDeclaration implements Definition {
    private String name;
    private List<Member> members;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Member> getMembers() {
        return members;
    }

    public void setMembers(List<Member> members) {
        this.members = members;
    }

    @Override
    public boolean isIsModule() {
        return false;
    }

    @Override
    public boolean isIsException() {
        return false;
    }

    @Override
    public boolean isIsInterface() {
        return false;
    }

    @Override
    public boolean isIsTypeDeclaration() {
        return true;
    }

    @Override
    public boolean isIsConstDeclaration() {
        return false;
    }

    @Override
    public boolean isIsAnnotation() {
        return false;
    }
}

package com.baeldung.antlr.idl;

/**
 * 成员变量或方法
 *
 * @author duhongming
 * @see
 * @since 1.0.0
 */
public class Member {
    private String name;
    private String typeCode;

    public Member(String typeCode, String name) {
        super();
        this.typeCode = typeCode;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String m_name) {
        this.name = m_name;
    }

    public String getTypeCode() {
        return typeCode;
    }

    public void setTypeCode(String typeCode) {
        this.typeCode = typeCode;
    }
}

IDL解析遍历器

package com.baeldung.antlr.idl;


import com.baeldung.antlr.IDLBaseVisitor;
import com.baeldung.antlr.IDLParser;
import org.antlr.v4.runtime.tree.ParseTree;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

public class IDLVisitor extends IDLBaseVisitor {
    @Override
    public TypeDeclaration visitType_decl(IDLParser.Type_declContext ctx) {
        TypeDeclaration typeDeclaration = new TypeDeclaration();
        Optional<String> optional = Optional.ofNullable(ctx)
                .map(IDLParser.Type_declContext::struct_type)
                .map(IDLParser.Struct_typeContext::identifier)
                .map(IDLParser.IdentifierContext::ID)
                .map(ParseTree::getText);
        if (!optional.isPresent()) {
            return null;
        }
        typeDeclaration.setName(optional.get());
        List<IDLParser.MemberContext> members = ctx.struct_type().member_list().member();
        List<Member> memberList = new ArrayList<>();
        for (IDLParser.MemberContext member : members) {
            String type = member.type_spec().getText();
            String name = member.declarators().getText();
            Member m = new Member(type.replace("unsigned", ""), name);
            memberList.add(m);
        }
        typeDeclaration.setMembers(memberList);
        return typeDeclaration;
    }

    @Override
    public Module visitModule(IDLParser.ModuleContext ctx) {
        Optional<String> optional = Optional.ofNullable(ctx)
                .map(IDLParser.ModuleContext::identifier)
                .map(IDLParser.IdentifierContext::ID)
                .map(ParseTree::getText);
        if (!optional.isPresent()) {
            return null;
        }

        Module module = new Module();
        String moduleName = optional.get();
        module.setName(moduleName);

        List<IDLParser.DefinitionContext> definitions = ctx.definition();
        for (IDLParser.DefinitionContext definition : definitions) {
            Module subModule = visitModule(definition.module());
            if (Objects.nonNull(subModule)) {
                module.setChild(subModule);
            }
            TypeDeclaration typeDeclaration = visitType_decl(definition.type_decl());
            if (Objects.nonNull(typeDeclaration)) {
                module.setChild(typeDeclaration);
            }
        }
        return module;
    }

    @Override
    public Specification visitSpecification(IDLParser.SpecificationContext ctx) {
        Specification specification = new Specification();
        if (ctx != null) {
            for (IDLParser.DefinitionContext definition : ctx.definition()) {
                Module module = visitModule(definition.module());
                if (Objects.nonNull(module)) {
                    specification.addChild(module);
                }
            }
        }
        return specification;
    }
}

单元测试

package com.baeldung.antlr;

import com.baeldung.antlr.idl.IDLVisitor;
import com.baeldung.antlr.idl.Specification;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

public class IDLParserUnitTest {
    public static Specification parseIdl(InputStream inputStream) throws IOException {
        return parseIdl(CharStreams.fromStream(inputStream));
    }

    public static Specification parseIdl(CharStream charStream) {
        // 用 in 构造词法分析器 lexer,词法分析的作用是将字符聚集成单词或者符号
        Lexer lexer = new IDLLexer(charStream);
        // 用词法分析器 lexer 构造一个记号流 tokens
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        // 再使用 tokens 构造语法分析器 parser,至此已经完成词法分析和语法分析的准备工作
        IDLParser parser = new IDLParser(tokens);
        ParseTree tree = parser.specification();
        IDLVisitor visitor = new IDLVisitor();
        return (Specification) visitor.visit(tree);
    }

    @Test
    public void testV1() throws IOException {
        InputStream inputStream = Files.newInputStream(Paths.get("src/test/resources/V1.idl"));
        Specification specification = parseIdl(inputStream);
        System.out.println(specification);
    }

    @Test
    public void testV2() throws IOException {
        InputStream inputStream = Files.newInputStream(Paths.get("src/test/resources/V2.idl"));
        Specification specification = parseIdl(inputStream);
        System.out.println(specification);
    }
}

最终目录情况及单元测试情况如下:
在这里插入图片描述

参考

https://github.com/eProsima/IDL-Parser