Question

J'ai travaillé sur un analyseur pour un langage de modèle simple.J'utilise Ragel.

Les exigences sont modestes.J'essaie de trouver des [[tags]] qui peuvent être intégrés n'importe où dans la chaîne d'entrée.

J'essaie d'analyser un langage de modèle simple, quelque chose qui peut avoir des balises telles que {{foo}} intégrées dans HTML.J'ai essayé plusieurs approches pour analyser cela, mais j'ai dû recourir à un scanner Ragel et utiliser l'approche inefficace consistant à ne faire correspondre qu'un seul caractère comme "fourre-tout".Je pense que ce n’est pas la bonne façon de procéder.J'abuse essentiellement du biais de correspondance la plus longue du scanner pour implémenter ma règle par défaut (elle ne peut comporter qu'un seul caractère, elle devrait donc toujours être le dernier recours).

%%{

  machine parser;

  action start      { tokstart = p; }          
  action on_tag     { results << [:tag, data[tokstart..p]] }            
  action on_static  { results << [:static, data[p..p]] }            

  tag  = ('[[' lower+ ']]') >start @on_tag;

  main := |*
    tag;
    any      => on_static;
  *|;

}%%

(actions écrites en rubis, mais devraient être faciles à comprendre).

Comment procéderiez-vous pour écrire un analyseur pour un langage aussi simple ?Ragel n’est-il peut-être pas le bon outil ?Il semble que vous deviez combattre Ragel bec et ongles si la syntaxe est imprévisible comme celle-ci.

Était-ce utile?

La solution

Ragel fonctionne bien.Vous devez juste faire attention à ce que vous associez.Votre question utilise les deux [[tag]] et {{tag}}, mais votre exemple utilise [[tag]], donc je suppose que c'est ce que vous essayez de traiter comme spécial.

Ce que vous voulez faire, c'est manger du texte jusqu'à ce que vous atteigniez une parenthèse ouverte.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 une parenthèse proche.Étant donné que le texte de la balise ne peut inclure aucun crochet, vous savez que le seul caractère non erroné qui peut suivre ce crochet fermant est un autre crochet fermant.À ce stade, vous revenez là où vous avez commencé.

Eh bien, voici une description textuelle de cette machine :

tag = '[[' lower+ ']]';

main := (
  (any - '[')*  # eat text
  ('[' ^'[' | tag)  # try to eat a tag
)*;

La partie délicate est la suivante : où appelez-vous vos actions ?Je ne prétends pas avoir la meilleure réponse à cette question, mais voici ce que j'ai trouvé :

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 :

  • Le eof une action est nécessaire parce que %PrintTextNode n'est invoqué qu'en quittant une machine.Si l'entrée se termine par un texte normal, il n'y aura aucune entrée pour lui faire quitter cet état.Parce qu'il sera également appelé lorsque l'entrée se termine par une balise et qu'il n'y a pas de nœud de texte final non imprimé, PrintTextNode teste qu'il a du texte à imprimer.
  • Le %PrintTextNode action nichée après le ^'[' est nécessaire parce que, même si nous avons marqué le début lorsque nous avons atteint le [, après avoir heurté un non-[, nous allons recommencer à essayer d'analyser quoi que ce soit et remarquer le point de départ.Nous devons vider ces deux personnages avant que cela ne se produise, d'où cette invocation d'action.

L'analyseur complet suit.Je l'ai fait en C parce que c'est ce que je sais, mais vous devriez pouvoir le transformer assez facilement dans le langage dont vous avez besoin :

/* 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 voici le résultat 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 nœud de texte final contient uniquement la nouvelle ligne à la fin du fichier.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top