是否有可能为规则设定优先级以避免“最长最早”的匹配模式?

时间:2011-12-04 23:02:28

标签: c lex rules flex-lexer

另一个简单的问题:是否有任何方法可以告诉flex更喜欢与匹配更长时间的规则匹配短项的规则?我找不到任何关于此的好文件。

这就是我需要的原因:我为一个伪语言解析一个文件,其中包含一些与控制指令相对应的关键字。我希望它们是绝对的优先级,这样它们就不会被解析为表达式的一部分。我实际上需要这个优先级的东西,因为我不需要为我的项目写一个完整的语法(在我的情况下,因为我对解析的程序执行结构分析,我不需要知道细节,这将是完全矫枉过正的.. 。),所以我不能使用精细的语法调整来确保这些块不会被解析成表达式。

任何帮助将不胜感激。

以下是解析文件的示例:

If a > 0 Then read(b); Endif
c := "If I were...";
While d > 5 Do d := d + 1 Endwhile

我只是想收集关于Ifs,Thens,Endifs等的信息......其余的对我来说无关紧要。这就是为什么我喜欢Ifs,Thens等...相关规则要优先考虑,而不必编写语法。

1 个答案:

答案 0 :(得分:14)

来自龙书第2版,第3.5.3节" Lex"中的冲突解决:

We have alluded to the two rules that Lex uses to decide on the proper lexeme
to select, when several prefixes of the input match one or more patterns:
    1. Always prefer a longer prefix to a shorter prefix.
    2. If the longest possible prefix matches two or more patterns, prefer the
       pattern listed first in the Lex program.

上述规则也适用于Flex。以下是 Flex手册所说的内容(第7章:输入如何匹配。)

When the generated scanner is run, it analyzes its input looking for strings 
which match any of its patterns. If it finds more than one match, it takes the 
one matching the most text (for trailing context rules, this includes the length 
of the trailing part, even though it will then be returned to the input). If it 
finds two or more matches of the same length, the rule listed first in the flex 
input file is chosen.

如果我理解正确,你的词法分析器会将Endif之类的关键字视为标识符,因此它将被视为表达式的一部分。如果这是您的问题,只需将关键字规则放在您的规范之上,例如:(假设每个单词都是一个对应于令牌的预定义枚举)

"If"                      { return IF;         }
"Then"                    { return THEN;       }
"Endif"                   { return ENDIF;      }
"While"                   { return WHILE;      }
"Do"                      { return DO;         }
"EndWhile"                { return ENDWHILE;   }
\"(\\.|[^\\"])*\"         { return STRING;     }
[a-zA-Z_][a-zA-Z0-9_]*    { return IDENTIFIER; }

然后,由于规则2,关键字将始终在标识符之前匹配。

修改

感谢您的评论,kol。我忘了为字符串添加规则。 但我认为我的解决方案是错误的。例如,如果标识符为If_this_is_an_identifier,规则 1 将适用,则标识符规则将生效(因为它匹配最长的字符串)。我写了一个简单的测试用例,在我的解决方案中没有遇到任何问这是我的lex.l文件:

%{
  #include <iostream>
  using namespace std;
%}

ID       [a-zA-Z_][a-zA-Z0-9_]*

%option noyywrap
%%

"If"                      { cout << "IF: " << yytext << endl;         }
"Then"                    { cout << "THEN: " << yytext << endl;       }
"Endif"                   { cout << "ENDIF: " << yytext << endl;      }
"While"                   { cout << "WHILE: " << yytext << endl;      }
"Do"                      { cout << "DO: " << yytext << endl;         }
"EndWhile"                { cout << "ENDWHILE: " << yytext << endl;   }
\"(\\.|[^\\"])*\"         { cout << "STRING: " << yytext << endl;     }
{ID}                      { cout << "IDENTIFIER: " << yytext << endl; }
.                         { cout << "Ignore token: " << yytext << endl; }

%%

int main(int argc, char* argv[]) {
  ++argv, --argc;  /* skip over program name */
  if ( argc > 0 )
    yyin = fopen( argv[0], "r" );
  else
    yyin = stdin;

  yylex();
}

我使用以下测试用例测试了我的解决方案:

If If_this_is_an_identifier > 0 Then read(b); Endif
    c := "If I were...";
While While_this_is_also_an_identifier > 5 Do d := d + 1 Endwhile

它给了我以下输出(忽略了与你提到的问题无关的其他输出。)

IF: If
IDENTIFIER: If_this_is_an_identifier
......
STRING: "If I were..."
......
WHILE: While
IDENTIFIER: While_this_is_also_an_identifier

根据flex manual :(使用相同的方法匹配标识符中的关键字)的示例修改lex.l程序

另请查看the ANSI C grammar, Lex specification

我在个人项目中也使用过这种方法,到目前为止我还没有发现任何问题。