
|
|
|
もくじ ・ 前のページ ・ 次のページ
5.文字列データが保持するパース処理に必要なデータについて
Brackets の各項目と構文要素を表現するデータは、複数行に渡って影響を及ぼしますが、TEditor の内部文字列オブジェクトである TEditorScreenStrings の基底クラス TRowAttributeStrings はこのデータを保持するフィールドを持っています。ファイルを開いた時、文字列に変更があった時、TEditor は該当する行文字列に対してパースを実行し、その結果を行文字列オブジェクト各行のデータフィールドに格納しています。その際、ある行をパースし終わった時点でのデータを、その次の行をパースするためのデータとして格納するという方式をとっています。
WordWrap が False の場合は、Brackets と構文要素についてだけで良いのですが、True の場合、処理は複雑になります。というのも、TEditor の文字列オブジェクトは、折り返し表示されたイメージそのままにデータを保持していますので、ある行の先頭にある語句が前の行末にある語句のつづきである場合もあるからです。この処理を行っているのが TFountainParser の LastTokenBracket メソッドです。
//////////////////////////////////////////////////////////////////////// 文字列データをどう保持するかというのは、エディタ設計者を一番悩ませる部 分ではないかと思います。 TEditor のように折り返しイメージそのままの仕様にすると、画面上端に現在 表示されている行番号とリストへのインデックスが一致するので、行番号表示や 描画の処理にかかる時間的なコストを下げることが出来ますし、キャレット位置 から文字列上の行・カラムを求める処理も簡単になる反面、細切れの文字列を パースしなければならないことによる複雑な処理が必要になります。 際限なくメモリを贅沢に使ってしまうことが許されるのであれば、 実際の文字列 1行目 12345678901234567890123456789012345678901234567890 2行目 abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij これを WrapByte 20 で折り返し処理する場合 1.1 12345678901234567890123456789012345678901234567890 1.2 12345678901234567890 1.3 1234567890 2.1 abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij 2.2 abcdefghijabcdefghij 2.3 abcdefghij といったようなデータ構造にして、パースやセーブなどは x.1 を利用すると いった方法も考えられます。 TEditor 開発当初はパースという概念すら持たずに、ひたすら動作するエディ タコンポを目指していましたので、こういう仕様は思いつきませんでした。 486Dx 50MHz という非力なマシンで、描画速度最優先が現在の仕様を生みました。 ////////////////////////////////////////////////////////////////////////
さて、折り返し処理によって分断されたトークンの扱いですが、heFountain.pas の LastTokenBracket メソッドにあるコメントを、よりわかりやすい形で述べたいと思います。
通常、文字列の先頭からパースを開始するにあたって必要と思われるデータは以下の2つです。
・Bracets で囲まれた領域かどうかを判別するためのデータ ・求める構文要素の中にあるトークンであるかどうかを判別するためのデータ
さらに、前の行末で折り返し処理によって分断されたトークンの場合では
・折り返し位置からトークンの終端までの長さ(下記パターン1の場合は7) ・その時のトークンの種類 ・構文要素を取得するために、そのトークンの直前にあったトークンの種類
といったデータがあれば、決められた長さの文字列を指定されたトークンとして切り出せば良さそうに思えます。
「折り返されたトークン」には以下の2つのパターンがあります。
パターン1 2行にまたがるトークン +---------------------+ 1 行目 | *******| +---------------------+ 2 行目 |******* | +---------------------+ パターン2 3行以上にまたがるトークン +---------------------+ 1 行目 | *******| +---------------------+ 2 行目 |*********************| +---------------------+ n-1 行目 |*********************| +---------------------+ n 行目 |******* | +---------------------+
ここで2行目の文字列をパースする場合について考ると、パターン1の場合は、7文字分を指定されたトークンとして切り出すことが可能ですが、パターン2の場合21文字を一つのトークンとして切り出してしまうと n - 1 行目を新たなトークンとして扱わなければならなくなり、この方式は使えません。
ここからが複雑怪奇劇場の始まりです。
この場合(2行目が raWrapped の場合「*1」)折り返し位置からトークンの終端までの長さを取得する方式を諦め、この値を保持する WrappedBytes フィールドには0を格納し、2行目をパースするのは、指定されたトークン専用のパースメソッドを用意することにします。この指定されたトークン専用のパースメソッド配列が FTokenMethodTable です。InitMethodTable メソッドでは、
............... // FTokenMethodTable for C := #0 to #255 do case C of toSymbol: FTokenMethodTable[C] := SymbolProc; toInteger, toFloat: FTokenMethodTable[C] := IntegerProc; toBracket: FTokenMethodTable[C] := BracketProc; toReserve: FTokenMethodTable[C] := ReserveWordProc; toComment: FTokenMethodTable[C] := CommenterProc; toAnk: FTokenMethodTable[C] := AnkProc; .............. else FTokenMethodTable[C] := SymbolProc; end;
という具合に、パースを開始するにあたって、指定されたトークンとしてパースするためのメソッドテーブルが用意されています。この「指定されたトークン」というデータは StartToken データフィールドに格納されます。
次に、WrappedBytes 分ポインタを進める処理では、そこにタブ文字があるとロジックが破綻するという「タブ文字問題」があります。これは、TEditor の描画メソッドが、タブ文字が展開された文字列受け取ってそれをパースしながら描画するという仕様に原因があります。これも描画速度を得るため、タブ文字を展開された文字列を一旦ノーマルの状態で描いてから、それをパースし、必要な部分だけ色づけして上描きするという仕様によっています。
タブ文字を含む可能性のあるトークン toComment, toSingleQuotation, toDoubleQuotation 「*2」についても WrappedBytes には0を格納し、FTokenMethodTable に処理を委ねる仕様としています。
さらに(これで終わりです)toBracket もタブ文字を含む場合があります。 NextToken メソッドでは、Brackets プロパティ値によって BracketProc へ処理を分岐しますので、折り返されたトークンが toBracket の場合も WrappedBytes には0を格納すれば良いことになるのですが、BracketProc では、該当 RightBracket に指定された文字列が折り返されている場合「*3」 RightBracket を発見することが出来ないという問題があるので、この場合だけ WrappedBytes の仕組みを利用します。
#ユーザーの皆さんが、toBracket, toComment, toSingleQuotatio, toDoubleQuotation 以外で、タブ文字を内包するトークンを取得出来るようなパーサーを書いた場合、このシステムは破綻します。
LastTokenBracket メソッドは以上のような処理を行っています。
もくじ ・ 前のページ ・ 次のページ
|
|