GreenPadの正規表現エンジンをC言語に移植してみた

私がGreenPadのファンと言うことは前に述べましたが、なんといってもソースコードNYSLで公開されているところが大きいです。
これにより、テキストエディタの実装の仕方が確実に理解でき、文字列処理の大半がこのソースで学べるのは資料代が年間ん〜ぜん円の私にはかけがえの無いソースなのです。


実は、私、正規表現が前々から良く理解していなくて、困る事が結構あったのです。*1
なので、正規表現の実装を見れば直感的に分かるかな?
と、思い、C++のRSearch.cppをC言語に移植してみました。
以下に移植時に起こった事、思った事を書いて行こうと思います。

移植時に苦労した所 集

  • メモリ管理

    いわいるauto_ptrのようなスマートポインタを使っていたのでメモリ管理が一苦労
    ガベコレの有難さを知る ○| ̄|_

  • トークン解析の所
    元ソース

    RegToken RegLexer::GetToken()
    {
    const wchar_t*& x = (*sub_ ? sub_ : pat_);
    if( x == end_ ) return R_End;
    switch( *x++ )
    {
    case L'.': return R_Any;
    case L'[': return R_Lcl;
    case L']': return R_Rcl;
    case L'^': return R_Ncl;
    case L'-': return R_Range;
    case L'(': return R_Lbr;
    case L')': return R_Rbr;
    case L'|': return R_Bar;
    case L'*': return R_Star;
    case L'+': return R_Plus;
    case L'?': return R_Quest;
    case L'\\': if( x==end_ ) return R_End; switch( *x++ ) {
    case L't': chr_=L'\t'; return R_Char;
    case L'w': sub_=L"[0-9a-zA-Z_]"; return GetToken();
    case L'W': sub_=L"[^0-9a-zA-Z_]"; return GetToken();
    case L'd': sub_=L"[0-9]"; return GetToken();
    case L'D': sub_=L"[^0-9]"; return GetToken();
    case L's': sub_=L"[\t ]"; return GetToken();
    case L'S': sub_=L"[^\t ]"; return GetToken();
    } // fall through...
    default:
    chr_ = *(x-1);
    return R_Char;
    }
    }
    間違い移植ソース

    const wchar_t* x = (*p->sub_ ? p->sub_ : p->pat_);
    if( x == p->end_ ) return R_End;
    switch( *x++ )
    {
    case L'.': return R_Any;
    case L'[': return R_Lcl;
    case L']': return R_Rcl;
    case L'^': return R_Ncl;
    case L'-': return R_Range;
    case L'(': return R_Lbr;
    case L')': return R_Rbr;
    case L'|': return R_Bar;
    case L'*': return R_Star;
    case L'+': return R_Plus;
    case L'?': return R_Quest;
    case L'\\':
    if( x==p->end_ ) return R_End;
    switch( *x++ )
    {
    case L't': p->chr_=L'\t'; return R_Char;
    case L'w': p->sub_=L"[0-9a-zA-Z_]"; return RegLexerGetToken(p);
    case L'W': p->sub_=L"[^0-9a-zA-Z_]"; return RegLexerGetToken(p);
    case L'd': p->sub_=L"[0-9]"; return RegLexerGetToken(p);
    case L'D': p->sub_=L"[^0-9]"; return RegLexerGetToken(p);
    case L's': p->sub_=L"[\t ]"; return RegLexerGetToken(p);
    case L'S': p->sub_=L"[^\t ]"; return RegLexerGetToken(p);
    } // fall through...
    default:
    p->chr_ = *(x-1);
    return R_Char;
    }

    決定稿


    static DKC_INLINE int RegLexerGetTokenImpl(RegLexer *p){

    //const wchar_t* x = (*p->sub_ ? p->sub_ : p->pat_);
    const wchar_t *x = L"\0";
    int r = R_Char;
    if( p->sub_ == p->end_ )
    { r = R_End; goto End;}

    if(*p->sub_)
    x = p->sub_;
    else
    x = p->pat_;

    dkcmFORCE_NOT_ASSERT(x > p->end_);

    switch( *x++ )
    {
    case L'.':
    r = R_Any;break;
    case L'[':
    r = R_Lcl;break;
    case L']':
    r = R_Rcl;break;
    case L'^':
    r = R_Ncl;break;
    case L'-':
    r = R_Range;break;
    case L'(':
    r = R_Lbr;break;
    case L')':
    r = R_Rbr;break;
    case L'|':
    r = R_Bar;break;
    case L'*':
    r = R_Star;break;
    case L'+':
    r = R_Plus;break;
    case L'?':
    r = R_Quest;break;
    case L'\\':
    if( x==p->end_ ){ r = R_End;break;}
    switch( *x++ )
    {
    case L't': p->chr_=L'\t';
    r = R_Char;break;
    case L'w': p->sub_=L"[0-9a-zA-Z_]";
    r = RegLexerGetTokenImpl(p);break;
    case L'W': p->sub_=L"[^0-9a-zA-Z_]";
    r = RegLexerGetTokenImpl(p);break;
    case L'd': p->sub_=L"[0-9]";
    r = RegLexerGetTokenImpl(p);break;
    case L'D': p->sub_=L"[^0-9]";
    r = RegLexerGetTokenImpl(p);break;
    case L's': p->sub_=L"[\t ]";
    r = RegLexerGetTokenImpl(p);break;
    case L'S': p->sub_=L"[^\t ]";
    r = RegLexerGetTokenImpl(p);break;
    } // fall through...
    default:
    p->chr_ = *(x-1);
    r = R_Char;
    }
    End:
    //update
    p->sub_ = x;
    return r;

    }