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);
}%%
Есть несколько неочевидных вещей:
- The
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(
)
Последний текстовый узел содержит только символ новой строки в конце файла.