我目前正在为简单的编程语言实现词法分析器。到目前为止,我可以正确地标记标识符,赋值符号和整数文字;一般来说,空白是微不足道的。
对于输入foo = 42
,可识别三个标记:
foo
(标识符)=
(符号)42
(整数字面)到目前为止,这么好。但是,请考虑输入foo = 42bar
,由于42
和bar
之间的(重要)缺失空间,无效。我的词法分析器错误地识别以下标记:
foo
(标识符)=
(符号)42
(整数字面)bar
(标识符)一旦词法分析器看到数字4
,它就会一直读到它遇到非数字。因此,它使用2
并将42
存储为整数文字标记。因为空格是无关紧要的,所以词法分析器会丢弃任何空格(如果有的话)并开始读取下一个标记:它找到标识符bar
。
现在,我的问题在这里:是否有责任认识到该位置不允许使用标识符?或者该检查是否属于解析器的职责?
答案 0 :(得分:3)
我认为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
的有效性,片段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
,如果用作字符串则假定为""
。)
答案 1 :(得分:2)
我不同意这里的其他答案。它应该由词法分子完成。如果数字后面的字符不是空格或特殊字符,则表示您处于非法令牌中间,特别是不以字母开头的标识符。
或者只是单独返回45和'bar',让解析器将其作为语法错误处理。
答案 2 :(得分:1)
是的,像这样的上下文检查属于解析器。
另外,您说foo = 42bar
无效。从词法分析者的角度来看,它并非如此。您的词法分析器识别的4个令牌(可能)是正确的(您不会发布您的令牌定义)。
foo = 42bar
可能是也可能不是您所用语言的有效陈述。
答案 3 :(得分:0)
编辑:我刚刚意识到这实际上是您语言的无效令牌。所以是的,它会在那个时候使词法分子失败,因为你没有匹配它的规则。否则,它会是什么,InvalidTokenToken?
但是,让我们说这是一个有效的令牌。假设你写了一个lexer规则,说id = <number>
没问题......你对id = <number> + <number> - <number>
做了什么,以及导致的所有各种组合? lexer如何为你提供任何AST?这是解析器的用武之地。
您使用的是解析器组合框架吗?我问,因为有时候解析器和词法分析器之间的区别开始变得随意,特别是因为你可能没有明确的语法。但是你正在解析的语言仍然有一个语法,而算作解析器规则的每一个都是语法的产生。在#34;底部&#34;如果您有描述单个终端的规则,例如&#34;数字是一个或多个数字,&#34;而这一点,这就是词法分析器的用法 - 原因在于它可以加速解析器并简化其实现。