Antlr

作者: 宇宙湾 | 来源:发表于2019-05-13 11:14 被阅读0次

    什么是 Antlr?

    ANTLR™ (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing, or translating structured text or binary files. It's widely used to build languages, tools, and frameworks. From a grammar, ANTLR generates a parser that can build and walk parse trees.

    为什么要有 Antlr?

    简易性

    可以通过断言(Predicate)解决识别冲突
     支持动作(Action)和返回值(Return Value)
     它可以根据输入自动生成语法树,并可视化的展示出来

    模块化

    复杂情况下,需要基于语法树遍历(walking the tree)生成目标代码
     Embeded action 将处理代码跟语法描述混合起来,语法复杂时使语法文件臃肿
     语法可能经常需要修改,而语法的主要表达式却不会变动,因此,Antlr 将语法识别与转换、生成(目标代码)的处理分离

    Antlr 工作机制

    词法分析器(Lexer)

    分析量化字符流,翻译成离散的字符组(也就是一堆 Token), 包括关键字,标识符,符号(symbols)和操作符,以供语法分析器使用

    语法分析器(Parser)

    将 Tokens 组织起来,并转换成为目标语言语法(默认是 Java)定义所允许的序列

    树分析器(Tree Parser)

    用于对语法分析生成的抽象语法树进行遍历,并在先序经过每个树节点的时候,进行一些定制操作

    Antlr 运作流程

    安装

    # Download
    $ cd /usr/local/lib
    $ curl -O https://www.antlr.org/download/antlr-4.7.2-complete.jar
    
    # Add antlr-4.7.2-complete.jar to your CLASSPATH
    # Create aliases for the ANTLR Tool, and TestRig
    $ vim ~/.bashrc
      export CLASSPATH=".:/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH"
      alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
      alias grun='java -Xmx500M -cp "/usr/local/lib/antlr-4.7.2-complete.jar:$CLASSPATH" org.antlr.v4.gui.TestRig'
    
    $ source ~/.bashrc
    

    编写源文件

    $ vim Benedict.g4
      // Define a grammar called Benedict
      grammar Benedict;
      r    : 'hello' Name ;         // match keyword hello followed by an identifier
      Name : [a-z]+ ;               // match lower-case identifiers
      WS   : [ \t\r\n]+ -> skip ;   // skip spaces, tabs, newlines
    

    词法语法分析

    $ antlr4 Benedict.g4
    $ ll
      total 36K
      -rw-r--r-- 1 benedictjin wheel   93  1 15 14:47 Benedict.g4
      -rw-r--r-- 1 benedictjin wheel  321  1 15 14:47 Benedict.interp
      -rw-r--r-- 1 benedictjin wheel   38  1 15 14:47 Benedict.tokens
      -rw-r--r-- 1 benedictjin wheel 1.3K  1 15 14:47 BenedictBaseListener.java
      -rw-r--r-- 1 benedictjin wheel 1.3K  1 15 14:47 BenedictLexer.interp
      -rw-r--r-- 1 benedictjin wheel 3.7K  1 15 14:47 BenedictLexer.java
      -rw-r--r-- 1 benedictjin wheel   38  1 15 14:47 BenedictLexer.tokens
      -rw-r--r-- 1 benedictjin wheel  569  1 15 14:47 BenedictListener.java
      -rw-r--r-- 1 benedictjin wheel 3.9K  1 15 14:47 BenedictParser.java
    

    编译

    $ javac Benedict*.java
    $ ll
      total 60K
      -rw-r--r-- 1 benedictjin wheel   93  1 15 14:47  Benedict.g4
      -rw-r--r-- 1 benedictjin wheel  321  1 15 14:47  Benedict.interp
      -rw-r--r-- 1 benedictjin wheel   38  1 15 14:47  Benedict.tokens
      -rw-r--r-- 1 benedictjin wheel  822  1 15 14:48  BenedictBaseListener.class
      -rw-r--r-- 1 benedictjin wheel 1.3K  1 15 14:47  BenedictBaseListener.java
      -rw-r--r-- 1 benedictjin wheel 3.8K  1 15 14:48  BenedictLexer.class
      -rw-r--r-- 1 benedictjin wheel 1.3K  1 15 14:47  BenedictLexer.interp
      -rw-r--r-- 1 benedictjin wheel 3.7K  1 15 14:47  BenedictLexer.java
      -rw-r--r-- 1 benedictjin wheel   38  1 15 14:47  BenedictLexer.tokens
      -rw-r--r-- 1 benedictjin wheel  329  1 15 14:48  BenedictListener.class
      -rw-r--r-- 1 benedictjin wheel  569  1 15 14:47  BenedictListener.java
      -rw-r--r-- 1 benedictjin wheel  896  1 15 14:48 'BenedictParser$SayContext.class'
      -rw-r--r-- 1 benedictjin wheel 4.4K  1 15 14:48  BenedictParser.class
      -rw-r--r-- 1 benedictjin wheel 3.9K  1 15 14:47  BenedictParser.java
    
    $ grun Benedict r -tree
      hello benedict
      (r hello benedict)
    

    可视化

    $ grun Benedict r -gui
      hello benedict
      ^D
    

    踩到的坑

    给每次 antlr 的结果文件添加 package

    在 .g4 中使用 @header{ ... } 添加

    @header{
        package com.yuzhouwan.antlr;
    }
    

    确定节点与子节点之间的关系

    使用 if-elseexit/enter() 中判别,节点与子节点之间的关系,确定是否是 operator

    say
        : 'say' Colon Name    #Colon
        ;
    
    `benedictListener` 将会得到一个定位粒度为 Colon 的访问节点
    
    /**
     * Enter a parse tree produced by the {@code Colon}
     * labeled alternative in {@link benedictParser#say}.
     * @param ctx the parse tree
     */
    void enterColon(benedictParser.ColonContext ctx);
    /**
     * Exit a parse tree produced by the {@code Colon}
     * labeled alternative in {@link benedictParser#say}.
     * @param ctx the parse tree
     */
    void exitColon(benedictParser.ColonContext ctx);
    

    欢迎直接访问我的个人博客,阅读效果更佳:https://yuzhouwan.com/posts/55501/

    相关文章

      网友评论

        本文标题:Antlr

        本文链接:https://www.haomeiwen.com/subject/jchlaqtx.html