github.com/Mrs4s/MiraiGo@v0.0.0-20240226124653-54bdd873e3fe/internal/generator/jce_gen/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "flag" 6 "fmt" 7 "go/ast" 8 "go/format" 9 "go/parser" 10 "go/token" 11 "io" 12 "os" 13 "reflect" 14 "strconv" 15 ) 16 17 type JceField struct { 18 Name string 19 Type ast.Expr 20 Id int 21 } 22 23 type JceStruct struct { 24 Name string 25 Fields []JceField 26 } 27 28 type Generator struct { 29 JceStructs []JceStruct 30 } 31 32 func main() { 33 file := flag.String("file", "structs.txt", "file to parse") 34 output := flag.String("o", "", "output file") 35 flag.Parse() 36 f, err := parser.ParseFile(token.NewFileSet(), *file, nil, parser.ParseComments) 37 assert(err == nil, err) 38 g := Generator{} 39 ast.Inspect(f, func(n ast.Node) bool { 40 switch n := n.(type) { 41 case *ast.FuncDecl: 42 return false 43 case *ast.TypeSpec: 44 x, ok := n.Type.(*ast.StructType) 45 if !ok { 46 return false 47 } 48 if x.Fields != nil { 49 var jce JceStruct 50 for _, f := range x.Fields.List { 51 if f.Tag == nil { 52 continue 53 } 54 unquoted, _ := strconv.Unquote(f.Tag.Value) 55 tag, ok := reflect.StructTag(unquoted).Lookup("jceId") 56 if !ok { 57 continue 58 } 59 id, _ := strconv.Atoi(tag) 60 if len(f.Names) != 1 { 61 panic("unexpected name count") 62 } 63 jce.Fields = append(jce.Fields, JceField{ 64 Name: f.Names[0].Name, 65 Type: f.Type, 66 Id: id, 67 }) 68 } 69 jce.Name = n.Name.Name 70 g.JceStructs = append(g.JceStructs, jce) 71 } 72 } 73 return true 74 }) 75 buf := new(bytes.Buffer) 76 assert(err == nil, err) 77 g.Generate(buf) 78 formated, err := format.Source(buf.Bytes()) 79 if err != nil { 80 fmt.Printf("%s\n", buf.Bytes()) 81 panic(err) 82 } 83 _ = os.WriteFile(*output, formated, 0o644) 84 } 85 86 func (g Generator) Generate(w io.Writer) { 87 io.WriteString(w, "// Code generated by internal/generator/jce_gen; DO NOT EDIT.\n\npackage jce\n\n") 88 for _, jce := range g.JceStructs { 89 if jce.Name == "" || len(jce.Fields) == 0 { 90 continue 91 } 92 93 var generate func(typ ast.Expr, i int) 94 generate = func(typ ast.Expr, i int) { 95 field := jce.Fields[i] 96 switch typ := typ.(type) { 97 case *ast.Ident: 98 switch typ.Name { 99 case "uint8", "uint16", "uint32", "uint64": 100 typename := []byte(typ.Name)[1:] 101 casted := fmt.Sprintf("%s(pkt.%s)", typename, field.Name) 102 typename[0] ^= ' ' 103 fmt.Fprintf(w, "w.Write%s(%s, %d)\n", typename, casted, field.Id) 104 105 case "int8", "int16", "int32", "int64", 106 "byte", "string": 107 typename := []byte(typ.Name) 108 typename[0] ^= ' ' 109 fmt.Fprintf(w, "w.Write%s(pkt.%s, %d)\n", typename, field.Name, field.Id) 110 111 case "int": 112 fmt.Fprintf(w, "w.WriteInt64(int64(pkt.%s), %d)\n", field.Name, field.Id) 113 114 default: 115 fmt.Fprintf(w, `{ // write %s tag=%d 116 w.writeHead(10, %d) 117 w.buf.Write(pkt.%s.ToBytes()) 118 w.writeHead(11, 0)}`+"\n", field.Name, field.Id, field.Id, field.Name) 119 } 120 case *ast.StarExpr: 121 _ = typ.X.(*ast.Ident) // assert 122 generate(typ.X, i) 123 case *ast.ArrayType: 124 assert(typ.Len == nil, "unexpected array len") 125 var method string 126 switch typeName(typ) { 127 case "[]byte": 128 method = "WriteBytes" 129 case "[]int64": 130 method = "WriteInt64Slice" 131 case "[][]byte": 132 method = "WriteBytesSlice" 133 134 default: 135 fmt.Fprintf(w, 136 "\t"+`{ // write pkt.%s tag=%d 137 w.writeHead(9, %d) 138 if len(pkt.%s) == 0 { 139 w.writeHead(12, 0) // w.WriteInt32(0, 0) 140 } else { 141 w.WriteInt32(int32(len(pkt.%s)), 0) 142 for _, i := range pkt.%s { 143 w.writeHead(10, 0) 144 w.buf.Write(i.ToBytes()) 145 w.writeHead(11, 0) 146 } 147 }}`+"\n", field.Name, field.Id, field.Id, field.Name, field.Name, field.Name) 148 return 149 } 150 assert(method != "", typeName(typ)) 151 fmt.Fprintf(w, "w.%s(pkt.%s, %d)\n", method, field.Name, field.Id) 152 case *ast.MapType: 153 var method string 154 typName := typeName(typ) 155 switch typName { 156 case "map[string]string": 157 method = "writeMapStrStr" 158 case "map[string][]byte": 159 method = "writeMapStrBytes" 160 case "map[string]map[string][]byte": 161 method = "writeMapStrMapStrBytes" 162 } 163 assert(method != "", typName) 164 fmt.Fprintf(w, "w.%s(pkt.%s, %d)\n", method, field.Name, field.Id) 165 } 166 } 167 168 fmt.Fprintf(w, "func (pkt *%s) ToBytes() []byte {\nw := NewJceWriter()\n", jce.Name) 169 for i := range jce.Fields { 170 generate(jce.Fields[i].Type, i) 171 } 172 fmt.Fprintf(w, "return w.Bytes()\n}\n\n") 173 } 174 } 175 176 func assert(cond bool, val any) { 177 if !cond { 178 panic("assertion failed: " + fmt.Sprint(val)) 179 } 180 } 181 182 func typeName(typ ast.Expr) string { 183 switch typ := typ.(type) { 184 case *ast.Ident: 185 return typ.Name 186 case *ast.StarExpr: 187 return "*" + typeName(typ.X) 188 case *ast.ArrayType: 189 if typ.Len != nil { 190 panic("unexpected array type") 191 } 192 return "[]" + typeName(typ.Elt) 193 case *ast.MapType: 194 return "map[" + typeName(typ.Key) + "]" + typeName(typ.Value) 195 } 196 panic("unexpected type") 197 }