forked from ebhomengo/niki
188 lines
5.8 KiB
Go
188 lines
5.8 KiB
Go
|
package swag
|
||
|
|
||
|
import (
|
||
|
"go/ast"
|
||
|
"go/token"
|
||
|
"reflect"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// PackageDefinitions files and definition in a package.
|
||
|
type PackageDefinitions struct {
|
||
|
// files in this package, map key is file's relative path starting package path
|
||
|
Files map[string]*ast.File
|
||
|
|
||
|
// definitions in this package, map key is typeName
|
||
|
TypeDefinitions map[string]*TypeSpecDef
|
||
|
|
||
|
// const variables in this package, map key is the name
|
||
|
ConstTable map[string]*ConstVariable
|
||
|
|
||
|
// const variables in order in this package
|
||
|
OrderedConst []*ConstVariable
|
||
|
|
||
|
// package name
|
||
|
Name string
|
||
|
|
||
|
// package path
|
||
|
Path string
|
||
|
}
|
||
|
|
||
|
// ConstVariableGlobalEvaluator an interface used to evaluate enums across packages
|
||
|
type ConstVariableGlobalEvaluator interface {
|
||
|
EvaluateConstValue(pkg *PackageDefinitions, cv *ConstVariable, recursiveStack map[string]struct{}) (interface{}, ast.Expr)
|
||
|
EvaluateConstValueByName(file *ast.File, pkgPath, constVariableName string, recursiveStack map[string]struct{}) (interface{}, ast.Expr)
|
||
|
FindTypeSpec(typeName string, file *ast.File) *TypeSpecDef
|
||
|
}
|
||
|
|
||
|
// NewPackageDefinitions new a PackageDefinitions object
|
||
|
func NewPackageDefinitions(name, pkgPath string) *PackageDefinitions {
|
||
|
return &PackageDefinitions{
|
||
|
Name: name,
|
||
|
Path: pkgPath,
|
||
|
Files: make(map[string]*ast.File),
|
||
|
TypeDefinitions: make(map[string]*TypeSpecDef),
|
||
|
ConstTable: make(map[string]*ConstVariable),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// AddFile add a file
|
||
|
func (pkg *PackageDefinitions) AddFile(pkgPath string, file *ast.File) *PackageDefinitions {
|
||
|
pkg.Files[pkgPath] = file
|
||
|
return pkg
|
||
|
}
|
||
|
|
||
|
// AddTypeSpec add a type spec.
|
||
|
func (pkg *PackageDefinitions) AddTypeSpec(name string, typeSpec *TypeSpecDef) *PackageDefinitions {
|
||
|
pkg.TypeDefinitions[name] = typeSpec
|
||
|
return pkg
|
||
|
}
|
||
|
|
||
|
// AddConst add a const variable.
|
||
|
func (pkg *PackageDefinitions) AddConst(astFile *ast.File, valueSpec *ast.ValueSpec) *PackageDefinitions {
|
||
|
for i := 0; i < len(valueSpec.Names) && i < len(valueSpec.Values); i++ {
|
||
|
variable := &ConstVariable{
|
||
|
Name: valueSpec.Names[i],
|
||
|
Type: valueSpec.Type,
|
||
|
Value: valueSpec.Values[i],
|
||
|
Comment: valueSpec.Comment,
|
||
|
File: astFile,
|
||
|
}
|
||
|
pkg.ConstTable[valueSpec.Names[i].Name] = variable
|
||
|
pkg.OrderedConst = append(pkg.OrderedConst, variable)
|
||
|
}
|
||
|
return pkg
|
||
|
}
|
||
|
|
||
|
func (pkg *PackageDefinitions) evaluateConstValue(file *ast.File, iota int, expr ast.Expr, globalEvaluator ConstVariableGlobalEvaluator, recursiveStack map[string]struct{}) (interface{}, ast.Expr) {
|
||
|
switch valueExpr := expr.(type) {
|
||
|
case *ast.Ident:
|
||
|
if valueExpr.Name == "iota" {
|
||
|
return iota, nil
|
||
|
}
|
||
|
if pkg.ConstTable != nil {
|
||
|
if cv, ok := pkg.ConstTable[valueExpr.Name]; ok {
|
||
|
return globalEvaluator.EvaluateConstValue(pkg, cv, recursiveStack)
|
||
|
}
|
||
|
}
|
||
|
case *ast.SelectorExpr:
|
||
|
pkgIdent, ok := valueExpr.X.(*ast.Ident)
|
||
|
if !ok {
|
||
|
return nil, nil
|
||
|
}
|
||
|
return globalEvaluator.EvaluateConstValueByName(file, pkgIdent.Name, valueExpr.Sel.Name, recursiveStack)
|
||
|
case *ast.BasicLit:
|
||
|
switch valueExpr.Kind {
|
||
|
case token.INT:
|
||
|
// handle underscored number, such as 1_000_000
|
||
|
if strings.ContainsRune(valueExpr.Value, '_') {
|
||
|
valueExpr.Value = strings.Replace(valueExpr.Value, "_", "", -1)
|
||
|
}
|
||
|
if len(valueExpr.Value) >= 2 && valueExpr.Value[0] == '0' {
|
||
|
var start, base = 2, 8
|
||
|
switch valueExpr.Value[1] {
|
||
|
case 'x', 'X':
|
||
|
//hex
|
||
|
base = 16
|
||
|
case 'b', 'B':
|
||
|
//binary
|
||
|
base = 2
|
||
|
default:
|
||
|
//octet
|
||
|
start = 1
|
||
|
}
|
||
|
if x, err := strconv.ParseInt(valueExpr.Value[start:], base, 64); err == nil {
|
||
|
return int(x), nil
|
||
|
} else if x, err := strconv.ParseUint(valueExpr.Value[start:], base, 64); err == nil {
|
||
|
return x, nil
|
||
|
} else {
|
||
|
panic(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//a basic literal integer is int type in default, or must have an explicit converting type in front
|
||
|
if x, err := strconv.ParseInt(valueExpr.Value, 10, 64); err == nil {
|
||
|
return int(x), nil
|
||
|
} else if x, err := strconv.ParseUint(valueExpr.Value, 10, 64); err == nil {
|
||
|
return x, nil
|
||
|
} else {
|
||
|
panic(err)
|
||
|
}
|
||
|
case token.STRING:
|
||
|
if valueExpr.Value[0] == '`' {
|
||
|
return valueExpr.Value[1 : len(valueExpr.Value)-1], nil
|
||
|
}
|
||
|
return EvaluateEscapedString(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil
|
||
|
case token.CHAR:
|
||
|
return EvaluateEscapedChar(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil
|
||
|
}
|
||
|
case *ast.UnaryExpr:
|
||
|
x, evalType := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack)
|
||
|
if x == nil {
|
||
|
return x, evalType
|
||
|
}
|
||
|
return EvaluateUnary(x, valueExpr.Op, evalType)
|
||
|
case *ast.BinaryExpr:
|
||
|
x, evalTypex := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack)
|
||
|
y, evalTypey := pkg.evaluateConstValue(file, iota, valueExpr.Y, globalEvaluator, recursiveStack)
|
||
|
if x == nil || y == nil {
|
||
|
return nil, nil
|
||
|
}
|
||
|
return EvaluateBinary(x, y, valueExpr.Op, evalTypex, evalTypey)
|
||
|
case *ast.ParenExpr:
|
||
|
return pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack)
|
||
|
case *ast.CallExpr:
|
||
|
//data conversion
|
||
|
if len(valueExpr.Args) != 1 {
|
||
|
return nil, nil
|
||
|
}
|
||
|
arg := valueExpr.Args[0]
|
||
|
if ident, ok := valueExpr.Fun.(*ast.Ident); ok {
|
||
|
name := ident.Name
|
||
|
if name == "uintptr" {
|
||
|
name = "uint"
|
||
|
}
|
||
|
value, _ := pkg.evaluateConstValue(file, iota, arg, globalEvaluator, recursiveStack)
|
||
|
if IsGolangPrimitiveType(name) {
|
||
|
value = EvaluateDataConversion(value, name)
|
||
|
return value, nil
|
||
|
} else if name == "len" {
|
||
|
return reflect.ValueOf(value).Len(), nil
|
||
|
}
|
||
|
typeDef := globalEvaluator.FindTypeSpec(name, file)
|
||
|
if typeDef == nil {
|
||
|
return nil, nil
|
||
|
}
|
||
|
return value, valueExpr.Fun
|
||
|
} else if selector, ok := valueExpr.Fun.(*ast.SelectorExpr); ok {
|
||
|
typeDef := globalEvaluator.FindTypeSpec(fullTypeName(selector.X.(*ast.Ident).Name, selector.Sel.Name), file)
|
||
|
if typeDef == nil {
|
||
|
return nil, nil
|
||
|
}
|
||
|
return arg, typeDef.TypeSpec.Type
|
||
|
}
|
||
|
}
|
||
|
return nil, nil
|
||
|
}
|