Ragel でテンプレート言語を解析するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/3331731

  •  29-09-2020
  •  | 
  •  

質問

私は単純なテンプレート言語のパーサーに取り組んでいます。ラジェル使ってます。

要件は控えめです。入力文字列のどこにでも埋め込むことができる [[tags]] を見つけようとしています。

HTML 内に {{foo}} などのタグを埋め込むことができる単純なテンプレート言語を解析しようとしています。これを解析するためにいくつかのアプローチを試しましたが、Ragel スキャナーを使用し、「すべてをキャッチ」として 1 つの文字のみを照合するという非効率的なアプローチを使用する必要がありました。これは間違ったやり方だと思います。私は基本的にスキャナーの最長一致バイアスを悪用してデフォルトのルールを実装しています(長さは 1 文字のみなので、常に最後の手段である必要があります)。

%%{

  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;
  *|;

}%%

(アクションはルビで書かれていますが、理解しやすいはずです)。

このような単純な言語のパーサーを作成するにはどうすればよいでしょうか?Ragel は適切なツールではないのでしょうか?このように構文が予測できない場合は、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 の後にあるアクション ^'[' が必要なのは、打ったときにスタートをマークしましたが、 [, 、非を打った後、[, 、もう一度何かを解析して開始点を確認してみます。それが起こる前に、これらの 2 文字をフラッシュする必要があるため、そのアクションが呼び出されます。

完全なパーサーが続きます。私が知っていることなので 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(
)

最後のテキスト ノードには、ファイルの末尾の改行のみが含まれます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top