ホーム | Software Lab | Profile | Favorites | Diary | Links | About This Site | Feedback

 

 

第10回は、 各言語のソースを HTML に変換する部分の実装をやります。WTL とは直接関係ありませんが、ここがないと動かないので面白くないので・・・。

今回のポイント
COMMAND_RANGE_HANDLER とその使い方

  •  変換エンジン概要

    大分時間が空いてしまって申し訳ありません。第10回は、変換エンジンの例として CPP のソースから HTML に変換する部分の実装をしてみます。

    もう、既に御自分でやってしまわれた方もいらっしゃるかと思いますが、WTL のテクニックだけでなく、実践的なサンプルとして参考になるものを取り上げていきたいというのが趣旨ですので、WTL 以外の部分に注力することもあることを御了承ください。

    では、早速実装の解説に移っていきたいと思います。

    まず、今回の変換エンジンですが、交換可能なアルゴリズムとして設計する前提ですので、変換にあたって共通な部分とそうでない部分を明確にしたいと思います。

    共通部分とは、HTML のお約束に近い部分で、たとえば今回の例の場合は、ソースは PRE タグというタグに囲まれた形で出力されることを前提としています。

    全体の流れを以下の図で見てみましょう。

    処理の流れ

    まずソースを読み込みます。これは前回までに作ったエクスプローラ風のフォルダツリーでユーザーが選択したものを右上のソースビューに読み込みます。読み込みながら逐次解析する方法もあると思いますが、ここではとりあえず全部読んでしまいます(変換対象がそんなに大きくないと思うので)

    次に読み込んだテキストを解析していきます。ここでは構文解析とか意味解析とか大げさな表現を使いましたが、今回の目的はキーワードを抽出することにありますから、これら3つの作業は同時に進行します。そのため本来の構文解析のようにトークンを作ったりはしません。

    構文解析(字句解析かな)は単純に区切り文字までの文字列を読み込み、それがどのようなキーワードかを判定するという方法になりますが、C++ の文法は複雑なので(といっても他も同じようなものだけど)いくつか注意する点があります。

    一つはコメントの扱いです。コメントには // で始まり改行で終わる行コメントと、/* 〜 */ のブロックで囲まれるブロックコメントがあります。ブロックコメントの場合は複数行にまたがるので、その辺の処理をきちんとしてあげる必要があります。今回は基本的に最初から全部(あるいはユーザーが選択した範囲)を上から順に解析するので、コメントの途中から始まると言った状況は考慮していません。

    同じようなものに、文字リテラルがあります。これは「”」や「’」で始まるものです。これらコメントとリテラルが見つかった場合、その最中に他の区切り文字等が来ても、あくまでもコメント、又は文字リテラルとして扱わなければなりません。つまりステートマシン(状態遷移)を把握する必要があります。


    簡単な状態遷移図

    微妙に正確じゃなくて申し訳ないんですが、こんな感じっていうことで一つ。

  • 抽出するキーワード

    この状態遷移に基づいてキーワードを抽出します。抽出したキーワードは意味解析によって、以下の7つに分類されます。

    • プリプロセッサディレクティブ
      # で始まる include 文やプラグマなどです。

    • 行コメント

    • ブロックコメント
      これらは特に二つに分ける理由はないのですが、もし色を塗り分けたい場合には分けてください(サンプルは別れています)

    • 数字
      これも数字を識別子とは分けて着色したい場合に分けてください

    • 予約語
      これは各言語で定められている予約語の一覧です。Web とか規格書などから取得してください。今回は Initialize()という関数で予約語を登録しています。予約語は STL の list をコンテナに使っています。

    • ユーザー定義キーワード
      これはユーザーが任意で色をつけたいキーワードを指定します。たとえば、Win32 や WTL のプログラムで API の呼び出しは別な色にしたいという場合などが該当します。 今回ユーザー定義キーワードはファイルから読み込む事にしました。ファイル名は UserType.dat と VC6 のキーワードファイルと同じファイル名、書式にしてみました。指定したいキーワードを一つずつ改行して書いていくだけです。
       例)
         SetWindowText
         GetClientRect
         InvalidateRect

       このようなファイルを作るのが面倒な人は、拙作 KeywordMaker を使って作ってください。

    • 識別子
      上記、いずれにも該当しないキーワードは識別子という扱いになります。

    字句解析で気をつけることは、タブ文字の扱いでしょうか。HTML ではタブをそのまま使うのが好ましくないようなので、指定されたタブサイズを元に、スペースに展開して出力するようにしました。行頭からの位置を踏まえて展開する 必要があるのでちょっとややこしいです。

    おまけで行頭に行番号を入れる機能もつけてみました。ちょっと冗長な書き方になっていますが、改良して自分の納得のいくものをつくちゃってください。

    抽出したキーワードの加工にはいくつかルールがあります。一つは HTML の規則で、&、<、> の記号は置き換える必要があるということです。どこで置き換えるのが適当か迷いますが、今回は意味解析する前に置き換えることにしました。

    TranslateSymbol という関数が入力された文字列(第1引数)から対象文字列(第2引数)を探して、変換文字列(第3引数)に置換します。

    また色の指定は単純に font タグではさむだけかと思ったら、COLORREF って HTML の色指定と違って 0x00BBGGRR という並びなので、直してから出力する必要がありました。

    という感じで、エンジンの実装は終了です。同様に Java 用の変換エンジンなどにもチャレンジしてみてください。・・・と思ったのですが、実際に似たような処理書くのも面倒でしょうから、Java からの変換エンジンもやってみました。

    Java ちゃんとやったことがないので、間違ってたら申し訳ないですが、予約語が違う点とコメントに /** 〜 */ という形式がある他はほとんど同じでいけそうです。なんかあまり変わらないのでコピペになりそうですね。ついでに VB も作ってみました。

    コピペをベースにしているので、非常に無駄が多いですが、ここはリファクタリングネタにでもしようかと思うので、気になる人は自分でやっちゃってください。

  •  UI 部分の実装

    さて、変換モードが複数あるので、その部分の UI を作る必要があります。まずはメニューからそれらのモードを切り替えられる必要があるので、メニューリソースを追加します。

    次にこれらのメニューが切り替えられた時のハンドラを定義します。今回は似たような処理をする一連のメッセージへの対応なので、COMMAND_RANGE_HANDLER を使って見ましょう。このハンドラは一定範囲のメッセージに対して同一の処理を行う時に使います。

    同一といっても、設定する内容は微妙に違うので、その部分だけ変えることを忘れないようにしてください。ちなみにこのテクニック(そんなおおげさではないけど)はリソース ID が連続していることが条件ですので、気をつけて下さい(resource.hで確認可能)

    これによって現在のモードが切り替えられるので、GetConverter によって、適切な変換エンジンが選択されます。

    また、以前(第7回)の課題にあったように、変換モードごとに変換対象とする拡張子を決めておいて、合致するもののみ変換用のコンテキストメニューを出すようにしてみました。

    ちょっと WTL の話題が少なかったのですが、これでなんとか目的のソースを手に入れられるようになりました。あとは、リファクタリングと UI の改善をやっていきます。

  •  次回は・・・

    ここまでの内容はこちらからダウンロードできます(181 KB)

    次回は、これらのリファクタリングと作成した HTML のプレビューをやります。

    第11回

最終更新日 : 2005/06/12