缘起
最近阅读 [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地址:
原文链接:
目标
- 参考spring boot常用注解,使用golang编写“基于注解的静态代码增强器/生成器”
子目标(Day 7)
- 今天继续the hard part:struct/field/method元素的扫描
- common/Tokens.go:添加数据类型的词法解析支持
- scanner/IStructScanner.go: 结构体扫描器的接口及实现
common/Tokens.go
添加数据类型的词法解析支持:
- 分别解析基本类型/自定义类型/指针类型/数组类型/map类型
- 自定义类型需要注意排除’map’关键字
- 指针,数组和map类型都是复合类型,需递归解析
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 word
return 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
}
scanner/IStructScanner.go
结构体扫描器的接口及实现
package scanner
import (
"errors"
"learning/gooop/spring/autogen/common"
"learning/gooop/spring/autogen/domain"
"regexp"
"strings"
)
type IStructScanner interface {
ScanStruct(file *domain.CodeFileInfo)
}
type tStructScanner int
func (me *tStructScanner) ScanStruct(file *domain.CodeFileInfo) {
bInStruct := false
var stru *domain.StructInfo
for lineNO,line := range file.CleanLines {
if bInStruct {
// end?
if gStructEndRegexp.MatchString(line) {
bInStruct = false
me.scanMethod(stru, lineNO + 1)
stru = nil
continue
}
}
// start?
if gStructStartRegexp.MatchString(line) {
bInStruct = true
ss := gStructStartRegexp.FindAllString(line, -1)
stru := domain.NewStructInfo()
stru.LineNO = lineNO
stru.CodeFile = file
stru.Name = ss[1]
continue
}
// in struct block
ok,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)
// declare
declare := ss[0]
offset := len(declare)
// receiver
receiver := ss[1]
if receiver != stru.Name {
continue
}
method := domain.NewMethodInfo()
// name
method.Name = ss[2]
// method input args
e,args := me.scanMethodArgs(method, strings.TrimSpace(line[offset:]))
if e != nil {
panic(e)
}
offset += len(args)
// method return args
e = me.scanReturnArgs(method, strings.TrimSpace(line[offset:]))
if e != nil {
panic(e)
}
// end scan method
stru.AppendMethod(method)
}
}
func (me *tStructScanner) scanMethodArgs(method *domain.MethodInfo, s string) (error, string) {
t := s
offset := 0
for {
// name
b1, s1 := common.Tokens.MatchRegexp(t, `\w+(\s*,\s*\w+)\s+`)
if !b1 {
break
}
argNames := s1
offset += len(s1)
t = s[offset:]
// data type
b2, s2 := common.Tokens.MatchDataType(t)
if !b2 {
return gInvalidMethodArgs, ""
}
argDataType := s2
offset += 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: fixme
panic("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)
(未完待续)