我的词法分析器做的太多了吗——它在做解析器的工作吗?

Is my lexer doing too much -- is it doing the work of the parser?

我的输入由一系列名称组成,每个名称占一个新行。每个名字都由名字、可选的中间名首字母和姓氏组成。名称字段由制表符分隔。这是一个示例输入:

Sally   M.    Smith
Tom     V.    Jones
John          Doe

以下是我的 Flex 词法分析器的规则。它工作正常,但我担心我的词法分析器做的太多了:它确定一个标记是名字或中间名首字母或姓氏。该确定应该在解析器而不是词法分析器中完成吗?我是否在滥用 Flex 状态功能?我正在寻求的是对我的词法分析器的批评。我只是一个初学者,解析专家如何为这个输入创建词法分析器规则?

<INITIAL>{
         [a-zA-Z]+          { yylval.strval = strdup(yytext); return(FIRSTNAME); }
         \t                 { BEGIN MI_STATE; }
         .                  { BEGIN JUNK_STATE; }
}
<MI_STATE>{
        [A-Z]\.             { yylval.strval = strdup(yytext); return(MI); }
        \t                  { BEGIN LASTNAME_STATE; }
         .                  { BEGIN JUNK_STATE; }
}
<LASTNAME_STATE>{
         [a-zA-Z]+          { yylval.strval = strdup(yytext); return(LASTNAME); }
         \n                 { BEGIN INITIAL; return EOL; }
         .                  { BEGIN JUNK_STATE; }
}
<JUNK_STATE>.               { printf("JUNK: %s\n", yytext);  }

您可以像在本题中一样使用词法分析器状态。但最好将它们用作 a means to conditionally activate rules. For examples, think of handling multi-line comments or here documents 或(对于我们银背)嵌入 SQL.

在您的问题中,给定名称和姓氏之间没有词汇差异——如果您要扩展词法分析器,它们都与 [a-zA-Z]+ 匹配,中间名也是如此。

简短回答:是的,lex NAME 标记并让解析器确定一行中是否有三个 NAME 标记。

是;你的词法分析器正在解析。主要证据是它在不同的开始状态下实施相同的规则。两条规则具有完全相同的模式。

词法分析上下文中的起始状态的目的是修改词法语法,以保护解析器免受某些差异的影响。它与解析器一起工作。例如,假设您有一些文档语言,其中 $ 转换为具有不同分词规则的数学表达式模式。词法分析器在数学模式下仍然只是 returns 个标记;它不会尝试解析数学表达式。解析器将确定,如果括号是平衡的,那么另一个 $ 可以退出数学模式。

在您的代码中,返回姓氏和名字的规则是相同的;你已经习惯了开始状态来处理短语结构语法:姓氏晚于名字的事实。

词法分析器正在解析的另一个明显证据是词法分析器本身处理所有开始条件变化。在我们的 $...$ 数学模式示例中,当词法分析器看到 $ 符号时,我们可能会让它进入开始状态。但是,如果词法分析器也识别数学模式的 end,那么这就是它正在解析数学模式表达式的证据。结尾只能通过数学模式表达式的嵌套短语结构语法来识别。您处理的方式是让词法分析器公开一个函数 lex_end_math_mode()。当解析器处理和减少整个数学模式表达式时,它调用这个函数来告诉词法分析器切换回数学模式之外的词法语法。 math-mode-terminating 美元符号也可能作为标记对解析器可见,尽管前导符号可能不可见。也就是说,解析器解析 math_mode_expr : expr '$': 一个数学模式表达式,后面跟着一个必需的美元符号来结束数学模式。该规则的操作将包括对 lex_end_math_mode 的调用,因此词法分析器 returns 到数学模式之外的标记化规则,以便在 $.[=22= 结束后扫描下一个标记]

没有正确或错误的答案,因为它都是解析。每一种文法都被划分为记号和短语结构规则,可以用一个包含记号结构规则的统一文法来表达。

为什么我们经常使用词法扫描和解析分离的设计是因为:

  • 将词法和短语结构文法合二为一,将LL(1)变为LL(k)。然后 recursive-descent 解析器需要提前查看 k 个符号来做出解析决定。例如,如果您使用这种整体方法解析 C,则需要将 int 视为保留关键字。那需要向前看的四个符号:你必须识别int,然后如果下一个符号表示令牌已经结束,你就把它当作关键字,否则一个标识符。

  • 性能:词法扫描使用针对该任务量身定制的高效技术,它利用了词法语法规则的限制。

  • 与规范的对应关系:如果您有一种语言,其规范是根据与短语结构语法分开的词法语法来描述的,那么如果您以这种方式实现它,那么您的代码的功能会更多很容易追溯到需求规范的特征。您可以编写单元测试,分别显示词法分析和解析符合规范。

  • 学习:参加过包括编译器构建课程的 CS 课程的人,他们的脑子里有单独的词法分析和语法分析,每当他们在以后的职业生涯中出现时,他们只是依靠那智慧。他们从来没有遇到过他们认为这不是一个好方法的情况,也不会质疑它。

在你的个人情况下,无论你分析什么,任何工作都推翻了理论。如果您可以方便地识别词法分析器中的一些 phrase-like 片段,并且您能够说服自己这是一种干净的方法,那么一定要这样做。