Ragel は正常に動作します。何を合わせるかだけは注意が必要です。あなたの質問では と の両方[[tag]]
を使用し{{tag}}
ていますが、あなたの例では を使用して[[tag]]
いるため、それが特別なものとして扱われようとしていると思います。
あなたがしたいことは、開き括弧にぶつかるまでテキストを食べることです. その括弧の後に別の括弧が続く場合は、閉じ括弧に到達するまで小文字を使い始めます。タグ内のテキストには角かっこを含めることはできないため、その閉じかっこの後に続くことができる唯一の非エラー文字は別の閉じかっこであることがわかります。その時点で、あなたは出発点に戻っています。
さて、それはこのマシンの逐語的な説明です:
tag = '[[' lower+ ']]';
main := (
(any - '[')* # eat text
('[' ^'[' | tag) # try to eat a tag
)*;
難しいのは、自分のアクションをどこで呼び出すかということです。私はそれに対する最良の答えを持っているとは主張していませんが、ここに私が思いついたものがあります:
static char *text_start;
%%{
machine parser;
action MarkStart { text_start = fpc; }
action PrintTextNode {
int text_len = fpc - text_start;
if (text_len > 0) {
printf("TEXT(%.*s)\n", text_len, text_start);
}
}
action PrintTagNode {
int text_len = fpc - text_start - 1; /* drop closing bracket */
printf("TAG(%.*s)\n", text_len, text_start);
}
tag = '[[' (lower+ >MarkStart) ']]' @PrintTagNode;
main := (
(any - '[')* >MarkStart %PrintTextNode
('[' ^'[' %PrintTextNode | tag) >MarkStart
)* @eof(PrintTextNode);
}%%
明らかでないことがいくつかあります。
- マシンを離れるときにのみ呼び出されるため、
eof
アクションが必要です。%PrintTextNode
入力が通常のテキストで終了した場合、その状態から抜け出すための入力はありません。入力がタグで終了し、最終的な未印刷のテキスト ノードがない場合にも呼び出されるため、PrintTextNode
印刷するテキストがあるかどうかをテストします。
- の
%PrintTextNode
後にあるアクション^'['
が必要なのは、 をヒットしたときに開始をマークしました[
が、非 をヒットした後、[
再び何かを解析しようとし始め、開始点を再マークするためです。その前にこれら 2 つの文字をフラッシュする必要があるため、アクションが呼び出されます。
完全なパーサーが続きます。それは私が知っていることなので、Cで行いましたが、必要な言語に簡単に変換できるはずです。
/* ragel so_tag.rl && gcc so_tag.c -o so_tag */
#include <stdio.h>
#include <string.h>
static char *text_start;
%%{
machine parser;
action MarkStart { text_start = fpc; }
action PrintTextNode {
int text_len = fpc - text_start;
if (text_len > 0) {
printf("TEXT(%.*s)\n", text_len, text_start);
}
}
action PrintTagNode {
int text_len = fpc - text_start - 1; /* drop closing bracket */
printf("TAG(%.*s)\n", text_len, text_start);
}
tag = '[[' (lower+ >MarkStart) ']]' @PrintTagNode;
main := (
(any - '[')* >MarkStart %PrintTextNode
('[' ^'[' %PrintTextNode | tag) >MarkStart
)* @eof(PrintTextNode);
}%%
%% write data;
int
main(void) {
char buffer[4096];
int cs;
char *p = NULL;
char *pe = NULL;
char *eof = NULL;
%% write init;
do {
size_t nread = fread(buffer, 1, sizeof(buffer), stdin);
p = buffer;
pe = p + nread;
if (nread < sizeof(buffer) && feof(stdin)) eof = pe;
%% write exec;
if (eof || cs == %%{ write error; }%%) break;
} while (1);
return 0;
}
ここにいくつかのテスト入力があります:
[[header]]
<html>
<head><title>title</title></head>
<body>
<h1>[[headertext]]</h1>
<p>I am feeling very [[emotion]].</p>
<p>I like brackets: [ is cool. ] is cool. [] are cool. But [[tag]] is special.</p>
</body>
</html>
[[footer]]
パーサーからの出力は次のとおりです。
TAG(header)
TEXT(
<html>
<head><title>title</title></head>
<body>
<h1>)
TAG(headertext)
TEXT(</h1>
<p>I am feeling very )
TAG(emotion)
TEXT(.</p>
<p>I like brackets: )
TEXT([ )
TEXT(is cool. ] is cool. )
TEXT([])
TEXT( are cool. But )
TAG(tag)
TEXT( is special.</p>
</body>
</html>
)
TAG(footer)
TEXT(
)
最後のテキスト ノードには、ファイルの末尾にある改行のみが含まれます。