TSsParser

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

主な用途

使用条件

SYNOPSIS - 基本的な使い方

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文字目が\なのにMarkUpTypeがmtTagでも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 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」というタグはエラーである、と解析するようになります。

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

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 + '<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;

使用例 - サーフィス判定

一番最後のサーフィスが何になるかを判定します。ただし本来は、\h, \uによる切り替えなどが必要でしょう。

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 begin end;
    end;
  end;
end;

諸注意

再解析について

with SsParser1 do InputString := InputString;

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

改行文字について

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

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

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

解析についての注意

「任意」phase 40.01以前の解析問題

「あれ以外の何か with 任意 phase 40」には、スクリプト解析に関して、以下に挙げる問題があります。原因は面倒なので挙げませんが。

TSsParserとは関連しませんが、%songname %enamyname 等が危険タグを含む文字列に変換された場合にそのまま実行される、といった問題点もあります。最新版では以上の問題はすべて解決されています。

TSsParserは以上の問題点を含まないはずです(パターンの書き方によっては別ですが)ので、TSsParserの解析は、「何か phase inverse 22.00」以降互換、ということになります。Phase40.01以前対応を視野に入れたプログラム作成でセキュリティに考慮したい場合、上に挙げた問題点に対して自力で対応してください。

TSsParserは独自SSTPサーバ作成補助も視野にいれたコンポーネントですので、上記の問題に対して対応する予定はありません。