【问题标题】:Flex identifying end of lineFlex 识别行尾
【发布时间】:2017-03-15 23:29:48
【问题描述】:

我们正在尝试纠正我们的词法分析解析器中的一个错误。我们正在使用 flex 并且我们必须支持多行字符串。问题是当字符串的结尾与开头的 " 不在同一行时,我们不计算新行。我们有两种情况,换行符和 \n 从程序员插入到字符串中是否有办法理解行尾并以某种方式在规则中计算它?

【问题讨论】:

    标签: compiler-construction flex-lexer


    【解决方案1】:

    如果您使用%option yylineno,那么 flex 将为您维护行数(在变量 yylineno 中)。只要您不致电input,它就会正确。

    有两个小问题:

    1. yylineno 的值对于读取的最后一个字符是正确的,它是令牌中的最后一个字符。但是,如果您有多行标记,您通常还想知道标记开头的行号;您需要将 yylineno 的值存储在前一个令牌的末尾。

      幸运的是,您可以将宏YY_USER_ACTION 定义为一些C 代码;这将被插入到每个动作的开头(包括没有任何显式动作的规则。所以这样的事情将确保以前的值始终作为yylineno_start 可用:

      #define YY_USER_ACTION  \
        yylineno_start = yylineno_saved; \
        yylineno_saved = yylineno;
      

      当然,您还需要声明这些变量。

    2. Flex 不跟踪列位置。但也许你没关系。否则,您可以在上面提到的YY_USER_ACTION 中添加更多代码。简单的方法是也保存一个总字符数,并在当前行的末尾记录总字符数。维护总字符数很容易;您只需每次添加 yyleng 的值。为了保持行首的计数,您需要检查yylineno 的值是否发生了变化,如果是,则在标记中向后搜索以找到最后一个换行符。这只听起来效率低下。大多数时候,扫描时间很短。

    示例代码

    这是一个仅使用行号跟踪的最小解决方案:

    %option yylineno
    %option noinput nounput noyywrap nodefault
    
    %{
    int yylineno_saved = 1;
    #define YY_USER_ACTION  \
      yylineno_start = yylineno_saved; \
      yylineno_saved = yylineno;
    %}
    
    %%
                           int yylineno_start;
    [[:space:]]            // Ignore whitespace including newlines
    [[:digit:]]+           { printf("Integer %s at line %d\n", yytext, yylineno); }
    \"(\\(.|\n)|[^\\"])*\" { printf("String from line %d to line %d\n",
                                     yylineno_start, yylineno);
                           }
    .                      // Ignore everything else
    %%
    
    int main(int argc, char** argv) {
      return yylex();
    }
    

    正如上面第 2 点所建议的,这是一个更复杂的,它也跟踪字符位置。这个使用yylloc 全局变量,这是将完整令牌边界传达给野牛的常用方式。 (请注意,此代码不会与 lessmore 合作。如果您使用这些功能,则需要为它们编写包装器。)

    %option yylineno
    %option noinput nounput noyywrap nodefault
    
    %{
    /* The following would usually be generated by bison if you
     * enable location tracking in your bison definition.
     */
    struct YYLTYPE {
      int first_line;
      int first_column;
      int last_line;
      int last_column;
    };
    struct YYLTYPE yylloc = {1,1,1,1};
    /* We also need to keep the absolute character position, and the
     * position at the beginning of the current line.
     */
    int char_position = 0;
    int line_start = 0;
    
    #define YY_USER_ACTION                              \
      char_position += yyleng;                          \
      if (yylineno != yylloc.last_line) {               \
        char* p = yytext + yyleng;                      \
        line_start = char_position;                     \
        while (*--p != '\n') --line_start;              \
      }                                                 \
      yylloc.first_line = yylloc.last_line;             \
      yylloc.first_column = yylloc.last_column;         \
      yylloc.last_line = yylineno;                      \
      yylloc.last_column = char_position - line_start + 1;
    
    /* Just for show */
    void show_with_loc(const char* msg) {
      printf("[%d:%d->%d:%d] %s",
              yylloc.first_line, yylloc.first_column,
              yylloc.last_line, yylloc.last_column,
              msg);
    }
    
    %}
    
    %%
    [[:space:]]            // Ignore whitespace including newlines
    [[:digit:]]+           { show_with_loc("Integer\n"); }
    \"(\\(.|\n)|[^\\"])*\" { show_with_loc("String\n"); }
    .                      // Ignore everything else
    %%
    
    
    int main(int argc, char** argv) {
      return yylex();
    }
    

    【讨论】:

    • 我尝试了你建议的东西,但没有任何具体的工作问题是,当解析字符串的规则消耗字符时,yylineno 不会增加,我的问题是,有没有办法区分行尾和\n 程序员在 yytext 中写的?
    • @george:你说的“程序员写的”是什么意思?听起来您正在做一些不寻常的事情,在这种情况下,您将不得不展示您的代码。
    • 我的意思是源文件可能是这样的“你好,我的名字是乔治\n EOL” EOL 是实际的行尾,\n 是用于字符串格式化如何区分差异? string \"(\\.|[^\\"])*\" 是多行字符串的规则
    • @GeorgeXanthakis: %option yylineno 只识别换行符。 \n 是两个字符,一个反斜杠和一个 n。那不是换行符。我不知道为什么%option yylineno 不适合你;您实际上必须发布minimal reproducible example
    • @GeorgeXanthakis:如果问题是我的答案中的小但明显的错字(我刚刚修复),那么如果您提到错误消息而不是仅仅说“没有任何效果”,那将会很有帮助: )
    【解决方案2】:

    您可以在 Flex 中使用state or start condition 的概念。

    • 每当遇到"(双引号)时,都会启动一个名为<STRING> 的状态。
    • <STRING> 状态中,您可以编写不同的规则集,例如 - 当您在字符串中得到一个反斜杠后跟一个新行时,您就知道它是一个多行字符串。您还可以在<STRING> 状态中单独检测换行符\n
    • 收到另一个"(双引号)后,结束<STRING> 状态并返回<INITIAL> 状态。

    source code : string.l

    %option noyywrap
    
    %x STRING
    
    %{    
    int line_count = 1;
    %}
    
    %%
    
    \"   {
                    printf("%d: String started\n", line_count);
                    BEGIN(STRING);
         }
    
    <STRING>"\\\n"  { line_count++; }
    
    
    <STRING>\"  {
                    printf("%d: String ended\n", line_count);
                    BEGIN(INITIAL);
                }
    
    <STRING>"\\n"  { 
                    printf("new line\n");
                }                   
    
    <STRING>.   {   
                    printf("%s\n", yytext);
                }
    
    
    \n          { line_count++; }
    
    .           {}
    
    
    
    %%
    int main(int argc,char *argv[]){
        yyin = fopen(argv[1], "r");      // taking input from a file
        yylex();
        printf("\nTotal Lines: %d\n", line_count);
        return 0;
    }
    

    试试这个输入法。

    "single line" 
    "multi\
    line"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-07
      • 1970-01-01
      • 2020-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多