1package main
2
3import (
4 "bytes"
5 "embed"
6 "fmt"
7 "go/format"
8 "os"
9 "path/filepath"
10 "sort"
11 "strings"
12 "text/template"
13)
14
15//go:embed templates/*.tmpl
16var templates embed.FS
17
18func writeTemplate(path, name string, data any) error {
19 t, err := template.ParseFS(templates, "templates/"+name+".tmpl")
20 if err != nil {
21 return err
22 }
23 var b bytes.Buffer
24 if err = t.Execute(&b, data); err != nil {
25 return err
26 }
27 code, err := format.Source(b.Bytes())
28 if err != nil {
29 return fmt.Errorf("format %s: %w\n%s", path, err, b.String())
30 }
31 if err = os.MkdirAll(filepath.Dir(path), 0755); err != nil {
32 return err
33 }
34 return os.WriteFile(path, code, 0644)
35}
36func putScalar(t *Type, src, dst string) string {
37 if t.Base == "bool" {
38 if t.Size == 1 {
39 return fmt.Sprintf("if %s { %s[0]=1 } else { %s[0]=0 };", src, dst, dst)
40 }
41 return fmt.Sprintf("if %s { binary.LittleEndian.PutUint32(%s,1) } else { binary.LittleEndian.PutUint32(%s,0) };", src, dst, dst)
42 }
43 if t.Size == 1 {
44 return fmt.Sprintf("%s[0]=byte(%s);", dst, src)
45 }
46 return fmt.Sprintf("binary.LittleEndian.PutUint%d(%s,uint%d(%s));", 8*t.Size, dst, 8*t.Size, src)
47}
48func getScalar(t *Type, goType, src string) string {
49 value := src + "[0]"
50 if t.Size > 1 {
51 value = fmt.Sprintf("binary.LittleEndian.Uint%d(%s)", 8*t.Size, src)
52 }
53 if t.Base == "bool" {
54 return "(" + value + "!=0)"
55 }
56 return goType + "(" + value + ")"
57}
58func isScalar(t *Type) bool { return t.Base != "" }
59func typeDecl(t *Type) (string, error) {
60 if isScalar(t) {
61 return fmt.Sprintf("type %s %s", t.GoType, t.Base), nil
62 }
63 var b strings.Builder
64 if t.Kind == "union" {
65 fmt.Fprintf(&b, "type %s struct { raw []byte }; const %sSize=%d;\n", t.GoType, t.GoType, t.Size)
66 fmt.Fprintf(&b, "func Get%s(p []byte) %s { return %s{raw:p[:%d]} };\n", t.GoType, t.GoType, t.GoType, t.Size)
67 fmt.Fprintf(&b, "func (v %s) Put(p []byte) { copy(p[:%d],v.raw) };\n", t.GoType, t.Size)
68 for _, f := range t.Fields {
69 if f.Kind == "pad" {
70 continue
71 }
72 if f.Kind == "list" {
73 fmt.Fprintf(&b, "func (v %s) %s() (out %s) { for i:=range out { out[i]=%s }; return };\n", t.GoType, f.Name, f.GoType, getScalar(f.Type, qualified(t.Module, f.Type.Module, f.Type.GoType), fmt.Sprintf("v.raw[i*%d:]", f.Type.Size)))
74 } else {
75 fmt.Fprintf(&b, "func (v %s) %s() %s { return %s };\n", t.GoType, f.Name, f.GoType, getScalar(f.Type, f.GoType, "v.raw"))
76 }
77 }
78 return b.String(), nil
79 }
80 fmt.Fprintf(&b, "type %s struct {\n", t.GoType)
81 for _, f := range t.Fields {
82 if f.Kind != "pad" {
83 name := f.Name
84 if name == "Size" || name == "Put" {
85 name += "_"
86 }
87 fmt.Fprintf(&b, "%s %s\n", name, f.GoType)
88 }
89 }
90 b.WriteString("}\n")
91 fmt.Fprintf(&b, "const %sSize=%d;\n", t.GoType, t.Size)
92 if t.Variable {
93 fmt.Fprintf(&b, "func (v %s) Size() int { off:=0;", t.GoType)
94 for _, f := range t.Fields {
95 name := f.Name
96 if name == "Size" || name == "Put" {
97 name += "_"
98 }
99 if f.Align > 0 {
100 fmt.Fprintf(&b, "off=(off+%d)&^%d;", f.Align-1, f.Align-1)
101 } else if f.Kind == "list" && f.Count == 0 {
102 if f.Type.Variable {
103 fmt.Fprintf(&b, "for i:=range v.%s { off+=v.%s[i].Size() };", name, name)
104 } else {
105 fmt.Fprintf(&b, "off+=len(v.%s)*%d;", name, f.Type.Size)
106 }
107 } else {
108 fmt.Fprintf(&b, "off+=%d;", f.Size)
109 }
110 }
111 b.WriteString("return off };\n")
112 }
113 fmt.Fprintf(&b, "func (v %s) Put(p []byte) { ", t.GoType)
114 if t.Variable {
115 b.WriteString("off:=0;")
116 } else {
117 fmt.Fprintf(&b, "clear(p[:%d]);", t.Size)
118 }
119 for _, f := range t.Fields {
120 name := f.Name
121 if name == "Size" || name == "Put" {
122 name += "_"
123 }
124 off := fmt.Sprint(f.Off)
125 if t.Variable {
126 off = "off"
127 }
128 if f.Kind == "pad" {
129 if t.Variable {
130 if f.Align > 0 {
131 fmt.Fprintf(&b, "{ next:=(off+%d)&^%d; clear(p[off:next]); off=next };", f.Align-1, f.Align-1)
132 } else {
133 fmt.Fprintf(&b, "clear(p[off:off+%d]); off+=%d;", f.Size, f.Size)
134 }
135 }
136 continue
137 }
138 src := "v." + name
139 dst := "p[" + off + ":]"
140 if f.Kind == "list" {
141 fmt.Fprintf(&b, "for i:=range %s {", src)
142 item := src + "[i]"
143 target := fmt.Sprintf("p[%s+i*%d:]", off, f.Type.Size)
144 if f.Type.Variable {
145 target = "p[off:]"
146 }
147 if isScalar(f.Type) {
148 b.WriteString(putScalar(f.Type, item, target))
149 } else {
150 fmt.Fprintf(&b, "%s.Put(%s);", item, target)
151 }
152 if f.Type.Variable {
153 fmt.Fprintf(&b, "off+=%s.Size();", item)
154 }
155 b.WriteString("};")
156 if t.Variable && !f.Type.Variable {
157 fmt.Fprintf(&b, "off+=len(%s)*%d;", src, f.Type.Size)
158 }
159 } else {
160 if isScalar(f.Type) {
161 b.WriteString(putScalar(f.Type, src, dst))
162 } else {
163 fmt.Fprintf(&b, "%s.Put(%s);", src, dst)
164 }
165 if t.Variable {
166 fmt.Fprintf(&b, "off+=%d;", f.Size)
167 }
168 }
169 }
170 b.WriteString("}\n")
171 if !t.Variable {
172 fmt.Fprintf(&b, "func Get%s(p []byte) (v %s) { _=p[%d];", t.GoType, t.GoType, t.Size-1)
173 for _, f := range t.Fields {
174 if f.Kind == "pad" {
175 continue
176 }
177 dst := "v." + f.Name
178 src := fmt.Sprintf("p[%d:]", f.Off)
179 if f.Kind == "list" {
180 fmt.Fprintf(&b, "for i:=range %s { %s[i]=%s };", dst, dst, getScalar(f.Type, qualified(t.Module, f.Type.Module, f.Type.GoType), fmt.Sprintf("p[%d+i*%d:]", f.Off, f.Type.Size)))
181 } else if isScalar(f.Type) {
182 fmt.Fprintf(&b, "%s=%s;", dst, getScalar(f.Type, f.GoType, src))
183 } else {
184 fmt.Fprintf(&b, "%s=%s(%s);", dst, qualified(t.Module, f.Type.Module, "Get"+f.Type.GoType), src)
185 }
186 }
187 b.WriteString("return };\n")
188 }
189 return b.String(), nil
190}
191func importsFor(m *Module, decls string) string {
192 paths := map[string]string{"binary.": "encoding/binary", "bits.": "math/bits"}
193 for _, dep := range m.Imports {
194 paths[dep.Pkg+"."] = "bnema.dev/xturbo/" + dep.Pkg
195 }
196 var imports []string
197 for prefix, path := range paths {
198 if strings.Contains(decls, prefix) {
199 imports = append(imports, fmt.Sprintf("import %q", path))
200 }
201 }
202 sort.Strings(imports)
203 return strings.Join(imports, "\n")
204}
205func emitTypes(modules []*Module) error {
206 for _, m := range modules {
207 for _, t := range m.Types {
208 decl, err := typeDecl(t)
209 if err != nil {
210 return err
211 }
212 m.Decls = append(m.Decls, decl)
213 }
214 for _, e := range m.Enums {
215 var b strings.Builder
216 if !e.Existing {
217 fmt.Fprintf(&b, "type %s %s;\n", e.Name, e.Base)
218 }
219 for _, item := range e.Items {
220 fmt.Fprintf(&b, "const %s %s = %d;\n", item.Name, e.Name, item.Value)
221 }
222 m.Decls = append(m.Decls, b.String())
223 }
224 imp := importsFor(m, strings.Join(m.Decls, "\n"))
225 m.Decls = append([]string{imp}, m.Decls...)
226 if err := writeTemplate(filepath.Join(m.Pkg, "types_generated.go"), "types", m); err != nil {
227 return err
228 }
229 for _, t := range m.Types {
230 if t.Kind == "struct" || t.Kind == "union" {
231 m.SizeChecks = append(m.SizeChecks, fmt.Sprintf("if %sSize != %d { t.Errorf(%q, %sSize) }", t.GoType, t.Size, t.GoType+" fixed prefix = %d", t.GoType))
232 }
233 }
234 if err := packetSizeChecks(m); err != nil {
235 return err
236 }
237 if err := writeTemplate(filepath.Join(m.Pkg, "sizes_test.go"), "sizes", m); err != nil {
238 return err
239 }
240 }
241 var extensions []*Module
242 for _, m := range modules {
243 if m.ExtName != "" {
244 count, err := eventCount(m)
245 if err != nil {
246 return err
247 }
248 m.NumEvents, m.NumErrors = count, errorCount(m)
249 extensions = append(extensions, m)
250 }
251 }
252 return writeTemplate("ext_generated.go", "ext", extensions)
253}
254
255func sizeCheck(name string, got, want int) string {
256 return fmt.Sprintf("if got:= %d; got != %d { t.Errorf(%q,got) }", got, want, name+" fixed size = %d")
257}