首页 > 解决方案 > ANTLR4:使用关键字(别名?)清理语法和树

问题描述

我正在寻找一个简单问题的解决方案。

这个例子 :

SELECT date, date(date)
FROM date;

这是一个相当愚蠢的示例,其中一个表、它的列和一个函数都具有名称“日期”。

我的语法片段(非常简化):

simple_select
    : SELECT selected_element (',' selected_element) FROM from_element ';'
    ;

selected_element
    : function
    | REGULAR_WORD
    ;

function
    : REGULAR_WORD '(' function_argument ')'
    ;

function_argument
    : REGULAR_WORD
    ;

from_element
    : REGULAR_WORD
    ;


DATE:     D A T E;
FROM:     F R O M;
SELECT:   S E L E C T;

REGULAR_WORD
    : (SIMPLE_LETTER) (SIMPLE_LETTER | '0'..'9')*
    ;

fragment SIMPLE_LETTER
    : 'a'..'z'
    | 'A'..'Z'
    ;

DATE 是一个关键字(它在语法的其他地方使用)。如果我希望它被我的语法识别为普通单词,这是我的解决方案:

1)我将它添加到我使用 REGULAR_WORD 的任何地方,在它旁边。例子 :

selected_element
    : function
    | REGULAR_WORD
    | DATE
    ;

=> 我不想要这个解决方案。我不仅将“DATE”作为关键字,而且我有许多使用 REGULAR_WORD 的规则,因此我需要将许多(50+)关键字(如 DATE)添加到许多(20+)解析器规则中:它会是绝对丑陋。

优点:做一棵干净的树

缺点:弄脏语法

2)我在两者之间使用解析器规则来获取所有这些关键字,然后,我用该解析器规则替换每次出现的 REGULAR_WORD。例子 :

word
    : REGULAR_WORD
    | DATE
    ;

selected_element
    : function
    | word
    ;

=> 我也不想要这个解决方案,因为它在树中添加了一个解析器规则并污染了信息(我不想知道“日期”是一个词,我想知道它是一个 selected_element,一个函数, function_argument 或 from_element ...

优点:制作一个干净的语法

缺点:做一棵脏树

无论哪种方式,我都有一棵脏树或脏语法。没有办法让两者都干净吗?

我寻找别名,解析器片段等价物,但似乎 ANTLR4 没有任何?

感谢您有一个愉快的一天 !

标签: antlrantlr4grammar

解决方案


Antlr4 语法库中有四种不同的 SQL 方言语法,它们都使用您的第二种策略。因此,Antlr4 sql 语法编写者之间似乎达成了共识。鉴于 Antlr4 词法分析器的设计,我认为没有更好的解决方案。

正如您所说,这会导致完整解析树中出现一些噪音,但是相关的非终结符(function,selected_element等)肯定存在,而且在我看来,将单元生成折叠出来并不困难解析树。

据我了解,在设计 Antlr4 时,决定只自动生成完整的解析树,因为压缩(“抽象”)语法树的设计过于特殊,无法适应语法 DSL。因此,如果您发现 AST 更方便,您有责任自己生成一个。这通常是直截了当的,尽管它涉及很多样板。

其他解析器生成器确实具有可以处理“半保留关键字”的机制。特别是,作为 Sqlite 项目的一部分的 Lemon 解析器生成器包含一个%fallback声明,该声明允许您指定一个或多个标记应在没有语法规则允许使用它们的上下文中自动重新分类。不幸的是,Lemon 不生成 Java 解析器。

另一个类似的选择是使用支持“无扫描”解析的解析器生成器。此类解析器通常使用 Earley/GLL/GLR 等能够解析任意 CFG 的算法,以解决比 LALR(1) 等固定前瞻算法方便支持的更多前瞻需求。


推荐阅读