mc root source folder cleanup
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TypesInfo struct {
|
||||
Types
|
||||
Direction string
|
||||
State string
|
||||
}
|
||||
|
||||
type PacketInfo struct {
|
||||
TName, Name, Direction, State, PacketId, ProtocolVersion, MinecraftVersion string
|
||||
PacketDef any
|
||||
}
|
||||
|
||||
func GeneratePacketInfoFile(versions []string, packetInfos []PacketInfo) (err error) {
|
||||
f := NewFile("proto_generated")
|
||||
imports := []string{"github.com/admin-else/strom/mc/proto_base"}
|
||||
for _, v := range versions {
|
||||
vUnderscore := strings.ReplaceAll(v, ".", "_")
|
||||
imports = append(imports, "github.com/admin-else/strom/mc/proto_generated/v"+vUnderscore)
|
||||
}
|
||||
AppendDecl(f, Import(imports...))
|
||||
|
||||
var packetInfoExprs []ast.Expr
|
||||
for _, p := range packetInfos {
|
||||
vUnderscore := "v" + strings.ReplaceAll(p.MinecraftVersion, ".", "_")
|
||||
typeName := p.TName
|
||||
var typeExpr ast.Expr
|
||||
if p.PacketDef == "void" {
|
||||
typeExpr = Selector("proto_base", "Void")
|
||||
} else {
|
||||
typeExpr = Selector(vUnderscore, typeName)
|
||||
}
|
||||
|
||||
packetId, _ := strconv.ParseInt(p.PacketId, 0, 32)
|
||||
protocolVersion, _ := strconv.Atoi(p.ProtocolVersion)
|
||||
|
||||
packetInfoExprs = append(packetInfoExprs, CompLit(nil, []ast.Expr{
|
||||
KeyValueExpr(Ident("Type"), AddrOf(CompLit(typeExpr, nil))),
|
||||
KeyValueExpr(Ident("Name"), StrLit(p.Name)),
|
||||
KeyValueExpr(Ident("Direction"), Selector("proto_base", p.Direction)),
|
||||
KeyValueExpr(Ident("State"), Selector("proto_base", p.State)),
|
||||
KeyValueExpr(Ident("PacketId"), NumLit(int(packetId))),
|
||||
KeyValueExpr(Ident("ProtocolVersion"), NumLit(protocolVersion)),
|
||||
}))
|
||||
}
|
||||
|
||||
var supportedVersionsExprs []ast.Expr
|
||||
for _, v := range versions {
|
||||
supportedVersionsExprs = append(supportedVersionsExprs, StrLit(v))
|
||||
}
|
||||
AppendDecl(f, VarValues("SupportedVersions", CompLit(Slice(Ident("string")), supportedVersionsExprs)))
|
||||
AppendDecl(f, VarValues("Packets", CompLit(Slice(Selector("proto_base", "PacketInfo")), packetInfoExprs)))
|
||||
out, err := os.Create("proto_generated/packet_info.go")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
return PrintToFile(f, out)
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
data2 "github.com/admin-else/strom/mc/data"
|
||||
util2 "github.com/admin-else/strom/mc/util"
|
||||
)
|
||||
|
||||
var ToDoError = errors.New("to do")
|
||||
|
||||
type Types struct {
|
||||
Types map[string]any
|
||||
}
|
||||
|
||||
type State struct {
|
||||
ToClient, ToServer Types
|
||||
}
|
||||
|
||||
type Protocol struct {
|
||||
Types
|
||||
Handshaking, Status, Login, Configuration, Play State
|
||||
}
|
||||
|
||||
type ContainerStackEntry struct {
|
||||
Data []struct {
|
||||
Name string
|
||||
Type any
|
||||
Anon bool
|
||||
}
|
||||
VarToSet ast.Expr
|
||||
}
|
||||
|
||||
type ExprGeneratorFunc func(g *Generator, data any) (ast.Expr, error)
|
||||
|
||||
type CompareToGeneratorFunc func(g *Generator, parts []string, inExpr ast.Expr, data any) (e ast.Expr, cet CaseExprType, err error)
|
||||
|
||||
type FunctionGeneratorFunc func(g *Generator, varToSet ast.Expr, data any, name string) ([]ast.Stmt, error)
|
||||
|
||||
type Generator struct {
|
||||
packetInfos []PacketInfo
|
||||
|
||||
// These persist the entire generate-call
|
||||
Natives map[string]ExprGeneratorFunc
|
||||
DecoderNatives map[string]FunctionGeneratorFunc
|
||||
EncoderNatives map[string]FunctionGeneratorFunc
|
||||
CompareToNatives map[string]CompareToGeneratorFunc
|
||||
Protocol Protocol
|
||||
File *ast.File
|
||||
|
||||
// These persist in the current GenerateType call
|
||||
CurrentlyGeneratingTypes Types
|
||||
|
||||
// These persist 1 type generation
|
||||
CurrentlyGeneratingTypesPrefix string
|
||||
CurrentlyGeneratingTypeName string
|
||||
|
||||
// Change during type generation
|
||||
Depth int
|
||||
Declared []string
|
||||
ContainerStack []ContainerStackEntry
|
||||
}
|
||||
|
||||
func (g *Generator) Decl(name string, t token.Token, e ast.Expr) {
|
||||
if slices.Contains(g.Declared, name) {
|
||||
return
|
||||
}
|
||||
g.Declared = append(g.Declared, name)
|
||||
AppendDecl(g.File, &ast.GenDecl{
|
||||
Tok: t,
|
||||
Specs: []ast.Spec{
|
||||
&ast.ValueSpec{
|
||||
Names: []*ast.Ident{
|
||||
Ident(name),
|
||||
},
|
||||
Values: []ast.Expr{e},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func ParseType(t any) (tName string, tData any, err error) {
|
||||
switch t := t.(type) {
|
||||
case string:
|
||||
tName = t
|
||||
return
|
||||
case []any:
|
||||
if len(t) != 2 {
|
||||
err = fmt.Errorf("unable to parse type %v", t)
|
||||
return
|
||||
}
|
||||
var ok bool
|
||||
tName, ok = t[0].(string)
|
||||
if !ok {
|
||||
err = fmt.Errorf("unable to parse type %v", t)
|
||||
return
|
||||
}
|
||||
tData = t[1]
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("unable to parse type %v", t)
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) VisitType(data any) (e ast.Expr, err error) {
|
||||
tName, tData, err := ParseType(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitNameAndData(tName, tData)
|
||||
}
|
||||
|
||||
func (g *Generator) VisitNameAndData(tName string, tData any) (e ast.Expr, err error) {
|
||||
g.Depth += 1
|
||||
defer func() { g.Depth -= 1 }()
|
||||
n, found := g.Natives[tName]
|
||||
if found {
|
||||
return n(g, tData)
|
||||
}
|
||||
t, found := g.Protocol.Types.Types[tName]
|
||||
if t != "native" && found {
|
||||
return Ident(util2.CamelCase(tName)), nil
|
||||
}
|
||||
t, found = g.CurrentlyGeneratingTypes.Types[tName]
|
||||
if found {
|
||||
return Ident(g.CurrentlyGeneratingTypesPrefix + util2.CamelCase(tName)), nil
|
||||
}
|
||||
err = fmt.Errorf("unknown type %s", tName)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (g *Generator) VisitDecoder(varToSet ast.Expr, data any, name string) (e []ast.Stmt, err error) {
|
||||
g.Depth += 1
|
||||
defer func() { g.Depth -= 1 }()
|
||||
|
||||
tName, tData, err := ParseType(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
d, found := g.DecoderNatives[tName]
|
||||
if found {
|
||||
return d(g, varToSet, tData, name)
|
||||
}
|
||||
t, found := g.Protocol.Types.Types[tName]
|
||||
if t == "native" {
|
||||
err = fmt.Errorf("decoder native not implemented %v", tName)
|
||||
return
|
||||
}
|
||||
return DefaultDecoder(g, varToSet, tData, name)
|
||||
}
|
||||
|
||||
func (g *Generator) VisitEncoder(varToSet ast.Expr, data any, name string) (e []ast.Stmt, err error) {
|
||||
g.Depth += 1
|
||||
defer func() { g.Depth -= 1 }()
|
||||
|
||||
tName, tData, err := ParseType(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
d, found := g.EncoderNatives[tName]
|
||||
if found {
|
||||
return d(g, varToSet, tData, name)
|
||||
}
|
||||
t, found := g.Protocol.Types.Types[tName]
|
||||
if t == "native" {
|
||||
err = fmt.Errorf("encoder native not implemented %v", tName)
|
||||
return
|
||||
}
|
||||
return DefaultEncoder(g, varToSet, tData, name)
|
||||
}
|
||||
|
||||
// FixPointerReceivers
|
||||
// We cannot declare a function on a ptr type as described by https://go.dev/ref/spec#Method_declarations.
|
||||
// So we wrap them in a struct.
|
||||
func FixPointerReceivers(e ast.Expr) (td ast.Expr, ret ast.Expr) {
|
||||
_, isStruct := e.(*ast.StructType)
|
||||
ret = Ident("ret")
|
||||
if isStruct {
|
||||
td = e
|
||||
return
|
||||
}
|
||||
ret = Selector("ret", "Val")
|
||||
s := NewStruct()
|
||||
AddFieldToStruct(s, "Val", e)
|
||||
td = s
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) GenerateTypes(prefix string, types Types) (err error) {
|
||||
g.CurrentlyGeneratingTypes = types
|
||||
g.CurrentlyGeneratingTypesPrefix = prefix
|
||||
for _, k := range util2.OrderedKeys(types.Types) {
|
||||
v := types.Types[k]
|
||||
g.Depth = 0
|
||||
e, err2 := g.VisitType(v)
|
||||
if errors.Is(err2, ToDoError) {
|
||||
e = Selector("proto_base", "ToDo")
|
||||
err2 = nil
|
||||
}
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
if e == nil { // This is for types we implement ourselves
|
||||
continue
|
||||
}
|
||||
|
||||
tName := prefix + util2.CamelCase(k)
|
||||
g.CurrentlyGeneratingTypeName = tName
|
||||
|
||||
e, retExpr := FixPointerReceivers(e)
|
||||
AppendDecl(g.File, TypeDecl(tName, e))
|
||||
|
||||
args := []NameAndType{{"r", Ident("io.ReadSeeker")}}
|
||||
rets := []NameAndType{{"err", Ident("error")}}
|
||||
decodeFunction := NewFuncWithReceiver("Decode", "ret", Pointer(Ident(tName)), args, rets)
|
||||
g.Depth = 0
|
||||
s, err2 := g.VisitDecoder(retExpr, v, tName)
|
||||
if err2 != nil {
|
||||
s = ToDoStmts
|
||||
fmt.Println("failed to make decoder for", k, err2)
|
||||
}
|
||||
decodeFunction.Body = NewBlock(append(s, Return()))
|
||||
AppendDecl(g.File, decodeFunction)
|
||||
|
||||
args = []NameAndType{{"w", Ident("io.Writer")}}
|
||||
rets = []NameAndType{{"err", Ident("error")}}
|
||||
encodeFunction := NewFuncWithReceiver("Encode", "ret", Pointer(Ident(tName)), args, rets)
|
||||
g.Depth = 0
|
||||
s, err2 = g.VisitEncoder(retExpr, v, tName)
|
||||
if err2 != nil {
|
||||
s = ToDoStmts
|
||||
fmt.Println("failed to make encoder for", k, err2)
|
||||
}
|
||||
encodeFunction.Body = NewBlock(append(s, Return()))
|
||||
AppendDecl(g.File, encodeFunction)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) GenerateProtocol(protocol Protocol, version string) (err error) {
|
||||
versionData, err := data2.LookUpVersionByName(version)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prefixTypeMap := map[string]TypesInfo{
|
||||
"": {protocol.Types, "", ""},
|
||||
"HandshakingToServer": {protocol.Handshaking.ToServer, "ToServer", "Handshaking"},
|
||||
"HandshakingToClient": {protocol.Handshaking.ToClient, "ToClient", "Handshaking"},
|
||||
"StatusToServer": {protocol.Status.ToServer, "ToServer", "Status"},
|
||||
"StatusToClient": {protocol.Status.ToClient, "ToClient", "Status"},
|
||||
"LoginToServer": {protocol.Login.ToServer, "ToServer", "Login"},
|
||||
"LoginToClient": {protocol.Login.ToClient, "ToClient", "Login"},
|
||||
"ConfigurationToServer": {protocol.Configuration.ToServer, "ToServer", "Configuration"},
|
||||
"ConfigurationToClient": {protocol.Configuration.ToClient, "ToClient", "Configuration"},
|
||||
"PlayToServer": {protocol.Play.ToServer, "ToServer", "Play"},
|
||||
"PlayToClient": {protocol.Play.ToClient, "ToClient", "Play"},
|
||||
}
|
||||
|
||||
// Move common packet to their own areas to make packets more identifiable
|
||||
for _, k := range util2.OrderedKeys(protocol.Types.Types) {
|
||||
v := protocol.Types.Types[k]
|
||||
if strings.Contains(k, "packet") {
|
||||
delete(protocol.Types.Types, k)
|
||||
for _, prefix := range util2.OrderedKeys(prefixTypeMap) {
|
||||
types := prefixTypeMap[prefix]
|
||||
if prefix == "" {
|
||||
continue
|
||||
}
|
||||
types.Types.Types[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, prefix := range util2.OrderedKeys(prefixTypeMap) {
|
||||
types := prefixTypeMap[prefix]
|
||||
for _, k := range util2.OrderedKeys(types.Types.Types) {
|
||||
typeData := types.Types.Types[k]
|
||||
if k != "packet" {
|
||||
continue
|
||||
}
|
||||
packetIds := typeData.([]any)[1].([]any)[0].(map[string]any)["type"].([]any)[1].(map[string]any)["mappings"].(map[string]any)
|
||||
packetIdsStrings := util2.AssertAndConvertMapValues[string](packetIds)
|
||||
packetIdsRev := util2.ReverseMap(packetIdsStrings)
|
||||
v := typeData.([]any)[1].([]any)[1].(map[string]any)["type"].([]any)[1].(map[string]any)["fields"].(map[string]any)
|
||||
for _, k2 := range util2.OrderedKeys(v) {
|
||||
v2 := v[k2].(string)
|
||||
typeName := prefix + util2.CamelCase(v2)
|
||||
g.packetInfos = append(g.packetInfos, PacketInfo{
|
||||
PacketDef: v2,
|
||||
TName: typeName,
|
||||
Name: k2,
|
||||
Direction: types.Direction,
|
||||
State: types.State,
|
||||
PacketId: packetIdsRev[k2],
|
||||
ProtocolVersion: strconv.Itoa(int(versionData.Version)),
|
||||
MinecraftVersion: version,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, prefix := range util2.OrderedKeys(prefixTypeMap) {
|
||||
types := prefixTypeMap[prefix]
|
||||
err = g.GenerateTypes(prefix, types.Types)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ToolVersion() string {
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return "devel"
|
||||
}
|
||||
return info.Main.Version
|
||||
}
|
||||
|
||||
func GoVersion() string {
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return "unknown"
|
||||
}
|
||||
return info.GoVersion
|
||||
}
|
||||
|
||||
func HashFile(path string) (string, error) {
|
||||
// Read from embedded filesystem since minecraft-data is embedded via go:embed
|
||||
b, err := data2.MinecraftData.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
h := sha256.New()
|
||||
h.Write(b)
|
||||
return fmt.Sprintf("sha256:%x", h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
func Generate(version string, w io.Writer, sourceHash string) (packetInfos []PacketInfo, err error) {
|
||||
protocol := Protocol{}
|
||||
err = data2.LoadVersionedJson(version, "protocol", &protocol)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g := &Generator{Protocol: protocol}
|
||||
|
||||
g.File = NewFile("v" + strings.ReplaceAll(version, ".", "_"))
|
||||
|
||||
// Add file header comment for IDE recognition and reproducibility
|
||||
sourcePath := "minecraft-data/" + data2.Paths.Data[version]["protocol"] + "/protocol.json"
|
||||
toolVersion := ToolVersion()
|
||||
goVersion := GoVersion()
|
||||
comment := fmt.Sprintf("Code generated by strom. DO NOT EDIT.\nsource: %s (%s)\nversion: %s (%s)",
|
||||
sourcePath, sourceHash, toolVersion, goVersion)
|
||||
AddFileComment(g.File, comment)
|
||||
|
||||
AppendDecl(g.File, Import("encoding/binary", "io", "github.com/admin-else/strom/mc/proto_base", "github.com/admin-else/strom/mc/nbt", "github.com/google/uuid"))
|
||||
g.RegisterNatives()
|
||||
g.RegisterDecoderNatives()
|
||||
g.RegisterEncoderNatives()
|
||||
g.RegisterCompareToNatives()
|
||||
err = g.GenerateProtocol(protocol, version)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
packetInfos = g.packetInfos
|
||||
err = PrintToFile(g.File, w)
|
||||
return
|
||||
}
|
||||
|
||||
func generateVersion(v string) (packetInfos []PacketInfo, err error) {
|
||||
vUnderscore := strings.ReplaceAll(v, ".", "_")
|
||||
err = os.MkdirAll("proto_generated/v"+vUnderscore, 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f, err := os.Create("proto_generated/v" + vUnderscore + "/proto.go")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Compute hash of source protocol.json for reproducibility
|
||||
sourcePath := "minecraft-data/" + data2.Paths.Data[v]["protocol"] + "/protocol.json"
|
||||
sourceHash, err := HashFile(sourcePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
packetInfos, err = Generate(v, f, sourceHash)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GenerateVersions(versions []string) (err error) {
|
||||
err = os.RemoveAll("proto_generated/")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = os.MkdirAll("proto_generated/", 0755)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var packetInfos, newPacketInfos []PacketInfo
|
||||
for _, version := range versions {
|
||||
fmt.Println("generating", version)
|
||||
newPacketInfos, err = generateVersion(version)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
packetInfos = append(packetInfos, newPacketInfos...)
|
||||
}
|
||||
return GeneratePacketInfoFile(versions, packetInfos)
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := GenerateVersions([]string{"1.21.8", "1.21.9", "1.21.11"})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
"github.com/admin-else/strom/mc/proto_generator/protodef"
|
||||
util2 "github.com/admin-else/strom/mc/util"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
func (g *Generator) ParseCompareTo(compareTo string) (e ast.Expr, cet CaseExprType, err error) {
|
||||
parts := strings.Split(compareTo, "/")
|
||||
downPrefixCount := 0
|
||||
for _, part := range parts {
|
||||
if part == ".." {
|
||||
downPrefixCount++
|
||||
}
|
||||
}
|
||||
parts = parts[downPrefixCount:]
|
||||
startingPoint := g.ContainerStack[len(g.ContainerStack)-downPrefixCount-1]
|
||||
e, cet, err = g.VisitCompareTo(parts, startingPoint.VarToSet, util2.CombineNamAndData("container", startingPoint.Data))
|
||||
// return ParseCompareToLegacy(compareTo, varToSet)
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) VisitCompareTo(parts []string, inExpr ast.Expr, data any) (e ast.Expr, cet CaseExprType, err error) {
|
||||
tName, tData, err := ParseType(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
d, found := g.CompareToNatives[tName]
|
||||
if found {
|
||||
return d(g, parts, inExpr, tData)
|
||||
}
|
||||
err = fmt.Errorf("native compare to not implemented for %v", tName)
|
||||
return
|
||||
}
|
||||
|
||||
func ContainerCompareTo(g *Generator, parts []string, inExpr ast.Expr, dataRaw any) (e ast.Expr, cet CaseExprType, err error) {
|
||||
var data protodef.Container
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
name, parts, ok := util2.PopFront(parts)
|
||||
if !ok {
|
||||
err = errors.New("expected name part")
|
||||
return
|
||||
}
|
||||
for _, field := range data {
|
||||
if field.Name == name {
|
||||
return g.VisitCompareTo(parts, SelectorExprAndStr(inExpr, util2.CamelCase(field.Name)), field.Type)
|
||||
}
|
||||
}
|
||||
err = errors.New("field not found")
|
||||
return
|
||||
}
|
||||
|
||||
func ReturnInputCompareTo(_ *Generator, _ []string, inExpr ast.Expr, _ any) (e ast.Expr, cet CaseExprType, err error) {
|
||||
return inExpr, CaseExprTypeUnset, nil
|
||||
}
|
||||
|
||||
func BitfieldCompareTo(_ *Generator, parts []string, inExpr ast.Expr, dataRaw any) (e ast.Expr, cet CaseExprType, err error) {
|
||||
var data protodef.BitField
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fieldName, parts, found := util2.PopFront(parts)
|
||||
if !found {
|
||||
err = errors.New("expected name part")
|
||||
return
|
||||
}
|
||||
var field protodef.BitFieldEntry
|
||||
for _, f := range data {
|
||||
if f.Name == fieldName {
|
||||
field = f
|
||||
break
|
||||
}
|
||||
}
|
||||
if field.Size == 1 {
|
||||
cet = CaseExprTypeOnlyBool
|
||||
} else {
|
||||
cet = CaseExprTypeOnlyNumber
|
||||
}
|
||||
|
||||
e = SelectorExprAndStr(inExpr, util2.CamelCase(fieldName))
|
||||
return
|
||||
}
|
||||
|
||||
func BitflagsCompareTo(_ *Generator, parts []string, inExpr ast.Expr, dataRaw any) (e ast.Expr, cet CaseExprType, err error) {
|
||||
var data protodef.BitFlags
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
name, parts, ok := util2.PopFront(parts)
|
||||
if !ok {
|
||||
err = errors.New("expected name part")
|
||||
return
|
||||
}
|
||||
|
||||
for i, flag := range data.Flags {
|
||||
if flag != name {
|
||||
continue
|
||||
}
|
||||
e = NotEquals(BinAnd(inExpr, NumLitHex(1<<i)), NumLit(0))
|
||||
return
|
||||
}
|
||||
err = errors.New("flag not found")
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) RegisterCompareToNatives() {
|
||||
g.CompareToNatives = map[string]CompareToGeneratorFunc{
|
||||
"container": ContainerCompareTo,
|
||||
"bool": ReturnInputCompareTo,
|
||||
"mapper": ReturnInputCompareTo,
|
||||
"varint": ReturnInputCompareTo,
|
||||
"bitfield": BitfieldCompareTo,
|
||||
"u8": ReturnInputCompareTo,
|
||||
"i8": ReturnInputCompareTo,
|
||||
"bitflags": BitflagsCompareTo,
|
||||
"switch": ReturnInputCompareTo,
|
||||
"varlong": ReturnInputCompareTo,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,659 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"github.com/admin-else/strom/mc/proto_generator/protodef"
|
||||
util2 "github.com/admin-else/strom/mc/util"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
func DefaultDecoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign(
|
||||
Exprs(Ident("err")),
|
||||
Exprs(Call(SelectorExprAndStr(varToSet, "Decode"), Ident("r"))),
|
||||
),
|
||||
IfErrNil(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func UUIDDecoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
//_, err = io.ReadFull(r, U[:])
|
||||
s = Stmts(
|
||||
Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("io", "ReadFull"), Ident("r"), MultiIndex(varToSet, nil, nil, nil)))),
|
||||
IfErrNil(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func ContainerDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Container
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g.ContainerStack = append(g.ContainerStack, ContainerStackEntry{
|
||||
Data: data,
|
||||
VarToSet: varToSet,
|
||||
})
|
||||
defer func() { g.ContainerStack = g.ContainerStack[:len(g.ContainerStack)-1] }()
|
||||
|
||||
for _, field := range data {
|
||||
var statements []ast.Stmt
|
||||
cName := util2.CamelCase(field.Name)
|
||||
if field.Anon {
|
||||
cName = "Anon"
|
||||
}
|
||||
statements, err = g.VisitDecoder(SelectorExprAndStr(varToSet, cName), field.Type, name+cName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s, statements...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SimpleTypeDecoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign(
|
||||
Exprs(Ident("err")),
|
||||
Exprs(Call(Selector("binary", "Read"), Ident("r"), Selector("binary", "BigEndian"), AddrOf(varToSet))),
|
||||
),
|
||||
IfErrNil(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func StringDecoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign(Exprs(varToSet, Ident("err")), Exprs(Call(Selector("proto_base", "DecodeString"), Ident("r")))),
|
||||
IfErrNil(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func OptionDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
pVarName := name + "Present"
|
||||
tVarName := name + "PresentValue"
|
||||
boolStmts, err := g.VisitDecoder(Ident(pVarName), "bool", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t, err := g.VisitType(dataRaw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tStmts1 := Stmts(VarStmt(tVarName, t))
|
||||
tStmts2, err := g.VisitDecoder(Ident(tVarName), dataRaw, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// This looks so shit
|
||||
tStmts := append(tStmts1, tStmts2...)
|
||||
tStmts = append(tStmts, Assign121(varToSet, AddrOf(Ident(tVarName))))
|
||||
s = append(s, VarStmt(pVarName, Ident("bool")))
|
||||
s = append(s, boolStmts...)
|
||||
s = append(s, If(Ident(pVarName), NewBlock(tStmts)))
|
||||
return
|
||||
}
|
||||
|
||||
func ArrayDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Array
|
||||
lName := "l" + util2.CamelCase(name)
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var s2 []ast.Stmt
|
||||
countType, err := g.VisitNameAndData("varint", nil)
|
||||
if data.CountType != nil {
|
||||
countType, err = g.VisitType(data.CountType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s2, err = g.VisitDecoder(Ident(lName), data.CountType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
var n int
|
||||
n, err = strconv.Atoi(data.Count)
|
||||
var e ast.Expr
|
||||
if err == nil {
|
||||
e = NumLit(n)
|
||||
} else {
|
||||
e, _, err = g.ParseCompareTo(data.Count)
|
||||
}
|
||||
s2 = append(s2, Assign121(Ident(lName), e))
|
||||
}
|
||||
|
||||
normalType, err := g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
arrayType, err := g.VisitNameAndData("array", dataRaw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tName := name + "Element"
|
||||
rangeStatements := Stmts(VarStmt(tName, normalType))
|
||||
s5, err := g.VisitDecoder(Ident(tName), data.Type, tName)
|
||||
rangeStatements = append(rangeStatements, s5...)
|
||||
rangeStatements = append(rangeStatements, Assign121(varToSet, Call(Ident("append"), varToSet, Ident(tName))))
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s1 := VarStmt(lName, countType)
|
||||
s3 := Assign121(varToSet, CompLit(arrayType, Exprs()))
|
||||
s4 := ForRange(nil, Ident(lName), NewBlock(rangeStatements))
|
||||
s = append(s, s1)
|
||||
s = append(s, s2...)
|
||||
s = append(s, s3)
|
||||
s = append(s, s4)
|
||||
return
|
||||
}
|
||||
|
||||
func TypeForwardDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.TypeForward
|
||||
if err = mapstructure.Decode(dataRaw, &data); err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitDecoder(varToSet, data.Type, name)
|
||||
}
|
||||
|
||||
var BooleanValues = []string{"true", "false"}
|
||||
|
||||
// CaseExprType This exists to fix this
|
||||
// "switch",
|
||||
//
|
||||
// {
|
||||
// "compareTo": "itemCount",
|
||||
// "fields": {
|
||||
// "0": "void",
|
||||
// "false": "void"
|
||||
// },
|
||||
// "default": [
|
||||
//
|
||||
// Protodef is best!!!
|
||||
// they are also inconsistent with bool compare to sometimes they use false sometimes they use numbers
|
||||
// that's why this bullshit exists
|
||||
type CaseExprType int
|
||||
|
||||
const (
|
||||
CaseExprTypeUnset CaseExprType = iota
|
||||
CaseExprTypeNumber
|
||||
CaseExprTypeBool
|
||||
CaseExprTypeString
|
||||
|
||||
// CaseExprTypeOnlyNumber CaseExprTypeOnlyBool CaseExprTypeStringOnly These are special overwrite values retuned by
|
||||
// the compare to for the case of being certain that it's the right type.
|
||||
|
||||
CaseExprTypeOnlyNumber
|
||||
CaseExprTypeOnlyBool
|
||||
CaseExprTypeStringOnly
|
||||
)
|
||||
|
||||
func MultiTypeFix(s string, t *CaseExprType) (e ast.Expr) {
|
||||
n, err := strconv.Atoi(s)
|
||||
ct := CaseExprTypeUnset
|
||||
if err == nil {
|
||||
err = nil
|
||||
e = NumLit(n)
|
||||
ct = CaseExprTypeNumber
|
||||
} else if slices.Contains(BooleanValues, s) {
|
||||
e = Ident(s)
|
||||
ct = CaseExprTypeBool
|
||||
} else {
|
||||
e = StrLit(s)
|
||||
ct = CaseExprTypeString
|
||||
}
|
||||
|
||||
if *t == CaseExprTypeUnset {
|
||||
*t = ct
|
||||
return
|
||||
}
|
||||
if *t == ct {
|
||||
return
|
||||
}
|
||||
|
||||
if ct == CaseExprTypeNumber && *t == CaseExprTypeOnlyBool {
|
||||
if s == "0" {
|
||||
e = Ident("false")
|
||||
} else {
|
||||
e = Ident("true")
|
||||
}
|
||||
return
|
||||
}
|
||||
e = nil
|
||||
return
|
||||
}
|
||||
|
||||
func SwitchDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Switch
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
compareToExpr, cet, err := g.ParseCompareTo(data.CompareTo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, fName := range util2.OrderedKeys(data.Fields) {
|
||||
fType := data.Fields[fName]
|
||||
e := MultiTypeFix(fName, &cet)
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
var tType ast.Expr
|
||||
tType, err = g.VisitType(fType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tName := name + util2.CamelCase(fName) + "Tmp"
|
||||
s1 := VarStmt(tName, tType)
|
||||
s3 := Assign121(varToSet, Ident(tName))
|
||||
var caseDecodeValueStmts []ast.Stmt
|
||||
caseDecodeValueStmts, err = g.VisitDecoder(Ident(tName), fType, tName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var caseStmts []ast.Stmt
|
||||
caseStmts = append(caseStmts, s1)
|
||||
caseStmts = append(caseStmts, caseDecodeValueStmts...)
|
||||
caseStmts = append(caseStmts, s3)
|
||||
s = append(s, Case(Exprs(e), caseStmts))
|
||||
}
|
||||
if data.Default != nil {
|
||||
var tType ast.Expr
|
||||
tType, err = g.VisitType(data.Default)
|
||||
tName := name + "Tmp"
|
||||
s1 := VarStmt(tName, tType)
|
||||
s3 := Assign121(varToSet, Ident(tName))
|
||||
var caseDecodeValueStmts []ast.Stmt
|
||||
caseDecodeValueStmts, err = g.VisitDecoder(Ident(tName), data.Default, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var caseStmts []ast.Stmt
|
||||
caseStmts = append(caseStmts, s1)
|
||||
caseStmts = append(caseStmts, caseDecodeValueStmts...)
|
||||
caseStmts = append(caseStmts, s3)
|
||||
s = append(s, Case(nil, caseStmts))
|
||||
}
|
||||
if len(s) != 0 {
|
||||
s = Stmts(SwitchStmt(compareToExpr, NewBlock(s)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var ToDoStmts = Stmts(Assign121(Ident("err"), Selector("proto_base", "ToDoError")))
|
||||
|
||||
func ToDoDecoder(_ *Generator, _ ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
err = ToDoError
|
||||
return
|
||||
}
|
||||
|
||||
func MapperDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Mapper
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
kName := util2.CamelCase(name) + "Key"
|
||||
kType, err := g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s1 := VarStmt(kName, kType)
|
||||
s2, err := g.VisitDecoder(Ident(kName), data.Type, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mName := name + "Map"
|
||||
|
||||
expressions := Exprs()
|
||||
for _, k := range util2.OrderedKeys(data.Mappings) {
|
||||
v := data.Mappings[k]
|
||||
expressions = append(expressions, KeyValueExpr(NumLitStr(k), StrLit(v)))
|
||||
}
|
||||
g.Decl(mName, token.VAR, CompLit(MapType(kType, Ident("string")), expressions))
|
||||
s3 := Assign(Exprs(varToSet, Ident("err")), Exprs(Call(Selector("proto_base", "ErroringIndex"), Ident(mName), Ident(kName))))
|
||||
s = append(s, s1)
|
||||
s = append(s, s2...)
|
||||
s = append(s, s3)
|
||||
s = append(s, IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func BufferDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Buffer
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// rawString, err := io.ReadAll(io.LimitReader(r, int64(l)))
|
||||
|
||||
if data.CountType != nil {
|
||||
lName := "l" + util2.CamelCase(name)
|
||||
var countType ast.Expr
|
||||
countType, err = g.VisitType(data.CountType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var s2 []ast.Stmt
|
||||
s2, err = g.VisitDecoder(Ident(lName), data.CountType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s1 := VarStmt(lName, countType)
|
||||
s3 := Assign(Exprs(varToSet, Ident("err")), Exprs(Call(Selector("io", "ReadAll"), Call(Selector("io", "LimitReader"), Ident("r"), Call(Ident("int64"), Ident(lName))))))
|
||||
|
||||
s = append(s, s1)
|
||||
s = append(s, s2...)
|
||||
s = append(s, s3)
|
||||
} else {
|
||||
s = Stmts(Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("r", "Read"), MultiIndex(varToSet, nil, nil, nil)))))
|
||||
}
|
||||
|
||||
s = append(s, IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func BitFieldDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.BitField
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
totalSize := 0
|
||||
for _, field := range data {
|
||||
totalSize += field.Size
|
||||
}
|
||||
packedTypeProtodef, err := BitSizeToProtodef(totalSize, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t, err := g.VisitType(packedTypeProtodef)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
packed := name + "Packed"
|
||||
packedVarStmt := VarStmt(packed, t)
|
||||
stmts, err := g.VisitDecoder(Ident(packed), packedTypeProtodef, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s, packedVarStmt)
|
||||
s = append(s, stmts...)
|
||||
lShiftBy := 0
|
||||
for _, field := range data {
|
||||
var fieldTypeProtodef string
|
||||
fieldTypeProtodef, err = BitSizeToProtodef(field.Size, field.Signed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var fieldType ast.Expr
|
||||
fieldType, err = g.VisitType(fieldTypeProtodef)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rShiftBy := totalSize - field.Size
|
||||
fieldToSet := SelectorExprAndStr(varToSet, util2.CamelCase(field.Name))
|
||||
|
||||
var getDataExpr ast.Expr
|
||||
getDataExpr = RightShift(
|
||||
LeftShift(
|
||||
Ident(packed),
|
||||
NumLit(lShiftBy)),
|
||||
NumLit(rShiftBy),
|
||||
)
|
||||
if fieldTypeProtodef == "bool" {
|
||||
getDataExpr = Equals(getDataExpr, NumLit(1))
|
||||
} else if fieldTypeProtodef != packedTypeProtodef {
|
||||
getDataExpr = Call(fieldType, getDataExpr)
|
||||
}
|
||||
|
||||
s = append(s, Assign121(fieldToSet, getDataExpr))
|
||||
if field.Signed {
|
||||
s = append(s, If(
|
||||
GreaterThanOrEqual(
|
||||
fieldToSet,
|
||||
LeftShift(NumLit(1), NumLit(field.Size-1)),
|
||||
),
|
||||
NewBlockEllipsis(
|
||||
SubAssign(
|
||||
fieldToSet,
|
||||
LeftShift(NumLit(1), NumLit(field.Size)),
|
||||
),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
lShiftBy += field.Size
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func RegistryEntryHolderSetDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.RegistryEntryHolderSet
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var countType ast.Expr
|
||||
countType, err = g.VisitType("varint")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
baseType, err := g.VisitType(data.Base.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
otherwiseArrayType, err := g.VisitNameAndData("array", map[string]any{"type": data.Otherwise.Type})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
otherwiseType, err := g.VisitType(data.Otherwise.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lName := "l" + util2.CamelCase(name)
|
||||
s1 := VarStmt(lName, countType)
|
||||
s2, err := g.VisitDecoder(Ident(lName), "varint", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s3 := Assign121(Ident(lName), Sub(Ident(lName), NumLit(1)))
|
||||
resultName := name + "Result"
|
||||
b1s1 := VarStmt(resultName, baseType)
|
||||
b1s2, err := g.VisitDecoder(Ident(resultName), data.Base.Type, resultName)
|
||||
b1s3 := Assign121(varToSet, Ident(resultName))
|
||||
b1s4 := Return()
|
||||
var b1 []ast.Stmt
|
||||
b1 = append(b1, b1s1)
|
||||
b1 = append(b1, b1s2...)
|
||||
b1 = append(b1, b1s3)
|
||||
b1 = append(b1, b1s4)
|
||||
|
||||
tName := name + "Element"
|
||||
rangeStatements := Stmts(VarStmt(tName, otherwiseType))
|
||||
rangeStatementsDecode, err := g.VisitDecoder(Ident(tName), data.Otherwise.Type, tName)
|
||||
rangeStatements = append(rangeStatements, rangeStatementsDecode...)
|
||||
rangeStatements = append(rangeStatements, Assign121(Ident(resultName), Call(Ident("append"), Ident(resultName), Ident(tName))))
|
||||
|
||||
s5 := If(Equals(Ident(lName), NumLit(-1)), NewBlock(b1))
|
||||
s6 := VarStmt(resultName, otherwiseArrayType)
|
||||
s7 := ForRange(nil, Ident(lName), NewBlock(rangeStatements))
|
||||
s = append(s, s1)
|
||||
s = append(s, s2...)
|
||||
s = append(s, s3)
|
||||
s = append(s, s5)
|
||||
s = append(s, s6)
|
||||
s = append(s, s7)
|
||||
return
|
||||
}
|
||||
|
||||
func RegistryEntryHolderDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.EntryHolderSet
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
idName := name + "Id"
|
||||
varIntType, err := g.VisitNameAndData("varint", nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
IdVar := VarStmt(idName, varIntType)
|
||||
idDecodeStatements, err := g.VisitDecoder(Ident(idName), "varint", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
otherwiseType, err := g.VisitType(data.Otherwise.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resName := name + "Result"
|
||||
resNameIdent := Ident(resName)
|
||||
var otherwisePath []ast.Stmt
|
||||
otherwisePath = append(otherwisePath, VarStmt(resName, otherwiseType))
|
||||
|
||||
otherwiseDecodeStatements, err := g.VisitDecoder(resNameIdent, data.Otherwise.Type, name+"Otherwise")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
otherwisePath = append(otherwisePath, otherwiseDecodeStatements...)
|
||||
otherwisePath = append(otherwisePath, Assign121(varToSet, resNameIdent))
|
||||
idPath := IfElse(NotEquals(Ident(idName), NumLit(0)), NewBlockEllipsis(Assign121(varToSet, Ident(idName))), NewBlock(otherwisePath))
|
||||
|
||||
s = append(s, IdVar)
|
||||
s = append(s, idDecodeStatements...)
|
||||
s = append(s, idPath)
|
||||
return
|
||||
}
|
||||
|
||||
func VarIntDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(Assign(Exprs(varToSet, Ident("err")), Exprs(Call(Selector("proto_base", "DecodeVarInt"), Ident("r")))), IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func VarLongDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(Assign(Exprs(varToSet, Ident("err")), Exprs(Call(Selector("proto_base", "DecodeVarLong"), Ident("r")))), IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func VoidDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func EntityMetadataLoopDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.EntityMetadataLoop
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
innerType, err := g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
assignVts := Assign121(varToSet, Call(Ident("make"), MapType(Ident("uint8"), innerType)))
|
||||
idVar := VarStmt("id", Ident("uint8"))
|
||||
|
||||
decodeIdStatements, err := g.VisitDecoder(Ident("id"), "u8", name)
|
||||
endCheck := If(Equals(Ident("id"), NumLit(data.EndVal)), NewBlockEllipsis(Break()))
|
||||
// r.Seek(-1, io.SeekCurrent)
|
||||
seekBack := Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("r", "Seek"), NumLit(-1), Selector("io", "SeekCurrent"))))
|
||||
|
||||
declEntry := VarStmt("entry", innerType)
|
||||
decodingEntryStmts, err := g.VisitDecoder(Ident("entry"), data.Type, name+"Inner")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assignEntry := Assign121(Index(varToSet, Ident("id")), Ident("entry"))
|
||||
|
||||
var loopStmts []ast.Stmt
|
||||
loopStmts = append(loopStmts, decodeIdStatements...)
|
||||
loopStmts = append(loopStmts, endCheck)
|
||||
loopStmts = append(loopStmts, seekBack)
|
||||
loopStmts = append(loopStmts, IfErrNil())
|
||||
loopStmts = append(loopStmts, declEntry)
|
||||
loopStmts = append(loopStmts, decodingEntryStmts...)
|
||||
loopStmts = append(loopStmts, assignEntry)
|
||||
|
||||
s = Stmts(assignVts, idVar, ForStmt(nil, nil, nil, NewBlock(loopStmts)))
|
||||
/*
|
||||
vts = make(map[uint8]{{data.type}})
|
||||
var id uint8
|
||||
for {
|
||||
err = binary.Read(r, binary.BigEndian, &id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if id == {{data.EndId}} {
|
||||
return
|
||||
}
|
||||
var data {{type(data.type)}}
|
||||
{{unpack(data.type)}}
|
||||
vts[id] = data
|
||||
}
|
||||
*/
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) RegisterDecoderNatives() {
|
||||
g.DecoderNatives = map[string]FunctionGeneratorFunc{
|
||||
"container": ContainerDecoder,
|
||||
"array": ArrayDecoder,
|
||||
"option": OptionDecoder,
|
||||
"bitflags": TypeForwardDecoder,
|
||||
"switch": SwitchDecoder,
|
||||
"mapper": MapperDecoder,
|
||||
"buffer": BufferDecoder,
|
||||
"bitfield": BitFieldDecoder,
|
||||
"string": StringDecoder,
|
||||
|
||||
"void": VoidDecoder,
|
||||
"varint": VarIntDecoder,
|
||||
"varlong": VarLongDecoder,
|
||||
"anonymousNbt": DefaultDecoder,
|
||||
"anonOptionalNbt": DefaultDecoder, // I have no idea what the difference is between these two
|
||||
"lpVec3": DefaultDecoder,
|
||||
"UUID": UUIDDecoder,
|
||||
"restBuffer": DefaultDecoder,
|
||||
|
||||
"f32": SimpleTypeDecoder,
|
||||
"f64": SimpleTypeDecoder,
|
||||
"i8": SimpleTypeDecoder,
|
||||
"i16": SimpleTypeDecoder,
|
||||
"i32": SimpleTypeDecoder,
|
||||
"i64": SimpleTypeDecoder,
|
||||
"u8": SimpleTypeDecoder,
|
||||
"u16": SimpleTypeDecoder,
|
||||
"u32": SimpleTypeDecoder,
|
||||
"u64": SimpleTypeDecoder,
|
||||
"bool": SimpleTypeDecoder,
|
||||
|
||||
"registryEntryHolder": RegistryEntryHolderDecoder,
|
||||
"registryEntryHolderSet": ToDoDecoder, // RegistryEntryHolderSetDecoder
|
||||
"entityMetadataLoop": EntityMetadataLoopDecoder,
|
||||
"topBitSetTerminatedArray": ToDoDecoder,
|
||||
"todo": ToDoDecoder,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"github.com/admin-else/strom/mc/proto_generator/protodef"
|
||||
util2 "github.com/admin-else/strom/mc/util"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
func DefaultEncoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign121(
|
||||
Ident("err"),
|
||||
Call(SelectorExprAndStr(varToSet, "Encode"), Ident("w")),
|
||||
),
|
||||
IfErrNil(),
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func ContainerEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Container
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g.ContainerStack = append(g.ContainerStack, ContainerStackEntry{
|
||||
Data: data,
|
||||
VarToSet: varToSet,
|
||||
})
|
||||
defer func() { g.ContainerStack = g.ContainerStack[:len(g.ContainerStack)-1] }()
|
||||
|
||||
for _, field := range data {
|
||||
var statements []ast.Stmt
|
||||
cName := util2.CamelCase(field.Name)
|
||||
if field.Anon {
|
||||
cName = "Anon"
|
||||
}
|
||||
statements, err = g.VisitEncoder(SelectorExprAndStr(varToSet, cName), field.Type, name+util2.CamelCase(cName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s, statements...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func SimpleTypeEncoder(_ *Generator, data ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign121(
|
||||
Ident("err"),
|
||||
Call(Selector("binary", "Write"), Ident("w"), Selector("binary", "BigEndian"), data),
|
||||
),
|
||||
IfErrNil(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func ArrayEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Array
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if data.CountType == nil {
|
||||
data.CountType = "varint"
|
||||
}
|
||||
ct, err := g.VisitType(data.CountType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
iName := "i" + name
|
||||
//s, err = g.VisitEncoder(varToSet, data.Type, name)
|
||||
var s1 []ast.Stmt
|
||||
if data.Count == "" {
|
||||
s1, err = g.VisitEncoder(Call(ct, Call(Ident("len"), varToSet)), data.CountType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
s2, err := g.VisitEncoder(Index(varToSet, Ident(iName)), data.Type, name+"Inner")
|
||||
if err != nil {
|
||||
}
|
||||
s2 = Stmts(ForRange(Ident(iName), Call(Ident("len"), varToSet), NewBlock(s2)))
|
||||
s = append(s1, s2...)
|
||||
return
|
||||
}
|
||||
|
||||
func ToDoEncoder(_ *Generator, _ ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
err = ToDoError
|
||||
return
|
||||
}
|
||||
|
||||
func OptionEncoder(g *Generator, data ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
cond := NotEquals(data, Nil())
|
||||
s1, err := g.VisitEncoder(cond, "bool", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s2, err := g.VisitEncoder(DeRef(data), dataRaw, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s1, If(cond, NewBlock(s2)))
|
||||
return
|
||||
}
|
||||
|
||||
func BufferEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Buffer
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if data.CountType != nil {
|
||||
var countType ast.Expr
|
||||
countType, err = g.VisitType(data.CountType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var s2 []ast.Stmt
|
||||
s2, err = g.VisitEncoder(Call(countType, Call(Ident("len"), varToSet)), data.CountType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s, s2...)
|
||||
} else {
|
||||
s = append(s, Define121(Ident("arr"), varToSet))
|
||||
varToSet = MultiIndex(Ident("arr"), nil, nil, nil)
|
||||
}
|
||||
s = append(s, Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("w", "Write"), varToSet))))
|
||||
s = append(s, IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func BitFieldEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.BitField
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
totalSize := 0
|
||||
for _, field := range data {
|
||||
totalSize += field.Size
|
||||
}
|
||||
packedVarName := name + "Packed"
|
||||
packedExpr := Ident(packedVarName)
|
||||
packedTypeStr := "uint" + strconv.Itoa(totalSize)
|
||||
|
||||
s = append(s, VarStmt(packedVarName, Ident(packedTypeStr)))
|
||||
|
||||
slices.Reverse(data)
|
||||
shiftBy := 0
|
||||
for _, field := range data {
|
||||
boolFix := packedTypeStr
|
||||
if field.Size == 1 {
|
||||
boolFix = "proto_base.Bool2" + boolFix
|
||||
}
|
||||
|
||||
s = append(s, OrAssign( // lisp ahh
|
||||
packedExpr,
|
||||
LeftShift(
|
||||
Grouping(
|
||||
BinAnd(
|
||||
Call(
|
||||
Ident(boolFix),
|
||||
SelectorExprAndStr(
|
||||
varToSet,
|
||||
util2.CamelCase(field.Name),
|
||||
),
|
||||
),
|
||||
NumLitHex((1<<field.Size)-1),
|
||||
),
|
||||
),
|
||||
NumLitHex(shiftBy),
|
||||
),
|
||||
))
|
||||
shiftBy += field.Size
|
||||
}
|
||||
|
||||
encodePacked, err := g.VisitEncoder(packedExpr, "u"+strconv.Itoa(totalSize), name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s = append(s, encodePacked...)
|
||||
return
|
||||
}
|
||||
|
||||
func SwitchEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Switch
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
compareToExpr, cet, err := g.ParseCompareTo(data.CompareTo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, fName := range util2.OrderedKeys(data.Fields) {
|
||||
fType := data.Fields[fName]
|
||||
e := MultiTypeFix(fName, &cet)
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
var tType ast.Expr
|
||||
tType, err = g.VisitType(fType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
varName := name
|
||||
if fType == "void" {
|
||||
varName = "_"
|
||||
}
|
||||
s1 := Define(Exprs(Ident(varName), Ident("ok")), Exprs(TypeAssert(varToSet, tType)))
|
||||
s2 := If(Not(Ident("ok")), NewBlockEllipsis(Assign121(Ident("err"), Selector("proto_base", "BadTypeError")), Return()))
|
||||
var caseDecodeValueStmts []ast.Stmt
|
||||
caseDecodeValueStmts, err = g.VisitEncoder(Ident(name), fType, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var caseStmts []ast.Stmt
|
||||
caseStmts = append(caseStmts, s1)
|
||||
caseStmts = append(caseStmts, s2)
|
||||
caseStmts = append(caseStmts, caseDecodeValueStmts...)
|
||||
s = append(s, Case(Exprs(e), caseStmts))
|
||||
}
|
||||
if data.Default != nil {
|
||||
var tType ast.Expr
|
||||
tType, err = g.VisitType(data.Default)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s1 := Define(Exprs(Ident("_"), Ident("ok")), Exprs(TypeAssert(varToSet, tType)))
|
||||
s2 := If(Not(Ident("ok")), NewBlockEllipsis(Assign121(Ident("err"), Selector("proto_base", "BadTypeError")), Return()))
|
||||
defaultStmts := []ast.Stmt{s1, s2}
|
||||
var defaultEncodingStmts []ast.Stmt
|
||||
defaultEncodingStmts, err = g.VisitEncoder(TypeAssert(varToSet, tType), data.Default, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defaultStmts = append(defaultStmts, defaultEncodingStmts...)
|
||||
s = append(s, Case(nil, defaultStmts))
|
||||
}
|
||||
if len(s) != 0 {
|
||||
s = Stmts(SwitchStmt(compareToExpr, NewBlock(s)))
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func TypeForwardEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.TypeForward
|
||||
if err = mapstructure.Decode(dataRaw, &data); err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitEncoder(varToSet, data.Type, name)
|
||||
}
|
||||
|
||||
func MapperEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.Mapper
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
kType, err := g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mName := name + "ReverseMap"
|
||||
|
||||
exprs := Exprs()
|
||||
for _, k := range util2.OrderedKeys(data.Mappings) {
|
||||
v := data.Mappings[k]
|
||||
exprs = append(exprs, KeyValueExpr(StrLit(v), NumLitStr(k)))
|
||||
}
|
||||
g.Decl(mName, token.VAR, CompLit(MapType(Ident("string"), kType), exprs))
|
||||
|
||||
vName := "v" + name
|
||||
s1 := VarStmt(vName, kType)
|
||||
s2 := Assign(Exprs(Ident(vName), Ident("err")), Exprs(Call(Selector("proto_base", "ErroringIndex"), Ident(mName), varToSet)))
|
||||
s3 := IfErrNil()
|
||||
s4, err := g.VisitEncoder(Ident(vName), data.Type, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s = append(s, s1)
|
||||
s = append(s, s2)
|
||||
s = append(s, s3)
|
||||
s = append(s, s4...)
|
||||
return
|
||||
}
|
||||
|
||||
func StringEncoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(
|
||||
Assign121(Ident("err"), Call(Selector("proto_base", "EncodeString"), Ident("w"), varToSet)), IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func UUIDEncoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("w", "Write"), MultiIndex(varToSet, nil, nil, nil)))), IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func RegistryEntryHolderEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.EntryHolderSet
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
otherwiseType, err := g.VisitType(data.Otherwise.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
knownTypeName := name + "KnownType"
|
||||
encode0statements, err := g.VisitEncoder(NumLit(0), "varint", name+"Zero")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
otherwiseEncodeStatements, err := g.VisitEncoder(Ident(knownTypeName), data.Otherwise.Type, name+"Otherwise")
|
||||
otherwiseEncodeStatements = append(encode0statements, otherwiseEncodeStatements...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
baseType, err := g.VisitType("varint")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
baseEncodeStatements, err := g.VisitEncoder(Ident(knownTypeName), "varint", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
statements := Stmts()
|
||||
statements = append(statements, Case(Exprs(baseType), baseEncodeStatements))
|
||||
statements = append(statements, Case(Exprs(otherwiseType), otherwiseEncodeStatements))
|
||||
statements = append(statements, Case(nil, Stmts(Assign121(Ident("err"), Selector("proto_base", "BadTypeError")))))
|
||||
|
||||
s = Stmts(TypeSwitch(Define121(Ident(knownTypeName), TypeAssert(varToSet, Ident("type"))), NewBlock(statements)))
|
||||
return
|
||||
}
|
||||
|
||||
func VarIntEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(Assign121(Ident("err"), Call(Selector("proto_base", "EncodeVarInt"), Ident("w"), varToSet)), IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func VarLongEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
s = Stmts(Assign121(Ident("err"), Call(Selector("proto_base", "EncodeVarLong"), Ident("w"), varToSet)), IfErrNil())
|
||||
return
|
||||
}
|
||||
|
||||
func VoidEncoder(_ *Generator, _ ast.Expr, _ any, _ string) (s []ast.Stmt, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func EntityMetadataLoopEncoder(g *Generator, vts ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
|
||||
var data protodef.EntityMetadataLoop
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
for _, v := range vts.Val {
|
||||
WriteData(w, v)
|
||||
}
|
||||
WriteU8(w, 0xFF)
|
||||
*/
|
||||
encodeV, err := g.VisitEncoder(Ident("v"), data.Type, name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
forStmt := ForRangeKV(Ident("_"), Ident("v"), vts, NewBlock(encodeV))
|
||||
|
||||
encodeEndId, err := g.VisitEncoder(Call(Ident("uint8"), NumLit(data.EndVal)), "u8", name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s = append(s, forStmt)
|
||||
s = append(s, encodeEndId...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) RegisterEncoderNatives() {
|
||||
g.EncoderNatives = map[string]FunctionGeneratorFunc{
|
||||
"container": ContainerEncoder,
|
||||
"array": ArrayEncoder,
|
||||
"option": OptionEncoder,
|
||||
"bitflags": TypeForwardEncoder,
|
||||
"switch": SwitchEncoder,
|
||||
"mapper": MapperEncoder,
|
||||
"buffer": BufferEncoder,
|
||||
"bitfield": BitFieldEncoder,
|
||||
|
||||
"void": VoidEncoder,
|
||||
"varint": VarIntEncoder,
|
||||
"varlong": VarLongEncoder,
|
||||
"anonymousNbt": DefaultEncoder,
|
||||
"anonOptionalNbt": DefaultEncoder, // I have no idea what the difference is between these two
|
||||
"UUID": UUIDEncoder,
|
||||
"restBuffer": DefaultEncoder,
|
||||
"lpVec3": DefaultEncoder,
|
||||
"string": StringEncoder,
|
||||
|
||||
"f32": SimpleTypeEncoder,
|
||||
"f64": SimpleTypeEncoder,
|
||||
"i8": SimpleTypeEncoder,
|
||||
"i16": SimpleTypeEncoder,
|
||||
"i32": SimpleTypeEncoder,
|
||||
"i64": SimpleTypeEncoder,
|
||||
"u8": SimpleTypeEncoder,
|
||||
"u16": SimpleTypeEncoder,
|
||||
"u32": SimpleTypeEncoder,
|
||||
"u64": SimpleTypeEncoder,
|
||||
"bool": SimpleTypeEncoder,
|
||||
|
||||
"registryEntryHolder": RegistryEntryHolderEncoder,
|
||||
"registryEntryHolderSet": ToDoEncoder,
|
||||
"entityMetadataLoop": EntityMetadataLoopEncoder,
|
||||
"topBitSetTerminatedArray": ToDoEncoder,
|
||||
"todo": ToDoEncoder,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"github.com/admin-else/strom/mc/proto_generator/protodef"
|
||||
"github.com/admin-else/strom/mc/util"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
func VisitContainerType(g *Generator, dataRaw any) (ast.Expr, error) {
|
||||
var data []struct {
|
||||
Name string
|
||||
Type any
|
||||
Anon bool
|
||||
}
|
||||
err := mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.ContainerStack = append(g.ContainerStack, ContainerStackEntry{
|
||||
Data: data,
|
||||
VarToSet: nil,
|
||||
})
|
||||
defer func() { g.ContainerStack = g.ContainerStack[:len(g.ContainerStack)-1] }()
|
||||
|
||||
s := NewStruct()
|
||||
for _, field := range data {
|
||||
var t ast.Expr
|
||||
t, err = g.VisitType(field.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field.Anon {
|
||||
field.Name = "Anon" // lol
|
||||
}
|
||||
AddFieldToStruct(s, util.CamelCase(field.Name), t)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func VisitBufferType(_ *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
var data struct {
|
||||
CountType any
|
||||
Count int
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if data.CountType != nil {
|
||||
if data.CountType == "varint" {
|
||||
e = Slice(Ident("byte"))
|
||||
return
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported count type: %v", data.CountType) // TODO: old versions use uint16 as count type
|
||||
}
|
||||
e = Array(Ident("byte"), data.Count)
|
||||
return
|
||||
}
|
||||
|
||||
func VisitArrayTypeVisitorType(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
var data struct {
|
||||
CountType any
|
||||
Type any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e, err = g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Slice(e), nil
|
||||
}
|
||||
|
||||
func VisitDontGenerateType(_ *Generator, _ any) (ast.Expr, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func VisitToDoType(_ *Generator, _ any) (e ast.Expr, err error) {
|
||||
err = ToDoError
|
||||
return
|
||||
}
|
||||
|
||||
func VisitOptionType(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
e, err = g.VisitType(dataRaw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e = Pointer(e)
|
||||
return
|
||||
}
|
||||
|
||||
func BitSizeToProtodef(totalSize int, singed bool) (e string, err error) {
|
||||
if !singed && totalSize == 1 {
|
||||
e = "bool"
|
||||
return
|
||||
}
|
||||
if singed {
|
||||
e = "i"
|
||||
} else {
|
||||
e = "u"
|
||||
}
|
||||
if totalSize <= 8 {
|
||||
e += "8"
|
||||
} else if totalSize <= 16 {
|
||||
e += "16"
|
||||
} else if totalSize <= 32 {
|
||||
e += "32"
|
||||
} else if totalSize <= 64 {
|
||||
e += "64"
|
||||
} else {
|
||||
err = errors.New("bitfield size too large")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func VisitBitFieldType(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
var data protodef.BitField
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ret := NewStruct()
|
||||
for _, field := range data {
|
||||
var p string
|
||||
p, err = BitSizeToProtodef(field.Size, field.Signed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var fieldType ast.Expr
|
||||
fieldType, err = g.VisitType(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
AddFieldToStruct(ret, util.CamelCase(field.Name), fieldType)
|
||||
}
|
||||
e = ret
|
||||
return
|
||||
}
|
||||
|
||||
func VisitBitFlagsType(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
var data struct {
|
||||
Flags []string
|
||||
Type any
|
||||
}
|
||||
err = mapstructure.Decode(dataRaw, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return g.VisitType(data.Type)
|
||||
}
|
||||
|
||||
func MakeIdentVisitor(name string) func(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
return func(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
e = Ident(name)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func MakeSelectorVisitor(x, sel string) func(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
return func(g *Generator, dataRaw any) (e ast.Expr, err error) {
|
||||
e = Selector(x, sel)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func VoidType(_ *Generator, _ any) (e ast.Expr, err error) {
|
||||
e = NewStruct()
|
||||
return
|
||||
}
|
||||
|
||||
func EntityMetadataLoopType(g *Generator, v any) (e ast.Expr, err error) {
|
||||
var data protodef.EntityMetadataLoop
|
||||
err = mapstructure.Decode(v, &data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
innerType, err := g.VisitType(data.Type)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
e = MapType(Ident("byte"), innerType)
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generator) RegisterNatives() {
|
||||
g.Natives = map[string]ExprGeneratorFunc{
|
||||
"container": VisitContainerType,
|
||||
"buffer": VisitBufferType,
|
||||
"varint": MakeIdentVisitor("int32"),
|
||||
"varlong": MakeIdentVisitor("int64"),
|
||||
"array": VisitArrayTypeVisitorType,
|
||||
"mapper": MakeIdentVisitor("string"),
|
||||
"native": VisitDontGenerateType,
|
||||
"anonymousNbt": MakeSelectorVisitor("nbt", "Anon"),
|
||||
"anonOptionalNbt": MakeSelectorVisitor("nbt", "Anon"), // I have no idea what the difference is between these two
|
||||
"void": VoidType,
|
||||
"bool": MakeIdentVisitor("bool"),
|
||||
"u8": MakeIdentVisitor("uint8"),
|
||||
"u16": MakeIdentVisitor("uint16"),
|
||||
"u32": MakeIdentVisitor("uint32"),
|
||||
"u64": MakeIdentVisitor("uint64"),
|
||||
"i8": MakeIdentVisitor("int8"),
|
||||
"i16": MakeIdentVisitor("int16"),
|
||||
"i32": MakeIdentVisitor("int32"),
|
||||
"i64": MakeIdentVisitor("int64"),
|
||||
"f32": MakeIdentVisitor("float32"),
|
||||
"f64": MakeIdentVisitor("float64"),
|
||||
"registryEntryHolder": MakeIdentVisitor("any"), // Go really fucking needs good tagged unions
|
||||
"registryEntryHolderSet": MakeIdentVisitor("any"),
|
||||
"entityMetadataLoop": EntityMetadataLoopType,
|
||||
"option": VisitOptionType,
|
||||
"switch": MakeIdentVisitor("any"),
|
||||
"UUID": MakeSelectorVisitor("uuid", "UUID"),
|
||||
"bitfield": VisitBitFieldType,
|
||||
"pstring": VisitDontGenerateType,
|
||||
"string": MakeIdentVisitor("string"),
|
||||
"restBuffer": MakeSelectorVisitor("proto_base", "RestBuffer"),
|
||||
"bitflags": VisitBitFlagsType,
|
||||
"topBitSetTerminatedArray": VisitToDoType,
|
||||
"todo": VisitToDoType,
|
||||
"lpVec3": MakeSelectorVisitor("proto_base", "LpVec3d"),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/token"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewStruct() (s *ast.StructType) {
|
||||
return &ast.StructType{
|
||||
Fields: &ast.FieldList{
|
||||
List: []*ast.Field{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func AddFieldToStruct(s *ast.StructType, fieldName string, t ast.Expr) {
|
||||
s.Fields.List = append(s.Fields.List, &ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
Name: fieldName,
|
||||
},
|
||||
},
|
||||
Type: t,
|
||||
})
|
||||
}
|
||||
|
||||
//func AddAnonFieldToStruct(s *ast.StructType, t ast.Expr) {
|
||||
// s.Fields.List = append(s.Fields.List, &ast.Field{
|
||||
// Names: []*ast.Ident{},
|
||||
// Type: t,
|
||||
// })
|
||||
//}
|
||||
|
||||
func Import(path ...string) ast.Decl {
|
||||
specs := make([]ast.Spec, len(path))
|
||||
for i, p := range path {
|
||||
specs[i] = &ast.ImportSpec{
|
||||
Path: StrLit(p),
|
||||
}
|
||||
}
|
||||
return &ast.GenDecl{
|
||||
Tok: token.IMPORT,
|
||||
Specs: specs,
|
||||
}
|
||||
}
|
||||
|
||||
func Ident(name string) (id *ast.Ident) {
|
||||
return ast.NewIdent(name)
|
||||
}
|
||||
|
||||
func VarStmt(name string, t ast.Expr) *ast.DeclStmt {
|
||||
return &ast.DeclStmt{Decl: Var(name, t)}
|
||||
}
|
||||
|
||||
func VarValues(name string, v ...ast.Expr) ast.Decl {
|
||||
return &ast.GenDecl{
|
||||
Tok: token.VAR,
|
||||
Specs: []ast.Spec{
|
||||
&ast.ValueSpec{
|
||||
Names: []*ast.Ident{Ident(name)},
|
||||
Values: v,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Var(name string, t ast.Expr) ast.Decl {
|
||||
return &ast.GenDecl{
|
||||
Tok: token.VAR,
|
||||
Specs: []ast.Spec{
|
||||
&ast.ValueSpec{
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
Name: name,
|
||||
},
|
||||
},
|
||||
Type: t,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Slice(elementType ast.Expr) (e ast.Expr) {
|
||||
return &ast.ArrayType{
|
||||
Len: nil, // nil Length means it's a slice, not an array
|
||||
Elt: elementType,
|
||||
}
|
||||
}
|
||||
|
||||
func NumLit(n int) *ast.BasicLit {
|
||||
return NumLitStr(fmt.Sprint(n))
|
||||
}
|
||||
|
||||
func NumLitHex(n int) *ast.BasicLit {
|
||||
return NumLitStr(fmt.Sprintf("0x%X", n))
|
||||
}
|
||||
|
||||
func NumLitStr(n string) *ast.BasicLit {
|
||||
return &ast.BasicLit{
|
||||
ValuePos: 0,
|
||||
Kind: token.INT,
|
||||
Value: n,
|
||||
}
|
||||
}
|
||||
|
||||
func Array(elementType ast.Expr, len int) ast.Expr {
|
||||
return &ast.ArrayType{
|
||||
Len: NumLit(len),
|
||||
Elt: elementType,
|
||||
}
|
||||
}
|
||||
|
||||
func TypeDecl(name string, t ast.Expr) ast.Decl {
|
||||
return &ast.GenDecl{
|
||||
Tok: token.TYPE,
|
||||
Specs: []ast.Spec{
|
||||
&ast.TypeSpec{
|
||||
Name: &ast.Ident{
|
||||
Name: name,
|
||||
},
|
||||
Type: t,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Selector(x, sel string) (e ast.Expr) {
|
||||
return &ast.SelectorExpr{
|
||||
X: Ident(x),
|
||||
Sel: Ident(sel),
|
||||
}
|
||||
}
|
||||
|
||||
func SelectorExprAndStr(x ast.Expr, sel string) (e ast.Expr) {
|
||||
return &ast.SelectorExpr{
|
||||
X: x,
|
||||
Sel: Ident(sel),
|
||||
}
|
||||
}
|
||||
|
||||
func Pointer(elementType ast.Expr) (e ast.Expr) {
|
||||
return &ast.StarExpr{
|
||||
X: elementType,
|
||||
}
|
||||
}
|
||||
|
||||
func Call(fn ast.Expr, args ...ast.Expr) (e *ast.CallExpr) {
|
||||
return &ast.CallExpr{
|
||||
Fun: fn,
|
||||
Args: args,
|
||||
}
|
||||
}
|
||||
|
||||
func Assign121(lhs, rhs ast.Expr) (s *ast.AssignStmt) {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{lhs},
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: []ast.Expr{rhs},
|
||||
}
|
||||
}
|
||||
|
||||
func Assign(lhs, rhs []ast.Expr) (s *ast.AssignStmt) {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: lhs,
|
||||
Tok: token.ASSIGN,
|
||||
Rhs: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func Define(lhs, rhs []ast.Expr) (s *ast.AssignStmt) {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: lhs,
|
||||
Tok: token.DEFINE,
|
||||
Rhs: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func Define121(lhs, rhs ast.Expr) (s *ast.AssignStmt) {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{lhs},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{rhs},
|
||||
}
|
||||
}
|
||||
|
||||
func SubAssign(lhs, rhs ast.Expr) (s *ast.AssignStmt) {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{lhs},
|
||||
Tok: token.SUB_ASSIGN,
|
||||
Rhs: []ast.Expr{rhs},
|
||||
}
|
||||
}
|
||||
|
||||
func OrAssign(lhs, rhs ast.Expr) (s *ast.AssignStmt) {
|
||||
return &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{lhs},
|
||||
Tok: token.OR_ASSIGN,
|
||||
Rhs: []ast.Expr{rhs},
|
||||
}
|
||||
}
|
||||
|
||||
func Exprs(exprs ...ast.Expr) []ast.Expr {
|
||||
return exprs
|
||||
}
|
||||
|
||||
func Stmts(stmts ...ast.Stmt) []ast.Stmt {
|
||||
return stmts
|
||||
}
|
||||
|
||||
func ExprStmt(expr ast.Expr) *ast.ExprStmt {
|
||||
return &ast.ExprStmt{
|
||||
X: expr,
|
||||
}
|
||||
}
|
||||
|
||||
func If(cond ast.Expr, body *ast.BlockStmt) *ast.IfStmt {
|
||||
return &ast.IfStmt{
|
||||
Cond: cond,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
func IfElse(cond ast.Expr, body *ast.BlockStmt, els *ast.BlockStmt) *ast.IfStmt {
|
||||
return &ast.IfStmt{
|
||||
Cond: cond,
|
||||
Body: body,
|
||||
Else: els,
|
||||
}
|
||||
}
|
||||
|
||||
func NotEquals(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.NEQ,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func Equals(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.EQL,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func GreaterThanOrEqual(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.GEQ,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func Nil() ast.Expr {
|
||||
return Ident("nil")
|
||||
}
|
||||
|
||||
func NewFile(name string) *ast.File {
|
||||
return &ast.File{
|
||||
Name: &ast.Ident{
|
||||
Name: name,
|
||||
},
|
||||
Decls: []ast.Decl{},
|
||||
}
|
||||
}
|
||||
|
||||
type NameAndType struct {
|
||||
Name string
|
||||
Type ast.Expr
|
||||
}
|
||||
|
||||
func NewFunc(name string, arguments []NameAndType, returns []NameAndType) *ast.FuncDecl {
|
||||
params := &ast.FieldList{
|
||||
List: make([]*ast.Field, len(arguments)),
|
||||
}
|
||||
for i, arg := range arguments {
|
||||
params.List[i] = &ast.Field{
|
||||
Names: []*ast.Ident{Ident(arg.Name)},
|
||||
Type: arg.Type,
|
||||
}
|
||||
}
|
||||
|
||||
results := &ast.FieldList{
|
||||
List: make([]*ast.Field, len(returns)),
|
||||
}
|
||||
for i, ret := range returns {
|
||||
results.List[i] = &ast.Field{
|
||||
Names: []*ast.Ident{{Name: ret.Name}},
|
||||
Type: ret.Type,
|
||||
}
|
||||
}
|
||||
|
||||
return &ast.FuncDecl{
|
||||
Name: Ident(name),
|
||||
Type: &ast.FuncType{
|
||||
Params: params,
|
||||
Results: results,
|
||||
},
|
||||
Body: &ast.BlockStmt{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewFuncWithReceiver(name, receiverName string, receiver ast.Expr, arguments []NameAndType, returns []NameAndType) *ast.FuncDecl {
|
||||
f := NewFunc(name, arguments, returns)
|
||||
f.Recv = &ast.FieldList{
|
||||
List: []*ast.Field{
|
||||
{
|
||||
Names: []*ast.Ident{{Name: receiverName}},
|
||||
Type: receiver,
|
||||
},
|
||||
},
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func NewBlock(statements []ast.Stmt) *ast.BlockStmt {
|
||||
return &ast.BlockStmt{
|
||||
List: statements,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBlockEllipsis(statements ...ast.Stmt) *ast.BlockStmt {
|
||||
return &ast.BlockStmt{
|
||||
List: statements,
|
||||
}
|
||||
}
|
||||
|
||||
func Break() *ast.BranchStmt {
|
||||
return &ast.BranchStmt{
|
||||
Tok: token.BREAK,
|
||||
}
|
||||
}
|
||||
|
||||
//func MergeBlocks(blocks ...*ast.BlockStmt) *ast.BlockStmt {
|
||||
// list := make([]ast.Stmt, 0)
|
||||
// for _, block := range blocks {
|
||||
// if block != nil {
|
||||
// list = append(list, block.List...)
|
||||
// }
|
||||
// }
|
||||
// return &ast.BlockStmt{
|
||||
// List: list,
|
||||
// }
|
||||
//}
|
||||
|
||||
func Return(exprs ...ast.Expr) *ast.ReturnStmt {
|
||||
return &ast.ReturnStmt{
|
||||
Results: exprs,
|
||||
}
|
||||
}
|
||||
|
||||
func AppendDecl(file *ast.File, decl ast.Decl) {
|
||||
file.Decls = append(file.Decls, decl)
|
||||
}
|
||||
|
||||
// AddFileComment adds a comment block at the top of the file
|
||||
func AddFileComment(f *ast.File, comment string) {
|
||||
f.Doc = &ast.CommentGroup{}
|
||||
lines := strings.Split(comment, "\n")
|
||||
for _, line := range lines {
|
||||
f.Doc.List = append(f.Doc.List, &ast.Comment{Text: "// " + line})
|
||||
}
|
||||
}
|
||||
|
||||
func PrintToFile(f *ast.File, w io.Writer) error {
|
||||
// Extract doc comment header if present
|
||||
var header string
|
||||
if f.Doc != nil {
|
||||
for _, c := range f.Doc.List {
|
||||
header += c.Text + "\n"
|
||||
}
|
||||
f.Doc = nil // Clear so go/format doesn't print it
|
||||
}
|
||||
|
||||
// Format the file
|
||||
var buf bytes.Buffer
|
||||
if err := format.Node(&buf, token.NewFileSet(), f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find where "package " starts in formatted output
|
||||
formatted := buf.String()
|
||||
pkgIndex := strings.Index(formatted, "package ")
|
||||
if pkgIndex < 0 {
|
||||
pkgIndex = 0
|
||||
}
|
||||
|
||||
// Write header, then formatted content starting from package
|
||||
w.Write([]byte(header))
|
||||
w.Write([]byte(formatted[pkgIndex:]))
|
||||
return nil
|
||||
}
|
||||
|
||||
func IfErrNil() *ast.IfStmt {
|
||||
return If(
|
||||
NotEquals(Ident("err"), Nil()), NewBlockEllipsis(Return()),
|
||||
)
|
||||
}
|
||||
|
||||
func AddrOf(e ast.Expr) *ast.UnaryExpr {
|
||||
return &ast.UnaryExpr{
|
||||
Op: token.AND,
|
||||
X: e,
|
||||
}
|
||||
}
|
||||
|
||||
func DeRef(e ast.Expr) *ast.StarExpr {
|
||||
return &ast.StarExpr{
|
||||
X: e,
|
||||
}
|
||||
}
|
||||
|
||||
func ForRange(K, X ast.Expr, block *ast.BlockStmt) *ast.RangeStmt {
|
||||
return &ast.RangeStmt{
|
||||
Key: K, // The loop variable "i"
|
||||
Value: nil, // No second variable (common for index-only range loops)
|
||||
X: X, // The collection being ranged over "l"
|
||||
Tok: token.DEFINE, // ":=" token
|
||||
Body: block,
|
||||
}
|
||||
}
|
||||
|
||||
func ForRangeKV(K, V, X ast.Expr, block *ast.BlockStmt) *ast.RangeStmt {
|
||||
return &ast.RangeStmt{
|
||||
Key: K,
|
||||
Value: V,
|
||||
X: X,
|
||||
Tok: token.DEFINE,
|
||||
Body: block,
|
||||
}
|
||||
}
|
||||
|
||||
func Index(X, I ast.Expr) *ast.IndexExpr {
|
||||
return &ast.IndexExpr{
|
||||
X: X,
|
||||
Index: I,
|
||||
}
|
||||
}
|
||||
|
||||
func MultiIndex(X ast.Expr, Low, High, Max ast.Expr) *ast.SliceExpr {
|
||||
return &ast.SliceExpr{
|
||||
X: X,
|
||||
Low: Low,
|
||||
High: High,
|
||||
Max: Max,
|
||||
Slice3: Max != nil,
|
||||
}
|
||||
}
|
||||
|
||||
func SwitchStmt(Tag ast.Expr, block *ast.BlockStmt) *ast.SwitchStmt {
|
||||
return &ast.SwitchStmt{
|
||||
Tag: Tag,
|
||||
Body: block,
|
||||
}
|
||||
}
|
||||
|
||||
//func SwitchStmtInit(init ast.Stmt, block *ast.BlockStmt) *ast.SwitchStmt {
|
||||
// return &ast.SwitchStmt{
|
||||
// Init: init,
|
||||
// Body: block,
|
||||
// }
|
||||
//}
|
||||
|
||||
func StrLit(s string) *ast.BasicLit {
|
||||
return &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: `"` + s + `"`,
|
||||
}
|
||||
}
|
||||
|
||||
func Case(exprs []ast.Expr, stmts []ast.Stmt) *ast.CaseClause {
|
||||
return &ast.CaseClause{
|
||||
List: exprs,
|
||||
Body: stmts,
|
||||
}
|
||||
}
|
||||
|
||||
func MapType(key, value ast.Expr) ast.Expr {
|
||||
return &ast.MapType{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func CompLit(t ast.Expr, elts []ast.Expr) *ast.CompositeLit {
|
||||
return &ast.CompositeLit{
|
||||
Type: t,
|
||||
Elts: elts,
|
||||
}
|
||||
}
|
||||
|
||||
//func VarAssign(name string, value ast.Expr) *ast.GenDecl {
|
||||
// return &ast.GenDecl{
|
||||
// Tok: token.VAR,
|
||||
// Specs: []ast.Spec{
|
||||
// &ast.ValueSpec{
|
||||
// Names: []*ast.Ident{
|
||||
// {
|
||||
// Name: name,
|
||||
// },
|
||||
// },
|
||||
// Values: []ast.Expr{value},
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//}
|
||||
|
||||
func KeyValueExpr(key, value ast.Expr) *ast.KeyValueExpr {
|
||||
return &ast.KeyValueExpr{
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func TypeAssert(x, t ast.Expr) *ast.TypeAssertExpr {
|
||||
return &ast.TypeAssertExpr{
|
||||
X: x,
|
||||
Type: t,
|
||||
}
|
||||
}
|
||||
|
||||
func TypeSwitch(x ast.Stmt, block *ast.BlockStmt) *ast.TypeSwitchStmt {
|
||||
return &ast.TypeSwitchStmt{
|
||||
Assign: x,
|
||||
Body: block,
|
||||
}
|
||||
}
|
||||
|
||||
func Not(x ast.Expr) *ast.UnaryExpr {
|
||||
return &ast.UnaryExpr{
|
||||
Op: token.NOT,
|
||||
X: x,
|
||||
}
|
||||
}
|
||||
|
||||
func Sub(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.SUB,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func BinAnd(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.AND,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func RightShift(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.SHR,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func LeftShift(lhs, rhs ast.Expr) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{
|
||||
X: lhs,
|
||||
Op: token.SHL,
|
||||
Y: rhs,
|
||||
}
|
||||
}
|
||||
|
||||
func Grouping(e ast.Expr) *ast.ParenExpr {
|
||||
return &ast.ParenExpr{
|
||||
X: e,
|
||||
}
|
||||
}
|
||||
|
||||
func ForStmt(init ast.Stmt, cond ast.Expr, post ast.Stmt, body *ast.BlockStmt) *ast.ForStmt {
|
||||
return &ast.ForStmt{
|
||||
Init: init,
|
||||
Cond: cond,
|
||||
Post: post,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package protodef
|
||||
|
||||
type (
|
||||
BitFieldEntry struct {
|
||||
Name string
|
||||
Signed bool
|
||||
Size int
|
||||
}
|
||||
|
||||
BitField []BitFieldEntry
|
||||
|
||||
EntryHolderSet struct {
|
||||
BaseName string
|
||||
Otherwise struct {
|
||||
Name string
|
||||
Type any
|
||||
}
|
||||
}
|
||||
|
||||
Switch struct {
|
||||
CompareTo string
|
||||
Fields map[string]any
|
||||
Default any
|
||||
}
|
||||
|
||||
// TypeForward is not a real type, I just didn't implement it all yet, and it's just the underlying type
|
||||
TypeForward struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
Buffer struct {
|
||||
Count int
|
||||
CountType any
|
||||
}
|
||||
|
||||
Array struct {
|
||||
CountType any
|
||||
Count string
|
||||
Type any
|
||||
}
|
||||
|
||||
Container []struct {
|
||||
Name string
|
||||
Type any
|
||||
Anon bool
|
||||
}
|
||||
|
||||
Mapper struct {
|
||||
Mappings map[string]string
|
||||
Type any
|
||||
}
|
||||
|
||||
RegistryEntryHolderSet struct {
|
||||
Base struct {
|
||||
Name string
|
||||
Type any
|
||||
}
|
||||
Otherwise struct {
|
||||
Name string
|
||||
Type any
|
||||
}
|
||||
}
|
||||
BitFlags struct {
|
||||
Flags []string
|
||||
Type any
|
||||
}
|
||||
|
||||
EntityMetadataLoop struct {
|
||||
EndVal int
|
||||
Type any
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user