美文网首页GO与微服务
手撸golang 仿spring ioc/aop 之7 扫码2

手撸golang 仿spring ioc/aop 之7 扫码2

作者: 老罗话编程 | 来源:发表于2021-04-15 22:08 被阅读0次

    手撸golang 仿spring ioc/aop 之7 扫码2

    缘起

    最近阅读 [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类型都是复合类型,需递归解析
    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)
    

    (未完待续)

    相关文章

      网友评论

        本文标题:手撸golang 仿spring ioc/aop 之7 扫码2

        本文链接:https://www.haomeiwen.com/subject/fenjlltx.html