数值字面值

数值字面量的形式为:

  1. hexdigit = digit | 'A'..'F' | 'a'..'f'
  2. octdigit = '0'..'7'
  3. bindigit = '0'..'1'
  4. unary_minus = '-' # See the section about unary minus
  5. HEX_LIT = unary_minus? '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
  6. DEC_LIT = unary_minus? digit ( ['_'] digit )*
  7. OCT_LIT = unary_minus? '0' 'o' octdigit ( ['_'] octdigit )*
  8. BIN_LIT = unary_minus? '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
  9. INT_LIT = HEX_LIT
  10. | DEC_LIT
  11. | OCT_LIT
  12. | BIN_LIT
  13. INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
  14. INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
  15. INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
  16. INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
  17. UINT_LIT = INT_LIT ['\''] ('u' | 'U')
  18. UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
  19. UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
  20. UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
  21. UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'
  22. exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
  23. FLOAT_LIT = unary_minus? digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent)
  24. FLOAT32_SUFFIX = ('f' | 'F') ['32']
  25. FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX
  26. | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX
  27. FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D'
  28. FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX
  29. | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX
  30. CUSTOM_NUMERIC_LIT = (FLOAT_LIT | INT_LIT) '\'' CUSTOM_NUMERIC_SUFFIX
  31. # CUSTOM_NUMERIC_SUFFIX is any Nim identifier that is not
  32. # a pre-defined type suffix.

从描述中可以看出,数值字面值可以包含下划线,以便于阅读。整数和浮点数可以用十进制(无前缀)、二进制(前缀 0b )、八进制(前缀 0o )和十六进制(前缀 0x )注解表示。

像 -1 这样的数值字面值中的一元减号 - 是字面值的一部分,为了让 -128’i8 等表达式有效,后来被添加到语言中。如果没有这种例外,则只有 -128 有效, — 128 将不是有效的 int8 值。

unary_minus “一元减号”规则有一些限制,这在正式语法中没有提到。 - 是数值字面值的一部分时,前面的字符必须在 {‘ ‘, ‘\t’, ‘\n’, ‘\r’, ‘,’, ‘;’, ‘(‘, ‘[‘, ‘{‘} 集合中,这个设计是为了涵盖大多数合理情况。

在下面的例子中, -1 是单独的 Token 标记:

  1. echo -1
  2. echo(-1)
  3. echo [-1]
  4. echo 3,-1
  5. "abc";-1

在下面的例子中, -1 被解析为两个独立的 Token 标记( - 1 ):

  1. echo x-1
  2. echo (int)-1
  3. echo [a]-1
  4. "abc"-1

以撇号 ( \‘ ) 开始的后缀被称为 type suffix “类型后缀”。没有类型后缀的字面值是整数类型,当包含一个点或 E|e 时是 float 类型。如果字面值的范围在 low(int32)..high(int32) 之间,那么这个整数类型就是 int ,否则就是 int64 。为了记数方便,如果类型后缀明确,那么后缀的撇号是可选的(只有带类型后缀的十六进制浮点数字面值的含义才会不明确)。

预定义的类型后缀有:

类型后缀产生的字面值类型
‘i8int8
‘i16int16
‘i32int32
‘i64int64
‘uuint
‘u8uint8
‘u16uint16
‘u32uint32
‘u64uint64
‘ffloat32
‘dfloat64
‘f32float32
‘f64float64

浮点数字面值也可以采用二进制、八进制或十六进制的注解: 0B0_10001110100_0000101001000111101011101111111011000101001101001001’f64 根据 IEEE 浮点标准,约为 1.72826e35 。

字面值必须匹配数据类型,例如, 333’i8 是一个无效的字面值。以非 10 进制表示的字面值主要用于标记和比特位模式, 因此检查是对位宽而不是值范围进行的,所以: 0b10000000’u8 == 0x80’u8 == 128,但是, 0b10000000’i8 == 0x80’i8 == -128 而不是 -1。

自定义数值字面值

如果后缀未预定义,那么后缀会被认为是对 proc 过程、 template 模板、 macro 宏或其他可调用标识符的调用, 包含字面值的字符串被传递给该标识符。可调用标识符需要用特定的 ‘ 前缀声明。

  1. import strutils
  2. type u4 = distinct uint8 # 一个4位无符号整数,又称 "nibble"
  3. proc `'u4`(n: string): u4 =
  4. # 这是必需的。
  5. result = (parseInt(n) and 0x0F).u4
  6. var x = 5'u4

更确切地说,自定义的数值字面值 123’custom 在解析步骤中被转换为 r”123”.’custom 。并没有对应于这种转换的 AST 节点种类。通过这种转换,在额外参数传递给被调用者时,处理更合理。

  1. import strutils
  2. type u4 = distinct uint8 # 4 位无符号整数,又称 "nibble"
  3. proc `'u4`(n: string; moreData: int): u4 =
  4. result = (parseInt(n) and 0x0F).u4
  5. var x = 5'u4(123)

自定义数值字面值由名称为 CUSTOM_NUMERIC_LIT 的语法规则涵盖。自定义的数值字面值是单独的 Token 标记。