对 Hplsql.g4 或 Hive.g4 的阵列支持
Array support for Hplsql.g4 or 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) 来自 x
b) SELECT column_name[2] 来自 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 分别是 [整数])。我也尝试了很多变化。我的问题是:
- 我使用的语法文件是否正确 - 如果没有,是否有更新的带有 IF 和数组处理的文件?
- 有没有人成功地扩展了这个语法来处理我上面的情况?
根据 Bart 的建议:
我更新了 ident.
我更新了 expr_atom.
我添加了 array_index.
我有 // | '['.*? ']'之前被注释掉了。
测试Sql:select来自t
的[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 匹配数组索引。
我无法调和为什么会发生这种情况,特别是因为身份已更新。
摘自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 传递给 grun,例如
select a[1] from t
解析树应该类似于这样:
而不是 expr_atom,我想看到 expr_array 它将分成 expr_atom 代表 a 和 array_index 代表 [1]。
注意这里有一个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
| '`' .*? '`'
;
和create/edit语法是这样的:
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 上 post 它)
用这个解析 select a[0] from t
将导致解析树:
并用这个解析 select a[0] from [t]
将产生这个解析树:
您还可以通过 运行 以下 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);
大家好,
我正在使用 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) 来自 x
b) SELECT column_name[2] 来自 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 分别是 [整数])。我也尝试了很多变化。我的问题是:
- 我使用的语法文件是否正确 - 如果没有,是否有更新的带有 IF 和数组处理的文件?
- 有没有人成功地扩展了这个语法来处理我上面的情况?
根据 Bart 的建议:
我更新了 ident.
我更新了 expr_atom.
我添加了 array_index.
我有 // | '['.*? ']'之前被注释掉了。
测试Sql:select来自t
的[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 匹配数组索引。 我无法调和为什么会发生这种情况,特别是因为身份已更新。
摘自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 传递给 grun,例如
select a[1] from t
解析树应该类似于这样:
而不是 expr_atom,我想看到 expr_array 它将分成 expr_atom 代表 a 和 array_index 代表 [1]。
注意这里有一个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
| '`' .*? '`'
;
和create/edit语法是这样的:
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 上 post 它)
用这个解析 select a[0] from t
将导致解析树:
并用这个解析 select a[0] from [t]
将产生这个解析树:
您还可以通过 运行 以下 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);