具有
use SQL::Abstract::Tree
在Perl中,我能够通过以下方式为SQL生成AST:
my $sqlat = SQL::Abstract::Tree->new;
my $tree = $sqlat->parse($query_str);
哪里
$query_str
是一个SQL查询。
例如,使用查询字符串
SELECT cust_id, a as A, z SUM(price) as q, from orders WHERE status > 55
,产生:
[
[
"SELECT",
[
[
"-LIST",
[
["-LITERAL", ["cust_id"]],
["AS", [["-LITERAL", ["a"]], ["-LITERAL", ["A"]]]],
[
"AS",
[
["-LITERAL", ["z"]],
["SUM", [["-PAREN", [["-LITERAL", ["price"]]]]]],
["-LITERAL", ["q"]],
],
],
[],
],
],
],
],
["FROM", [["-LITERAL", ["orders"]]]],
[
"WHERE",
[[">", [["-LITERAL", ["status"]], ["-LITERAL", [55]]]]],
],
]
我想浏览AST并获得有关它的某些信息。
我想知道是否有一个指南/教程/示例源代码可以以这种格式引导AST。
我发现的大多数考虑行走AST的文献通常假设我有某种类层次结构,描述了行走AST的访问者模式的某种变化。
我的具体用例是将简单的SQL查询转换为聚合框架的Mongo查询,并给出了一些示例
here
.
以下是我到目前为止一直在做的事情:
我先打电话给
parse
具有树的函数在给定其类型和(这是每个子树中的第一个参数)的情况下对每个子树进行调度,并用树的其余部分调用它。这是我的
作语法分析
功能:
sub parse {
my ($tree) = @_;
my %results = (ret => []);
for my $subtree (@$tree) {
my ($node_type, $node) = @$subtree;
my $result_dic = $dispatch{$node_type}->($node);
if ($result_dic->{type}) {
my $type = $result_dic->{type};
$results{$type} = [] unless $results{$type};
push $results{$type}, $result_dic->{ret};
%results = merge_except_for($result_dic, \%results, 'ret', $type);
}
else {
push @{$results{ret}}, @{$result_dic->{ret}};
}
}
return \%results;
}
使用以下调度表:
my %dispatch = (
SELECT => sub {
my $node = shift;
my $result_dic = parse($node);
$result_dic->{type} = 'select';
if ($result_dic->{as}) {
push $result_dic->{ret}, $result_dic->{as}->[0][0];
}
return $result_dic;
},
'-LITERAL' => sub {
my $node = shift;
my $literal = $node;
return {ret => $node};
},
'-LIST' => sub {
my $node = shift;
my $result_dic = parse($node);
my $ret = flatten_ret($result_dic);
return flatten_ret($result_dic);
},
WHERE => sub {
my $tree = shift;
my @bin_ops = qw/= <= < >= >/;
my $op = $tree->[0];
if ($op ~~ @bin_ops) {
# Not yet implemented
}
return {ret => ''};
},
FROM => sub {
my $tree = shift;
my $parse_result = parse($tree);
return {ret => $parse_result->{ret},
type => 'database'};
},
AS => sub {
my $node = shift;
my $result_dic = parse($node);
$result_dic->{type} = 'as';
return $result_dic;
}
);
sub flatten_ret {
my $result_dic = shift;
return {ret => [
map {
ref($_) ? $_->[0] : $_
} @{$result_dic->{ret}}]};
}
但我不确定某些事情,比如我是否应该检查节点名是否为
"AS"
在
SELECT
子程序或找到一种递归的方法来填充数据。
此外,每次调度调用应该返回什么类型的数据,以及如何在最后将其组合?
此外,我是AST处理的新手,希望能掌握它,所以关于如何改进我的问题的建议也将不胜感激。