【问题标题】:How to parse template languages in Ragel?如何在 Ragel 中解析模板语言?
【发布时间】:2010-07-26 01:24:03
【问题描述】:

我一直在研究简单模板语言的解析器。我正在使用雷格尔。

要求不高。我正在尝试找到可以嵌入输入字符串中任何位置的 [[tags]] 。

我正在尝试解析一种简单的模板语言,这种语言可以在 HTML 中嵌入诸如 {{foo}} 之类的标签。我尝试了几种方法来解析它,但不得不求助于使用 Ragel 扫描仪并使用仅匹配单个字符作为“全部捕获”的低效方法。我觉得这是错误的做法。我实际上是在滥用扫描仪的最长匹配偏差来实现我的默认规则(它只能是 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;
  *|;

}%%

(用 ruby​​ 编写的动作,但应该易于理解)。

您将如何为如此简单的语言编写解析器? Ragel 可能不是正确的工具吗?如果语法是这样不可预测的,您似乎必须与 Ragel 牙齿和指甲作斗争。

【问题讨论】:

    标签: parsing lexer fsm ragel


    【解决方案1】:

    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 动作位于^'[' 之后是必需的,因为虽然我们在点击[ 时标记了开始,但在我们点击非[ 之后,我们将开始尝试解析任何内容再次并备注起点。我们需要在这发生之前刷新这两个字符,从而调用动作。

    完整的解析器如下。我是用 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(
    )
    

    最后的文本节点只包含文件末尾的换行符。

    【讨论】:

      猜你喜欢
      • 2017-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多