Antlr4 g4通配符配置

maijun 发表于 2021/04/29 10:56:03 2021/04/29
【摘要】 在Antlr4中,定义文法规则时,通配符通常会贯穿文法定义始终,包括 关键字、字符串、注释注解、变量 等,都离不开通配符的使用。在通配符使用中,有贪婪匹配和非贪婪匹配两种方式,本文简单通过示例介绍这两种匹配方式,并介绍了因为通配符使用导致的部分问题的处理。

在Antlr4中,定义文法规则时,通配符通常会贯穿文法定义始终,包括 关键字、字符串、注释注解、变量 等,都离不开通配符的使用。在通配符使用中,有贪婪匹配和非贪婪匹配两种方式,本文将简单通过示例介绍这两种匹配方式。

1. 贪婪匹配和非贪婪匹配

在Antlr4中,使用 g4 文件定义文法规则时,使用的是EBNF范式。EBNF范式,在处理 (...)?,(...)* 和 (...)+ 时,是贪婪匹配的,就是尽可能地匹配更多的字符。当然,也可以设置只匹配首次匹配到的数据后即返回,这种称为非贪婪匹配。可以通过在原来的 贪婪匹配的后面,加上一个 ? 来表示非贪婪匹配。下面将通过两个例子,来描述贪婪匹配和非贪婪匹配的区别。

(1) 字符串规则

Antlr4 g4 规则(贪婪匹配)如下:

s : STRING+ ;
STRING : ('hua'|'wei')*'wei';

如上面两行内容,STRING 是一个 Lexer,s 是一个 Parser,其中,STRING 需要以字符串 wei 结束,('hua' | 'wei') 表示是 字符串 'hua' 或者 'wei',因为使用的是 ()*,所以是贪婪匹配(*表示匹配0次或多次)。考虑下面的三个字符串:

huaweiweiwei
huahuaweiweihuawei
huaweiabc

通过贪婪匹配,可以匹配到的结果是:

huaweiweiwei
huahuaweiweihuawei
huawei

如下图所示:

当我们将Antlr4 g4规则修改为非贪婪匹配:

s : STRING+ ;
STRING : ('hua'|'wei')*?'wei';

如上,所示,对于上面的文法,我们可以匹配到多个,还是上面的文法,匹配到的结果如下:

huawei wei wei
huahuawei wei huawei
huawei

如上所示,第一个字符串 huaweiweiwei 经过上面的Antlr4规则匹配,匹配成了三个字符串,分别是 huawei、wei、wei,因为 基于非贪婪匹配。在 STRING 中,首先匹配到 huawei,然后在剩下的 weiwei 里面,再匹配到 wei(前面*没有匹配到数据),然后在剩下的 wei 里面,又匹配到 wei,结束。

(2) Comment语法

对于 Comment 语法,我们首先介绍 非贪婪匹配使用,g4定义如下:

c : COMMENT+ ;
COMMENT : '/*' .*? '*/';

test如上,COMMENT 以 /* 开头,以 */ 结尾,中间 .* 表示匹配 0个或者多个字符,问号表示最小匹配(非贪婪),如下面的例子:

/* abc */ ... /* dfas */

表示两个注释,中间 ... 表示其他内容,匹配到的结果如下:

如上,的确匹配到两个注释,中间的 ... 因为没有其他规则可以匹配,因此会报错,但是不影响我们识别。

但是,如果我们修改为贪婪匹配呢?如下所示的语法定义:

c : COMMENT+ ;
COMMENT : '/*' .* '*/';

可以看到匹配的结果如下:

将两个注释及中间的内容,全部合并成了一个注释,其实这种匹配,是不合理的。如果这样设置,那么在代码中,多个注释合并,多个注释中间的代码,不会认为是有效代码。

2. 注意及说明

2.1 正确使用贪婪匹配和非贪婪匹配

在写 g4 文件时,我们需要基于代码解析场景,正确使用贪婪匹配和非贪婪匹配,在 Antlr4 中,绝大部分场景,都是贪婪匹配的,例如 字符串生成、变量名、标识符、数字等,但是也有一些场景,是非贪婪匹配的,例如上面介绍到的 注释匹配。

Antlr4在基于g4生成解析的SDK的时候,在可能有问题的场景中,会给出提示,如下面的提示:

$ java -jar antlr-4.9.2-complete.jar -Dlanguage=Java -visitor Scala.g4
warning(131): Scala.g4:1351:25: greedy block ()* contains wildcard; the non-greedy syntax ()*? may be preferred

原因是,在 Scala.g4 文件(github Scala.g4)里面,对应的地方,Antlr4认为应该是非贪婪匹配,但是此处的语法是贪婪匹配:

2.2 一个错误场景的处理

这里,介绍一个,我在使用Antlr4中,因为上面的warning导致无法正确生成词法和语法的解析代码的问题。

上面,在 2.1节中,提示了一个 warning,但是,词法和语法的解析代码还是会正常生成,但是我在使用Antlr4 Maven插件时,无法生成 ScalaLexer.java 文件,当时,我 Antlr4 Maven插件的设置如下:

<build>
    <plugins>
      <plugin>
        <groupId>org.antlr</groupId>
        <artifactId>antlr4-maven-plugin</artifactId>
        <version>4.9.2</version>
        <executions>
          <execution>
            <id>antlr</id>
            <goals>
              <goal>antlr4</goal>
            </goals>
            <phase>generate-sources</phase>
          </execution>
        </executions>
        <configuration>
          <sourceDirectory>${basedir}/src/main/resources</sourceDirectory>
          <outputDirectory>${project.build.directory}/generated-sources/antlr4/zmj/test/antlr4/parser</outputDirectory>
          <listener>true</listener>
          <visitor>true</visitor>
          <treatWarningsAsErrors>true</treatWarningsAsErrors>
        </configuration>
      </plugin>
    </plugins>
  </build>

原因就在于:在配置中,我设置了 <treatWarningsAsErrors>true</treatWarningsAsErrors>,将 warning 认为是 error,此时,Antlr4 Maven 插件,在 生成代码时,因为有个 warning,所以将 warning 识别为 error,导致执行失败。

如果认为 g4 中的 warning 无需修改,可以忽略,可以将该参数设置为 false。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。