4

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

 3 years ago
source link: https://my.oschina.net/ioly/blog/5021203
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

手撸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)

(未完待续)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK