[英]Responsibilities of the Lexer and the Parser
我目前正在为简单的编程语言实现词法分析器。 到目前为止,我可以正确地标记标识符,赋值符号和整数文字; 一般来说,空白是微不足道的。
对于输入foo = 42
,可识别三个标记:
foo
(标识符) =
(符号) 42
(整数字面) 到现在为止还挺好。 但是,请考虑输入foo = 42bar
,由于42
和bar
之间的(显着)缺失空间而无效 。 我的词法分析器错误地识别以下标记:
foo
(标识符) =
(符号) 42
(整数字面) bar
(标识符) 一旦词法分析器看到数字4
,它就会一直读到它遇到非数字。 因此它消耗2
并将42
存储为整数文字标记。 因为空格是无关紧要的,所以词法分析器会丢弃任何空格(如果有的话)并开始读取下一个标记:它找到标识符bar
。
现在,这是我的问题:词作者是否仍有责任认识到该位置不允许使用标识符? 或者该检查是否属于解析器的职责?
我认为42foo
是否应被视为无效数字或两个令牌的问题没有达成共识。 这是一个风格问题,两种用法在众所周知的语言中很常见。
例如:
$ python -c 'print 42and False'
False
$ lua -e 'print(42and false)'
lua: (command line):1: malformed number near '42a'
$ perl -le 'print 42and 0'
42
# Not an idiosyncracy of tcc; it's defined by the standard
$ tcc -D"and=&&" -run - <<<"main(){return 42and 0;}"
stdin:1: error: invalid number
# gcc has better error messages
$ gcc -D"and=&&" -x c - <<<"main(){return 42and 0;}" && ./a.out
<stdin>: In function ‘main’:
<stdin>:1:15: error: invalid suffix "and" on integer constant
<stdin>:1:21: error: expected ‘;’ before numeric constant
$ ruby -le 'print 42and 1'
42
# And now for something completely different (explained below)
$ awk 'BEGIN{print 42foo + 3}'
423
因此,这两种可能性都是常用的。
如果你要拒绝它,因为你认为一个数字和一个单词应该用空格分隔,你应该在词法分析器中拒绝它。 解析器不能(或不应该)知道空格是否分隔两个标记。 与42and
的有效性42and
,片段42 + 1
42and
42 + 1
和42+1
42+ 1
)都应该被相同地解析 。 (也许,在Fortress中。但这是一个异常。)如果你不介意将数字和单词推在一起,那么让解析器拒绝它,如果(并且只有)它是语法错误。
作为旁注,在C和C ++中, 42and
最初被称为“预处理器编号”。 在预处理之后,需要对其进行重新绑定,并且此时会生成错误消息。 这种奇怪行为的原因是将两个片段粘贴在一起以生成有效数字是完全合法的:
$ gcc -D"c_(x,y)=x##y" -D"c(x,y)=c_(x,y)" -x c - <<<"int main(){return c(12E,1F);}"
$ ./a.out; echo $?
120
12E
和1F
都是无效整数,但与##
运算符粘贴在一起,它们形成一个完全合法的浮点数。 ##
运算符仅适用于单个标记,因此12E
和1F
都需要作为单个标记使用。 c(12E+,1F)
不起作用,但c(12E0,1F)
也没问题。
这也是为什么你应该总是在C中的+
运算符周围放置空格:经典技巧C问题:“ 0x1E+2
的值是多少?”
最后,awk行的解释:
$ awk 'BEGIN{print 42foo + 3}'
423
这被awk称为BEGIN{print 42 foo + 3}
,然后将其解析为好像已经写成BEGIN{print (42)(foo + 3);}
。 在awk中,字符串连接是在没有运算符的情况下编写的,但它与任何算术运算符的绑定都不那么紧密。 因此,通常的建议是在涉及连接的表达式中使用显式括号,除非它们非常简单。 (另外,假定未定义的变量在算术上使用时为0
如果用作字符串则假定为""
。)
我在这里不同意其他答案。 它应该由词法分子完成。 如果数字后面的字符不是空格或特殊字符,则表示您处于非法令牌中间,特别是不以字母开头的标识符。
或者只是单独返回45和'bar',让解析器处理它作为语法错误。
是的,像这样的上下文检查属于解析器。
另外,你说foo = 42bar
无效。 从词法分析者的角度来看,它并非如此。 您的词法分析器识别的4个令牌(可能)是正确的(您不会发布您的令牌定义)。
foo = 42bar
可能是也可能不是您所用语言的有效声明。
编辑:我刚刚意识到这实际上是您语言的无效令牌。 所以是的,它会在那个时候使词法分子失败,因为你没有匹配它的规则。 否则,它会是什么,InvalidTokenToken?
但是,让我们说这是一个有效的令牌。 假设你写了一个lexer规则,说id = <number>
是好的...你怎么处理id = <number> + <number> - <number>
,以及导致的所有各种组合? lexer如何为你提供任何AST? 这是解析器的用武之地。
您使用的是解析器组合框架吗? 我问,因为有时候解析器和词法分析器之间的区别开始变得随意,特别是因为你可能没有明确的语法。 但是你正在解析的语言仍然有一个语法,而算作解析器规则的是每个语法的产生。 如果你有描述单个终端的规则,就像“一个数字是一个或多个数字”,这就是非常“底部”,而这就是词法分析器的用途 - 原因是它可以加速解析器并简化其实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.