7

テーブルを作成および変更してデータベースに追加できるデータベースエンジンを作成しました。SQLクエリを解析するために、EBNFフォームを使用してBoost.Spiritライブラリを実装しました。パーサーを適切に設定し、すべてのルールを正常に解析します。

私の問題は、2つを統合する方法がわからないことです。Boost.Spiritパーサーは、入力が正しいことを検証するだけですが、実際に何かを行うにはそれが必要です。セマンティックアクションを調べましたが、探しているものを処理していないようです。

たとえば、次のようなクエリがある場合:
new_table <- SELECT (id < 5) old_table;

ルールを使用して入力を検証してから、関数を呼び出し、
Table Database::Select(Table t , Condition c){ ... }
トークンを引数として渡します。

パーサーを統合するにはどうすればよいですか?

4

1 に答える 1

18

注:あなたの質問はあなたの質問を示していないので、私はデモンストレーションの目的でここでサンプル文法を発明することを選びました。ここで推奨されているアプローチを使用すると、解析後にクエリを実行する関数をコーディングするのは難しくありません。

解析ツリーを構築することをお勧めします。

セマンティックアクションよりも属性の伝播をお勧めします。たとえばを参照してください

属性伝播ルールは、Spiritでは非常に柔軟です。デフォルトの公開された属性タイプは、各パーサーのドキュメントで適切にドキュメント化されています

例-qi::char_結果はにboost::optional<char>なり、qi::double_ | qi::int_結果はになりboost::variant<double, int>ます。

解析された要素を独自の発明のASTデータ型に蓄積することをお勧めします。例:

struct SelectStatement
{
    std::vector<std::string> columns, fromtables; 
    std::string whereclause; // TODO model as a vector<WhereCondition> :) 

    friend std::ostream& operator<<(std::ostream& os, SelectStatement const& ss)
    {
        return os << "SELECT [" << ss.columns.size() << " columns] from [" << ss.fromtables.size() << " tables]\nWHERE " + ss.whereclause;
    }
};

構造体をFusionシーケンスとして適応させることにより、これをSpirits属性伝播機構に適応させることができます。

BOOST_FUSION_ADAPT_STRUCT(SelectStatement, 
        (std::vector<std::string>, columns)
        (std::vector<std::string>, fromtables)
        (std::string, whereclause)
       )

これで、次のルールをそのタイプに解析できます。

sqlident = lexeme [ alpha >> *alnum ]; // table or column name

columns  = no_case [ "select" ] >> (sqlident % ',');
tables   = no_case [ "from" ]   >> (sqlident % ',');

start    = columns >> tables 
    >> no_case [ "where" ]
    >> lexeme [ +(char_ - ';') ]
    >> ';';

このコードがライブで実行されているのをここで確認できます:http://liveworkspace.org/code/0b525234dbce22cbd8becd69f84065c1

完全なデモコード:

// #define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi    = boost::spirit::qi;

struct SelectStatement
{
    std::vector<std::string> columns, fromtables; 
    std::string whereclause; // TODO model as a vector<WhereCondition> :) 

    friend std::ostream& operator<<(std::ostream& os, SelectStatement const& ss)
    {
        return os << "SELECT [" << ss.columns.size() << " columns] from [" << ss.fromtables.size() << " tables]\nWHERE " + ss.whereclause;
    }
};

BOOST_FUSION_ADAPT_STRUCT(SelectStatement, 
        (std::vector<std::string>, columns)
        (std::vector<std::string>, fromtables)
        (std::string, whereclause)
       )

template <typename It, typename Skipper = qi::space_type>
    struct parser : qi::grammar<It, SelectStatement(), Skipper>
{
    parser() : parser::base_type(start)
    {
        using namespace qi;

        sqlident = lexeme [ alpha >> *alnum ]; // table or column name

        columns  = no_case [ "select" ] >> (sqlident % ',');
        tables   = no_case [ "from" ]   >> (sqlident % ',');

        start    = columns >> tables 
            >> no_case [ "where" ]
            >> lexeme [ +(char_ - ';') ]
            >> ';';

        BOOST_SPIRIT_DEBUG_NODE(start);
        BOOST_SPIRIT_DEBUG_NODE(sqlident);
        BOOST_SPIRIT_DEBUG_NODE(columns);
        BOOST_SPIRIT_DEBUG_NODE(tables);
    }

  private:
    qi::rule<It, std::string()             , Skipper> sqlident;
    qi::rule<It, std::vector<std::string>(), Skipper> columns  , tables;
    qi::rule<It, SelectStatement()         , Skipper> start;
};

template <typename C, typename Skipper>
    bool doParse(const C& input, const Skipper& skipper)
{
    auto f(std::begin(input)), l(std::end(input));

    parser<decltype(f), Skipper> p;
    SelectStatement query;

    try
    {
        bool ok = qi::phrase_parse(f,l,p,skipper,query);
        if (ok)   
        {
            std::cout << "parse success\n";
            std::cout << "query: " << query << "\n";
        }
        else      std::cerr << "parse failed: '" << std::string(f,l) << "'\n";

        if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
        return ok;
    } catch(const qi::expectation_failure<decltype(f)>& e)
    {
        std::string frag(e.first, e.last);
        std::cerr << e.what() << "'" << frag << "'\n";
    }

    return false;
}

int main()
{
    const std::string input = "select id, name, price from books, authors where books.author_id = authors.id;";
    bool ok = doParse(input, qi::space);

    return ok? 0 : 255;
}

出力を出力します:

parse success
query: SELECT [3 columns] from [2 tables]
WHERE books.author_id = authors.id
于 2012-09-27T22:46:15.643 に答える