手?jǐn)]golang 仿spring ioc/aop 之7 掃碼2

手?jǐn)]golang 仿spring ioc/aop 之7 掃碼2

緣起

最近閱讀 [Spring Boot技術(shù)內(nèi)幕: 架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)原理] (朱智勝 , 2020.6)
本系列筆記擬采用golang練習(xí)之
Talk is cheap, show me the code.

Spring

Spring的主要特性:
1. 控制反轉(zhuǎn)(Inversion of Control, IoC)
2. 面向容器
3. 面向切面(AspectOriented Programming, AOP)

源碼gitee地址:
https://gitee.com/ioly/learning.gooop

原文鏈接:
https://my.oschina.net/ioly

目標(biāo)

  • 參考spring boot常用注解降瞳,使用golang編寫“基于注解的靜態(tài)代碼增強(qiáng)器/生成器”

子目標(biāo)(Day 7)

  • 今天繼續(xù)the hard part:struct/field/method元素的掃描
    • common/Tokens.go:添加數(shù)據(jù)類型的詞法解析支持
    • scanner/IStructScanner.go: 結(jié)構(gòu)體掃描器的接口及實(shí)現(xiàn)

common/Tokens.go

添加數(shù)據(jù)類型的詞法解析支持:

  • 分別解析基本類型/自定義類型/指針類型/數(shù)組類型/map類型
  • 自定義類型需要注意排除'map'關(guān)鍵字
  • 指針墨缘,數(shù)組和map類型都是復(fù)合類型,需遞歸解析
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

結(jié)構(gòu)體掃描器的接口及實(shí)現(xiàn)

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)

(未完待續(xù))

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驱富,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子匹舞,更是在濱河造成了極大的恐慌褐鸥,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赐稽,死亡現(xiàn)場(chǎng)離奇詭異叫榕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)姊舵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門晰绎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人括丁,你說我怎么就攤上這事荞下。” “怎么了史飞?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵尖昏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我祸憋,道長(zhǎng)会宪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任蚯窥,我火速辦了婚禮掸鹅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拦赠。我一直安慰自己巍沙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布荷鼠。 她就那樣靜靜地躺著句携,像睡著了一般。 火紅的嫁衣襯著肌膚如雪允乐。 梳的紋絲不亂的頭發(fā)上矮嫉,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天削咆,我揣著相機(jī)與錄音,去河邊找鬼蠢笋。 笑死拨齐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昨寞。 我是一名探鬼主播瞻惋,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼援岩!你這毒婦竟也來了歼狼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤享怀,失蹤者是張志新(化名)和其女友劉穎羽峰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凹蜈,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡限寞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仰坦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片履植。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖悄晃,靈堂內(nèi)的尸體忽然破棺而出玫霎,到底是詐尸還是另有隱情,我是刑警寧澤妈橄,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布庶近,位于F島的核電站,受9級(jí)特大地震影響眷蚓,放射性物質(zhì)發(fā)生泄漏鼻种。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一沙热、第九天 我趴在偏房一處隱蔽的房頂上張望叉钥。 院中可真熱鬧,春花似錦篙贸、人聲如沸投队。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)敷鸦。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扒披,已是汗流浹背值依。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碟案,地道東北人鳞滨。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蟆淀,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子澡匪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容