1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "os"
7 "path/filepath"
8 "strconv"
9 "strings"
10)
11
12var builtin = map[string]*Type{}
13
14func init() {
15 for _, b := range []struct {
16 xml, goType string
17 size int
18 }{
19 {"CARD8", "uint8", 1}, {"CARD16", "uint16", 2}, {"CARD32", "uint32", 4}, {"CARD64", "uint64", 8},
20 {"INT8", "int8", 1}, {"INT16", "int16", 2}, {"INT32", "int32", 4}, {"INT64", "int64", 8},
21 {"BOOL", "bool", 1}, {"BOOL32", "bool", 4}, {"char", "byte", 1}, {"void", "byte", 1}, {"BYTE", "byte", 1},
22 } {
23 builtin[b.xml] = &Type{Name: b.xml, GoType: b.goType, Base: b.goType, Size: b.size, Kind: "scalar", Resolved: true}
24 }
25}
26func loadModules() ([]*Module, error) {
27 var manifest struct {
28 Files []struct {
29 Path string `json:"path"`
30 } `json:"files"`
31 }
32 b, err := os.ReadFile("spec/manifest.json")
33 if err != nil {
34 return nil, err
35 }
36 if err = json.Unmarshal(b, &manifest); err != nil {
37 return nil, err
38 }
39 var nodes []*Node
40 for _, f := range manifest.Files {
41 if filepath.Ext(f.Path) != ".xml" {
42 continue
43 }
44 n, err := parseFile(f.Path)
45 if err != nil {
46 return nil, err
47 }
48 nodes = append(nodes, n)
49 }
50 return resolveModules(nodes)
51}
52func (m *Module) claim(name string, n *Node) error {
53 if old := m.Names[name]; old != nil {
54 return n.fail("Go name collision %s (previously %s:%d)", name, old.File, old.Line)
55 }
56 m.Names[name] = n
57 return nil
58}
59func resolveModules(nodes []*Node) ([]*Module, error) {
60 var modules []*Module
61 byHeader := map[string]*Module{}
62 for _, n := range nodes {
63 m := &Module{Header: n.Attr["header"], Pkg: strings.ReplaceAll(n.Attr["header"], "_", ""), XName: n.Attr["extension-xname"], ExtName: n.Attr["extension-name"], Root: n, Symbols: map[string]*Type{}, Names: map[string]*Node{}}
64 if byHeader[m.Header] != nil {
65 return nil, n.fail("duplicate module %s", m.Header)
66 }
67 byHeader[m.Header] = m
68 modules = append(modules, m)
69 for _, c := range n.Children {
70 switch c.Kind {
71 case "xidtype", "xidunion", "typedef", "struct", "union":
72 raw := c.Attr["name"]
73 if c.Kind == "typedef" {
74 raw = c.Attr["newname"]
75 }
76 name := goName(raw)
77 if err := m.claim(name, c); err != nil {
78 return nil, err
79 }
80 typ := &Type{Name: raw, GoType: name, Kind: c.Kind, Node: c, Module: m}
81 m.Types = append(m.Types, typ)
82 m.Symbols[raw] = typ
83 }
84 }
85 }
86 for _, m := range modules {
87 for _, n := range m.Root.Children {
88 if n.Kind == "import" {
89 dep := byHeader[n.Text]
90 if dep == nil {
91 return nil, n.fail("unknown import %s", n.Text)
92 }
93 m.Imports = append(m.Imports, dep)
94 }
95 }
96 }
97 for _, m := range modules {
98 for _, typ := range m.Types {
99 if err := resolveType(typ); err != nil {
100 return nil, err
101 }
102 }
103 }
104 for _, m := range modules {
105 if err := resolveEnums(m); err != nil {
106 return nil, err
107 }
108 }
109 // Re-resolve enum annotations after widths are known, without changing wire sizes.
110 for _, m := range modules {
111 for _, typ := range m.Types {
112 for i := range typ.Fields {
113 f := &typ.Fields[i]
114 if f.Kind != "field" {
115 continue
116 }
117 if enum := f.Node.Attr["enum"]; enum != "" {
118 if e, owner := findEnum(m, enum); e != nil {
119 f.GoType = qualified(m, owner, e.Name)
120 }
121 }
122 if mask := f.Node.Attr["mask"]; mask != "" {
123 if e, owner := findEnum(m, mask); e != nil {
124 f.GoType = qualified(m, owner, e.Name)
125 }
126 }
127 }
128 }
129 }
130 return modules, nil
131}
132func lookup(m *Module, name string) (*Type, error) {
133 if t := builtin[name]; t != nil {
134 return t, nil
135 }
136 if strings.Contains(name, ":") {
137 parts := strings.SplitN(name, ":", 2)
138 for _, dep := range m.Imports {
139 if dep.Header == parts[0] {
140 return lookup(dep, parts[1])
141 }
142 }
143 return nil, m.Root.fail("unknown qualified type %s", name)
144 }
145 if t := m.Symbols[name]; t != nil {
146 return t, nil
147 }
148 var found *Type
149 for _, dep := range m.Imports {
150 if t := dep.Symbols[name]; t != nil {
151 if found != nil && found != t {
152 return nil, m.Root.fail("ambiguous type %s", name)
153 }
154 found = t
155 }
156 }
157 if found != nil {
158 return found, nil
159 }
160 return nil, m.Root.fail("unknown type %s", name)
161}
162func qualified(m, owner *Module, name string) string {
163 if owner == nil || owner == m {
164 return name
165 }
166 return owner.Pkg + "." + name
167}
168func resolveType(t *Type) error {
169 if t.Resolved {
170 return nil
171 }
172 if t.Resolving {
173 return t.Node.fail("recursive type %s", t.Name)
174 }
175 t.Resolving = true
176 switch t.Kind {
177 case "xidtype", "xidunion":
178 t.Size = 4
179 t.Base = "uint32"
180 case "typedef":
181 base, err := lookup(t.Module, t.Node.Attr["oldname"])
182 if err != nil {
183 return err
184 }
185 if err := resolveType(base); err != nil {
186 return err
187 }
188 t.Size = base.Size
189 t.Base = base.Base
190 t.Variable = base.Variable
191 default:
192 off := 0
193 for _, n := range t.Node.Children {
194 if n.Kind == "doc" {
195 continue
196 }
197 f := Field{Name: goName(n.Attr["name"]), Param: paramName(n.Attr["name"]), Kind: n.Kind, Node: n, Off: off}
198 if t.Kind == "union" {
199 f.Off = 0
200 }
201 switch n.Kind {
202 case "pad":
203 if n.Attr["align"] != "" {
204 f.Align, _ = strconv.Atoi(n.Attr["align"])
205 f.Size = (-off) & (f.Align - 1)
206 } else {
207 f.Size, _ = strconv.Atoi(n.Attr["bytes"])
208 }
209 case "field", "list":
210 elem, err := lookup(t.Module, n.Attr["type"])
211 if err != nil {
212 return n.fail("%v", err)
213 }
214 if err := resolveType(elem); err != nil {
215 return err
216 }
217 f.Type = elem
218 f.GoType = qualified(t.Module, elem.Module, elem.GoType)
219 f.Size = elem.Size
220 if n.Kind == "list" {
221 if len(n.Children) > 0 {
222 f.Expr = n.Children[0]
223 }
224 if f.Expr != nil && f.Expr.Kind == "value" && !elem.Variable {
225 count, err := strconv.Atoi(f.Expr.Text)
226 if err != nil || count < 0 {
227 return n.fail("invalid constant list size")
228 }
229 f.Count = count
230 f.Size *= count
231 f.GoType = fmt.Sprintf("[%d]%s", count, f.GoType)
232 } else {
233 t.Variable = true
234 f.Size = 0
235 f.GoType = "[]" + f.GoType
236 if n.Attr["type"] == "char" {
237 f.GoType = "string"
238 }
239 }
240 }
241 if elem.Variable {
242 t.Variable = true
243 }
244 default:
245 return n.fail("unsupported type member %s", n.Kind)
246 }
247 t.Fields = append(t.Fields, f)
248 if t.Kind == "union" {
249 if f.Size > off {
250 off = f.Size
251 }
252 } else if !t.Variable {
253 off += f.Size
254 }
255 }
256 t.Size = off
257 }
258 t.Resolving = false
259 t.Resolved = true
260 return nil
261}
262func findEnum(m *Module, name string) (*Enum, *Module) {
263 if strings.Contains(name, ":") {
264 p := strings.SplitN(name, ":", 2)
265 for _, dep := range m.Imports {
266 if dep.Header == p[0] {
267 return findEnum(dep, p[1])
268 }
269 }
270 return nil, nil
271 }
272 for i := range m.Enums {
273 if m.Enums[i].Node.Attr["name"] == name {
274 return &m.Enums[i], m
275 }
276 }
277 for _, dep := range m.Imports {
278 for i := range dep.Enums {
279 if dep.Enums[i].Node.Attr["name"] == name {
280 return &dep.Enums[i], dep
281 }
282 }
283 }
284 return nil, nil
285}
286func resolveEnums(m *Module) error {
287 for _, n := range m.Root.Children {
288 if n.Kind != "enum" {
289 continue
290 }
291 e := Enum{Name: goName(n.Attr["name"]), Base: "uint32", Node: n}
292 if old := m.Names[e.Name]; old != nil {
293 if !contains("xidtype xidunion typedef", old.Kind) {
294 return n.fail("Go name collision %s", e.Name)
295 }
296 e.Existing = true
297 } else if err := m.claim(e.Name, n); err != nil {
298 return err
299 }
300 var max int64
301 for _, item := range n.Children {
302 if item.Kind == "doc" {
303 continue
304 }
305 ex := item.Children[0]
306 v, err := strconv.ParseInt(ex.Text, 0, 64)
307 if err != nil {
308 return ex.fail("invalid enum value")
309 }
310 if ex.Kind == "bit" {
311 if v < 0 || v > 31 {
312 return ex.fail("enum bit out of range")
313 }
314 v = 1 << uint(v)
315 }
316 if v < 0 || v > 0xffffffff {
317 return ex.fail("enum value out of range")
318 }
319 if v > max {
320 max = v
321 }
322 name := e.Name + goName(item.Attr["name"])
323 if err := m.claim(name, item); err != nil {
324 return err
325 }
326 e.Items = append(e.Items, EnumItem{name, v})
327 }
328 // Narrow only when every wire use has the same narrower width.
329 width := 0
330 mixed := false
331 var scan func(*Node)
332 scan = func(x *Node) {
333 if x.Attr["enum"] == n.Attr["name"] || x.Attr["mask"] == n.Attr["name"] || x.Attr["altenum"] == n.Attr["name"] {
334 typ, err := lookup(m, x.Attr["type"])
335 if err == nil {
336 if width != 0 && width != typ.Size {
337 mixed = true
338 }
339 width = typ.Size
340 }
341 }
342 for _, c := range x.Children {
343 scan(c)
344 }
345 }
346 scan(m.Root)
347 if !mixed && width == 1 && max <= 255 {
348 e.Base = "uint8"
349 }
350 if !mixed && width == 2 && max <= 65535 {
351 e.Base = "uint16"
352 }
353 m.Enums = append(m.Enums, e)
354 }
355 return nil
356}