如何收集不连续的输入片段并将这些片段连接在一起?
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。 :-)
这里是 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。 :-)