词法分析器的工作包括解析数字和字符串吗?

37

词法分析器(lexer)的职责是解析数字和字符串吗?

这个问题听起来可能有些愚蠢,因为我在问一个词法分析器是否应该解析输入。但实际上,我不确定这是否是词法分析器的职责还是语法分析器(parser)的职责,因为为了正确进行词法分析,词法分析器需要首先解析字符串/数字,如果语法分析器也解析将会导致代码重复。

那么,究竟是词法分析器的工作吗?还是仅需将类似于123.456这样的字符串拆分成123.456三部分,然后让语法分析器处理剩余部分呢?但对于字符串而言,这么做就没那么简单了...


这是用于类似于Lex/YACC的东西吗? - Grady Player
@Grady:有点,但也不完全是。xD 我正在尝试手动制作自己的词法分析器(希望还能做出解析器),但我无法弄清楚哪个应该用于解析数字和字符串。我没有使用像Lex/YACC这样的外部工具,所以在这方面上,不是。 - user541686
有趣,但仍不清楚。您的意思是词法分析器的作用是为解析器识别数字的类型,而不仅仅是识别标记吗?这种情况下,就像您所说的,存在一种循环引用(在知道类型之前,它如何知道格式进行解析)。我认为这就是为什么C#(例如)允许在浮点数上使用后缀(F和D)以将浮点数与双精度数区分开来,供词法分析器使用。 我猜想当词法分析器完成时,标记应该是没有问题的,但可能有一些关于类型的问题。文本格式必须被设计成允许这种情况。 - harpo
1
@harpo: 不,我不是指类型。(或许我是?你对“类型”的定义让我有些困惑。)我只是指一个词法分析器要能够说“这是一个字符串字面量”,它需要处理所有的转义代码和其他内容。但如果它只是返回输入的一部分,那么解析器将不得不再做完全相同的事情--这是重复,不是吗? - user541686
你的意思是它必须通过识别转义序列并将其视为字符串的一部分来“处理”它们,并将其“逐字”传递给解析器,然后由解析器对它们进行编码。或者,词法分析器可以执行编码并将“准备好”的字符串交给解析器。对于字符串来说,我更倾向于后者,但对于数字来说,这似乎意味着词法分析器正在将输入转换为解析器的准备数字,这似乎超出了范围。我只是在这里随便想想。 - harpo
谁说你需要词法分析器呢?词法分析(标记化)和解析之间的区别非常任意,并且是工具使用的属性,而不是解析本身。 - Gene Bushuyev
3个回答

34
简单回答是:“是的”。
抽象地说,您根本不需要词法分析器。您可以编写一个语法,将单个字符用作令牌(实际上这正是SGLR解析器所做的,但这是另一天的故事)。
您需要词法分析器,因为使用字符作为基本元素构建的解析器不如将输入流分成“令牌”那样高效,其中令牌是您正在解析的语言的基本元素(空格、关键字、标识符、数字、运算符、字符串、注释等)。[如果您不关心效率,可以跳过本答案的其余部分并阅读有关SGLR解析器的内容]。
良好的词法分析器通常需要使用表示语言元素的正则表达式集合,并将它们编译成能够快速将输入流分割成这些语言元素的有效有限状态机。 (对于简单语言,如果您不想使用词法分析器生成器,则可以自己编码FSA)。这种编译后的FSAs每个输入字符只执行几十条机器指令(从输入缓冲区获取字符,切换到新状态,确定令牌是否完成,如果没有,请重试),因此速度非常快。
这样的词法分析器的输出通常是表示语言元素的代码(如果解析器将忽略它,则为空白),以及一些位置信息(在文件foo中开始,第17行第3列),以便进行错误报告。

可以在此停止并获得有用的词法分析器。通常有必要进行转换步骤,将字符串转换为该令牌的等效本机机器值,无论是在收集字符时还是在令牌完成时进行,因为仍然具有涉及令牌中特定字符的知识。这用于将目标语言中的数字(具有不同基数)转换为其本机二进制等效物,将包含转义序列的文字字符串转换为组成字符串的实际字符,甚至取标识符名称并查找它们在哈希表中,以便轻松确定相同的标识符。解析器通常对这些转换后的值不感兴趣,但超过解析(语义分析、检查优化、代码生成)的步骤需要这些转换后的值,因此您可能会在发现它们时将它们转换。 (您可以推迟此转换直到需要其二进制值,但在实践中,您几乎总是需要该值,因此推迟转换不会带来太多好处)。


你最后一段完全不适用于交叉编译器。转换为目标格式是代码生成的一个功能,而不是输入扫描。我在1979年由Frank de Remer详细教授了这一点。 - user207421
@EJP:考虑使用交叉编译器:它应该知道目标是小端字节序,并且可以知道或被告知其本地机器值是大端或小端字节序。有了这个知识,很容易生成适当的小端字节序值。我不必向Frank提出这个问题。 - Ira Baxter
等一下。我没有排除规范表示法。我排除了在前端转换为目标表示形式。前端应该是与机器无关的,所以它完全不知道正在进行交叉编译。对此,后端也一样。 - user207421
@EJP:我从未说过前端应该转换为目标表示。我也没有说过机器无关性。我主张的是,前端词法分析器应该为翻译程序所在的机器生成规范的本地值(不失精度),以便翻译程序能够使用本地指令集最大限度地方便地操作该值。如果你正在进行目标代码生成,我建议将该本地形式转换为所需的输出格式。 - Ira Baxter
请允许我指向您在上一个答案的最后一段中的第二句话,您在那里确切地说了这一点,包括“本地”一词。我完全同意规范表示的重要性,而且也没有说过其他的话。但这不是您上面回答所说的。在我的评论之后,直到这里才出现“规范”的字眼。 - user207421
显示剩余6条评论

0
一个词法分析器从输入中识别TOKEN。在这种情况下,词法分析器可能会将数字作为浮点数TOKEN进行“匹配”。解析器从本质上讲是处理TOKEN并进行语法分析的。

@Sai:是的,标记化很有道理(这就是为什么我在我的问题中添加了标签:P),但我不确定“匹配”和“处理”在这里的区别是什么。例如,词法分析器应该将"Hello\n World"转换成什么? - user541686
你可以将词法分析器视为一个预处理器,它有助于使解析器更容易解析。通常,词法分析器会将“Hello”识别为WORD,“\n”识别为NEWLINE,将World识别为WORD。 - Sai
@Mehrdad -- 抱歉没有正确阅读。我没有意识到它是一个字符串文字(尽管它很明显)。对此感到抱歉。这通常应该被解释为一个字符串文字。 - Sai
2
要明确的是,大多数词法分析器将传统字符串字面量提取为单个标记。 "Hello\World" 将产生一个单独的 STRINGLITERAL 条目,其二进制值是 H、e、l、... \n、W、o、... d 的字符代码。(更多...) - Ira Baxter
2
根据语言的不同,字符串字面量可能不是传统的,因此不能传统地进行词法分析。我们的DMS PHP前端将双引号“字符串字面量”解析为不同类型的字符串字面量片段系列。这是因为在PHP中这样的字符串字面量实际上是隐式表达式,它连接了一堆字符串值,其中有些确实是字面字符串片段,有些是插值变量或其他由PHP允许的运算符,位于字面字符串的“中间”。这个词法分析器仍然做了正确的事情:捕获语言元素。 - Ira Baxter
@Mehrdad 很棒的问题,点赞+1!@Ira 很好的解释,点赞+1! - Sai

0

我猜你想把“123.456”作为一个整体值来处理,这种情况下,你需要将其全部传递给解析器,除非你需要对其进行编码,比如

struct DecimalRep{
    double mantissa,
    double exponent 

}

但我猜这完全取决于解析器的期望。


那么它实际上应该将数据传递给解析器,以更合适的内部格式,而不仅仅是作为常规字符串? - user541686
听起来取决于你 ;) 我会将其视为单个标记。 - Grady Player
@Mehrdad:您希望将字面值转换为易于编译器其余部分操作的表示形式。如果您有一个浮点字面量,则应将其转换(如果没有精度损失)为IEEE格式浮点数;然后,如果编译器必须添加两个字面量(例如,常量折叠),它可以使用CPU中内置的本机IEEE浮点进行操作。同样适用于字符串/整数值。(如果将所有这些内容保留为原始字符序列,则可能通过不执行转换来节省一些时间,但是这样会使编译器变得混乱。 - Ira Baxter
@IraBaxter:是啊,六年过去了,我现在相当明白这些内容 :) 谢谢! - user541686
@torek:将浮点数直接存储为文本字符串,编译时进行数学计算会变得非常棘手。 :-} - Ira Baxter
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接