Flex& Bison:是否可以使用Flex& Bison中的Backreference解析Raw字符串文字?

时间:2018-02-15 06:18:58

标签: parsing bison flex-lexer lexical-analysis

我尝试使用Flex& Bison解析原始字符串文字。但无法解析它。

输入:

R"foo(Hello World)foo"

软硬度:

...
raw_string     R"([^\(]*)\(([^\)]*)\)\1" 

%%
{raw_string}   {return raw_string;}
%%

野牛:

%{
...
%}

%token raw_string
%start run

%%
run : raw_string;

%%

int main()
{
  yyin = stdin;
  do
  {
    yyparse();
  } while(!feof(yyin));
  return =0;
}

错误:

 invalid Syntax: at raw_string

请帮助我使用Flex和Bison解析原始字符串文字。如果无法进行后向引用,则可以使用任何替代方法来解析flex和bison中的原始字符串。

1 个答案:

答案 0 :(得分:2)

Flex patterns既没有反向引用也没有非贪婪的匹配,你需要这两个匹配才能正确识别带有“常规”表达式的原始字符串。 [注1]

然而,flex确实有一个功能 - "start conditions" - 这使得实现这些功能变得非常简单,至少在这种情况下,您永远不需要回溯来尝试不同的反向引用子模式。< / p>

开始条件是对给定词汇上下文使用不同规则集的一种方式;在这种情况下,原始字符串中的上下文。

因此(简化的)C ++原始字符串的骨架解决方案是:

%x C_RAW_STRING
d_char_sequence [^()\\[:space:]]{0,16}
%%
   size_t delim_len;
R["]{d_char_sequence}[(]    { delim_len = yyleng - 3;
                              yymore();
                              BEGIN(C_RAW_STRING);
                            }
   /* Rules for other tokens omitted */
[[:space:]]+                ;                /* Ignore whitespace */
.                           return *yytext;  /* Fallback rule */
<C_RAW_STRING>{
   [^)]+                    yymore();
   [)]+                     yymore();
   [)]+{d_char_sequence}["] { if (yytext[yyleng - (delim_len + 2)] == ')' &&
                                  memcmp(yytext + yyleng - (delim_len + 1),
                                  yytext + 2, delim_len) == 0) {
                                    BEGIN(INITIAL);
                                    return raw_string;
                                  }
                              yymore();
                            }
   <<EOF>>                  { yyerror("Unterminated raw string");
                              BEGIN(INITIAL);
                              return 0;
                            }
}

一些解释

  • 第1行:将开始条件C_RAW_STRING声明为独占。 (参见上面的弹性手册,链接)。

  • 第2行:d_char_sequence匹配C ++标准所称的“d-char-sequence”(参见[lex.string],§5.13.5):最多16个字符,除了括号,反斜杠或空格字符。有关详细信息,请参阅Flex手册章节。

  • 第4行:在第一条规则之前的规则部分开头的缩进语句只是插入yylext函数的顶部。因此,它们可用于声明在yylex的单个调用期间使用的局部变量。

  • 第5-8行:当我们检测到原始字符串文字的开头时,我们:

    • 记住分隔符字符串的长度。 (因为我们将分隔符字符串保留在令牌的开头,我们不需要将其复制到任何地方。只需要知道它有多长。)
    • 使用yymore()告诉词法分析器将下一个匹配附加到当前令牌,而不是启动新令牌。
    • 使用BEGIN更改为C_RAW_STRING开始条件。
  • 第12行:启动C_RAW_STRING开始条件的规则块。这是一个flex扩展,在其他lex实现中不可用。为了兼容性,所有模式都必须使用<C_RAW_STRING>单独标记。
  • 第13行:除了右括号之外的任何字符序列都只是附加到令牌上。同样,yymore()表示我们尚未完成令牌。
  • 第14行:此外,任何数量的近括号都只是附加到令牌上。但请注意,此规则仅适用于下一个规则不适用的情况,因为flex始终选择最长匹配。再次,请参阅flex manual
  • 第15-22行:这匹配的内容看起来可能是一个接近的分隔符。因为我们是标记而不是正则表达式匹配,所以这将找到下一个这样的段而不是最后一个段,所以它有效地进行了非贪婪的重复。一旦我们匹配它,我们需要查看它是否实际匹配开放分隔符序列,该序列位于我们正在构建的令牌的开头。给定分隔符字符串的长度,我们首先确保起始)是我们期望的起始位置,然后使用memcmp来比较两个分隔符序列。 (我们使用memcmp而不是strcmp,因为我们比较的序列不是NUL终止的;它们是令牌的子串。strncmp可能是另一种可能。)

    如果分隔符字符串匹配,我们找到了令牌的结尾,我们可以返回它。在这种情况下,我们需要将开始条件重置为INITIAL,以便正常扫描下一个标记。如果字符串不匹配,我们使用yymore告诉yylex我们还没有完成令牌,并继续寻找正确的分隔符。请注意,我们可以跳过整个匹配的结束分隔符模式,因为分隔符字符不能包含右括号,因此下一个试验结束序列不能在此内部开始。

  • 第23-27行:如果在原始字符串中检测到输入结束,则表示原始字符串未正确终止。我们发出错误消息并返回0表示遇到了EOF。此时重置开始条件并不是必需的,但这样做似乎更清晰。

注释

  1. 例如,您可以使用以下Gnu grep命令(使用-P选项启用PCRE正则表达式功能),但由于它是一个简单的正则表达式搜索而不是标记符,因此它可以生成误报(例如,在注释中看起来像原始字符串的东西):

    grep -Po '"([^()\\[:space:]]*)\(.*?\)\1"'