antlr4 入门小结

1. 包的引入(使用maven):

<plugin>
    <groupId>org.antlr</groupId>
    <artifactId>antlr4-maven-plugin</artifactId>
    <version>4.9.2</version>
    <configuration>
        <sourceDirectory>src/main/antlr</sourceDirectory>
        <outputDirectory>src/main/java/net/rainforwind/antlr/gen</outputDirectory>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>antlr4</goal>
            </goals>
        </execution>
    </executions>
</plugin>

2. 代码生成(需要第三步的词法语法定义)

mvn antlr4:antlr4

3. 词法语法定义示例文件:

DemoLexer.g4

lexer grammar DemoLexer;

@header {
package net.rainforwind.antlr.gen;
}

WS: [ \t\r\n]+ -> skip ;
ID: [A-Za-z_] [A-Za-z0-9_]* ;
//String: '\'' (~'\'')* '\'' ;
Lquote: '\'' -> more, pushMode(STR) ;
Comma: ',';
Lparen: '(';
Rparen: ')';

mode STR ;
String: '\'' -> popMode ;
TEXT: . -> more ;

DemoParser.g4

parser grammar DemoParser;

options { tokenVocab=DemoLexer; }

@header {
package net.rainforwind.antlr.gen;
}

func: funcName Lparen (arg moreArg*)? Rparen ;
funcName: ID ;
arg: func | string ;
string: String;
moreArg: Comma arg ;

也可以在一个文件里面进行词法语法的混合声明,对应上面两个文件,文件名叫 Demo.g4,生成的代码会自动加Lexer/Parser后缀,结果和上面保持一致。

其中的文件开头改为

grammar Demo;
@header {
package net.rainforwind.antlr.gen;
}

 

生成的结果文件示例:

DemoLexer.java                      // 词法分析类
DemoLexer.interp                    // 词法符号表
DemoLexer.tokens                    // token表
DemoParser.java                      // 语法分析类
DemoParser.interp                    // 语法符号表
DemoParser.tokens                    // token表(同上)
DemoParserBaseListener.java     // 预发分析监听接口缺省实现(空实现)
DemoParserListner.java             // 语法分析监听接口

 

4. 生产Java类的使用:

先上示例:

@Test
public void testLexing() {
    DemoLexer lexer = new DemoLexer(CharStreams.fromString("hello('world', foo('bar'))"));
    lexer.addErrorListener(new BaseErrorListener(){
        @Override
        public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg,
                                RecognitionException e) {
            throw new RuntimeException("lexer syntax error.");
        }
    });
    DemoParser parser = new DemoParser(new CommonTokenStream(lexer));
    parser.addParseListener(new DemoParserBaseListener(){
        @Override
        public void enterFuncName(FuncNameContext ctx) {
            super.enterFuncName(ctx);
            System.out.println("enter funcName: " + ctx.toString());
        }
    });
    try {
        FuncContext r = parser.func();
        System.out.println(contextToString(r));
    } catch (RuntimeException e) {
        System.err.println(e.getMessage());
    }
}

private String contextToString(ParserRuleContext ctx) {
    StringBuilder result = new StringBuilder(getContextPrefix(ctx));
    result.append("(");
    boolean first = true;
    for (ParseTree child : ctx.children) {
        if (first) {
            first = false;
        } else {
            result.append(",");
        }
        result.append(contextToString(child));
    }
    result.append(")");
    return result.toString();
}

private String contextToString(ParseTree tree) {
    if (tree instanceof TerminalNode) {
        return "" + ((TerminalNode) tree).getSymbol().getText() + "";
    } else if (tree instanceof ParserRuleContext) {
        StringBuilder result = new StringBuilder(getContextPrefix((ParserRuleContext) tree)).append("(");

        boolean first = true;
        for (ParseTree child : ((ParserRuleContext) tree).children) {
            if (first) {
                first = false;
            } else {
                result.append(",");
            }
            result.append(contextToString(child));
        }
        result.append(")");
        return result.toString();
    } else {
        return "ERROR: tree type not handled: " + tree.getClass();
    }
}

private String getContextPrefix(ParserRuleContext ctx) {
    String className = ctx.getClass().getSimpleName();
    return className.substring(0, className.length() - 7);
}

示例输出:

enter funcName: [10]
enter funcName: [10 25 32 13]
Func(FuncName(hello),(,Arg(String('world')),MoreArg(,,Arg(Func(FuncName(foo),(,Arg(String('bar')),)))),))

 

说明:

  • Lexer实例化时使用CharStreams来转化输入,Parser实例化时使用CommonTokenStream来引入lexer实例。
  • Parser中的任何一个规则都对应生产了一个方法,直接调用该方法就执行了语法解析功能,返回一个对应类型的Context对象,即为结果语法树的跟。
  • 如果要在解析过程中进行回调,实现Listener接口中对应语法规则的enter/exit方法即可。
  • 解析错误默认会忽略,所以如果要拦截错误语法,则需要添加ErrorListener监听器来处理,如抛出异常。
  • more, mode, 属性, returns,type,channel 等语法参考官方文档,因为文档写的比较绕,所以补充几句。
    • more 用在词法定义中,代表本条规则不规约,将其匹配到的内容合并到后续执行中第一个非more且非skip的规则结果中。
    • mode、pushMode、PopMode 用在词法定义中,代表模式切换(缺省为DEFAULT_MODE
    • 语法规则的头部(冒号之前的部分)不止可以有规则名称,还可以声明属性、返回值(returns)、局部变量(locals),属性和返回值可以在其他规则引用该规则是使用。
    • 词法分析中,type可以声明实际规约到哪个token,在一个token有多种case的情况下,就不需要用或的关系来一次性写全,而是可以随时补充。
    • channel,将分析结果发送到其他通道,可以处理注释等内容。

    

posted @ 2021-10-12 21:42  rainforwind  阅读(801)  评论(0)    收藏  举报