溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么用golang編寫基于注解的靜態(tài)代碼增強器/生成器

發(fā)布時間:2021-10-13 14:19:08 來源:億速云 閱讀:135 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“怎么用golang編寫基于注解的靜態(tài)代碼增強器/生成器”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“怎么用golang編寫基于注解的靜態(tài)代碼增強器/生成器”吧!

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)代碼增強器/生成器”

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

  • 今天繼續(xù)the hard part:struct/field/method元素的掃描

    • common/Tokens.go:添加數(shù)據(jù)類型的詞法解析支持

    • scanner/IStructScanner.go: 結(jié)構(gòu)體掃描器的接口及實現(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)體掃描器的接口及實現(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)

到此,相信大家對“怎么用golang編寫基于注解的靜態(tài)代碼增強器/生成器”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI