web-dev-qa-db-ja.com

パーサーを書く

.NETでパーサーのようなSQLを書く

この時点で、最初の基本的な解析が行われます

col1 any( 'a'、 'b'、 'c')およびcol2 all( 'b'、 'c')のドキュメントからcol1、col2、col4を選択します

すべてのトークンタイプと呼んでいるものを識別できます

select is select
col1は列です
col2は列です
からです
どこがどこですか
col1は列です
いずれも条件です
( 'a'、 'b'、 'c')は複数の値です

次のレベルのルールに問題があります

選択した後、それは簡単な列でなければなりません

From有効な構文の前の列の後
、列
またはから

トークンタイプが有効かどうかを判断するために、2つ以上前または後ろに進む必要がないようです。

正しい(または良い)方法論/アプローチとは

多分私は手で転がる必要はありませんが、列名とテーブル名は動的なので、コードが進むべき道のように感じました。また、ばかげた理由で、.NETの外部でライブラリを使用したくありません。

1
paparazzo

recursive descent parser の書き方を学べば間違いありません。

それらは高速で、書きやすく、特別なツールは必要ありません。

それはプロがすることです。たとえば、 GCCパーサー は手書きの再帰下降です。

あなたが混乱しているので、追加しました:
一般的なアイデアを提供するための疑似コードを次に示します。私は意図的に詳細を省略し、問題を残して、適切と思われるように理解および/または変更します。

parsValues(){
    if (parsLiteral()){
    } else if (parsChar('('){
        do {
            if (!parsLiteral()) ERROR...
        } while (parsChar(','));
        if (!parsChar(')')) ERROR...
    } else {
        ERROR...
    }
}

parsAnyAll(){
    if (parsWord("any") || parsWord("all")){
        parsValues()
    } else {
        ERROR...
    }
}

parsAnd(){
    parsAnyAll()
    while(parsWord("and")){
        parsAnyAll()
    }
}

parsOr(){
    parsAnd()
    while(parsWord("or")){
        parsAnd()
    }
}

parsSelect() {
    do {
        if (!parsColName()) ERROR("colname expected");
    } while (parsChar(','));
    if (!parsWord("from")) ERROR ...
    if (!parsTableName()) ERROR ...
    if (parsWord("where")){
        parsOr()
    }
}
4
Mike Dunlavey

単純なパーサーを設定するための最善の策は、パーサージェネレーターです。

自明ではない解析コードはすぐに複雑になり、ひどく遅くすることなくそれを正しく行うことは簡単に巨大な混乱に変わる可能性があるため、標準的な解決策は、ドメイン固有で解析したい文法のタイプの基本的な概要を記述することです言語を設定し、パーサジェネレータにそれを実際の解析ロジックに変換させます。

C#で作業している場合は、C#コードを生成する非常に柔軟なパーサージェネレーターである Antlr4CS を参照してください。新しいコーディングコンセプトと同様に、ANTLRを使用するには多少の慣れが必要ですが、基本を理解すればかなりうまくいきます。

基本的な考え方は、ロジックを生成し、単純な解析ツリーを返すパーサークラスを作成し、リスナーまたはオブザーバーを使用して、ニーズに合わせてその解析ツリーを改良できるというものです。 (ビジターパターンに精通していると役立ちます。)

4
Mason Wheeler

再帰下降パーサーを作成するというアドバイスに同意します。最終的にこのようなジョブにパーサー生成ツールを使用することになったとしても、再帰下降を使用して手動で少なくとも1つのパーサーを作成することは十分に価値があります(おそらく、ジョブの少なくとも一部にシャントヤードアルゴリズムを使用して少なくとももう1つ)。

少なくとも私の心の中で、再帰的降下パーサーを書くことは、いくつかの重要なポイントを確実に実現し始めます:

  1. 最初はほとんど乗り越えられないように見えるタスクを実行し、実際には非常に親しみやすく扱いやすい方法でソリューションを定義します。
  2. 真に体系的な方法でコードを記述し、一連の関数がシームレスなシステムとして連携するようにします。

これらは必ずしも必要ではないかもしれませんが、どちらも「ハッカー」から「ソフトウェアエンジニア」へと進む上で非常に価値があります。

この経験のない人が書いたコードの多くは、航空機が「密集して飛んでいるスペアパーツのコレクション」であるという古い行を思い出させます。コードは隣り合っていますが、実際には形成されていません。一貫性のあるシステム(もちろん、パーサーを作成するだけでは、将来のコードが必然的により良くなることはもちろん保証されません)。

3
Jerry Coffin