2. 基础面值

面值是在程序代码中直接表示的值,其它的非零初始值只能由面值常量或常量表达式生成. 比如表达式x+2*y2就是面值,而xy则不是面值而是标识符.

2.1 基础面值定义

Go语言规范明确定义了基础面值只有整数浮点数复数符文字符串几种类型. 需要特别注意的是布尔类型truefalse并不是普通的面值,而是内置的布尔类型标识符(可能被重新定义为其它变量).

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的其它成员描述.