我认为关于
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
,如果用作字符串,则假定其值为""
。)