首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Flex&Bison :是否可以使用Flex & Bison中的Backreference解析原始字符串文本?

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

Stack Overflow用户
提问于 2018-02-15 06:18:58
回答 1查看 889关注 0票数 2

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

输入:

代码语言:javascript
复制
R"foo(Hello World)foo"

挠曲:

代码语言:javascript
复制
...
raw_string     R"([^\(]*)\(([^\)]*)\)\1" 

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

野牛:

代码语言:javascript
复制
%{
...
%}

%token raw_string
%start run

%%
run : raw_string;

%%

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

错误:

代码语言:javascript
复制
 invalid Syntax: at raw_string

请帮助我使用Flex和Bison解析原始字符串文本。如果不可能返回引用,那么在flex和bison中解析原始字符串是否有其他的方法。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-02-15 19:24:11

挠曲模式既没有反向引用,也没有非贪婪的匹配,为了正确识别带有“正则”表达式的原始字符串,您需要这两个匹配。注1

然而,flex确实有一个特性-- “启动条件” --这使得实现这些特性变得非常简单,至少在这种情况下,您不需要回溯就可以尝试不同的反引用子模式。

start条件是对给定的词法上下文使用不同规则集的一种方式;在本例中,是原始字符串中的上下文。

注释:原始代码有一个错误,如果原始字符串后面紧跟几个非空格字符和另一个双引号,则会导致它遗漏一个原始字符串的紧凑型序列。这是一个非常不可能的情况,这也许可以解释为什么没有人注意到这个错误。我把所有企图的聪明都移除了。向任何复制buggy代码的人道歉。

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

代码语言:javascript
复制
%x C_RAW_STRING
dchar_seq [^()\\[:space:]]{0,16}
%%
   size_t delim_len;
R["]{dchar_seq}[(]    { delim_len = yyleng - 3;
                        yymore();
                        BEGIN(C_RAW_STRING);
                      }
R["]{dchar_seq}       { yyerror("Invalid raw string opener"); }

   /* Rules for other tokens omitted */

[[:space:]]+          ;                /* Ignore whitespace */
.                     return *yytext;  /* Fallback rule */
<C_RAW_STRING>{
   [^"]+              yymore();
   ["]                { if (yytext[yyleng - (delim_len + 2)] == ')' &&
                            memcmp(yytext + yyleng - (delim_len + 1),
                                   yytext + 2, delim_len) == 0) {
                           BEGIN(INITIAL);
                           return STRING_LITERAL;
                         }
                         else yymore();
                       }
   <<EOF>>             { yyerror("Unterminated raw string");
                         BEGIN(INITIAL);
                         return 0;
                       }
}

一些解释

  • 第1行:将开始条件C_RAW_STRING声明为独占条件。(见上面的flex手册,链接)。
  • 第2行:dchar_seq与C++标准所称的“d-char-序列”(参见lex.string,第5.13.5节)匹配:最多16个字符,除了括号、反斜杠或空格字符之外。(注意,"是一个d-char。)有关正则表达式运算符的详细信息,请参阅关于模式的Flex手册章节。
  • 第4行:规则部分开头的缩进语句,在第一个规则之前,简单地插入到yylext函数的顶部。因此,它们可以用于声明在一次调用yylex期间使用的局部变量。
  • 第5-8行:当我们检测到原始字符串文字的开头时,我们:
代码语言:javascript
复制
- Remember how long the delimiter string is. (Since we will leave the delimiter string at the beginning of the token, we don't need to copy it anywhere. It's sufficient to know how long it is.)
- Use `yymore()` to tell the lexer to append the next match to the current token, instead of starting a new token.
- Use `BEGIN` to change to the `C_RAW_STRING` start condition.
  • 第9行:如果我们发现R"和第5行的模式不匹配,那么原始字符串分隔符是不正确的,要么是因为它太长,要么是因为它包含一个无效的字符,或者因为它没有以(结尾。只匹配R"是可以的,但我也选择了匹配最长的d-char序列,以避免对lexer进行回溯,尽管在这种特殊情况下没有太大的区别。因为有了最长匹配规则,这个规则只有在前面的规则没有的情况下才会触发,所以很难从这样的错误中恢复过来,因为没有很好的方法可以知道引用的字符串将在哪里结束。在这里,我没有尝试错误恢复,因为这不是这个答案的重点。
  • 第15行:为C_RAW_STRING启动条件启动一个规则块。这是一个flex扩展,在其他lex实现中是不可用的。为了兼容性,所有模式都必须单独使用<C_RAW_STRING>标记。
  • 第16行:除了双引号以外的任何字符序列都只是附加到令牌上。同样,yymore()表示我们还没有完成令牌。
  • 第17-24行:原始字符串必须以引号结尾,但是引号不一定以原始字符串结尾。所以我们检查我们遇到的每一个报价。首先,考虑到分隔符字符串的长度,确保有一个)在我们期望的位置,然后使用memcmp比较这两个分隔符序列。(我们使用memcmp而不是strcmp,因为我们比较的序列不是以NUL结尾的;它们是令牌的子字符串。strncmp可能是另一种可能性。) 如果分隔符字符串匹配,我们已经找到标记的结尾,我们可以返回它。在这种情况下,我们需要将开始条件重置为INITIAL,以便正常扫描下一个令牌。如果字符串不匹配,我们使用yymore()告诉yylex我们还没有完成标记,并继续寻找正确的分隔符。
  • 第25-29行:如果在原始字符串中检测到输入结束,这意味着未正确终止原始字符串。我们发出一条错误消息并返回0表示遇到EOF。在这一点上重置启动条件并不是真正必要的,但这样做似乎更干净。

备注

  1. 例如,您可以使用以下Gnu grep命令(使用-P选项启用PCRE regex特性),但由于它是一个简单的regex搜索而不是标记,它可能会产生假阳性(例如,注释中的原始字符串): grep -Po‘“([^()\:space:]*)(.*)\1”
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48801027

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档