go-cache/cachemap/main.go

171 lines
3.5 KiB
Go

package main
import (
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"text/template"
)
func fatal(v ...interface{}) {
fmt.Fprintln(os.Stderr, v...)
os.Exit(1)
}
func packageDir() string {
_, filename, _, ok := runtime.Caller(0)
if !ok {
panic("No caller information")
}
return path.Dir(filename)
}
// TODO: parse type if type is also not builtin type.
// find literal value of type.
func findInGenDecl(genDecl *ast.GenDecl, valueName string) string {
for _, spec := range genDecl.Specs {
valueSpec, ok := spec.(*ast.TypeSpec)
if ok {
if ok && valueSpec.Name.Name == valueName {
indent, ok := valueSpec.Type.(*ast.Ident)
if ok {
return indent.Name
}
}
}
}
return ""
}
func findInDecl(decl ast.Decl, valueName string) string {
genDecl, ok := decl.(*ast.GenDecl)
if ok {
g := findInGenDecl(genDecl, valueName)
if g != "" {
return g
}
}
return ""
}
// zeroValue returns literal zero value.
func zeroValue(s string) string {
// TODO: support func type.
switch s {
case "bool":
return "false"
case "string":
return "\"\""
case "int", "uint", "int64", "uint64", "uint32", "int32", "int16",
"uint16", "int8", "uint8", "byte", "rune", "float64", "float32",
"complex64", "complex32", "uintptr":
return "0"
default:
if s[0] == '*' || // Pointer
strings.Index(s, "map") == 0 || // map
strings.Index(s, "chan") == 0 || // chan
strings.Index(s, "[]") == 0 { // slice
return "nil"
}
return s + "{}"
}
}
var builtinTypes = []string{
"bool",
"string",
"int", "int8", "int16", "int32", "int64", // numbericType
"uint", "uint8", "uint16", "uint32", "uint64", "uintptr",
"float32", "float64",
"complex64", "complex128",
"byte",
"rune",
}
func isNumberic(s string) bool {
for _, v := range builtinTypes[2:] { // 2 is beginning of numberic types in builtinTypes.
if v == s {
return true
}
}
return false
}
func isBuiltin(s string) bool {
for _, v := range builtinTypes {
if v == s {
return true
}
}
return false
}
func main() {
keyType := flag.String("k", "", "key type")
valueType := flag.String("v", "", "value type")
flag.Parse()
if *keyType == "" {
fatal("key empty")
}
if *valueType == "" {
fatal("value empty")
}
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, ".", nil, parser.ParseComments)
if err != nil {
fatal(err)
}
packageName := "main"
typeName := ""
for name, pkg := range pkgs {
packageName = name
for _, f := range pkg.Files {
for _, decl := range f.Decls {
typeName = findInDecl(decl, *valueType)
}
}
}
if typeName == "" && !isBuiltin(*valueType) {
fatal(fmt.Errorf("found no definition of %s in files\n", *valueType))
}
if typeName == "" {
typeName = *valueType
}
fmt.Println("real", typeName, "value", *valueType)
zeroTypeValue := zeroValue(typeName)
f, err := os.OpenFile(fmt.Sprintf("%s2%s_cachemap.go", *keyType, *valueType), os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
fatal(err)
}
defer f.Close()
tpl, err := template.New("cache.tmpl").ParseFiles(filepath.Join(packageDir(), "cache.tmpl"))
if err != nil {
fatal(err)
}
if isBuiltin(*valueType) {
*valueType = strings.Title(*valueType)
}
err = tpl.Execute(
f,
map[string]interface{}{
"ValueType": *valueType,
"RealType": typeName,
"PackageName": packageName,
"Cache": fmt.Sprintf("String2%sCache", *valueType),
"ZeroValue": zeroTypeValue,
"IsNumberic": isNumberic,
},
)
if err != nil {
fatal(err)
}
}