シンボル

この symbols クラスはシンボルテーブルを実装する。 シンボルテーブルはシンボルの辞書を保持する。 ここで各シンボルは CharT のシーケンスである( charwchar_tint 、列挙体など)。 文字型( CharT )によってパラメータ化されたそのテンプレートクラスは、 8、16、32および64ビット文字でさえも効率的に動作することができる。 型 T の変更可能なデータが各シンボルに関連づけられる。

伝統的に、シンボルテーブル管理はセマンティックアクションによって、 BNF 文法の外側で別途行われる。 標準的な手法とは対照的に、 Spirit のシンボルテーブルクラス symbols は一つのパーサである。 そのインスタンスは EBNF 文法定義のあらゆる場所で用いられることがある。 それはダイナミックパーサの一例である。 ダイナミックパーサは実行時にその振る舞いを更新する能力によって特徴付けられている。 初期化時には(initially)、空の symbols オブジェクトは何にもマッチしない。 あらゆる時点で、 symbols は追加されて、それに従って、動的にその振る舞いを変化させる。

シンボルテーブルの各エントリは関連づけられた変更可能なデータスロットを持つ。 それ故、シンボルテーブルを 文字列をキーとするkey-valueの組の連想コンテナ(あるいは map )として見ることが出来る。

symbols クラスは二つのテンプレートパラメータを期待する(実際には三つ。詳細はコラム参照)。 最初のパラメータ T は各シンボルに関連づけられたデータ型を指定する(デフォルトは int)。二番目のパラメータ CharT はシンボルの文字型を指定する(デフォルトは char)。

    template
    <
        typename T = int,
        typename CharT = char,
        typename SetT = impl::tst<T, CharT>
    >
    class symbols;
Ternary State Trees

実際の集合の実装は SetT テンプレートパラメータ( symbols クラスの第三テンプレートパラメータ)によって供給される。 デフォルトでは、これは三分木(Ternary Search Tree)の実装である tst クラスを用いる。

三分木は多くの典型的な探索問題に対して、 特に探索インタフェースがイテレータベースであるときは、 ハッシュよりも高速である。 n 文字列からなる三分木での長さ k の文字列の探索は、たかだか O(log n+k) 回の文字比較で行われる。 不一致はほんの数文字を検査した後すぐに発見されるので、 成功しない探索については、 TST はハッシュテーブルよりも多くの場合高速である。 ハッシュテーブルは探索時に常にキー全体を検査する。

詳細は http://www.cs.princeton.edu/~rs/strings/を参照。

宣言の例をいくつか:

    symbols<> sym;
    symbols<short, wchar_t> sym2;

    struct my_info
    {
        int     id;
        double  value;
    };

    symbols<my_info> sym3;

我々のシンボルテーブルを宣言した後、 symbols はコンストラクトを用いて静的に追加される:

    sym = a, b, c, d ...;

ここで sym はシンボルテーブルであり、 a..d 等は文字列である。 カンマ演算子は、代入によってシンボルテーブルに追加されている項目を分けている。 演算子オーバーロードのため、これは可能であり、正しい (まあ慣れるまで多少掛かるが)ものであり、また多数のシンボルでシンボルテーブルを初期化する簡潔な方法である。 また、 シンボルテーブルにシンボル(やシンボルのグループ)を繰り返し追加する為に 別々の時間に多重代入することは完璧に正当な手続きである。

単純な例:

    sym = "pineapple", "orange", "banana", "apple", "mango";

同じシンボルを複数回シンボルテーブルに追加することは無効である点に注意。 しかし、あるシンボルに関連づけられている値は何度でも更新することが出来る。

これで sym を文法に使うことが出来る。例:

    fruits = sym >> *(',' >> sym);

また、 symbols はメンバファンクタ add によって動的に追加することも出来る(後述の symbol_inserter を参照)。 メンバファンクタ add は begin / end の組を取るセマンティックアクションとしてパーサに接続することが出来る:

    p[sym.add]

ここで p はパーサである(そして sym はシンボルテーブル)。 成功すれば、入力の一致した部分がシンボルテーブルに追加される。

add はまた直接的にデータを初期化するために使うことも出来る。例:

    sym.add("hello", 1)("crazy", 2)("world", 3);

もちろん、 sym に関連づけられているデータスロットは整数であると仮定している。

各シンボルに関連づけられたデータはいつでも更新できる。 最も明確な方法はもちろんセマンティックアクションを使うことである。 関数やファンクタは、通常、シンボルテーブルに接続することが出来る。 シンボルテーブルはその関数やファンクタが以下のシグネチャと互換であることを期待する:

関数のシグネチャ:

    void func(T data);

ファンクタのシグネチャ:

    struct ftor
    {
        void operator()(T data) const;
    };

ここで T はシンボルテーブル( T はそのテンプレートパラメータリストの中にある)のデータ型である。 シンボルテーブルが入力された何かに首尾良く一致した場合、 シンボルテーブルで一致したエントリに関連づけられたデータがセマンティックアクションに報告される。

シンボルテーブルユーティリティ

時々、シンボルテーブルを直接扱いたいと思うことがある。 そのためいくつかのシンボルテーブルユーティリティが提供されている。

add

    template <typename T, typename CharT, typename SetT>
    T*  add(symbols<T, CharT, SetT>& table, CharT const* sym, T const& data = T());

シンボルテーブル table にシンボル sym ( C 文字列)と そのシンボルに関連づけられた省略可能なデータ data を追加する。 そのシンボルに関連づけられたデータへのポインタを返すか、もし追加に失敗すれば(例えば、そのシンボルが以前既に追加されていれば) NULL を返す。

find

    template <typename T, typename CharT, typename SetT>
    T*  find(symbols<T, CharT, SetT> const& table, CharT const* sym);

シンボルテーブル table からシンボル sym ( C 文字列)を探す。 そのシンボルに関連づけられたデータへのポインタを返すか、もし見つからなければ NULL< を返す。

symbol_inserter

symbols クラスは add という名のこのクラスのインスタンスを保持している。これはちょうどメンバ関数のように直接呼び出すことができる。 first / last イテレータと省略可能なデータを渡すか:

    sym.add(first, last, data);

あるいは、 C 文字列と省略可能なデータを渡す:

    sym.add(c_string, data);

ここで sym はシンボルテーブルである。 data 引数はオプションである。 この仕掛けのすばらしいところは、多段にできることである。 これについては既に上で見ている。 以下はローマ数字パーサからの抜粋である

    //  シンボルテーブルを用いたローマ数字(1〜9)の構文解析。

    struct ones : symbols<unsigned>
    {
    	ones()
    	{
            add
                ("I"    , 1)
                ("II"   , 2)
                ("III"  , 3)
                ("IV"   , 4)
                ("V"    , 5)
                ("VI"   , 6)
                ("VII"  , 7)
                ("VIII" , 8)
                ("IX"   , 9)
    		;
    	}

    } ones_p;

ユーザ定義構造体 onessymbols はサブクラス化している。 その後、構築時に、 symbol_inserter の add を用いて全てのシンボルを追加している。

[ libs/spirit/example/fundamental/roman_numerals.cpp を参照]

繰り返すが、 add はアクションのインタフェースを満たすので、セマンティックアクションとして用いることも出来る(セマンティックアクションを参照):

    p[sym.add]

ここで p はもちろんパーサである



このドキュメントの対象: Boost Version 1.30.0
最新版ドキュメント(英語)