数值字面值
数值字面量的形式为:
hexdigit = digit | 'A'..'F' | 'a'..'f'
octdigit = '0'..'7'
bindigit = '0'..'1'
unary_minus = '-' # See the section about unary minus
HEX_LIT = unary_minus? '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
DEC_LIT = unary_minus? digit ( ['_'] digit )*
OCT_LIT = unary_minus? '0' 'o' octdigit ( ['_'] octdigit )*
BIN_LIT = unary_minus? '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
INT_LIT = HEX_LIT
| DEC_LIT
| OCT_LIT
| BIN_LIT
INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
UINT_LIT = INT_LIT ['\''] ('u' | 'U')
UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'
exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
FLOAT_LIT = unary_minus? digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent)
FLOAT32_SUFFIX = ('f' | 'F') ['32']
FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX
FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D'
FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX
| (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX
CUSTOM_NUMERIC_LIT = (FLOAT_LIT | INT_LIT) '\'' CUSTOM_NUMERIC_SUFFIX
# CUSTOM_NUMERIC_SUFFIX is any Nim identifier that is not
# a pre-defined type suffix.
从描述中可以看出,数值字面值可以包含下划线,以便于阅读。整数和浮点数可以用十进制(无前缀)、二进制(前缀 0b )、八进制(前缀 0o )和十六进制(前缀 0x )注解表示。
像 -1 这样的数值字面值中的一元减号 - 是字面值的一部分,为了让 -128’i8 等表达式有效,后来被添加到语言中。如果没有这种例外,则只有 -128 有效, — 128 将不是有效的 int8 值。
unary_minus “一元减号”规则有一些限制,这在正式语法中没有提到。 - 是数值字面值的一部分时,前面的字符必须在 {‘ ‘, ‘\t’, ‘\n’, ‘\r’, ‘,’, ‘;’, ‘(‘, ‘[‘, ‘{‘} 集合中,这个设计是为了涵盖大多数合理情况。
在下面的例子中, -1 是单独的 Token 标记:
echo -1
echo(-1)
echo [-1]
echo 3,-1
"abc";-1
在下面的例子中, -1 被解析为两个独立的 Token 标记( - 1 ):
echo x-1
echo (int)-1
echo [a]-1
"abc"-1
以撇号 ( \‘ ) 开始的后缀被称为 type suffix “类型后缀”。没有类型后缀的字面值是整数类型,当包含一个点或 E|e 时是 float 类型。如果字面值的范围在 low(int32)..high(int32) 之间,那么这个整数类型就是 int ,否则就是 int64 。为了记数方便,如果类型后缀明确,那么后缀的撇号是可选的(只有带类型后缀的十六进制浮点数字面值的含义才会不明确)。
预定义的类型后缀有:
类型后缀 | 产生的字面值类型 |
---|---|
‘i8 | int8 |
‘i16 | int16 |
‘i32 | int32 |
‘i64 | int64 |
‘u | uint |
‘u8 | uint8 |
‘u16 | uint16 |
‘u32 | uint32 |
‘u64 | uint64 |
‘f | float32 |
‘d | float64 |
‘f32 | float32 |
‘f64 | float64 |
浮点数字面值也可以采用二进制、八进制或十六进制的注解: 0B0_10001110100_0000101001000111101011101111111011000101001101001001’f64 根据 IEEE 浮点标准,约为 1.72826e35 。
字面值必须匹配数据类型,例如, 333’i8 是一个无效的字面值。以非 10 进制表示的字面值主要用于标记和比特位模式, 因此检查是对位宽而不是值范围进行的,所以: 0b10000000’u8 == 0x80’u8 == 128,但是, 0b10000000’i8 == 0x80’i8 == -128 而不是 -1。
自定义数值字面值
如果后缀未预定义,那么后缀会被认为是对 proc 过程、 template 模板、 macro 宏或其他可调用标识符的调用, 包含字面值的字符串被传递给该标识符。可调用标识符需要用特定的 ‘ 前缀声明。
import strutils
type u4 = distinct uint8 # 一个4位无符号整数,又称 "nibble"
proc `'u4`(n: string): u4 =
# 这是必需的。
result = (parseInt(n) and 0x0F).u4
var x = 5'u4
更确切地说,自定义的数值字面值 123’custom 在解析步骤中被转换为 r”123”.’custom 。并没有对应于这种转换的 AST 节点种类。通过这种转换,在额外参数传递给被调用者时,处理更合理。
import strutils
type u4 = distinct uint8 # 4 位无符号整数,又称 "nibble"
proc `'u4`(n: string; moreData: int): u4 =
result = (parseInt(n) and 0x0F).u4
var x = 5'u4(123)
自定义数值字面值由名称为 CUSTOM_NUMERIC_LIT 的语法规则涵盖。自定义的数值字面值是单独的 Token 标记。