答案 0 :(得分:20)
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
之后的^'['
操作,因为虽然我们在点击[
之后标记了开始,但是在我们点击非[
之后,我们将开始尝试再次解析任何内容并注明起点。在发生这种情况之前,我们需要刷新这两个字符,因此该动作调用。完整的解析器如下。我是用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(
)
最终文本节点仅包含文件末尾的换行符。