前言
token package 包含了 golang 词法分析相关的数据结构和方法,源代码位于 <go-src>/src/go/token
token.go
源代码中的注释很赞!
Token type
Token is the set of lexical tokens of the Go programming language
type Token int
tokens
The list of tokens(token ids)
const ( // Special tokens ILLEGAL Token = iota EOF COMMENT literal_begin ... literal_end operator_beg ... operator_end keyword_beg ... keyword_end )
使用 const 定义了 Go 语言 tokens,这里有一个地方值得学习:使用 xxx_beg 和 xxx_end 这一对伪 token 作为不同的 token group 分界,方便快速判断 token 类型,比如判断 token id 是否是一个关键字
func (tok Token) IsKeyword() bool { return keyword_beg < tok && tok < keyword_end }
接下来是 token 对应的字符串描述(token string) 和 上述的 const 一一对应
var tokens = [...]string { ILLEGAL: "ILLEGAL",EOF: "EOF",COMMENT: "COMMENT",... }
根据 token id 查询 token string
查询 tokens 数组,在此之前检查数组越界
func (tok Token) String() string { s := "" if 0 <= tok && tok < Token(len(tokens)) { s = tokens[tok] } if s == "" { s = "token(" + strconv.Itoa(int(tok)) + ")" } return s }
keywords
使用 map 保存关键字和 token id 对应关系
var keywords map[string]Token
查询一个 string 是标识符还是关键字
func Lookup(ident string) Token { if tok,is_keyword := keywords[ident]; is_keyword { return tok } return IDENT }
position.go
Position type
Position describes an arbitrary source position including the file,line,and column location
type Position struct { Filename string // filename,if any Offset int // offset,starting at 0 Line int // line number,starting at 1 Column int // column number,starting at 1 (byte count) }
File type
A File is a handle for a file belonging to a FileSet,it has a name,size and line offset table
type File struct { set *FileSet name string // file name as provided to AddFile base int // Pos value range for this file is [base...base+size] size int // file size as provided to AddFile lines []int infos []lineInfo }
lines and infos are protected by set.mutex
lines contains the offset of the first character for each line (the first entry is always 0)
@H_404_67@
lines(line table)
func (f *File) SetLinesForContent(content []byte) { var lines []int line := 0 for offset,b := range content { if line >= 0 { lines = append(lines,line) } line = -1 if b == '\n' { line = offset + 1 } } // set lines table f.set.mutex.Lock() f.lines = lines f.set.mutex.Unlock() }
lines 被定义成一个 slice,由于初始化时 line = 0,所以第一个被添加到 lines 中的值为 0
当检测到一个换行符 'n' 时保存新行 offset + 1 到 line,这里 +1 是为了跳过 n 字符
设置 File lines 字段时使用了 FileSet 的 mutex 进行锁保护
@H_404_67@
Pos type
Pos is a compact encoding of a source position within a file set.
type Pos int
这里的 Pos 容易和上文的 Position 混淆,Position 描述的是 File 内的位置信息,Pos 描述的是 FileSet 内的(编码后的)位置信息,它们之间可以相互转换
func (f *File) PositionFor(p Pos,adjusted bool) (pos Position) { if p != NoPos { if int(p) < f.base || int(p) > f.base+f.size { panic("illegal Pos value") } pos = f.position(p,adjusted) } return } func (f *File) position(p Pos,adjusted bool) (pos Position) { offset := int(p) - f.base pos.Offset = offset pos.Filename,pos.Line,pos.Column = f.unpack(offset,adjusted) return }
如上文所述,File 的 base 属性代表 File 在 FileSet 中的"起始"位置,所以 position 方法中 int(p) - f.base 得到 p 所代表的 Position 在文件的偏移量,f.unpack 根据得到的偏移量对 lines 和 lineInfos 进行二分查找计算行,列偏移量
func (f *File) unpack(offset int,adjusted bool) (filename string,column int) { filename = f.name if i := searchInts(f.lines,offset); i >= 0 { line,column = i+1,offset-f.lines[i]+1 } if adjusted && len(f.infos) > 0 { // almost no files have extra line infos if i := searchLineInfos(f.infos,offset); i >= 0 { alt := &f.infos[i] filename = alt.Filename if i := searchInts(f.lines,alt.Offset); i >= 0 { line += alt.Line - i - 1 } } } return }