如何收集不连续的输入片段并将这些片段连接在一起?

How to scoop up non-consecutive pieces of the input and concatenate the pieces together?

这里是 XML 开始标签和结束标签,在 start-tag/end-tag 对中带有 Hello, world

<foo>Hello, world</foo>

在 XML 中有一个叫做 CDATA 部分的东西。它有这种不寻常的语法:

<![CDATA[...]]>

CDATA 部分是数据的包装。

如果 start-tag/end-tag 对包含 CDATA 部分,则 start-tag/end-tag 对内的数据是 CDATA 部分外的数据与 CDATA 部分内的数据的串联。比如foo的内容:

<foo>First expression <![CDATA[A < B]]>, second expression <![CDATA[C < D + 1]]>.</foo>

这是:

First expression A < B, second expression C < D + 1.

问题:如何将foo中的每一个片段都取出来并连接在一起?也就是如何把这些片("First expression ""A < B"", second expression ""C < D + 1"".")捞起来拼接起来?

下面是我创建的词法分析器。如果 foo 没有任何 CDATA 部分,它工作正常,但当 foo 有 CDATA 部分时,词法分析器挂起。

请注意,我的词法分析器使用 yyless() 和 yymore()。我正在模仿 Flex & Bison 一书中第 137 页底部的示例。词法分析器收集 CDATA 部分之前的字符加上 CDATA 开始语法,然后将 CDATA 开始语法推回到输入中并调用 yymore()。另一条规则丢弃 CDATA 开始语法。我认为这不是正确的做法。实现这一目标的正确方法是什么?有没有不使用 yyless() 和 yymore() 就可以解决这个问题的方法?

%option noyywrap
%x ELEMENT_CONTENT
%{
  enum yytokentype {
    TOK_START_TAG = 258,
    TOK_END_TAG = 259,
    TOK_ELEMENT_CONTENT = 260
  };
%}
%%  
<INITIAL>{
   "<foo>"    { BEGIN(ELEMENT_CONTENT); return(TOK_START_TAG); }
   "</foo>"   { return(TOK_END_TAG); }
}

<ELEMENT_CONTENT>{
   [^<]+"<![CDATA["     { yyless(9); yymore(); }
   "<![CDATA["          { /* ignore CDATA start syntax */ }
   [^\]]+"]]>"          { yyless(3); yymore(); }
   "]]>"                { /* ignore CDATA end syntax */ }
   [^<]*                { BEGIN(INITIAL); return TOK_ELEMENT_CONTENT; }   
}
%%
int main(int argc, char *argv[])
{
    printf("In the lexer\n");
    yyin = fopen(argv[1], "r");
    int tok;
    while (tok = yylex()) {
       switch (tok){
          case 258:
             printf("TOK_START_TAG: %s\n", yytext);
             break;
          case 259:
             printf("TOK_END_TAG: %s\n", yytext);
             break;
          case 260:
             printf("TOK_ELEMENT_CONTENT: %s\n", yytext);
             break;
          default:
             printf("unexpected: %s\n", yytext);
       }
    }
    
    fclose(yyin);
    
    return 0;
}

HTML 标签内的文本可能会被很多东西打断,而不仅仅是 CDATA 部分。它可以包含实体引用或数字实体引用。它可能包含通常会被忽略的注释,或者它可能包含解析器不感兴趣的 content-less 标记。等等。消除这些东西的词法分析器可能在特定上下文中有用,或者它可能会产生一个问题,词法分析器的客户最终会撕毁他们的头发试图解决。总的来说,这些是词法分析器可能不应该尝试解决的问题;更好的设计是仅记录文本可能被词法化为几个连续的 TEXT 标记(或几个连续的“text-like”标记)这一事实。这当然是我会做的方式。

我了解到您正试图利用 Flex 的内部缓冲区来就地进行标记串联,从而避免额外的内存分配和复制。这是一个诱人的优化,但它会让您陷入细节和极端情况的曲折迷宫。此外,Flex 的设计理念是令牌“不太长”;极长的令牌会导致 Flex 算法效率低下。

有 well-known 优化字符串组装的技术,您最好使用其中之一(或实现高效字符串连接的库)并将 Flex 的内部结构留给 Flex。 :-)