Ragel funciona bien. Solo debes tener cuidado con lo que estás buscando. Su pregunta usa [[tag]]
y {{tag}}
, pero su ejemplo usa [[tag]]
, por lo que supongo que eso es lo que trata de tratar como especial.
Lo que quiere hacer es comer texto hasta que llegue a un corchete abierto. Si ese corchete es seguido por otro corchete, entonces es hora de comenzar a comer caracteres en minúscula hasta que llegue a un corchete. Debido a que el texto en la etiqueta no puede incluir ningún corchete, usted sabe que el único carácter sin errores que puede seguir a ese corchete es otro corchete. En ese punto, estás de vuelta donde comenzaste.
Bueno, eso es una descripción textual de esta máquina:
tag = '[[' lower+ ']]';
main := (
(any - '[')* # eat text
('[' ^'[' | tag) # try to eat a tag
)*;
La parte difícil es, ¿de dónde llama a sus acciones? No dicen que tienen la mejor respuesta a eso, pero esto es lo que ocurrió:
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);
}%%
Hay algunas cosas no obvias:
- La acción
eof
es necesario porque %PrintTextNode
solo se invoca al salir de una máquina. Si la entrada finaliza con texto normal, no habrá entrada para que salga de ese estado. Como también se llamará cuando la entrada finalice con una etiqueta, y no haya un nodo de texto final no impreso, PrintTextNode
prueba que tiene algo de texto para imprimir.
- La acción
%PrintTextNode
enclavado en después de la ^'['
es necesaria porque, a pesar de que marcamos el principio, cuando llegamos a la [
, después llegamos a un no [
, vamos a empezar a intentar analizar nada más y comente el punto de inicio. Necesitamos eliminar esos dos personajes antes de que eso ocurra, de ahí esa invocación de acción.
Sigue el analizador completo.Lo hice en C porque eso es lo que sé, pero usted debería ser capaz de convertirlo en cualquier idioma que necesita bastante fácilmente:
/* 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;
}
He aquí alguna entrada de prueba:
[[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]]
Y aquí está la salida del analizador:
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(
)
El nodo de texto final contiene solo la nueva línea al final del archivo.