TSsParser

概要

「さくらスクリプト」の多機能パーサ機能を持つ非ビジュアルコンポーネントです。単一行のさくらスクリプトを処理します。つまり典型的には\tから始まり\eで終わる、1行のスクリプトです。Entryなどが絡んだ複数行スクリプトには対応していませんが、補助としては使えます。

といった用途の両方に使えるよう設計されています。また、タグのパターンについては完全にカスタマイズが可能です。

基本的な使い方

TagPattern, MetaPatternプロパティに、さくらスクリプトの解析の基準となるパターンを指定します。添付されているテキストファイルをコピーすれば一応OKです。

InputStringプロパティに、スクリプトを指定すれば、スクリプトが解析され、Count, MarkUpType, Strの各プロパティを通してアクセスできます。

例えば、「\t\s[0]いらっしゃい%ませ、%usernameさん、\s[5]\\300のお\買い上げになりますね\e」という文字列をInputStringに入れた場合には、Count=10となり、MarkUpType, Strプロパティには以下のような値が入ります。

TSsParserでは、Indexで区別されるスクリプト素片、つまり下の表における各行を「マークアップ」と呼ぶことにします。(本来、タグやメタ文字以外の文字列までマークアップと呼ぶのは変ですが…)

Index Str[Index] MarkUpType[Index]
0 \t mtTag
1 \s[0] mtTag
2 いらっしゃい%ませ、 mtStr
3 %username mtMeta
4 さん、 mtStr
5 \s[5] mtTag
6 \\300のお mtStr
7 \買 mtTagErr
8 い上げになりますね mtStr
9 \e mtTag

リファレンス-プロパティ

LeaveEscape: boolean [実/設][読/書]
デフォルトはtrueです。mtStrで切り出された通常文字列に含まれる、\\\% の文字列を変換せずに残すかどうか設定します。falseにすることで、\\\% は1文字に変換されてmtStrマークアップに代入されます。
TSsParserをスクリプトの色分けや、危険タグチェックなどに使用する場合は、文字列長やスクリプトそのものが変わらないようにtrueにします。独自SSTPサーバ開発の場合などは、falseにすることで変換の手間を省けます。
変更した場合、その結果は次回の解析から反映されます。
EscapeInvalidMeta: boolean [実/設][読/書]
デフォルトはfalseです。MetaPatternによって、%文字以降がメタ文字列と判断できない場合、%文字を「\%」にエスケープするかどうか設定します。
TSsParserをスクリプトの色分けなどに使用する場合は、文字列長が変わらないようにfalseにします。堅牢なスクリプト作成のための文法チェックのためにはtrueにします。
変更した場合、その結果は次回の解析から反映されます。
TagPattern: TStrings [実/設][読/書]
MetaPattern: TStrings [実/設][読/書]
それぞれ、タグ切り出しのためのパターン、メタ文字列切り出しのためのパターンです。別項「パターンについて」参照。変更した場合、その結果は次回の解析から反映されます。
InputString: String [実のみ][読/書]
このプロパティに書き込んで、解析すべきスクリプトを設定します。読み出しの場合は最後に解析したスクリプトが入っています。
Count: integer [実のみ][読のみ]
スクリプト解析後のマークアップ総数を返します。スクリプトマークアップをループ処理する場合の上限値として使用します。
Str[Index: integer]: String [実のみ][読のみ]
解析後の文字列を返します。最初のマークアップはStr[0]、最後のマークアップはStr[Count-1]です。LeaveExcape, EscapeInavlidMetaの両プロパティの影響を受けます。
Extra[Index: integer]: String [実のみ][読/書]
ご自由にお使いください、の文字列です。解析結果にメモをつけたりする用途にどうぞ。
MarkUpType[Index: integer]: TSsMarkUpType [実のみ][読のみ]
type TSsMarkUpType = (mtTag, mtMeta, mtTagErr, mtStr);
マークアップの種類を返します。最初のマークアップはMarkUpType[0]、最後のマークアップはMarkUpType[Count-1]です。

リファレンス-イベント

OnSsParse: TSsParseEvent

TSsParseEvent = procedure (Sender: TObject; const Script: String;
var Len: integer; var MarkType: TSsMarkUpType; var Extra: String) of object;
タグまたはメタ文字列のパターンマッチ試行の前に呼び出されます。このイベントを使用すると、TagPattern, MetaPatternの仕様では切り出せないマークアップが将来できた場合に、スクリプトからのタグやメタ文字列の切り出しを自由にコーディングできます。
Scriptは解析途中のスクリプトです。1文字目は必ず「\」または「%」となっています。Lenは呼び出し時には0が代入されています。
このイベントハンドラ内で、Scriptの先頭から始まる部分を解析します。マークアップを解釈できた場合はそのマークアップの長さ(バイト数)をLen(>2)に、タイプをMarkTypeに、必要ならExtraに文字列を入れて、イベントハンドラを終了してください。解釈できない場合はLen=0のまま終了すれば、そのまま通常のパターンマッチ試行に入ります。
要は、文字列先頭からここまでがタグ(メタ文字列)だよ、というのを見つけて返してくれ、ということです。
Len>1であっても、Scriptの1文字目が\なのにMarkUpTypemtTagでもmtTagErrでもない場合、あるいはScriptの1文字目が%なのにMarkUpTypeがmtMetaでない場合は例外が発生します。

リファレンス-メソッド

function Match(Str, Pattern: String): integer;
SSTP解析用のパターンマッチングを行います。Strの先頭部分、Patternで示されるパターンが存在すれば、マッチした部分のバイト数(文字数ではない)を返します。マッチングが失敗した場合は0を返します。
例えば、
Match('ABC', 'AB') = 2
Match('ABC', 'A%.%.') = 3
Match('A20BC', 'A%D') = 3
Match('\s[20]', '\s%b') = 6
Match('\s2', '\s%b') = 0
function MatchP(PStr, PPattern: PChar): integer;
Matchと基本的に同機能ですが、引数に文字ポインタを使用します。不必要な文字列のコピーが避けられるため、繰り返し呼ぶ場合や、とある文字列の途中から解析したりする場合には高速です。
function MarkUpAt(const Pos: integer): integer;
Posバイト目(Pos文字目ではありません)にあるマークアップのインデックスを返します。
function GetParam(Tag: String; const Index: integer): String;
\s[3]\_c[こんにちは] , \q1[#cancel][キャンセル] といったマークアップから、スクウェアブラケットに囲まれたパラメータを取り出します。Tagは取り出したいタグ全体、Indexは何番目のパラメータを取り出すか、で、1から始まります。\\\] によるエスケープに対応し、これらのエスケープは自動的に元の形に戻されます。
Indexがゼロ以下の場合、パラメータが取り出せなかった場合などには、空文字列が返ります。
GetParam('\s[3]', 1) = '3';
GetParam('\s[3]', 2) = '';
GetParam('\j[http://www.yahoo.co.jp/index[1\].html]', 1) = 'http://www.yahoo.co.jp/index[1].html'
function EscapeParam(const Param: String): String;
\j[] の中に安全に代入できるように、\\\ に、]\] に変換した文字列を返します。堅牢なスクリプト作成のためには是非利用するようにしてください。

パターンについて

TagPattern, MetaPatternプロパティに、さくらスクリプトを解析するときのパターンを指定します。

アーカイブ同梱のテキストファイルからコピーすることもできますが、将来のタグ拡張等のためにこの仕様が存在します。このパターンリスト自身をテキストファイルなどから読み込むようにすることで、実行ファイルを更新せずにタグ解析部のみを更新することも可能です。

「タグが\で始まる」「メタ文字列が%で始まる」「\\や\%はエスケープ」などといった基本的な仕様が変更にならない限り、タグの通常の増減に関しては、Patternプロパティを変更することで、大抵対応できると思います。特殊な書き方の場合、イベントを利用する方法もあります。

パターンの書き方概略

TagPatternの各行が、1つのタグに応答するパターンです。例えば、「\e」という行をTagPatternプロパティに追加することで、「\e」というタグに反応できるようになり、「!_c」という行を追加すれば、「\_c」というタグはエラーである、と解析するようになります。

(このようにエラーになったタグを無視するのか、あるいはどう処理するのかどうかなどについては、解析者の実装にかかっています。TSsParserでは、そのタグをエラーとして属性づけるだけです。)

パターンは上から順番に試行され、マッチした時点で試行を中止します。

TagPatternの各行は、\記号または!記号で始めてください。\記号で始まるパターンは、マッチした場合それを正当なタグをして処理します。!記号で始まるパターンは、マッチした場合それをタグのエラーとして処理します。

例えば、「\w」タグの処理のためには、

\w%d
!w%.

という2行をTagPatternに加えるとよいでしょう。これで、\w9 等は正当なタグで、数字以外がwの後にきた場合は \wあ といった全体をタグエラーとして処理する、という意味になります。

MetaPatternの各行が、1つのメタ文字列に応答するパターンです。例えば「selfname」という行をMetaPatternプロパティに追加することで、「%selfname」というメタ文字列に反応します。MetaPatternの場合は、%以降に続く文字列をそのまま記述するような格好で大丈夫です。(パターンも使えます)

マッチ試行はパターンリストの上から順番に行われるため、MetaPatternプロパティで、例えば selfname2 という行は selfname より上に配置される必要があります。

パターンの記述方法

正規表現みたいなものですが、そこまで高機能ではありません。逆に正規表現では表現しづらい表記に対応してたりもしますが。

マッチング詳細

マッチングは以下のように行われます。

  1. マークアップと思われる「\」または「%」を先頭から探します。「\\」や「\%」はエスケープですので読み飛ばされます。その部分までは、マークアップではない通常の文字列です。
  2. 「%」を見つけた場合はメタ文字列の可能性があるので、マッチングを開始します。%以下が有効なメタ文字列として解釈できない場合は、「%」は意味をなさない通常の%文字列として、\%にエスケープされて(EscapeInvalidMetaで制御可能)前のmtStrマークアップにくっつきます。

  3. 「\」を見つけた場合のマッチングを開始します。\以下が正規のタグとして解釈できた場合はmtTagタイプとして、エラータグとなった場合はmtTagErrとして切り出します。TagPatternのどの行にもマッチしなかった場合は、\の次の1文字を含めて、エラータグとして切り出します。
  4. 最初に戻ります。

使用例

SSTPサーバ

独自SSTPサーバ構築補助に利用する場合は、LeaveEscape := false; EscapeInvalidMeta := false; とすると簡単です。

SsParser1.InputString := Edit1.Text;
for i := 0 to SsParser1.Count-1 do begin
  case SsParser.MarkUpType[i] of
    mtStr: Memo1.Lines.Add(SsParser.Str[i]);
    mtTag: {タグ関連の処理}
    mtTagErr: Memo1.Lines.Add(SsParser.Str[i]); //処理せずにそのまま表示
    mtMeta: {メタ文字列変換語表示}
  end;
end;

スクリプト色分けHTML作成

色分けが目的の場合、スクリプトが変わる心配のないよう、LeaveEscape := true; EscapeInvalidMeta := false; とします。

以下は、タグ部分に色を設定するHTMLマークアップです。

var Html: String;
//
SsParser1.InputString := Edit1.Text;
for i := 0 to SsParser1.Count-1 do begin
  case SsParser.MarkUpType[i] of
    mtStr:    Html := Html + SsParser.Str[i];
    mtTag:    Html := Html + '<font color="green">' + SsParser.Str[i] + '</font>';
    mtTagErr: Html := Html + '<font color="red">' + SsParser.Str[i] + '</font>';
    mtMeta:   Html := Html + '<font color="blue">' + SsParser.Str[i] + '</font>';
  end;
end;
Edit2.Text := Html;

OnSsParse使用例

以下は、OnSsParseイベントの使用例です。もっとも単純に、\uタグを判定します。TagPatternの1行目に \u と書いた場合と同じ動作となります。

procedure TForm1.SsParser1SsParse(Sender: TObject; const Script: String;
  var Len: integer; var MarkType: TSsMarkUpType; var Extra: String)
begin
  if Pos('\u', Script) = 1 then begin
    Len := Length('\u');
    MarkType := mtTag;
  end;
end;

使用例 - サーフィス判定

一番最後のサーフィスが何になるかを判定します。ただし本来は、\0, \1によるスコープ切り替え処理や、\_sによるシンクロナイズドセッションの処理が必要でしょう。

var Last: integer;
//
SsParser1.InputString := Edit1.Text;
for i := 0 to SsParser1.Count-1 do begin
  if SsParser.Match(SsParser.Str[i], '\s%b') > 0 then begin
    try
      Last := StrToInt(SsParser.GetParam(SsParser.Str[i]));
    except
      on EConvertError do
        ;
    end;
  end;
end;

諸注意

再解析について

with SsParser1 do InputString := InputString;

とすることで、EscapeInvalidMeta等の解析オプションや、MetaPattern等の解析パターンが変化したときに再解析を行えます。変な書き方ですが。

改行文字について

InputStringに改行文字やその他の空白文字が含まれていた場合、通常の文字と同じように扱います。つまり、改行文字だからといって特に何らかの処理が行われたり、逆に処理の邪魔になったりすることはありません。改行を \n に変更したい、などの場合はあらかじめ StringReplace などを利用して自分で変換してください。

タグエラーの利用について

TagPatternで行頭が ! で始まるパターンを指定することや、OnSsParseイベントでmtTagErrマークアップを返す事で、\で始まる任意の文字列をタグエラーと見なすことができます。また、TagPatternでマッチしなかった場合は、\記号の次の1文字までを含めてタグエラーとして2文字分切り出します。この利用方法ですが、

改版履歴

2003/04/01
内部を書き直して、10倍〜100倍ほど高速化。開発マシン(PentiumIII 1GHz)では、2KBの長文スクリプトを0.01秒以内に解析するようになりました。
2003/04/03
PositionMarkUpAtを追加。