Parsing 如何编写一个lexer解析器,其中标识符可以以关键字开头?

Parsing 如何编写一个lexer解析器,其中标识符可以以关键字开头?,parsing,keyword,lexer,dfa,Parsing,Keyword,Lexer,Dfa,假设您有一种标识符可能以关键字开头的语言。例如,假设“case”是一个关键字,但“caser”是一个有效标识符。还假设lexer规则只能处理正则表达式。然后,我似乎不能将关键字规则放在lexer中的标识符规则之前,因为这会将“caser”解析为“case”后跟“r”。我也不能将关键字词法规则放在标识符规则之后,因为标识符规则将匹配关键字,并且关键字规则永远不会触发 因此,我可以在lexer中创建一个关键字\或\标识符规则,并让解析器确定关键字\或\标识符是关键字还是标识符。这是正常的做法吗 我意

假设您有一种标识符可能以关键字开头的语言。例如,假设“case”是一个关键字,但“caser”是一个有效标识符。还假设lexer规则只能处理正则表达式。然后,我似乎不能将关键字规则放在lexer中的标识符规则之前,因为这会将“caser”解析为“case”后跟“r”。我也不能将关键字词法规则放在标识符规则之后,因为标识符规则将匹配关键字,并且关键字规则永远不会触发

因此,我可以在lexer中创建一个关键字\或\标识符规则,并让解析器确定关键字\或\标识符是关键字还是标识符。这是正常的做法吗


我意识到“使用具有前瞻性的不同lexer”是一个答案(有点),但我也对如何在传统的基于DFA的lexer中实现这一点感兴趣,因为我当前的lexer似乎是这样工作的。

大多数lexer,从原始的
lex
开始,匹配如下备选方案:

  • 使用最长的匹配

  • 如果有两个或两个以上的选项匹配最长的匹配,则在lexer定义中使用第一个选项

  • 这允许使用以下样式:

    "case"                  { return CASE; }
    [[:alpha:]][[:alnum:]]* { return ID; }
    
    如果输入模式是
    caser
    ,则将使用第二个备选方案,因为它是最长的匹配。如果输入模式为
    案例r
    ,则将使用第一个备选方案,因为它们都匹配
    案例
    ,根据上述规则(2),第一个方案获胜

    尽管这看起来有点武断,但它与DFA方法基本一致。首先,DFA不会在第一次达到接受状态时停止。如果是这样的话,那么像
    [[:alpha:][[:alnum:]]*
    这样的模式将是无用的,因为它们在第一个字符上进入接受状态(假定其字母顺序)。相反,基于DFA的lexer将继续,直到从当前状态没有可能的转换,然后它们将备份到最后一个接受状态。(见下文。)

    给定的DFA状态可能因为两个不同的规则而接受,但这也不是问题;只记录第一条接受规则

    公平地说,这与DFA的数学模型略有不同,DFA的数学模型在每个状态下都有一个符号转换(尽管其中许多符号可能是到“接收器”状态的转换),并且根据读取输入的最后一个符号时自动机是否处于接受状态匹配完整输入。lexer模型略有不同,但也可以很容易地进行形式化

    理论模型中唯一的困难是“回到最后的接受状态”。在实践中,这通常是通过在每次到达接受状态时记住状态和输入位置来实现的。这确实意味着可能需要倒带输入流,倒带量可能是任意的

    大多数语言不经常需要备份,很少有语言需要无限期备份。如果没有备份状态,一些lexer生成器可以生成更快的代码。(
    flex
    将在使用
    -Cf
    -Cf
    时执行此操作)

    导致无限备份的一种常见情况是无法为字符串文本提供适当的错误返回:

    ["][^"\n]*["]    { return STRING; }
     /* ... */
    .                { return INVALID; }
    
    这里,如果同一行上有匹配的
    ,则第一个模式将匹配以
    开头的字符串文字。(为了简单起见,我省略了
    \
    -转义。)如果字符串文字未终止,则最后一个模式将匹配,但输入将需要重新返回到
    ”。在大多数情况下,试图通过忽略不匹配的
    来继续词法分析是毫无意义的;忽略该行的整个剩余部分更有意义。因此,备份不仅效率低下,而且还可能导致错误消息爆炸。更好的解决方案可能是:

    ["][^"\n]*["]    { return STRING; } 
    ["][^"\n]*       { return INVALID_STRING; }
    

    这里,第二个备选方案只有在字符串未终止时才能成功,因为如果字符串终止,第一个备选方案将匹配多个字符。因此,这些备选方案的出现顺序并不重要,尽管我认识的每个人都会按照与我相同的顺序排列。

    为什么你认为不能先放置关键字规则?这确实是“通常的解决方案”,它与构建DFA并不矛盾。我编辑了这个问题来澄清这一点。谢谢!我认为“最长匹配”意味着每个模式匹配尽可能多的字符,但没有意识到lexer正在检查所有模式。谢谢你的详细解释!当遵循“最长匹配”规则时,等长匹配被解析为优先规则,这是正确的吗?@BenRI:这当然是lex/flex以及我所知道的大多数lexer生成器使用的规则。但我不能说它是普遍的。