ANTLR巨型教程

ANTLR是一个强大的解析器生成器,用于构建词法分析器和解析器,以识别和转换文本。本教程详细介绍了ANTLR的基础知识,包括设置ANTLR、在不同语言中使用ANTLR、测试解析器以及解决歧义等。从初级到高级,覆盖了ANTLR的广泛用例,如解析HTML、聊天应用程序和电子表格。在完成教程后,读者将能够创建自定义解析器,处理各种格式和语言,以及处理错误和测试语法。
摘要由CSDN通过智能技术生成

解析器是功能强大的工具,使用ANTLR,您可以编写可用于多种不同语言的各种解析器。

在本完整的教程中,我们将要:

  • 解释基础 :解析器是什么,解析器可以用于什么
  • 了解如何设置要从Javascript,Python,Java和C#中使用的ANTLR
  • 讨论如何测试解析器
  • 展示ANTLR中最先进,有用的功能 :您将学到解析所有可能的语言所需的一切
  • 显示大量示例

也许您已经阅读了一些过于复杂或过于局部的教程,似乎以为您已经知道如何使用解析器。 这不是那种教程。 我们只希望您知道如何编码以及如何使用文本编辑器或IDE。 而已。

在本教程的最后:

  • 您将能够编写一个解析器以识别不同的格式和语言
  • 您将能够创建构建词法分析器和解析器所需的所有规则
  • 您将知道如何处理遇到的常见问题
  • 您将了解错误,并且将知道如何通过测试语法来避免错误。

换句话说,我们将从头开始,到结束时,您将学到所有可能需要了解ANTLR的知识。

ANTLR Mega Tutorial Giant目录列表

什么是ANTLR?

ANTLR是解析器生成器,可帮助您创建解析器的工具。 解析器获取一段文本并将其转换为一个组织化的结构 ,例如抽象语法树(AST)。 您可以将AST看作是描述代码内容的故事,也可以看作是通过将各个部分放在一起而创建的逻辑表示。

欧氏算法的AST的图形表示

获得AST所需要做的事情:

  1. 定义词法分析器和语法分析器
  2. 调用ANTLR:它将以您的目标语言(例如Java,Python,C#,Javascript)生成一个词法分析器和解析器
  3. 使用生成的词法分析器和解析器:调用它们并传递代码以进行识别,然后它们会返回给您AST

因此,您需要首先为要分析的事物定义一个词法分析器和解析器语法。 通常,“事物”是一种语言,但它也可以是数据格式,图表或任何以文本表示的结构。

正则表达式不够吗?

如果您是典型的程序员,您可能会问自己: 为什么我不能使用正则表达式 ? 正则表达式非常有用,例如当您想在文本字符串中查找数字时,它也有很多限制。

最明显的是缺乏递归:除非您为每个级别手动编码,否则您无法在另一个表达式中找到一个(正则)表达式。 很快就无法维持的事情。 但是更大的问题是它并不是真正可扩展的:如果您只将几个正则表达式放在一起,就将创建一个脆弱的混乱,将很难维护。

使用正则表达式不是那么容易

您是否尝试过使用正则表达式解析HTML? 这是一个可怕的想法,因为您冒着召唤克苏鲁的危险,但更重要的是, 它实际上并没有奏效 。 你不相信我吗 让我们看一下,您想要查找表的元素,因此尝试像这样的常规扩展: <table>(.*?)</table> 。 辉煌! 你做到了! 除非有人向其表中添加诸如styleid类的属性。 没关系,您执行<table.*?>(.*?)</table> ,但实际上您关心表中的数据,因此您需要解析trtd ,但是它们已满标签。

因此,您也需要消除这种情况。 而且甚至有人甚至敢使用<!—我的评论&gtl->之类的评论。 注释可以在任何地方使用,并且使用正则表达式不容易处理。 是吗?

因此,您禁止Internet使用HTML中的注释:已解决问题。

或者,您也可以使用ANTLR,对您而言似乎更简单。

ANTLR与手动编写自己的解析器

好的,您确信需要一个解析器,但是为什么要使用像ANTLR这样的解析器生成器而不是构建自己的解析器呢?

ANTLR的主要优势是生产率

如果您实际上一直在使用解析器,因为您的语言或格式在不断发展,则您需要能够保持步伐,而如果您必须处理实现a的细节,则无法做到这一点。解析器。 由于您不是为了解析而解析,因此您必须有机会专注于实现目标。 而ANTLR使得快速,整洁地执行此操作变得更加容易。

其次,定义语法后,您可以要求ANTLR生成不同语言的多个解析器。 例如,您可以使用C#获得一个解析器,而使用Javascript获得一个解析器,以在桌面应用程序和Web应用程序中解析相同的语言。

有人认为,手动编写解析器可以使其更快,并且可以产生更好的错误消息。 这有些道理,但以我的经验,ANTLR生成的解析器总是足够快。 如果确实需要,您可以调整语法并通过处理语法来提高性能和错误处理。 只要对语法感到满意,就可以这样做。

目录还是可以的

两个小注意事项:

  • 本教程配套存储库中,您将找到所有带有测试的代码,即使我们在本文中没有看到它们
  • 示例将使用不同的语言,但是知识通常适用于任何语言

设定

  1. 设定ANTLR
  2. Javascript设置
  3. Python设置
  4. Java设置
  5. C#设定

初学者

  1. 词法分析器
  2. 创建语法
  3. 设计数据格式
  4. Lexer规则
  5. 解析器规则
  6. 错误与调整

中级

  1. 用Javascript设置聊天项目
  2. Antlr.js
  3. HtmlChatListener.js
  4. 与听众合作
  5. 用语义谓词解决歧义
  6. 用Python继续聊天
  7. 与侦听器配合使用的Python方法
  8. 用Python测试
  9. 解析标记
  10. 词汇模式
  11. 解析器文法

高级

  1. Java中的标记项目
  2. 主App.java
  3. 使用ANTLR转换代码
  4. 转换代码的喜悦与痛苦
  5. 高级测试
  6. 处理表情
  7. 解析电子表格
  8. C#中的电子表格项目
  9. Excel注定了
  10. 测试一切

结束语

  1. 技巧和窍门
  2. 结论

设定

在本节中,我们准备使用ANTLR的开发环境:解析器生成器工具,每种语言的支持工具和运行时。

1.设置ANTLR

ANTLR实际上由两个主要部分组成:用于生成词法分析器和解析器的工具,以及运行它们所需的运行时。

语言工程师将只需要您使用该工具,而运行时将包含在使用您的语言的最终软件中。

无论您使用哪种语言,该工具始终是相同的:这是开发计算机上所需的Java程序。 尽管每种语言的运行时都不同,但是开发人员和用户都必须可以使用它。

该工具的唯一要求是您已经安装了至少Java 1.7 。 要安装Java程序,您需要从官方站点下载最新版本,当前版本为:

http://www.antlr.org/download/antlr-4.6-complete.jar

使用说明

  1. 将下载的工具复制到通常放置第三方Java库的位置(例如/usr/local/libC:\Program Files\Java\lib
  2. 将工具添加到您的CLASSPATH 。 将其添加到您的启动脚本中(例如.bash_profile
  3. (可选)还在您的启动脚本中添加别名,以简化ANTLR的使用
在Linux / Mac OS上执行说明
// 1.
sudo cp antlr-4.6-complete.jar /usr/local/lib/
// 2. and 3.
// add this to your .bash_profile
export CLASSPATH=".:/usr/local/lib/antlr-4.6-complete.jar:$CLASSPATH"
// simplify the use of the tool to generate lexer and parser
alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.6-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
// simplify the use of the tool to test the generated code
alias grun='java org.antlr.v4.gui.TestRig'
在Windows上执行说明
// 1.
Go to System Properties dialog > Environment variables
-> Create or append to the CLASSPATH variable
// 2. and 3. Option A: use doskey
doskey antlr4=java org.antlr.v4.Tool $*
doskey grun =java org.antlr.v4.gui.TestRig $*
// 2. and 3. Option B: use batch files
// create antlr4.bat
java org.antlr.v4.Tool %*
// create grun.bat
java org.antlr.v4.gui.TestRig %*
// put them in the system path or any of the directories included in %path%

典型工作流程

使用ANTLR时,首先要编写语法 ,即扩展名为.g4的文件,其中包含要分析的语言规则。 然后,您可以使用antlr4程序来生成程序将实际使用的文件,例如词法分析器和解析器。

antlr4 <options> <grammar-file-g4>

运行antlr4时可以指定几个重要选项。

首先,您可以指定目标语言,以Python或JavaScript或任何其他不同于Java的目标(默认语言)生成解析器。 其他的用于生成访问者和侦听器(不要担心,如果您不知道这些是什么,我们将在后面进行解释)。

缺省情况下,仅生成侦听器,因此要创建访问者,请使用-visitor命令行选项,如果不想生成-no-listener则使用-no-listener listener。 也有相反的选项-no-visitor-listener ,但它们是默认值。

antlr4 -visitor <Grammar-file>

您可以使用一个名为TestRig (的小实用工具来优化语法测试TestRig (尽管,如我们所见,它通常是grun的别名)。

grun <grammar-name> <rule-to-test> <input-filename(s)>

文件名是可选的,您可以代替分析在控制台上键入的输入。

如果要使用测试工具,则即使您的程序是用另一种语言编写的,也需要生成Java解析器。 这可以通过选择与antlr4不同的选项来antlr4

手动测试语法初稿时,Grun非常有用。 随着它变得更加稳定,您可能希望继续进行自动化测试(我们将看到如何编写它们)。

Grun还有一些有用的选项: -tokens ,显示检测到的令牌, -gui生成AST的图像。

2. Javascript设置

您可以将语法与Javascript文件放在同一文件夹中。 包含语法的文件必须具有与语法相同的名称,该名称必须在文件顶部声明。

在下面的示例中,名称为Chat ,文件为Chat.g4

通过使用ANTLR4 Java程序指定正确的选项,我们可以创建相应的Javascript解析器。

antlr4 -Dlanguage=JavaScript Chat.g4

请注意,该选项区分大小写,因此请注意大写的“ S”。 如果您输入有误,则会收到类似以下的消息。

error(31):  ANTLR cannot generate Javascript code as of version 4.6

ANTLR可以与node.js一起使用,也可以在浏览器中使用。 对于浏览器,您需要使用webpackrequire.js 。 如果您不知道如何使用两者之一,可以查阅官方文档寻求帮助或阅读网络上的antlr教程。 我们将使用node.js ,只需使用以下标准命令即可为之安装ANTLR运行时。

npm install antlr4

3. Python设置

有了语法后,请将其放在与Python文件相同的文件夹中。 该文件必须具有与语法相同的名称,该名称必须在文件顶部声明。 在下面的示例中,名称为Chat ,文件为Chat.g4

通过使用ANTLR4 Java程序指定正确的选项,我们可以简单地创建相应的Python解析器。 对于Python,您还需要注意Python的版本2或3。

antlr4 -Dlanguage=Python3 Chat.g4

PyPi提供了运行时,因此您可以使用pio进行安装。

pip install antlr4-python3-runtime

同样,您只需要记住指定正确的python版本。

4. Java设定

要使用ANTLR设置我们的Java项目,您可以手动执行操作。 或者您可以成为文明的人并使用Gradle或Maven。

另外,您可以在IDE中查看ANTLR插件。

4.1使用Gradle进行Java设置

这就是我通常设置Gradle项目的方式。

我使用Gradle插件调用ANTLR,也使用IDEA插件生成IntelliJ IDEA的配置。

dependencies {
  antlr "org.antlr:antlr4:4.5.1"
  compile "org.antlr:antlr4-runtime:4.5.1"
  testCompile 'junit:junit:4.12'
}
 
generateGrammarSource {
    maxHeapSize = "64m"
    arguments += ['-package', 'me.tomassetti.mylanguage']
    outputDirectory = new File("generated-src/antlr/main/me/tomassetti/mylanguage".toString())
}
compileJava.dependsOn generateGrammarSource
sourceSets {
    generated {
        java.srcDir 'generated-src/antlr/main/'
    }
}
compileJava.source sourceSets.generated.java, sourceSets.main.java
 
clean{
    delete "generated-src"
}
 
idea {
    module {
        sourceDirs += file("generated-src/antlr/main")
    }
}

我将语法放在src / main / antlr /下 ,并且gradle配置确保它们在与程序包相对应的目录中生成。 例如,如果我希望解析器位于包me.tomassetti.mylanguage中,则必须将其生成到generate-src / antlr / main / me / tomassetti / mylanguage中

此时,我可以简单地运行:

# Linux/Mac
./gradlew generateGrammarSource
 
# Windows
gradlew generateGrammarSource

然后我从语法中生成了词法分析器和解析器。

然后我也可以运行:

# Linux/Mac
./gradlew idea
 
# Windows
gradlew idea

我已经准备好要打开一个IDEA项目。

4.2使用Maven进行Java设置

首先,我们将在POM中指定需要antlr4-runtime作为依赖项。 我们还将使用Maven插件通过Maven运行ANTLR。

我们还可以指定是否使用ANTLR来生成访问者或侦听器。 为此,我们定义了几个相应的属性。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
 
  [..]
 
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <antlr4.visitor>true</antlr4.visitor>
    <antlr4.listener>true</antlr4.listener>
  </properties>  
 
  <dependencies>
    <dependency>
      <groupId>org.antlr</groupId>
      <artifactId>antlr4-runtime</artifactId>
      <version>4.6</version>
    </dependency>
   [..]
  </dependencies>
 
  <build>
    <plugins>
      [..]
      <!-- Plugin to compile the g4 files ahead of the java files
           See https://github.com/antlr/antlr4/blob/master/antlr4-maven-plugin/src/site/apt/examples/simple.apt.vm
           Except that the grammar does not need to contain the package declaration as stated in the documentation (I do not know why)
           To use this plugin, type:
             mvn antlr4:antlr4
           In any case, Maven will invoke this plugin before the Java source is compiled
        -->
      <plugin>
        <groupId>org.antlr</groupId>
        <artifactId>antlr4-maven-plugin</artifactId>
        <version>4.6</version>                
        <executions>
          <execution>
            <goals>
              <goal>antlr4</goal>
            </goals>            
          </execution>
        </executions>
      </plugin>
      [..]
    </plugins>
  </build>
</project>

现在,您必须将语法的* .g4文件放在src/main/antlr4/me/tomassetti/examples/MarkupParser.

编写完语法后,您只需运行mvn package ,所有奇妙的事情就会发生:ANTLR被调用,它会生成词法分析器和解析器,并将它们与其余代码一起编译。

// use mwn to generate the package
mvn package

如果您从未使用过Maven,则可以查看Java目标的官方ANTLR文档Maven网站来入门。

使用Java开发ANTLR语法有一个明显的优势:有多个IDE的插件,这是该工具的主要开发人员实际使用的语言。 因此,它们是org.antlr.v4.gui.TestRig类的工具,可以轻松地集成到您的工作流中,如果您想轻松地可视化输入的AST,这些工具将非常有用。

5. C#设置

支持.NET Framework和Mono 3.5,但不支持.NET Core。 我们将使用Visual Studio创建我们的ANTLR项目,因为由C#目标的同一作者为Visual Studio创建了一个不错的扩展,称为ANTLR语言支持 。 您可以通过进入工具->扩展和更新来安装它。 当您构建项目时,此扩展将自动生成解析器,词法分析器和访问者/侦听器。

此外,该扩展名将允许您使用众所周知的菜单添加新项目来创建新的语法文件。 最后但并非最不重要的一点是,您可以在每个语法文件的属性中设置用于生成侦听器/访问者的选项。

另外,如果您更喜欢使用编辑器,则需要使用常规的Java工具生成所有内容。 您可以通过指示正确的语言来做到这一点。 在此示例中,语法称为“电子表格”。

antlr4 -Dlanguage=CSharp Spreadsheet.g4

请注意,CSharp中的“ S”为大写。

您仍然需要项目的ANTLR4运行时,并且可以使用良好的nu'nuget安装它。

初学者

在本节中,我们为使用ANTLR奠定了基础:什么是词法分析器和解析器,在语法中定义它们的语法以及可用于创建它们的策略。 我们还将看到第一个示例,以展示如何使用所学知识。 如果您不记得ANTLR的工作原理,可以回到本节。

6.词法分析器

在研究解析器之前,我们需要首先研究词法分析器,也称为令牌化器。 它们基本上是解析器的第一个垫脚石,当然ANTLR也允许您构建它们。 词法分析器将各个字符转换为令牌 (解析器用来创建逻辑结构的原子)。

想象一下,此过程适用于自然语言,例如英语。 您正在阅读单个字符,将它们放在一起直到形成一个单词,然后将不同的单词组合成一个句子。

让我们看下面的示例,并想象我们正在尝试解析数学运算。

437 + 734

词法分析器扫描文本,然后找到“ 4”,“ 3”,“ 7”,然后找到空格“”。 因此,它知道第一个字符实际上代表一个数字。 然后,它找到一个“ +”符号,因此知道它代表一个运算符,最后找到另一个数字。

它怎么知道的? 因为我们告诉它。

/*
 * Parser Rules
 */
 
operation  : NUMBER '+' NUMBER ;
 
/*
 * Lexer Rules
 */
 
NUMBER     : [0-9]+ ;
 
WHITESPACE : ' ' -> skip ;

这不是一个完整的语法,但是我们已经可以看到词法分析器规则全部为大写,而解析器规则全部为小写。 从技术上讲,关于大小写的规则仅适用于其名称的第一个字符,但通常为了清楚起见,它们全都为大写或小写。

规则通常按以下顺序编写:首先是解析器规则,然后是词法分析器规则,尽管在逻辑上它们是按相反的顺序应用的。 同样重要的是要记住, 词法分析器规则是按照它们出现的顺序进行分析的 ,它们可能是不明确的。

典型的例子是标识符:在许多编程语言中,它可以是任何字母字符串,但是某些组合(例如“ class”或“ function”)被禁止,因为它们表示一个classfunction 。 因此,规则的顺序通过使用第一个匹配项来解决歧义,这就是为什么首先定义标识关键字(例如函数)的令牌,而最后一个用于标识符的令牌的原因。

规则的基本语法很容易: 有一个名称,一个冒号,该规则的定义和一个终止分号

NUMBER的定义包含一个典型的数字范围和一个“ +”符号,表示允许一个或多个匹配项。 这些都是我认为您熟悉的非常典型的指示,否则,您可以阅读有关正则表达式的语法的更多信息。

最后,最有趣的部分是定义WHITESPACE令牌的词法分析器规则。 这很有趣,因为它显示了如何指示ANTLR忽略某些内容。 考虑一下忽略空白如何简化解析器规则:如果我们不能说忽略WHITESPACE,则必须将其包括在解析器的每个子规则之间,以便用户在所需的地方放置空格。 像这样:

operation  : WHITESPACE* NUMBER WHITESPACE* '+' WHITESPACE* NUMBER;

注释通常也是如此:它们可以出现在任何地方,并且我们不想在语法的每个部分中都专门处理它们,因此我们只是忽略它们(至少在解析时)。

7.创建语法

现在,我们已经了解了规则的基本语法,下面我们来看看定义语法的两种不同方法:自顶向下和自底向上。

自上而下的方法

这种方法包括从以您的语言编写的文件的一般组织开始。

文件的主要部分是什么? 他们的顺序是什么? 每个部分中包含什么?

例如,Java文件可以分为三个部分:

  • 包装声明
  • 进口
  • 类型定义

当您已经知道要为其设计语法的语言或格式时,此方法最有效。 具有良好理论背景的人或喜欢从“大计划”入手的人可能会首选该策略。

使用这种方法时,首先要定义代表整个文件的规则。 它可能会包括其他规则,以代表主要部分。 然后,您定义这些规则,然后从最一般的抽象规则过渡到底层的实用规则。

自下而上的方法

自下而上的方法包括首先关注小元素&#

  • 3
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值