ANTLR4 词法分析器规则在 perl 语法上产生错误或冲突

Posted

技术标签:

【中文标题】ANTLR4 词法分析器规则在 perl 语法上产生错误或冲突【英文标题】:ANTLR4 lexer rule creates errors or conflicts on perl grammar 【发布时间】:2021-10-22 05:30:36 【问题描述】:

我的 PERL 语法有问题,以下是我的语法的相关部分:

element
    : element (ASTERISK_CHAR | SLASH_CHAR | PERCENT_CHAR) element
    | word
    ;

SLASH_CHAR:                 '/';

REGEX_STRING
    : '/' (~('/' | '\r' | '\n') | NEW_LINE)* '/'
    ;

fragment NEW_LINE
    : '\r'? '\n'
    ;

如果规则 REGEX_STRING 没有被注释,那么下面的 perl 不会解析:

$b = 1/2;
$c = 1/2;
<2021/08/20-19:24:37> <ERROR> [parsing.AntlrErrorLogger] - Unit 1: <unknown>:2:6: extraneous input '/2;\r\n$c = 1/' expecting <EOF>, '=', '**=', '+=', '-=', '.=', '*=', '/=', '%=', CROSS_EQUAL, '&=', '|=', '^=', '&.=', '|.=', '^.=', '<<=', '>>=', '&&=', '||=', '//=', '==', '>=', '<=', '<=>', '<>', '!=', '>', '<', '~~', '++', '--', '**', '.', '+', '-', '*', '/', '%', '=~', '!~', '&&', '||', '//', '&', '&.', '|', '|.', '^', '^.', '<<', '>>', '..', '...', '?', ';', X_KEYWORD, AND, CMP, EQ, FOR, FOREACH, GE, GT, IF, ISA, LE, LT, OR, NE, UNLESS, UNTIL, WHEN, WHILE, XOR, UNSIGNED_INTEGER

请注意,在何处使用词法分析器规则 REGEX_STRING 并不重要,即使它不在解析器规则中的任何地方,只要出现在此处也会导致解析失败(因此问题出在词法分析器方面)。 如果我删除词法分析器规则 REGEX_STRING,那么它会被解析得很好,但是我无法解析:

$dateCalc =~ /^([0-9]4)([0-9]2)([0-9]2)/

另外,我注意到这个 perl 解析,所以第一个和第二个 '/' 之间似乎存在某种交互。

$b = 12;          # Removed the / between 1 and 2
$c = 1/2;         # Removing the / here would work as well.

我似乎找不到如何编写我的正则表达式词法分析器规则以不使某些事情失败。 我错过了什么?我怎样才能很好地解析这两个表达式?

【问题讨论】:

有句老话,“只有 perl 才能解析 perl”。祝你好运! 请注意,perl 自己的词法分析器大约是 10,000 行手动编写的高度上下文敏感的 C 代码 我的语法越多,我就越了解你来自哪里!假设我喜欢挑战;) 我同意解析 Perl 是一项最好留给 Perl 本身的任务。但是尝试解析 Perl 肯定会教你很多关于解析技术的知识,顺便说一下如何(不)设计一种语法可以被其他工具解析的语言。祝你锻炼顺利。 【参考方案1】:

这里的基本问题是 ANTLR4 与许多其他解析框架一样,执行独立于语法的词法分析;无论解析器可以接受哪些标记,都会生成相同的标记。因此,词法分析器必须决定给定的/ 是除法运算符还是正则表达式的开头,这个决定实际上只能使用句法信息做出。 (有些解析框架没有这个限制,因此可以用于实现无扫描解析器。包括基于 PEG 的解析器和 GLR/GLR 解析器。)

在ANTLR4 example directory 中有一个解决这种词汇歧义的示例,它也出现在解析 ECMAScript 中。 (这是一个 github 永久链接,因此下面引用的行号继续有效。)

基本策略是根据前一个标记来决定/ 是否可以启动正则表达式。这在 ECMAScript 中有效,因为运算符(例如 //=)可以出现的语法上下文与操作数可以出现的上下文不相交。这可能不会直接转化为 Perl 解析器,但它可能有助于展示可能性。

Line 780-782:正则表达式令牌本身受语义保护:

RegularExpressionLiteral
 : isRegexPossible()? '/' RegularExpressionBody '/' RegularExpressionFlags
 ;

Lines 154-182: 守卫功能本身很简单,但显然需要一定的语法分析才能生成正确的测试。 (注意:令牌列表已被缩写;完整列表请参见原始文件):

private boolean isRegexPossible() 
        if (this.lastToken == null) 
            return true;
        

        switch (this.lastToken.getType()) 
            case Identifier:
            case NullLiteral:
...
                // After any of the tokens above, no regex literal can follow.
                return false;
            default:
                // In all other cases, a regex literal _is_ possible.
                return true;
        
    

Lines 127-147 为了让它工作,扫描器必须保留成员变量last_token 中的前一个标记。 (因空间而删除评论):

    @Override
    public Token nextToken() 
        Token next = super.nextToken();
        if (next.getChannel() == Token.DEFAULT_CHANNEL) 
            this.lastToken = next;
        
        return next;
    

【讨论】:

所以没有“lexer-way”来解决这个歧义?这让我感到惊讶,因为 REGEX 规则不应该能够匹配由换行符分隔的两个斜杠。我会采用您的解决方案,非常感谢您的深入回答! @Kronos:您可以通过删除 | NEWLINE 来修复您的正则表达式。但这并不能解决歧义,因为您可以在同一行上进行两个划分。我应该提到正则表达式的问题,抱歉。 啊,是的,我傻了,我没有这样看,当然 NEWLINE 允许 REGEX 规则中的换行符......确实,它不会解决两个部门的问题无论如何都是同一行,因此无论如何都需要将规则从词法分析器传递到解析器并使用谓词。谢谢:)

以上是关于ANTLR4 词法分析器规则在 perl 语法上产生错误或冲突的主要内容,如果未能解决你的问题,请参考以下文章

Hive 源码解读 Driver 将 HQL 语句转换为 AST

Hive 源码解读 Driver 将 HQL 语句转换为 AST

ANTLR4权威指南 - 第5章 设计语法

无法在 Antlr4 中实现带有自定义分隔符的 q 引用字符串

符号表和抽象语法树是啥关系?两者在编译器设计中是不是必需

ANTLR4 如何编写语法文件之语法解析器规则