>正規表現ライブラリにおける kVerifier の適用例

正規表現ライブラリ kreg を kVerifier の advertising software として、ソースと共に公開します。テストも含んで公開しております。kVerifier の機能を実際のデバッガー上で動作させることでかくにんできます。(現在では Vc7.0 向けの配布のみです)

テスト(action scripts) をプログラム入力のドライブ入力に利用できます。Vc7.0 向けの IDE 上で action script に従って正規表現プログラムがテストしされながら動きます。特に設定をすることなく Action script に書いてある入力条件に従って正規表現ライブラリ・プログラムが動く様子をデバッガ上で見られます。デバッガ上でブレークやシングルステップを使って、データの変化を見ながら追ったほうが、下手なドキュメントを読むよりも、プログラム構造を掴めるはずです。 (注意:たぶん、Vc7 では、たぶん、Property Pages->debugging->Command Argments がブランクになっており、ユーザーがマニュアルで action script file v\test.vrf を一回は入力してやらないと、abnormal program termination になると思います)

>正規表現プログラムについて

ここでは、正規表現プログラムの kVerifier と接続する部分を中心に説明します。正規表現プログラム自体については、別に説明します。

正規表現ライブラリ kreg のソース・プログラムには kreg.h と regular.cpp しか、ありません。その他は vrf ディレクトリにある、kVerifier header だけです。

ライブラリ・プログラムのテストであり、 main は kVerifier 専用のものです。ライブラリ・プログラムは main() プログラムに依存品はずです。

class ClRegularString : public string {...} が正規表現パターン・マッチを検出するクラスです。このクラスが、

  1. 正規表現文字列パターンの文字列
  2. 正規表現文字列パターンをセットするメンバー関数
  3. パターン・マッチをテストされる文字列
  4. パターン・マッチの結果
を保持します。正規表現のテストは

kreg.h regular.cpp の DfVrfy, DfVrfyKreg_ に対して ifdef...endif で囲まれた部分が kVerifier テストのために追加されている部分です。テストの最中は /D"DfVrfy" /D"DfVrfyKreg_" オプションをつけてコンパイルします。ライブラリを作るときは、これらのオプションを取ります。

    #ifdef DfVrfy
        ・
        ・
    #endif //DfVrfy
class ClRegularString の最後に、クラス・データー・メンバー、クラス・メソッドをベリファイ変数にする、下のコードを挿入しています。


    #ifdef DfVrfyKreg_
        void CheckMatchedString( const string& );
        // WhatPrmtvRptRgl(.) is usefull to test parsed element 
        ClPrimitiveRgl* WhatPrmtvRptRgl( int inIndexAg)
            { return m_vctpClPrimitiveRgl[inIndexAg]; }

        void Register(ClTestVct* pClAg, const string& crStrAg)
        {
            RgstVerified(pClAg, tfNewVerified(*(string*)this, crStrAg+".Matched") );      //-0
            RgstVerified(pClAg, tfNewVerified( m_strPostMatched, crStrAg+".PostMatched") );  //-1
            RgstVerified(pClAg, tfNewVerified( m_strPreMatched, crStrAg+".PreMatched") );   //-2
            RgstVerified(pClAg, tfNewVerified( m_strTraversed, crStrAg+".Traversed") );    //-3
            RgstVerified(pClAg
                , tfNewVfClFnctnSpl( this, &ClRegularString::SetRegularString
                    , crStrAg+".SetRegularString") );    //-3
        }

        friend void checkMatchedString(const char* pChAg);
    #endif  // DfVrfyKreg_

ClRegularString のテストしたいデータ・メンバーを下のような名前で登録しています。

    ClRegularString::string                 crStrAg+".Matched"
    ClRegularString::m_strPostMatched       crStrAg+".PostMatched"
    ClRegularString::m_strPreMatched        crStrAg+".PreMatched"
    ClRegularString::m_strTraversed         crStrAg+".Traversed"

ここで、crStrAg は ClRegularString クラス・インスタンス自体にテストために付ける名前です。クラスインスタンスは複数作られる可能性があるため、インスタンスごとに名前を与えられるようにしています。ClRegularString::Register(.) 関数の引数を経由して名前を指定します。

正規表現パターンに誤りがあったときなど、ClRegularString だけでは完結していないテストをするために、regular.cpp ソースの最後に下の virifier コードを追加しています。


    #ifdef DfVrfy
        ・
    class ClTestVctD : public ClVfLibActn {
      public:
      protected:
        virtual void doAtInitialVl( const std::string& crStrAg);
    };
        ・
        ・
    static krg krgStrStt("");   //typedef ClRegularString krg;が kreg.h にあります
        ・
        ・
    void ClTestVctD::doAtInitialVl( kc std::string& crStrAg)
    {
    
        if ( IsSameNocase(crStrAg,"typical.vrf")
          || IsSameNocase(crStrAg,"test.vrf")
          || IsSameNocase(crStrAg,"v\\error.vrf")
          || IsSameNocase(crStrAg,"v\\abnormal.vrf")
          || IsSameNocase(crStrAg,"v\\after100cv.vrf")
          || IsSameNocase(crStrAg,"v\\sjistest.vrf")
          || IsSameNocase(crStrAg,"v\\test.vrf")
        ){
    
            ClVfLibActn::doAtInitialVl(crStrAg);  // open crStrAg file
            //openTestVct(crStrAg);  // 使うテスト・ベクタを指定する
            kcout << "Now verifying input file is " << crStrAg << " in main.cpp" << std::endl;
    
            RgstVerified(this, tfNewVerified(blDbgPrintStt, "dbgPrintFlag") );
    
    
            RgstVerified(this, tfNewVfFnctnSpl(setRglExprssnString, "setRglExprssnString") );
            RgstVerified(this, tfNewVfFnctnSpl(setSentenseString, "setSentenseString") );
            RgstVerified(this, tfNewVfFnctnSpl(checkRglElementString,"checkRglElementString") );
            RgstVerified(this, tfNewVfFnctnSpl(checkMatchedString,"checkMatchedString") );
    
            RgstVerified(this, tfNewVfFnctn(blIgnoreCapitalSmall,"blIgnoreCapitalSmall") );
    
            RgstVerified(this, tfNewVerified(strErrorThrowedStt, "strErrorThrowed") );
            RgstVerified(this, tfNewVerified(strSentenseStt, "strSentenseStt") );
    
            RgstVerified(this, tfNewVerified(krgStrStt, "krgStrStt") );
    
            RgstVerified(this, tfNewVfFnctn(makeCapatalSmallAtConstruct,"makeCapatalSmallAtConstruct") );
    
            RgstVerified(this, tfNewVerified(*(int*)&enTestOperationStt,"enTestOperationStt") );
    
            krgStrStt.Register(this, "krgStrStt");
        }
    }

krgStrStt が 正規表現クラス ClRegularString のインスタンスです。最後の krgStrStt.Register(this, "krgStrStt"); が、先ほどの、ClRegularString クラス・メンバーをベリファイ変数にする関数を呼び出している所です。インスタンス名に "krgStrStt" の文字列を割り当てています。"krgStrStt" は action scripts で使われます。


    RgstVerified(this, tfNewVfFnctnSpl(setRglExprssnString, "setRglExprssnString") );

は、crStrAg+".SetRegularString" と同じことをする関数をベリファイ変数として、登録しています。過去の経緯で、冗長なコードになってしまっています。テスト側のリファクタリングをサボっています。すいません。

>正規表現をテストする action scripts

正規表現パターンのマッチングをテストしている action scripts は v\test.vrf です。v ディレクトリには、エラーのときの動作をテストする error.vrf などもありますが、v\test.vrf が kreg ライブラリをテストする coverage の 90% 以上を占めます。

v\test.vrf の最初の部分は下のようになっています。


    ## "py" / "(p)y?" % 1== "" ----- 2003 04 01 NG

    +1 setRglExprssnString __set  "(p)y?"
    +0 setSentenseString   __set  "py"      #sentense
    +0 krgStrStt.Matched __compare "py"      #check regular matched result
    +0 checkMatchedString __function "1 p" #check regular matched result
            ・
            ・

この action scripts は、正規表現文字列 "(p)y?" を "py" 文字列に作用されたとき、
  1. 正規表現にマッチするのは "py" 文字列であり
  2. perl の $1 に相当する部分文字列にマッチするのは "p" である
ことをテスト・しています。このようなテストを coverage 100% になるまで、記述していきます。