缘起
最近阅读 [Spring Boot技术内幕: 架构设计与实现原理] (朱智胜 , 2020.6)
本系列笔记拟采用golang练习之
Talk is cheap, show me the code.
Spring
Spring的主要特性:1. 控制反转(Inversion of Control, IoC)2. 面向容器3. 面向切面(AspectOriented Programming, AOP)源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接:https://my.oschina.net/ioly
目标
- 参考spring boot常用注解,使用golang编写“基于注解的静态代码增强器/生成器”
子目标(Day 7)
- 今天继续the hard part:struct/field/method元素的扫描
- common/Tokens.go:添加数据类型的词法解析支持
- scanner/IStructScanner.go: 结构体扫描器的接口及实现
common/Tokens.go
添加数据类型的词法解析支持:
- 分别解析基本类型/自定义类型/指针类型/数组类型/map类型
- 自定义类型需要注意排除’map’关键字
- 指针,数组和map类型都是复合类型,需递归解析 ```go package common
import ( “regexp” “strings” “sync” )
type tTokens struct { cache map[string]regexp.Regexp rwmutex sync.RWMutex }
var Tokens = newTokensLib()
func newTokensLib() *tTokens { it := new(tTokens) it.init() return it }
func (me tTokens) init() { me.cache = make(map[string]regexp.Regexp) me.rwmutex = new(sync.RWMutex) }
func (me *tTokens) MatchString(s string, p string) bool { return strings.HasPrefix(s, p) }
func (me *tTokens) MatchRegexp(s string, p string) (bool, string) { me.rwmutex.RLock() r, ok := me.cache[p] me.rwmutex.RUnlock()
if !ok {me.rwmutex.Lock()if r, ok = me.cache[p]; !ok {r, _ = regexp.Compile(p)}me.rwmutex.Unlock()}if r == nil {return false, ""}if !r.MatchString(s) {return false, ""}return true, r.FindString(s)
}
func (me *tTokens) MatchIdentifier(s string) (bool, string) { return me.MatchRegexp(s, “^[_a-zA-Z]\w{0,99}”) }
func (me *tTokens) MatchSpaces(s string) (bool, string) { return me.MatchRegexp(s, “^\s+”) }
func (me tTokens) MatchDir(s string) (bool, string) { b, s := me.MatchRegexp(s, “^([a-zA-Z]\:)?([\\/][^\s/:?<>|\\”\\]+)+[\/]?”) if b { return b, s }
b, s = me.MatchRegexp(s, "^\\\"([a-zA-Z]\\:)?([\\\\/][^/:*?<>|\\\"\\\\]+)+[\\/]?\\\"")if b {return b, s}b, s = me.MatchRegexp(s, "^'([a-zA-Z]\\:)?([\\\\/][^'/:*?<>|\\\"\\\\]+)+[\\/]?'")if b {return b, s}return false, ""
}
func (me *tTokens) MatchDataType(s string) (bool, string) { if ok,t := me.MatchBasicType(s);ok { return true, t }
if ok,t := me.MatchCustomType(s);ok {return true, t}if ok,t := me.MatchPointerType(s);ok {return true, t}if ok,t := me.MatchArrayType(s);ok {return true, t}if ok,t := me.MatchMapType(s);ok {return true, t}return false, ""
}
func (me *tTokens) MatchBasicType(s string) (bool, string) { list := []string { “int”, “string”, “bool”, “byte”, “int32”, “int64”, “uint32”, “uint64”, “float32”, “float64”, “int8”, “uint8”, “int16”, “uint16”, “time.Time”, } for _,it := range list { if me.MatchString(s, it) { return true, it } }
return false, ""
}
func (me *tTokens) MatchCustomType(s string) (bool, string) {
t := s
b1, s1 := me.MatchRegexp(t, ^\w+\.)
if b1 {
t = t[len(s1):]
}
b2, s2 := me.MatchRegexp(t, `^\w+`)if !b2 {return false, ""}if s2 == "map" {// map is reserved wordreturn false, ""}return true, s1 + s2
}
func (me tTokens) MatchPointerType(s string) (bool, string) { t := s if t[0] != ‘‘ { return false,”” } t = t[1:]
b, s := me.MatchDataType(t)if !b {return false, ""}return true, "*" + s
}
func (me tTokens) MatchArrayType(s string) (bool, string) { t := s b1, s1 := me.MatchRegexp(s, `^[\s\d\s]\s*`) if !b1 { return false, “” } t = t[len(s1):]
b2, s2 := me.MatchDataType(t)if !b2 {return false, ""}return true, s1 + s2
}
func (me *tTokens) MatchMapType(s string) (bool, string) { t := s s1 := “map” if !me.MatchString(t, s1) { return false, “” } t = t[len(s1):]
b2, s2 := me.MatchRegexp(t, `^\s*\[\s*`)if !b2 {return false, ""}t = t[len(s2):]b3,s3 := me.MatchDataType(t)if !b3 {return false, ""}t = t[len(s3):]b4, s4 := me.MatchRegexp(t, `^\s*\]\s*`)if !b4 {return false, ""}t = t[len(s4):]b5, s5 := me.MatchDataType(t)if !b5 {return false, ""}return true, s1 + s2 + s3 + s4 + s5
}
<a name="lREZ3"></a># scanner/IStructScanner.go结构体扫描器的接口及实现```gopackage scannerimport ("errors""learning/gooop/spring/autogen/common""learning/gooop/spring/autogen/domain""regexp""strings")type IStructScanner interface {ScanStruct(file *domain.CodeFileInfo)}type tStructScanner intfunc (me *tStructScanner) ScanStruct(file *domain.CodeFileInfo) {bInStruct := falsevar stru *domain.StructInfofor lineNO,line := range file.CleanLines {if bInStruct {// end?if gStructEndRegexp.MatchString(line) {bInStruct = falseme.scanMethod(stru, lineNO + 1)stru = nilcontinue}}// start?if gStructStartRegexp.MatchString(line) {bInStruct = truess := gStructStartRegexp.FindAllString(line, -1)stru := domain.NewStructInfo()stru.LineNO = lineNOstru.CodeFile = filestru.Name = ss[1]continue}// in struct blockok,fname,ftype := me.scanField(line)if ok {stru.AppendField(lineNO, fname, ftype)}}}func (me *tStructScanner) scanField(line string) (ok bool, fldName string, fldType string) {if !gFieldStartRegexp.MatchString(line) {return false, "",""}fldName = strings.TrimSpace(gFieldStartRegexp.FindString(line))fldType = strings.TrimSpace(line[len(fldName):])return true, fldName, fldType}func (me *tStructScanner) scanMethod(stru *domain.StructInfo, fromLineNO int) {for i,max := fromLineNO, len(stru.CodeFile.CleanLines);i <= max;i++ {line := stru.CodeFile.CleanLines[i]if !gMethodStartRegex.MatchString(line) {continue}ss := gMethodStartRegex.FindAllString(line, -1)// declaredeclare := ss[0]offset := len(declare)// receiverreceiver := ss[1]if receiver != stru.Name {continue}method := domain.NewMethodInfo()// namemethod.Name = ss[2]// method input argse,args := me.scanMethodArgs(method, strings.TrimSpace(line[offset:]))if e != nil {panic(e)}offset += len(args)// method return argse = me.scanReturnArgs(method, strings.TrimSpace(line[offset:]))if e != nil {panic(e)}// end scan methodstru.AppendMethod(method)}}func (me *tStructScanner) scanMethodArgs(method *domain.MethodInfo, s string) (error, string) {t := soffset := 0for {// nameb1, s1 := common.Tokens.MatchRegexp(t, `\w+(\s*,\s*\w+)\s+`)if !b1 {break}argNames := s1offset += len(s1)t = s[offset:]// data typeb2, s2 := common.Tokens.MatchDataType(t)if !b2 {return gInvalidMethodArgs, ""}argDataType := s2offset += len(s2)t = s[offset:]for _,it := range strings.Split(argNames, ",") {method.AppendArgument(it, argDataType)}// ,\s+b3, s3 := common.Tokens.MatchRegexp(t, `\s*,\s*`)if !b3 {break}offset += len(s3)t = s[offset:]}return nil, s[0:offset]}func (me *tStructScanner) scanReturnArgs(method *domain.MethodInfo, s string) error {// todo: fixmepanic("implements me")}var gStructStartRegexp = regexp.MustCompile(`^\s*type\s+(\w+)\s+struct\s+\{`)var gStructEndRegexp = regexp.MustCompile(`^\s*}`)var gFieldStartRegexp = regexp.MustCompile(`^\s*\w+\s+`)var gMethodStartRegex = regexp.MustCompile(`\s*func\s+\(\s*\w+\s+\*?(\w+)\s*\)\s+(\w+)\s*\(`)var gInvalidMethodArgs = errors.New("invalid method arguments")var DefaultStructScanner IStructScanner = new(tStructScanner)
(未完待续)
