0

Perlではuse SQL::Abstract::Tree、次の方法で SQL の AST を生成できます。

my $sqlat = SQL::Abstract::Tree->new;
my $tree = $sqlat->parse($query_str);

$query_strSQL クエリです。

例として、クエリ文字列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 のウォーキングを検討して見つけた文献のほとんどは、通常、AST をウォークするためのビジター パターンのある種のバリエーションを記述したある種のクラス階層があることを前提としています。

私の具体的な使用例は、単純な SQL クエリを集計フレームワークの Mongo クエリに変換することです。ここにいくつかの例を示します

これが私がこれまでやってきたことです:

最初parseに、各サブツリーのタイプと (各サブツリーの最初のパラメーター) を指定してツリー ディスパッチを使用して関数を呼び出し、残りのツリーでそれを呼び出します。これが私の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 処理が初めてで、それを把握しようとしているので、質問を改善する方法についてのアドバイスもいただければ幸いです。

4

1 に答える 1