Ragel fonctionne très bien. Vous devez juste faire attention à ce que vous faites correspondre. Votre question utilise à la fois [[tag]]
et {{tag}}
, mais votre exemple utilise [[tag]]
, donc je pense que c'est ce que vous essayez de traiter comme spécial.
Ce que vous voulez faire est de manger du texte jusqu'à ce que vous frappiez un support ouvert. Si cette parenthèse est suivie d'une autre parenthèse, alors il est temps de commencer à manger des caractères minuscules jusqu'à ce que vous atteigniez un crochet. Comme le texte de la balise ne peut inclure aucune parenthèse, vous savez que le seul caractère non-erreur qui peut suivre ce close-bracket est un autre close-bracket. À ce stade, vous êtes de retour là où vous avez commencé.
Eh bien, c'est une description textuelle de cette machine:
tag = '[[' lower+ ']]';
main := (
(any - '[')* # eat text
('[' ^'[' | tag) # try to eat a tag
)*;
La partie délicate est, où appelez-vous vos actions? Je ne prétends pas avoir la meilleure réponse à cette question, mais voici ce que je suis venu avec:
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);
}%%
Il y a quelques choses non évidentes:
- L'action
eof
est nécessaire parce que %PrintTextNode
est seulement invoqué en quittant une machine. Si l'entrée se termine par du texte normal, il n'y aura pas d'entrée pour le faire quitter cet état. Comme il sera également appelé lorsque l'entrée se termine par une étiquette, et qu'il n'y a pas de nœud de texte final non imprimé, PrintTextNode
teste qu'il a du texte à imprimer.
- L'action
%PrintTextNode
niché après la ^'['
est nécessaire parce que, si nous avons marqué le début quand nous avons touché le [
, après que nous ayons atteint un [
non, nous allons commencer à essayer d'analyser à nouveau quoi que ce soit et remarque le point de départ. Nous devons vider ces deux caractères avant que cela n'arrive, d'où cette invocation d'action.
L'analyseur complet suit.Je l'ai fait en C parce que ce que je sais, mais vous devriez être en mesure de le transformer en quelque langue que vous avez besoin assez facilement:
/* 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;
}
Voici quelques entrées de test:
[[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]]
Et est ici la sortie de l'analyseur:
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(
)
le noeud texte final ne contient que la nouvelle ligne à la fin du fichier.