1
0
mirror of https://github.com/Mrs4s/MiraiGo.git synced 2025-05-04 19:17:38 +08:00

198 lines
4.6 KiB
Go

package main
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"io"
"os"
"reflect"
"strconv"
)
type JceField struct {
Name string
Type ast.Expr
Id int
}
type JceStruct struct {
Name string
Fields []JceField
}
type Generator struct {
JceStructs []JceStruct
}
func main() {
file := flag.String("file", "structs.txt", "file to parse")
output := flag.String("o", "", "output file")
flag.Parse()
f, err := parser.ParseFile(token.NewFileSet(), *file, nil, parser.ParseComments)
assert(err == nil, err)
g := Generator{}
ast.Inspect(f, func(n ast.Node) bool {
switch n := n.(type) {
case *ast.FuncDecl:
return false
case *ast.TypeSpec:
x, ok := n.Type.(*ast.StructType)
if !ok {
return false
}
if x.Fields != nil {
var jce JceStruct
for _, f := range x.Fields.List {
if f.Tag == nil {
continue
}
unquoted, _ := strconv.Unquote(f.Tag.Value)
tag, ok := reflect.StructTag(unquoted).Lookup("jceId")
if !ok {
continue
}
id, _ := strconv.Atoi(tag)
if len(f.Names) != 1 {
panic("unexpected name count")
}
jce.Fields = append(jce.Fields, JceField{
Name: f.Names[0].Name,
Type: f.Type,
Id: id,
})
}
jce.Name = n.Name.Name
g.JceStructs = append(g.JceStructs, jce)
}
}
return true
})
buf := new(bytes.Buffer)
assert(err == nil, err)
g.Generate(buf)
formated, err := format.Source(buf.Bytes())
if err != nil {
fmt.Printf("%s\n", buf.Bytes())
panic(err)
}
_ = os.WriteFile(*output, formated, 0o644)
}
func (g Generator) Generate(w io.Writer) {
io.WriteString(w, "// Code generated by internal/generator/jce_gen; DO NOT EDIT.\n\npackage jce\n\n")
for _, jce := range g.JceStructs {
if jce.Name == "" || len(jce.Fields) == 0 {
continue
}
var generate func(typ ast.Expr, i int)
generate = func(typ ast.Expr, i int) {
field := jce.Fields[i]
switch typ := typ.(type) {
case *ast.Ident:
switch typ.Name {
case "uint8", "uint16", "uint32", "uint64":
typename := []byte(typ.Name)[1:]
casted := fmt.Sprintf("%s(pkt.%s)", typename, field.Name)
typename[0] ^= ' '
fmt.Fprintf(w, "w.Write%s(%s, %d)\n", typename, casted, field.Id)
case "int8", "int16", "int32", "int64",
"byte", "string":
typename := []byte(typ.Name)
typename[0] ^= ' '
fmt.Fprintf(w, "w.Write%s(pkt.%s, %d)\n", typename, field.Name, field.Id)
case "int":
fmt.Fprintf(w, "w.WriteInt64(int64(pkt.%s), %d)\n", field.Name, field.Id)
default:
fmt.Fprintf(w, `{ // write %s tag=%d
w.writeHead(10, %d)
w.buf.Write(pkt.%s.ToBytes())
w.writeHead(11, 0)}`+"\n", field.Name, field.Id, field.Id, field.Name)
}
case *ast.StarExpr:
_ = typ.X.(*ast.Ident) // assert
generate(typ.X, i)
case *ast.ArrayType:
assert(typ.Len == nil, "unexpected array len")
var method string
switch typeName(typ) {
case "[]byte":
method = "WriteBytes"
case "[]int64":
method = "WriteInt64Slice"
case "[][]byte":
method = "WriteBytesSlice"
default:
fmt.Fprintf(w,
"\t"+`{ // write pkt.%s tag=%d
w.writeHead(9, %d)
if len(pkt.%s) == 0 {
w.writeHead(12, 0) // w.WriteInt32(0, 0)
} else {
w.WriteInt32(int32(len(pkt.%s)), 0)
for _, i := range pkt.%s {
w.writeHead(10, 0)
w.buf.Write(i.ToBytes())
w.writeHead(11, 0)
}
}}`+"\n", field.Name, field.Id, field.Id, field.Name, field.Name, field.Name)
return
}
assert(method != "", typeName(typ))
fmt.Fprintf(w, "w.%s(pkt.%s, %d)\n", method, field.Name, field.Id)
case *ast.MapType:
var method string
typName := typeName(typ)
switch typName {
case "map[string]string":
method = "writeMapStrStr"
case "map[string][]byte":
method = "writeMapStrBytes"
case "map[string]map[string][]byte":
method = "writeMapStrMapStrBytes"
}
assert(method != "", typName)
fmt.Fprintf(w, "w.%s(pkt.%s, %d)\n", method, field.Name, field.Id)
}
}
fmt.Fprintf(w, "func (pkt *%s) ToBytes() []byte {\nw := NewJceWriter()\n", jce.Name)
for i := range jce.Fields {
generate(jce.Fields[i].Type, i)
}
fmt.Fprintf(w, "return w.Bytes()\n}\n\n")
}
}
func assert(cond bool, val interface{}) {
if !cond {
panic("assertion failed: " + fmt.Sprint(val))
}
}
func typeName(typ ast.Expr) string {
switch typ := typ.(type) {
case *ast.Ident:
return typ.Name
case *ast.StarExpr:
return "*" + typeName(typ.X)
case *ast.ArrayType:
if typ.Len != nil {
panic("unexpected array type")
}
return "[]" + typeName(typ.Elt)
case *ast.MapType:
return "map[" + typeName(typ.Key) + "]" + typeName(typ.Value)
}
panic("unexpected type")
}