了解Go编译处理(四)—— go文件的解析一文初步介绍了.go
文件的解析过程,本文主要关注具体的解析过程——顶层声明的解析。
更多内容分享,欢迎关注公众号:Go开发笔记
在scanner的next处理中包含的关键字处理,即ident
.
func (s *scanner) next() {
...
if isLetter(c) || c >= utf8.RuneSelf && s.isIdentRune(c, true) {
s.ident()
return
}
...
}
ident
中包含了具体关键字的匹配处理,这里关键字是保存统一保存在一个string内,再通过一个数组记录所有关键字的起始位置。
tok对应的是关键字的结束位置。tok的处理是计算hash后存入map,在匹配时可根据hash获取对应的tok及tok对应的关键字。
func (s *scanner) ident() {
s.startLit()
// accelerate common case (7bit ASCII)
c := s.getr()
for isLetter(c) || isDecimal(c) {
c = s.getr()
}
// general case
if c >= utf8.RuneSelf {
for s.isIdentRune(c, false) {
c = s.getr()
}
}
s.ungetr()
lit := s.stopLit()
// possibly a keyword
if len(lit) >= 2 {
if tok := keywordMap[hash(lit)]; tok != 0 && tokStrFast(tok) == string(lit) {
s.nlsemi = contains(1<<_Break|1<<_Continue|1<<_Fallthrough|1<<_Return, tok)
s.tok = tok
return
}
}
s.nlsemi = true
s.lit = string(lit)
s.tok = _Name
}
func tokStrFast(tok token) string {// 获取token对应的关键字
return _token_name[_token_index[tok-1]:_token_index[tok]]
}
const _token_name = "EOFnameliteralopop=opop=:=<-*([{)]},;:....breakcasechanconstcontinuedefaultdeferelsefallthroughforfuncgogotoifimportinterfacemappackagerangereturnselectstructswitchtypevar"
var _token_index = [...]uint8{0, 3, 7, 14, 16, 19, 23, 24, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 42, 47, 51, 55, 60, 68, 75, 80, 84, 95, 98, 102, 104, 108, 110, 116, 125, 128, 135, 140, 146, 152, 158, 164, 168, 171, 171}
func (i token) String() string {// 获取token对应的关键字
i -= 1
if i >= token(len(_token_index)-1) {
return "token(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _token_name[_token_index[i]:_token_index[i+1]]
}
func hash(s []byte) uint {
return (uint(s[0])<<4 ^ uint(s[1]) + uint(len(s))) & uint(len(keywordMap)-1)
}
var keywordMap [1 << 6]token // size must be power of two
func init() {
// populate keywordMap
for tok := _Break; tok <= _Var; tok++ {
h := hash([]byte(tok.String()))
if keywordMap[h] != 0 {
panic("imperfect hash")
}
keywordMap[h] = tok
}
}
ident中前半部分是字符组的获取,获取后开始判断是变量名还是关键字。
keywordMap在初始化时,存入了关键字对应hash的tok。
匹配时,确认tok存在且对应的关键字与当前字符串相同,即为关键字;否则为变量名。如果为关键字,还需要标记是否将’\n’或EOF转译为’;’。
func (p *parser) name() *Name {
// no tracing to avoid overly verbose output
if p.tok == _Name {
n := p.newName(p.lit)
p.next()
return n
}
n := p.newName("_")
p.syntaxError("expecting name")
p.advance()
return n
}
// IdentifierList = identifier { "," identifier } .
// The first name must be provided.
func (p *parser) nameList(first *Name) []*Name {
if trace {
defer p.trace("nameList")()
}
if debug && first == nil {
panic("first name not provided")
}
l := []*Name{first}
for p.got(_Comma) {
l = append(l, p.name())
}
return l
}
name处理逻辑是在const、type、var之后紧接着就是变量名_Name,否则报错。
nameList则是以’,'分隔的多个声明变量的处理。
顶层声明中只能使用’=‘而不能使用’:=’,gotAssign是基于’='的处理,获取其后的值。
func (p *parser) gotAssign() bool {
switch p.tok {
case _Define:
p.syntaxError("expecting =")
fallthrough
case _Assign:
p.next()
return true
}
return false
}
// ExpressionList = Expression { "," Expression } .
func (p *parser) exprList() Expr {
if trace {
defer p.trace("exprList")()
}
x := p.expr()
if p.got(_Comma) {
list := []Expr{x, p.expr()}
for p.got(_Comma) {
list = append(list, p.expr())
}
t := new(ListExpr)
t.pos = x.Pos()
t.ElemList = list
x = t
}
return x
}
import,const ,type ,var可以将一组数据用括号括起来,其解析过程如下:
// appendGroup(f) = f | "(" { f ";" } ")" . // ";" is optional before ")"
func (p *parser) appendGroup(list []Decl, f func(*Group) Decl) []Decl {
if p.tok == _Lparen {
g := new(Group)
p.list(_Lparen, _Semi, _Rparen, func() bool {
list = append(list, f(g))
return false
})
} else {
list = append(list, f(nil))
}
if debug {
for _, d := range list {
if d == nil {
panic("nil list entry")
}
}
}
return list
}
func (p *parser) list(open, sep, close token, f func() bool) Pos {
p.want(open)
var done bool
for p.tok != _EOF && p.tok != close && !done {
done = f()
// sep is optional before close
if !p.got(sep) && p.tok != close {
p.syntaxError(fmt.Sprintf("expecting %s or %s", tokstring(sep), tokstring(close)))
p.advance(_Rparen, _Rbrack, _Rbrace)
if p.tok != close {
// position could be better but we had an error so we don't care
return p.pos()
}
}
}
pos := p.pos()
p.want(close)
return pos
}
如果存在’(’,说明存在声明组,处理是以’;‘为分隔,具体为各种类型的解析方式,最后以’('结束。
import的格式: (PackageName) ImportPath
PackageName可忽略,对于指定的PackageName,存在特殊的名字"."
,这个需要特殊处理。
// ImportSpec = [ "." | PackageName ] ImportPath .
// ImportPath = string_lit .
func (p *parser) importDecl(group *Group) Decl {
if trace {
defer p.trace("importDecl")()
}
d := new(ImportDecl)
d.pos = p.pos()
switch p.tok {
case _Name:
d.LocalPkgName = p.name()
case _Dot:
d.LocalPkgName = p.newName(".")
p.next()
}
d.Path = p.oliteral()
if d.Path == nil {
p.syntaxError("missing import path")
p.advance(_Semi, _Rparen)
return nil
}
d.Group = group
return d
}
一次获取指定包名(可不指定)、路径,注意此处名称可以使用'.'
,即import . "x"
,'_'
已包含在_Name中
const的格式:
格式一:
cons1 ,cons2,... consType = expr1, expr2,...
consType
可忽略,但每个cons必须要赋值
格式二:
(
var1 = iota
var2
...
)
存在iota时,除第一个外,其他可以不赋值
// ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
func (p *parser) constDecl(group *Group) Decl {
if trace {
defer p.trace("constDecl")()
}
d := new(ConstDecl)
d.pos = p.pos()
d.NameList = p.nameList(p.name())
if p.tok != _EOF && p.tok != _Semi && p.tok != _Rparen {
d.Type = p.typeOrNil()
if p.gotAssign() {
d.Values = p.exprList()
}
}
d.Group = group
return d
}
依次获取常量名(列表),常量的类型(未指定时,d.Type记录为nil),及常量对应的value(存在赋值的情况下)
type的格式: defined_type = Type
=
可以省略,必须要有声明的类型
// TypeSpec = identifier [ "=" ] Type .
func (p *parser) typeDecl(group *Group) Decl {
if trace {
defer p.trace("typeDecl")()
}
d := new(TypeDecl)
d.pos = p.pos()
d.Name = p.name()
d.Alias = p.gotAssign()
d.Type = p.typeOrNil()
if d.Type == nil {
d.Type = p.badExpr()
p.syntaxError("in type declaration")
p.advance(_Semi, _Rparen)
}
d.Group = group
d.Pragma = p.pragma
return d
}
处理是依次获取名称类型、是否包含赋值、类型Type(必须制定Type,否则非法),并记录在group。
var的格式:
格式一
var1 ,var2,... varType = expr1, expr2,...
varType
在有赋值的时候(每个var必须都有赋值)可忽略
varType
指定时,=
及赋值表达式可忽略
// VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
func (p *parser) varDecl(group *Group) Decl {
if trace {
defer p.trace("varDecl")()
}
d := new(VarDecl)
d.pos = p.pos()
d.NameList = p.nameList(p.name())
if p.gotAssign() {
d.Values = p.exprList()
} else {
d.Type = p.type_()
if p.gotAssign() {
d.Values = p.exprList()
}
}
d.Group = group
return d
}
处理依次:
本文主要介绍了.go
文件中顶层声明import、const、type、var等解析的具体过程。这些解析过程实际也是go的规范,必须满足代码规范,才能正确解析。顶层声明中还有func的解析,由于func相对复杂,后面的文章中会单独说明。
鄙人刚刚开通了公众号,专注于分享Go开发相关内容,望大家感兴趣的支持一下,在此特别感谢。