Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

go/token - Token 和位置信息

go/token 包提供了 Go 词法 token 的定义和位置信息的管理功能,是 Go 源码处理的基础组件。

概述

go/token 包定义了 Go 语言的词法 token、位置信息(Pos)和文件集(FileSet),用于词法分析、语法分析和错误报告。

包导入

import (
    "go/token"
    "fmt"
)

基本使用

// 1. 创建 FileSet
fset := token.NewFileSet()

// 2. 添加文件
file := fset.AddFile("main.go", fset.Base(), 100)

// 3. 获取 token 信息
tok := token.FUNC
fmt.Printf("Token: %s\n", tok)
fmt.Printf("字符串:%q\n", tok.String())
fmt.Printf("是否关键字:%v\n", tok.IsKeyword())

// 4. 位置转换
pos := file.Pos(10)
fmt.Printf("位置:%s\n", fset.Position(pos))

典型示例

示例 1:使用 FileSet 管理位置

package main

import (
    "fmt"
    "go/token"
)

func main() {
    // 创建 FileSet
    fset := token.NewFileSet()
    
    // 添加多个文件
    file1 := fset.AddFile("main.go", fset.Base(), 100)
    file2 := fset.AddFile("util.go", fset.Base(), 200)
    
    // 获取位置
    pos1 := file1.Pos(10)
    pos2 := file2.Pos(20)
    
    // 转换为可读位置
    fmt.Printf("main.go: %s\n", fset.Position(pos1))
    fmt.Printf("util.go: %s\n", fset.Position(pos2))
    
    // 获取文件信息
    fmt.Printf("文件数:%d\n", fset.FileCount())
}

运行

$ go run main.go
main.go: main.go:1:11
util.go: util.go:1:21
文件数:2

示例 2:Token 操作

package main

import (
    "fmt"
    "go/token"
)

func main() {
    // 各种 token
    tokens := []token.Token{
        token.FUNC,
        token.VAR,
        token.IDENT,
        token.INT,
        token.STRING,
        token.ADD,
        token.ASSIGN,
        token.LPAREN,
        token.EOF,
    }
    
    for _, tok := range tokens {
        fmt.Printf("%-10s 字符串:%-10q 关键字:%v 字面量:%v\n",
            tok,
            tok.String(),
            tok.IsKeyword(),
            tok.IsLiteral(),
        )
    }
}

运行

$ go run main.go
func       字符串:"func"      关键字:true   字面量:false
var        字符串:"var"       关键字:true   字面量:false
IDENT      字符串:"IDENT"     关键字:false  字面量:true
INT        字符串:"INT"       关键字:false  字面量:true
STRING     字符串:"STRING"    关键字:false  字面量:true
+          字符串:"+"         关键字:false  字面量:false
=          字符串:"="         关键字:false  字面量:false
(          字符串:"("         关键字:false  字面量:false
EOF        字符串:"EOF"       关键字:false  字面量:false

一、Token 类型

Token 类型定义

Token

定义

type Token int

说明

  • 表示 Go 语言的词法 token
  • 包括关键字、标识符、字面量、操作符等

特殊 Token

ILLEGAL

定义

const ILLEGAL Token = iota

说明

  • 非法 token
  • 表示无法识别的字符序列

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tok := token.ILLEGAL
    fmt.Printf("Token: %s\n", tok)
    fmt.Printf("值:%d\n", tok)
}

EOF

定义

const EOF Token = -(iota + 1)

说明

  • 文件结束标记
  • 值为负数,避免与有效 token 冲突

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tok := token.EOF
    fmt.Printf("Token: %s\n", tok)
    fmt.Printf("值:%d\n", tok)
}

运行

$ go run main.go
Token: EOF
值:-1

COMMENT

定义

const COMMENT Token = -(iota + 2)

说明

  • 注释 token
  • 包括单行注释(//)和多行注释(/* */)

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tok := token.COMMENT
    fmt.Printf("Token: %s\n", tok)
    fmt.Printf("是否字面量:%v\n", tok.IsLiteral())
}

字面量 Token

IDENT

定义

const IDENT Token = -(iota + 3)

说明

  • 标识符 token
  • 变量名、函数名、类型名等

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tok := token.IDENT
    fmt.Printf("Token: %s\n", tok)
    fmt.Printf("是否字面量:%v\n", tok.IsLiteral())
}

INT

定义

const INT Token = -(iota + 4)

说明

  • 整数字面量
  • 包括十进制、八进制、十六进制

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tok := token.INT
    fmt.Printf("Token: %s\n", tok)
    fmt.Printf("字面量类型:%s\n", tok.LitString())
}

FLOAT

定义

const FLOAT Token = -(iota + 5)

说明

  • 浮点数字面量
  • 包括小数和科学计数法

IMAG

定义

const IMAG Token = -(iota + 6)

说明

  • 虚数字面量
  • 如:1i, 2.5i

CHAR

定义

const CHAR Token = -(iota + 7)

说明

  • 字符字面量
  • 单引号括起来的字符

STRING

定义

const STRING Token = -(iota + 8)

说明

  • 字符串字面量
  • 双引号括起来的字符串

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tok := token.STRING
    fmt.Printf("Token: %s\n", tok)
    fmt.Printf("是否字面量:%v\n", tok.IsLiteral())
}

关键字 Token

PACKAGE

定义

const PACKAGE Token = -(iota + 9)

说明

  • package 关键字

FUNC

定义

const FUNC Token = -(iota + 10)

说明

  • func 关键字

VAR

定义

const VAR Token = -(iota + 11)

说明

  • var 关键字

CONST

定义

const CONST Token = -(iota + 12)

说明

  • const 关键字

TYPE

定义

const TYPE Token = -(iota + 13)

说明

  • type 关键字

IMPORT

定义

const IMPORT Token = -(iota + 14)

说明

  • import 关键字

其他关键字

// 控制流
const (
    BREAK       Token = -(iota + 15)
    CASE        Token = -(iota + 16)
    CONTINUE    Token = -(iota + 17)
    DEFAULT     Token = -(iota + 18)
    DEFER       Token = -(iota + 19)
    ELSE        Token = -(iota + 20)
    FALLTHROUGH Token = -(iota + 21)
    FOR         Token = -(iota + 22)
    GO          Token = -(iota + 23)
    GOTO        Token = -(iota + 24)
    IF          Token = -(iota + 25)
    RANGE       Token = -(iota + 26)
    RETURN      Token = -(iota + 27)
    SELECT      Token = -(iota + 28)
    SWITCH      Token = -(iota + 29)
)

// 类型关键字
const (
    INTERFACE Token = -(iota + 30)
    STRUCT    Token = -(iota + 31)
    MAP       Token = -(iota + 32)
    CHANNEL   Token = -(iota + 33)
)

// 其他
const (
    TRUE  Token = -(iota + 34)
    FALSE Token = -(iota + 35)
    NIL   Token = -(iota + 36)
)

操作符 Token

ADD

定义

const ADD Token = iota + 1

说明

  • 加法操作符(+)

SUB

定义

const SUB Token = iota + 2

说明

  • 减法操作符(-)

MUL

定义

const MUL Token = iota + 3

说明

  • 乘法操作符(*)

QUO

定义

const QUO Token = iota + 4

说明

  • 除法操作符(/)

REM

定义

const REM Token = iota + 5

说明

  • 取余操作符(%)

其他操作符

// 逻辑操作符
const (
    LAND Token = iota + 6  // &&
    LOR                    // ||
    NOT                    // !
)

// 位操作符
const (
    AND     Token = iota + 9  // &
    OR                        // |
    XOR                       // ^
    SHL                       // <<
    SHR                       // >>
    AND_NOT                   // &^
)

// 赋值操作符
const (
    ASSIGN Token = iota + 15  // =
    DEFINE                    // :=
    ADD_ASSIGN                // +=
    SUB_ASSIGN                // -=
    MUL_ASSIGN                // *=
    QUO_ASSIGN                // /=
    REM_ASSIGN                // %=
    AND_ASSIGN                // &=
    OR_ASSIGN                 // |=
    XOR_ASSIGN                // ^=
    SHL_ASSIGN                // <<=
    SHR_ASSIGN                // >>=
    AND_NOT_ASSIGN            // &^=
)

// 比较操作符
const (
    EQL Token = iota + 27  // ==
    LSS                    // <
    GTR                    // >
    ASSIGN                 // =
    NEQ                    // !=
    LEQ                    // <=
    GEQ                    // >=
)

// 其他
const (
    INC Token = iota + 34  // ++
    DEC                    // --
    ARROW                  // <-
    DOT                    // .
    COMMA                  // ,
    SEMICOLON              // ;
    COLON                  // :
    LPAREN                 // (
    LBRACK                 // [
    LBRACE                 // {
    RPAREN                 // )
    RBRACK                 // ]
    RBRACE                 // }
)

二、Pos 类型

位置类型

Pos

定义

type Pos int

说明

  • 表示源码中的位置
  • 是相对于 FileSet 基址的偏移量

方法

  • IsValid() bool - 检查位置是否有效
  • Offset() int - 获取偏移量

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    fset := token.NewFileSet()
    file := fset.AddFile("test.go", fset.Base(), 100)
    
    // 获取位置
    pos := file.Pos(10)
    
    fmt.Printf("位置:%d\n", pos)
    fmt.Printf("是否有效:%v\n", pos.IsValid())
    fmt.Printf("偏移量:%d\n", pos.Offset(fset.Base()))
}

三、File 和 FileSet

File 结构体

File

定义

type File struct {
    // 内部字段
}

说明

  • 表示一个源文件
  • 包含文件的位置信息

方法

  • Name() string - 文件名
  • Base() int - 基址
  • Size() int - 文件大小
  • Pos(offset int) Pos - 从偏移量获取 Pos
  • Offset(pos Pos) int - 从 Pos 获取偏移量
  • Line(pos Pos) int - 获取行号
  • Position(pos Pos) Position - 获取完整位置

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    fset := token.NewFileSet()
    file := fset.AddFile("main.go", fset.Base(), 1000)
    
    fmt.Printf("文件名:%s\n", file.Name())
    fmt.Printf("基址:%d\n", file.Base())
    fmt.Printf("大小:%d\n", file.Size())
    
    // 获取行号
    pos := file.Pos(50)
    fmt.Printf("位置 %d 的行号:%d\n", pos, file.Line(pos))
}

FileSet 结构体

FileSet

定义

type FileSet struct {
    // 内部字段
}

说明

  • 管理多个文件的位置信息
  • 为每个文件分配不重叠的位置范围

方法

  • NewFileSet() *FileSet - 创建新的 FileSet
  • Base() int - 获取下一个基址
  • AddFile(filename string, base int, size int) *File - 添加文件
  • File(pos Pos) *File - 获取位置对应的文件
  • Position(pos Pos) Position - 获取完整位置
  • FileCount() int - 获取文件数量

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    fset := token.NewFileSet()
    
    // 添加多个文件
    file1 := fset.AddFile("main.go", fset.Base(), 1000)
    file2 := fset.AddFile("util.go", fset.Base(), 2000)
    
    fmt.Printf("文件数:%d\n", fset.FileCount())
    fmt.Printf("下一个基址:%d\n", fset.Base())
    
    // 获取位置
    pos1 := file1.Pos(100)
    pos2 := file2.Pos(200)
    
    // 转换为可读位置
    fmt.Printf("main.go: %s\n", fset.Position(pos1))
    fmt.Printf("util.go: %s\n", fset.Position(pos2))
}

运行

$ go run main.go
文件数:2
下一个基址:3001
main.go: main.go:1:101
util.go: util.go:1:201

Position 结构体

Position

定义

type Position struct {
    Filename string // 文件名
    Offset   int    // 偏移量
    Line     int    // 行号
    Column   int    // 列号
}

说明

  • 表示源码中的完整位置信息
  • 包含文件名、偏移量、行号、列号

方法

  • IsValid() bool - 检查位置是否有效
  • String() string - 字符串表示

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    fset := token.NewFileSet()
    file := fset.AddFile("main.go", fset.Base(), 1000)
    
    pos := file.Pos(50)
    position := fset.Position(pos)
    
    fmt.Printf("文件名:%s\n", position.Filename)
    fmt.Printf("偏移量:%d\n", position.Offset)
    fmt.Printf("行号:%d\n", position.Line)
    fmt.Printf("列号:%d\n", position.Column)
    fmt.Printf("字符串:%s\n", position.String())
    fmt.Printf("是否有效:%v\n", position.IsValid())
}

运行

$ go run main.go
文件名:main.go
偏移量:50
行号:1
列号:51
字符串:main.go:1:51
是否有效:true

四、包级别函数(按字母顺序)

判断是否为标识符

IsIdentifier

定义

func IsIdentifier(s string) bool

说明

  • 检查字符串是否为合法的 Go 标识符

参数

  • s:待检查的字符串

返回值

  • bool:是否为合法标识符

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tests := []string{
        "x",
        "myVar",
        "Func123",
        "123abc",
        "my-var",
        "_private",
    }
    
    for _, s := range tests {
        fmt.Printf("%-10s: %v\n", s, token.IsIdentifier(s))
    }
}

运行

$ go run main.go
x         : true
myVar     : true
Func123   : true
123abc    : false
my-var    : false
_private  : true

判断是否为关键字

IsKeyword

定义

func IsKeyword(s string) bool

说明

  • 检查字符串是否为 Go 关键字

参数

  • s:待检查的字符串

返回值

  • bool:是否为关键字

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tests := []string{
        "func",
        "var",
        "myFunc",
        "if",
        "range",
        "print",
    }
    
    for _, s := range tests {
        fmt.Printf("%-10s: %v\n", s, token.IsKeyword(s))
    }
}

运行

$ go run main.go
func      : true
var       : true
myFunc    : false
if        : true
range     : true
print     : false

查找关键字

Lookup

定义

func Lookup(ident string) Token

说明

  • 查找标识符对应的 Token
  • 如果是关键字,返回对应的关键字 Token
  • 否则返回 IDENT

参数

  • ident:标识符字符串

返回值

  • Token:对应的 Token

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    idents := []string{
        "func",
        "var",
        "myVar",
        "if",
        "else",
        "custom",
    }
    
    for _, ident := range idents {
        tok := token.Lookup(ident)
        fmt.Printf("%-10s -> %s (关键字:%v)\n",
            ident, tok, tok.IsKeyword())
    }
}

运行

$ go run main.go
func       -> func (关键字:true)
var        -> var (关键字:true)
myVar      -> IDENT (关键字:false)
if         -> if (关键字:true)
else       -> else (关键字:true)
custom     -> IDENT (关键字:false)

创建 FileSet

NewFileSet

定义

func NewFileSet() *FileSet

说明

  • 创建新的 FileSet
  • 最常用的函数

返回值

  • *FileSet:新的 FileSet

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    fset := token.NewFileSet()
    
    // 添加文件
    file := fset.AddFile("main.go", fset.Base(), 1000)
    
    fmt.Printf("FileSet 创建成功\n")
    fmt.Printf("文件数:%d\n", fset.FileCount())
    fmt.Printf("文件名:%s\n", file.Name())
}

Token 字符串表示

String

定义

func (tok Token) String() string

说明

  • 返回 Token 的字符串表示

返回值

  • string:Token 的字符串

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tokens := []token.Token{
        token.FUNC,
        token.IDENT,
        token.INT,
        token.ADD,
        token.EOF,
    }
    
    for _, tok := range tokens {
        fmt.Printf("%s\n", tok.String())
    }
}

运行

$ go run main.go
func
IDENT
INT
+
EOF

判断是否为关键字

IsKeyword

定义

func (tok Token) IsKeyword() bool

说明

  • 检查 Token 是否为关键字

返回值

  • bool:是否为关键字

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tokens := []token.Token{
        token.FUNC,
        token.VAR,
        token.IDENT,
        token.INT,
        token.IF,
        token.ELSE,
    }
    
    for _, tok := range tokens {
        fmt.Printf("%-10s: %v\n", tok, tok.IsKeyword())
    }
}

运行

$ go run main.go
func      : true
var       : true
IDENT     : false
INT       : false
if        : true
else      : true

判断是否为字面量

IsLiteral

定义

func (tok Token) IsLiteral() bool

说明

  • 检查 Token 是否为字面量

返回值

  • bool:是否为字面量

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tokens := []token.Token{
        token.IDENT,
        token.INT,
        token.FLOAT,
        token.STRING,
        token.CHAR,
        token.FUNC,
        token.ADD,
    }
    
    for _, tok := range tokens {
        fmt.Printf("%-10s: %v\n", tok, tok.IsLiteral())
    }
}

运行

$ go run main.go
IDENT     : true
INT       : true
FLOAT     : true
STRING    : true
CHAR      : true
func      : false
+         : false

判断是否为运算符

IsOperator

定义

func (tok Token) IsOperator() bool

说明

  • 检查 Token 是否为运算符

返回值

  • bool:是否为运算符

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tokens := []token.Token{
        token.ADD,
        token.SUB,
        token.MUL,
        token.QUO,
        token.ASSIGN,
        token.EQL,
        token.IDENT,
    }
    
    for _, tok := range tokens {
        fmt.Printf("%-10s: %v\n", tok, tok.IsOperator())
    }
}

运行

$ go run main.go
+         : true
-         : true
*         : true
/         : true
=         : true
==        : true
IDENT     : false

获取字面量类型字符串

LitString

定义

func (tok Token) LitString() string

说明

  • 返回字面量类型的字符串表示
  • 仅对字面量 Token 有效

返回值

  • string:字面量类型字符串

示例

package main

import (
    "fmt"
    "go/token"
)

func main() {
    tokens := []token.Token{
        token.INT,
        token.FLOAT,
        token.STRING,
        token.CHAR,
        token.IDENT,
    }
    
    for _, tok := range tokens {
        if tok.IsLiteral() {
            fmt.Printf("%-10s: %s\n", tok, tok.LitString())
        }
    }
}

运行

$ go run main.go
INT       : int
FLOAT     : float
STRING    : string
CHAR      : char
IDENT     : ident

五、快速参考

Token 分类

分类Token 示例说明
特殊ILLEGAL, EOF, COMMENT特殊标记
字面量IDENT, INT, FLOAT, STRING, CHAR标识符和字面量
关键字PACKAGE, FUNC, VAR, IF, FORGo 语言关键字
运算符ADD, SUB, MUL, QUO算术和逻辑运算符
分隔符LPAREN, RPAREN, LBRACE, RBRACE括号和分隔符

包级别函数

函数说明输入输出
IsIdentifier(s)检查标识符stringbool
IsKeyword(s)检查关键字stringbool
Lookup(ident)查找 TokenstringToken
NewFileSet()创建 FileSet-*FileSet

Token 方法

方法说明返回值
tok.String()字符串表示string
tok.IsKeyword()是否关键字bool
tok.IsLiteral()是否字面量bool
tok.IsOperator()是否运算符bool
tok.LitString()字面量类型string

FileSet 方法

方法说明
NewFileSet()创建新的 FileSet
AddFile(filename, base, size)添加文件
File(pos)获取位置对应的文件
Position(pos)获取完整位置
FileCount()获取文件数量

File 方法

方法说明
Name()文件名
Base()基址
Size()文件大小
Pos(offset)从偏移量获取 Pos
Offset(pos)从 Pos 获取偏移量
Line(pos)获取行号
Position(pos)获取完整位置

Position 字段

字段类型说明
Filenamestring文件名
Offsetint字节偏移量
Lineint行号(从 1 开始)
Columnint列号(从 1 开始)

使用场景

场景推荐函数/方法
创建位置管理NewFileSet()
添加源文件fset.AddFile()
位置转换fset.Position(pos)
检查标识符IsIdentifier(s)
检查关键字IsKeyword(s) 或 Lookup(s)
Token 分类IsKeyword(), IsLiteral(), IsOperator()
错误报告fset.Position(pos)

六、最佳实践

1. 错误报告

package main

import (
    "fmt"
    "go/token"
)

func reportError(fset *token.FileSet, pos token.Pos, msg string) {
    p := fset.Position(pos)
    fmt.Printf("%s:%d:%d: error: %s\n",
        p.Filename, p.Line, p.Column, msg)
}

2. 扫描 Token

package main

import (
    "fmt"
    "go/scanner"
    "go/token"
)

func scanTokens(src []byte) {
    fset := token.NewFileSet()
    file := fset.AddFile("", fset.Base(), len(src))
    
    var s scanner.Scanner
    s.Init(file, src, nil, 0)
    
    for {
        pos, tok, lit := s.Scan()
        if tok == token.EOF {
            break
        }
        fmt.Printf("%s: %s %q\n", fset.Position(pos), tok, lit)
    }
}

3. 多文件管理

package main

import (
    "fmt"
    "go/token"
)

func manageMultipleFiles(files map[string][]byte) {
    fset := token.NewFileSet()
    
    // 添加所有文件
    for name, src := range files {
        fset.AddFile(name, fset.Base(), len(src))
    }
    
    fmt.Printf("管理 %d 个文件\n", fset.FileCount())
}

最后更新:2026-04-04
Go 版本:Go 1.23+