千家信息网

编写golang编写注解的静态代码增强器/生成器的方法

发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,本篇内容介绍了"编写golang编写注解的静态代码增强器/生成器的方法"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读
千家信息网最后更新 2025年01月20日编写golang编写注解的静态代码增强器/生成器的方法

本篇内容介绍了"编写golang编写注解的静态代码增强器/生成器的方法"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

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)

  • 因为struct/field/method的扫描是关键,因此今天针对这块做了单元测试

    • common/Tokens.go:修复MatchBasicType方法的正则匹配bug。其实func类型的DataType也没考虑到,但现在暂时可以用type alias规避,先不追求完美吧。

    • scanner/IStructScanner.go: 修复若干细节, 并添加返回类型的扫描

    • scanner/IStructScanner_test.go:struct扫描器的单元测试

common/Tokens.go

修复MatchBasicType方法的正则匹配bug。其实func类型的DataType也没考虑到,但现在暂时可以用type alias规避,先不追求完美吧。

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 ok, t := me.MatchRegexp(s, "^"+it+`(\s+|$)`); ok {                        return true, strings.TrimSpace(t)                }        }        return false, ""}

scanner/IStructScanner.go

修复若干细节, 并添加返回类型的扫描

package scannerimport (        "errors"        "learning/gooop/spring/autogen/common"        "learning/gooop/spring/autogen/domain"        "regexp"        "strings")type IStructScanner interface {        ScanStruct(file *domain.CodeFileInfo)}type tStructScanner intfunc (me *tStructScanner) ScanStruct(file *domain.CodeFileInfo) {        bInStruct := false        vStructs := []*domain.StructInfo{nil}        for lineNO, line := range file.CleanLines {                if bInStruct {                        // end?                        if gStructEndRegexp.MatchString(line) {                                me.scanMethod(vStructs[0], lineNO+1)                                file.AppendStruct(vStructs[0])                                bInStruct = false                                vStructs[0] = nil                                continue                        }                        // in struct block                        ok, fname, ftype := me.scanField(line)                        if ok {                                vStructs[0].AppendField(lineNO, fname, ftype)                        }                } else {                        // not in struck block                        // matching start?                        if gStructStartRegexp.MatchString(line) {                                bInStruct = true                                ss := gStructStartRegexp.FindStringSubmatch(line)                                vStructs[0] = domain.NewStructInfo()                                vStructs[0].LineNO = lineNO                                vStructs[0].CodeFile = file                                vStructs[0].Name = ss[1]                                continue                        }                }        }}func (me *tStructScanner) scanField(line string) (ok bool, fldName string, fldType string) {        if !gFieldStartRegexp.MatchString(line) {                return false, "", ""        }        t := line        s1 := gFieldStartRegexp.FindString(t)        fldName = strings.TrimSpace(s1)        t = t[len(s1):]        b2, s2 := common.Tokens.MatchDataType(t)        if !b2 {                return false, "", ""        }        fldType = strings.TrimSpace(s2)        return true, fldName, fldType}func (me *tStructScanner) scanMethod(stru *domain.StructInfo, fromLineNO int) {        for i, limit := fromLineNO, len(stru.CodeFile.CleanLines); i < limit; i++ {                line := stru.CodeFile.CleanLines[i]                if !gMethodStartRegex.MatchString(line) {                        continue                }                ss := gMethodStartRegex.FindStringSubmatch(line)                // 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 := strings.TrimSpace(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:]        }        b4, s4 := common.Tokens.MatchRegexp(t, `^\s*\)`)        if !b4 {                return errors.New("expecting right bracket"), ""        }        offset += len(s4)        return nil, s[0:offset]}func (me *tStructScanner) scanReturnArgs(method *domain.MethodInfo, s string) error {        // no args?        if gMethodEndRegexp.MatchString(s) {                return nil        }        // args start        t := s        b1, s1 := common.Tokens.MatchRegexp(t, `\s*\(\s*`)        if !b1 {                return errors.New("expecting left bracket")        }        t = t[len(s1):]        // unnamed args?        b2, s2 := common.Tokens.MatchDataType(t)        if b2 {                t = t[len(s2):]                method.AppendUnnamedReturn(s2)                // more unnamed args?                for {                        b3, s3 := common.Tokens.MatchRegexp(t, `^\s*,\s*`)                        if !b3 {                                break                        }                        t = t[len(s3):]                        b4, s4 := common.Tokens.MatchDataType(t)                        if !b4 {                                return errors.New("expecting data type")                        }                        t = t[len(s4):]                        method.AppendUnnamedReturn(s4)                }        } else {                // named args?                for {                        // name                        b3, s3 := common.Tokens.MatchIdentifier(t)                        if !b3 {                                return errors.New("expecting identifier")                        }                        t = t[len(s3):]                        // \s+                        b4, s4 := common.Tokens.MatchSpaces(t)                        if !b4 {                                return errors.New("expecting spaces")                        }                        t = t[len(s4):]                        // type                        b5, s5 := common.Tokens.MatchDataType(t)                        if !b5 {                                return errors.New("expecting data type")                        }                        t = t[len(s5):]                        // more?                        b6, s6 := common.Tokens.MatchRegexp(t, `^\s*,\s*`)                        if b6 {                                // yes more                                t = t[len(s6):]                        } else {                                // no more                                break                        }                }        }        // arguments end        b7, _ := common.Tokens.MatchRegexp(t, `^\s*\)\s*`)        if !b7 {                return errors.New("expecting end of arguments")        }        return nil}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 gMethodEndRegexp = regexp.MustCompile(`^\s*\{`)var DefaultStructScanner IStructScanner = new(tStructScanner)

scanner/IStructScanner_test.go

struct扫描器的单元测试

package scannerimport (        "encoding/json"        "learning/gooop/spring/autogen/domain"        "strings"        "testing")func Test_StructScan(t *testing.T) {        s := `type _mystruct struct {`        t.Log(gStructStartRegexp.MatchString(s))        code := `type StructInfo struct {        LineNO      int        Name        string        CodeFile    *CodeFileInfo        Fields      []*FieldInfo        Methods     []*MethodInfo        Annotations []*AnnotationInfo}func NewStructInfo() *StructInfo {        it := new(StructInfo)        it.Fields = []*FieldInfo{}        it.Methods = []*MethodInfo{}        it.Annotations = []*AnnotationInfo{}        return it}func (me *StructInfo) AppendField(lineNO int, name string, dataType string) {        fld := NewFieldInfo()        fld.Struct = me        fld.LineNO = lineNO        fld.Name = name        fld.DataType = dataType        me.Fields = append(me.Fields, fld)}func (me *StructInfo) AppendMethod(method *MethodInfo) {        me.Methods = append(me.Methods, method)}func (me *StructInfo) AppendAnnotation(ant *AnnotationInfo) {        me.Annotations = append(me.Annotations, ant)}`        file := domain.NewCodeFileInfo()        file.CleanLines = strings.Split(code, "\n")        DefaultStructScanner.ScanStruct(file)        file.CleanLines = nil        j, e := json.MarshalIndent(file.Structs, "", "  ")        if e != nil {                panic(e)        }        t.Log(string(j))}

测试输出

API server listening at: [::]:36077=== RUN   Test_StructScan    IStructScanner_test.go:12: true    IStructScanner_test.go:58: [          {            "LineNO": 1,            "Name": "StructInfo",            "Fields": [              {                "LineNO": 2,                "Name": "LineNO",                "DataType": "int",                "Annotations": []              },              {                "LineNO": 3,                "Name": "Name",                "DataType": "string",                "Annotations": []              },              {                "LineNO": 4,                "Name": "CodeFile",                "DataType": "*CodeFileInfo",                "Annotations": []              },              {                "LineNO": 5,                "Name": "Fields",                "DataType": "[]*FieldInfo",                "Annotations": []              },              {                "LineNO": 6,                "Name": "Methods",                "DataType": "[]*MethodInfo",                "Annotations": []              },              {                "LineNO": 7,                "Name": "Annotations",                "DataType": "[]*AnnotationInfo",                "Annotations": []              }            ],            "Methods": [              {                "LineNO": 0,                "Name": "AppendField",                "Arguments": [                  {                    "Name": "lineNO",                    "DataType": "int"                  },                  {                    "Name": "name",                    "DataType": "string"                  },                  {                    "Name": "dataType",                    "DataType": "string"                  }                ],                "Annotations": [],                "Returns": []              },              {                "LineNO": 0,                "Name": "AppendMethod",                "Arguments": [                  {                    "Name": "method",                    "DataType": "*MethodInfo"                  }                ],                "Annotations": [],                "Returns": []              },              {                "LineNO": 0,                "Name": "AppendAnnotation",                "Arguments": [                  {                    "Name": "ant",                    "DataType": "*AnnotationInfo"                  }                ],                "Annotations": [],                "Returns": []              }            ],            "Annotations": []          }        ]--- PASS: Test_StructScan (0.01s)PASSDebugger finished with exit code 0

"编写golang编写注解的静态代码增强器/生成器的方法"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0