微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

数组支持 Hplsql.g4 或 Hive.g4

如何解决数组支持 Hplsql.g4 或 Hive.g4

大家好, 我正在使用 antlr4 为 Hive sql (Hplsql.g4) 创建解析器和词法分析器。
我相信这是最新的语法文件
https://github.com/AngersZhuuuu/Spark-Hive/blob/master/hplsql/src/main/antlr4/org/apache/hive/hplsql/Hplsql.g4

但是,我发现至少需要两个添加项:IF 和数组索引。 例如,在一个 select 语句中,我可能有: a) SELECT if(a>8,12,20) FROM x
b) SELECT column_name[2] FROM x

两者都在 Hive 中有效,但当我从上面的 Hplsql.g4 为 java 创建解析器和词法分析器时,两者都不会解析。我为 IF 添加一个表达式,它似乎有效。

添加

 expr :
     ...
     | expr_if  //I added

和新规则:

expr_if : 
       T_IF T_OPEN_P bool_expr T_COMMA expr T_COMMA expr T_CLOSE_P  //I added
     ;

然而,弄清楚如何允许数组索引并不容易,因为语法允许别名:

select a from x
select a alias_of_a from x
select a[1] from x
select a[1] alias_of_a from x

应该都是有效的。 我尝试为此添加一个新表达式:

expr :
     ...
     | expr_array //I added

expr_array : 
       T_OPEN_SB L_INT T_OPEN_CB  //I added
     ;

这对我不起作用。 (T_OPEN_SB L_INT T_OPEN_CB 分别是[整数])。我也尝试了很多变化。我的问题是:

  1. 我是否使用了正确的语法文件 - 如果没有,是否有更新的具有 IF 和数组处理功能文件
  2. 有没有人成功地扩展了这个语法来处理我上面的案例?

根据 Bart 的建议: 我更新了ident
我更新了expr_atom
添加array_index
我有// | '[' .*? ']' 之前已注释掉。

测试sql:从t
中选择a[0] 结果: 第 1:8 行在输入“selecta[0]”处没有可行的替代方案
第 1:8 行不匹配的输入 '[0]'

树 (program (block stmt (stmt select) (stmt (expr_stmt (expr (expr_atom (ident a)))))) [0] from t)

我觉得问题与下面的 select_list_alias 有某种关系。 select_list_alias 包含 ident 和 T_AS 可选,ident 匹配数组索引。 我不明白为什么会发生这种情况,尤其是因为 ident 已更新。

摘自 Hplsql.sql

select_list :           
       select_list_set? select_list_limit? select_list_item (T_COMMA select_list_item)*
     ;
select_list_item :
       (ident T_EQUAL)? expr select_list_alias? 
     | select_list_asterisk   
     ;
select_list_alias :
       {!_input.LT(1).getText().equalsIgnoreCase("INTO") && !_input.LT(1).getText().equalsIgnoreCase("FROM")}? T_AS? ident
     | T_OPEN_P T_TITLE L_S_STRING T_CLOSE_P
     ;

如果我传入一个简单的 sql stmt to grun 如

select a[1] from t

解析树应该类似于:

Parse tree for select a from t

我想看到 expr_array 而不是 expr_atom,它会拆分为 a 的 expr_atom 和 [1] 的 array_index。

注意这里有一个 sql 语句。使用我现有的 g4,数组索引 [1](以及 stmt 的其余部分)被解析为单独的 sql 语句。

Bart,我从你的解析树中看到,解析导致了来自“select a[0] from t”的两条 sql 语句——我遇到了同样的情况。

我将继续探索不同的方法 - 我仍然怀疑结尾带有 T_AS? ident 的 select_list_alias。只是为了确认,我已经注释掉了 ident_part 中的一行,如下所示:// | '[' .*? ']'

解决方法

如评论中所述:[ ... ] 将被标记为 L_ID 标记。如果您不希望那样,请删除 | '[' .*? ']' 部分:

fragment
L_ID_PART  :
             [a-zA-Z] ([a-zA-Z] | L_DIGIT | '_')*                           // Identifier part
            | ('_' | '@' | ':' | '#' | '$') ([a-zA-Z] | L_DIGIT | '_' | '@' | ':' | '#' | '$')+     // (at least one char must follow special char)
            | '"' .*? '"'                                                   // Quoted identifiers
            // | '[' .*? ']' <-- removed
            | '`' .*? '`'
            ;

并像这样创建/编辑语法:

expr_atom :
       date_literal
     | timestamp_literal
     | bool_literal
     | expr_array // <-- added
     | ident
     | string
     | dec_number
     | int_number
     | null_const
     ;

// new rule
expr_array
     : ident array_index+
     ;

// new rule
array_index
     : T_OPEN_SB expr T_CLOSE_SB
     ;

上述规则将导致 select a[1] alias_of_a from x 被成功解析,但在输入时会失败,例如 select a[1] alias_of_a from [identifier][identifier] 将不会作为标识符进行匹配。

您可以尝试添加如下内容:

ident :
       L_ID
     | T_OPEN_SB ~T_CLOSE_SB+ T_CLOSE_SB // <-- added
     | non_reserved_words
     ;

这将正确解析 select a[1] alias_of_a from [identifier],但对整个语法(或对 HPL/SQL 的深入了解)没有很好的了解来确定这是否会搞砸其他事情:)

编辑

根据我提出的更改,语法如下所示:https://gist.github.com/bkiers/4aedd6074726cbcd5d87ede00000cd0d(由于字符限制,我无法在 SO 上发布)

用这个解析 select a[0] from t 将产生解析树:

enter image description here

用这个解析 select a[0] from [t] 会得到这个解析树:

enter image description here

您还可以通过运行以下 Java 代码来测试它:

String source = "select a[0] from [t]";
HplsqlLexer lexer = new HplsqlLexer(CharStreams.fromString(source));
HplsqlParser parser = new HplsqlParser(new CommonTokenStream(lexer));
ParseTree root = parser.program();

JFrame frame = new JFrame("Antlr AST");
JPanel panel = new JPanel();
TreeViewer viewer = new TreeViewer(Arrays.asList(parser.getRuleNames()),root);
viewer.setScale(1.5);
panel.add(viewer);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。