2. 基础面值
面值是在程序代码中直接表示的值,其它的非零初始值只能由面值常量或常量表达式生成.
比如表达式x+2*y
的2
就是面值,而x
和y
则不是面值而是标识符.
2.1 基础面值定义
Go语言规范明确定义了基础面值只有整数、浮点数、复数、符文和字符串几种类型.
需要特别注意的是布尔类型的true
和false
并不是普通的面值,而是内置的布尔类型标识符(可能被重新定义为其它变量).
在go/token
包中,基础面值也被定义为独立的Token:
literal_beg
// Identifiers and basic type literals
// (these tokens stand for classes of literals)
IDENT // main
INT // 12345
FLOAT // 123.45
IMAG // 123.45i
CHAR // 'a'
STRING // "abc"
literal_end
在go/ast
分别用两个结构体进行表示基础面值和标识符:
// 基础面值
type BasicLit struct {
ValuePos token.Pos // literal position
Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
Value string // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
}
// 标识符
type Ident struct {
NamePos token.Pos // identifier position
Name string // identifier name
Obj *Object // denoted object; or nil
}
整数型面值定义如下:
// 整数型面值分为十进制面值、二进制面值、八进制面值和十六进制面值四种形式. int_lit = decimal_lit | binary_lit | octal_lit | hex_lit . decimal_lit = "0" | ( "1" … "9" ) [ [ "" ] decimal_digits ] . binary_lit = "0" ( "b" | "B" ) [ "" ] binary_digits . octal_lit = "0" [ "o" | "O" ] [ "" ] octal_digits . hex_lit = "0" ( "x" | "X" ) [ "" ] hex_digits .
需要注意的是整数型面值并不支持科学计数法形式,同时数字中间可以添加下划线分隔数字.
浮点数面值定义如下:
// 浮点数面值又分为十进制浮点数和十六进制浮点数. float_lit = decimal_float_lit | hex_float_lit .
// 十进制浮点数 decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] | decimal_digits decimal_exponent | "." decimal_digits [ decimal_exponent ] . decimal_exponent = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .
// 十六进制浮点数 hex_float_lit = "0" ( "x" | "X" ) hex_mantissa hex_exponent . hex_mantissa = [ "" ] hex_digits "." [ hex_digits ] | [ "" ] hex_digits | "." hex_digits . hex_exponent = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .
复数面值定义如下:
imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .
符文面值和字符串面值定义如下:
// 符文面值是一个只有一个字符的字符串,由一对单引号包含. rune_lit = "'" ( unicode_value | byte_value ) "'" . unicode_value = unicode_char | little_u_value | big_u_value | escaped_char . byte_value = octal_byte_value | hex_byte_value .
// 字符串由一对双引号或反引号表示,其中可以包含多个字符,但是不能跨行. string_lit = raw_string_lit | interpreted_string_lit . raw_string_lit = "
" { unicode_char | newline } "
" . interpreted_string_lit ="
{ unicode_value | byte_value }"
- 普通的符文和字符串都可以通过转义字符包含特殊的符号,它们是通过一个特殊的
\
斜杠开始.- 反引号表示的字符串表示原生字符串,原生字符串可以跨域多行但是不支持转义字符,因此其内部是无法表示反引号这个字符的.
2.2 基础面值语法树
go/ast
包定义了基础面值结构体:
type BasicLit struct {
ValuePos token.Pos // literal position
Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
Value string // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
}
ValuePos
成员表示该词法元素开始的字节偏移量(并不包含文件名、行号和列号等信息)Kind
表示面值的类型(只有数值类型、字符和字符串三类)Value
是表示面值的原始代码
2.2.1构造基础面值
package main
import (
"go/ast"
"go/token"
)
func main() {
var lit9527 = &ast.BasicLit{
Kind: token.INT,
Value: "9527",
}
ast.Print(nil, lit9527)
}
// output:
// 0 *ast.BasicLit {
// 1 ValuePos: 1
// 2 Kind: INT
// 3 Value: "9527"
// 4 }
token.INT
表示基础面值的类型是整数,值是整数的十进制字符串表示. 如果把token.INT
改为token.FLOAT
则变成浮点数的9527, 如果改成token.STRING
则会变成“9527”字符串面值.
2.2.2 解析基础面值
func main() {
expr, _ := parser.ParseExpr(`9527`)
ast.Print(nil, expr)
}
// output:
// 0 *ast.BasicLit {
// 1 ValuePos: 1
// 2 Kind: INT
// 3 Value: "9527"
// 4 }
基础面值在语法树中是属于叶子结点的存在,在递归遍历语法树时遇到基础面值结点递归就会返回. 同时,通过基础面值、指针、结构体、数组和map等其它语法结构的相互嵌套和组合就可以构造出无穷无尽的复杂类型来.
2.3 标识符面值
go/ast
包定义了标识面值结构体:
type Ident struct {
NamePos token.Pos // identifier position
Name string // identifier name
Obj *Object // denoted object; or nil
}
NamePos
表示标识符的位置Name
是标识符的名字Obj
则表示标识符的类型获取其它的扩展信息.作为内置的标识符面值来说,我们主要关注标识符的名字即可.
2.3.1构造标识符面值
package main
import (
"go/ast"
)
func main() {
ast.Print(nil, ast.NewIdent(`x`))
}
// output:
// 0 *ast.Ident {
// 1 NamePos: 0
// 2 Name: "x"
// 3 }
2.2.2 解析基础面值
func main() {
expr, _ := parser.ParseExpr(`x`)
ast.Print(nil, expr)
}
// 从表达式解析标识符,则会通过Obj成员描述标识符额外的信息.
// output:
// 0 *ast.Ident {
// 1 . NamePos: 1
// 2 . Name: "x"
// 3 . Obj: *ast.Object {
// 4 . . Kind: bad
// 5 . . Name: ""
// 6 . }
// 7 }
ast.Object
是一个相对复杂的结构,其中Kind用于描述标识符的类型:
const (
Bad ObjKind = iota // for error handling
Pkg // package
Con // constant
Typ // type
Var // variable
Fun // function or method
Lbl // label
)
Bad表示未知的类型,其它的分别对应Go语言中包、常量、类型、变量、函数和标号等语法结构. 而对于标识符中更具体的类型(比如是整数还是布尔类型)则是由
ast.Object
的其它成员描述.