15
15
package gen
16
16
17
17
import (
18
+ "container/list"
18
19
"flag"
19
20
"go/format"
20
21
"io/ioutil"
22
+
23
+ qapischema "github.com/digitalocean/go-qemu/qapi-schema"
21
24
)
22
25
23
26
const specURL = `https://raw.githubusercontent.com/qemu/qemu/stable-2.11/qapi-schema.json`
@@ -32,16 +35,23 @@ var (
32
35
func Generate () error {
33
36
flag .Parse ()
34
37
35
- defs , err := readDefinitions (* inputSpec )
38
+ src , err := getQAPI (* inputSpec )
36
39
if err != nil {
37
40
return err
38
41
}
39
42
40
- // Generate and write out the code.
41
- symbols , err := parse (defs )
43
+ tree , err := qapischema .Parse (string (src ))
42
44
if err != nil {
43
45
return err
44
46
}
47
+
48
+ tree , err = completeParseTree (tree , * inputSpec )
49
+ if err != nil {
50
+ return err
51
+ }
52
+
53
+ symbols := lowerParseTree (tree )
54
+
45
55
need := neededTypes (symbols )
46
56
bs , err := renderAPI (* templates , symbols , need )
47
57
if err != nil {
@@ -57,3 +67,251 @@ func Generate() error {
57
67
58
68
return ioutil .WriteFile (* outputGo , formatted , 0640 )
59
69
}
70
+
71
+ // completeParseTree walks the parse tree and expands the Include statements.
72
+ // It returns the completely resolved parse tree.
73
+ func completeParseTree (tree * qapischema.Tree , base string ) (* qapischema.Tree , error ) {
74
+ if inc , ok := tree .Node .(qapischema.Include ); ok {
75
+ src , err := resolvePath (base , string (inc ))
76
+ if err != nil {
77
+ return nil , err
78
+ }
79
+
80
+ // Any include directives in a subtree of this node will use
81
+ // *this* include directive as its main path.
82
+ base = src
83
+
84
+ srcBytes , err := getQAPI (src )
85
+ if err != nil {
86
+ return nil , err
87
+ }
88
+
89
+ root , err := qapischema .Parse (string (srcBytes ))
90
+ if err != nil {
91
+ return nil , err
92
+ }
93
+
94
+ tree .Children = root .Children
95
+ }
96
+
97
+ for i , subtree := range tree .Children {
98
+ st , err := completeParseTree (subtree , base )
99
+ if err != nil {
100
+ return tree , err
101
+ }
102
+
103
+ tree .Children [i ] = st
104
+ }
105
+
106
+ return tree , nil
107
+ }
108
+
109
+ // lowerParseTree converts the parsed types from package qapischema into
110
+ // types that are wired up for Go code generation.
111
+ func lowerParseTree (tree * qapischema.Tree ) map [name ]interface {} {
112
+ types := make (map [name ]interface {})
113
+
114
+ remaining := list .New ()
115
+ remaining .PushFront (tree )
116
+
117
+ for remaining .Len () > 0 {
118
+ tree := remaining .Remove (remaining .Front ()).(* qapischema.Tree )
119
+
120
+ for _ , t := range tree .Children {
121
+ remaining .PushBack (t )
122
+ }
123
+
124
+ switch node := tree .Node .(type ) {
125
+ case qapischema.Root :
126
+ // Nothing to do, subtrees were already added to the queue above.
127
+ case qapischema.Include :
128
+ // Nothing to do, subtrees were already added to the queue above.
129
+ case qapischema.Pragma :
130
+ // Ignored by go-qemu.
131
+ case * qapischema.Struct :
132
+ v := structType {
133
+ Name : name (node .Name ),
134
+ Base : name (node .Base ),
135
+ }
136
+
137
+ for _ , member := range node .Members {
138
+ f := field {
139
+ Name : name (member .Name ),
140
+ Optional : member .Optional ,
141
+ }
142
+
143
+ if member .Type .TypeArray != "" {
144
+ f .Type = name (member .Type .TypeArray )
145
+ f .List = true
146
+ } else {
147
+ f .Type = name (member .Type .Type )
148
+ }
149
+ v .Fields = append (v .Fields , f )
150
+ }
151
+
152
+ types [v .Name ] = v
153
+
154
+ case * qapischema.Command :
155
+ v := command {
156
+ Name : name (node .Name ),
157
+ }
158
+
159
+ if node .Boxed != nil {
160
+ v .BoxedInput = * node .Boxed
161
+ }
162
+
163
+ if node .Data .Ref != nil {
164
+ v .Inputs .Ref = name (* node .Data .Ref )
165
+ } else if node .Data .Embed != nil {
166
+ var fs fields
167
+ for _ , member := range node .Data .Embed .Members {
168
+ f := field {
169
+ Name : name (member .Name ),
170
+ Optional : member .Optional ,
171
+ }
172
+
173
+ if member .Type .TypeArray != "" {
174
+ f .List = true
175
+ f .Type = name (member .Type .TypeArray )
176
+ } else {
177
+ f .Type = name (member .Type .Type )
178
+ }
179
+
180
+ fs = append (fs , f )
181
+ v .Inputs .AnonFields = fs
182
+ }
183
+ }
184
+
185
+ if node .Returns != nil {
186
+ // TODO: Output.Name is confusing
187
+ if node .Returns .TypeArray != "" {
188
+ v .Output = field {
189
+ Name : name (node .Returns .TypeArray ),
190
+ List : true ,
191
+ Type : name (node .Returns .TypeArray ),
192
+ // TODO: Optional:
193
+ }
194
+ } else {
195
+ v .Output = field {
196
+ Name : name (node .Returns .Type ),
197
+ Type : name (node .Returns .Type ),
198
+ // TODO: Optional:
199
+ }
200
+ }
201
+ }
202
+
203
+ types [v .Name ] = v
204
+ case * qapischema.Event :
205
+ v := event {
206
+ Name : name (node .Name ),
207
+ }
208
+
209
+ if node .Data .Selector .Embed != nil {
210
+ v .Data = fieldsOrRef {
211
+ Ref : name (node .Data .Selector .Ref ),
212
+ }
213
+ if v .Data .Ref != "" {
214
+ v .BoxedData = true
215
+ }
216
+
217
+ for _ , member := range node .Data .Selector .Embed .Members {
218
+ f := field {
219
+ Name : name (member .Name ),
220
+ Optional : member .Optional ,
221
+ }
222
+
223
+ if member .Type .TypeArray != "" {
224
+ f .List = true
225
+ f .Type = name (member .Type .TypeArray )
226
+ } else {
227
+ f .Type = name (member .Type .Type )
228
+ }
229
+
230
+ v .Data .AnonFields = append (v .Data .AnonFields , f )
231
+ }
232
+ }
233
+
234
+ types [v .Name ] = v
235
+ case * qapischema.Alternate :
236
+ v := alternate {
237
+ Name : name (node .Name ),
238
+ Options : make (map [name ]name ),
239
+ }
240
+
241
+ for _ , opt := range node .Data {
242
+ n := opt .Type .Type
243
+ if opt .Type .TypeArray != "" {
244
+ n = string (opt .Type .TypeArray )
245
+ }
246
+ v .Options [name (opt .Name )] = name (n )
247
+ }
248
+
249
+ types [v .Name ] = v
250
+ case * qapischema.Union :
251
+ if node .Discriminator == "" {
252
+ v := simpleUnion {
253
+ Name : name (node .Name ),
254
+ Options : make (map [name ]name ),
255
+ }
256
+
257
+ for _ , branch := range node .Branches {
258
+ b := string (branch .Type .Type )
259
+ if branch .Type .TypeArray != "" {
260
+ b = string (branch .Type .TypeArray )
261
+ }
262
+ v .Options [name (branch .Name )] = name (b )
263
+ }
264
+
265
+ types [v .Name ] = v
266
+ } else {
267
+ v := flatUnion {
268
+ Name : name (node .Name ),
269
+ Discriminator : name (node .Discriminator ),
270
+ Base : fieldsOrRef {
271
+ Ref : name (node .Base .Name ),
272
+ },
273
+ Options : make (map [name ]name ),
274
+ }
275
+
276
+ for _ , member := range node .Base .Members {
277
+ f := field {
278
+ Name : name (member .Name ),
279
+ Optional : member .Optional ,
280
+ }
281
+
282
+ if member .Type .TypeArray != "" {
283
+ f .List = true
284
+ f .Type = name (member .Type .TypeArray )
285
+ } else {
286
+ f .Type = name (member .Type .Type )
287
+ }
288
+
289
+ v .Base .AnonFields = append (v .Base .AnonFields , f )
290
+ }
291
+
292
+ for _ , branch := range node .Branches {
293
+ b := string (branch .Type .Type )
294
+ if branch .Type .TypeArray != "" {
295
+ b = string (branch .Type .TypeArray )
296
+ }
297
+ v .Options [name (branch .Name )] = name (b )
298
+ }
299
+
300
+ types [v .Name ] = v
301
+ }
302
+
303
+ case * qapischema.Enum :
304
+ v := enum {
305
+ Name : name (node .Name ),
306
+ }
307
+
308
+ for _ , val := range node .Values {
309
+ v .Values = append (v .Values , name (val .Value ))
310
+ }
311
+
312
+ types [v .Name ] = v
313
+ }
314
+ }
315
+
316
+ return types
317
+ }
0 commit comments