v0.1.0 602baa7

emit.go RawHistoryFolder

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 1

↑↓ moveenter openesc close