go-hep.org/x/hep@v0.38.1/cmd/podio-gen/generator.go (about) 1 // Copyright ©2020 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bytes" 9 "fmt" 10 "go/format" 11 "io" 12 "os" 13 "regexp" 14 "strings" 15 16 "golang.org/x/text/cases" 17 "golang.org/x/text/language" 18 "gopkg.in/yaml.v3" 19 ) 20 21 func toTitle(s string) string { 22 return cases.Title(language.Und, cases.NoLower).String(s) 23 } 24 25 type document struct { 26 Options struct { 27 Syntax bool `yaml:"getSyntax"` 28 ExposePODMembers bool `yaml:"exposePODMembers"` 29 } `yaml:"options"` 30 31 Components yaml.Node `yaml:"components"` 32 DataTypes yaml.Node `yaml:"datatypes"` 33 } 34 35 type dtype struct { 36 descr string 37 author string `yaml:"Author"` 38 members []member 39 vecmbrs []member 40 one2oneRels []member 41 one2manyRels []member 42 TransientMembers []string `yaml:"TransientMembers"` 43 Typedefs []string `yaml:"Typedefs"` 44 extraCode extraCode `yaml:"ExtraCode"` 45 constCode extraCode `yaml:"ConstExtraCode"` 46 } 47 48 type member struct { 49 Name string 50 Type string 51 Doc string 52 } 53 54 type extraCode struct { 55 Includes string `yaml:"includes"` 56 ConstDecl string `yaml:"const_declaration"` 57 Decl string `yaml:"declaration"` 58 Impl string `yaml:"implementation"` 59 } 60 61 var ( 62 builtins = map[string]string{ 63 "bool": "bool", 64 "short": "int16", 65 "int": "int32", 66 "long": "int64", 67 "long long": "int64", 68 "unsigned int": "uint32", 69 "unsigned": "uint32", 70 "unsigned long": "uint64", 71 "unsigned long long": "uint64", 72 "float": "float32", 73 "double": "float64", 74 "std::string": "string", 75 } 76 77 cxxMangle = strings.NewReplacer( 78 ":", "_", 79 "<", "_", 80 ">", "_", 81 ) 82 83 stdArrayRe = regexp.MustCompile(" *std::array *<([a-zA-Z0-9:]+) *, *([0-9]+)> *") 84 stdArrayDocRe = regexp.MustCompile(` *std::array *<([a-zA-Z0-9:]+) *, *([0-9]+)> *(\S+) *// *(.+)`) 85 ) 86 87 type generator struct { 88 w io.Writer 89 90 buf *bytes.Buffer 91 pkg string 92 doc document 93 94 comps map[string]string 95 types map[string]string 96 rules map[string]string 97 } 98 99 func newGenerator(w io.Writer, pkg, fname, rules string) (*generator, error) { 100 var ( 101 err error 102 gen = &generator{ 103 w: w, 104 buf: new(bytes.Buffer), 105 pkg: pkg, 106 107 comps: make(map[string]string), 108 types: make(map[string]string), 109 rules: make(map[string]string), 110 } 111 ) 112 113 raw, err := os.ReadFile(fname) 114 if err != nil { 115 return nil, fmt.Errorf("could not read input YAML file %q: %w", fname, err) 116 } 117 118 gen.doc.Options.Syntax = false 119 gen.doc.Options.ExposePODMembers = true 120 121 err = yaml.Unmarshal(raw, &gen.doc) 122 if err != nil { 123 return nil, fmt.Errorf("could not unmarshal YAML file %q: %w", fname, err) 124 } 125 126 for _, rule := range strings.Split(rules, ",") { 127 toks := strings.Split(rule, "->") 128 if len(toks) != 2 { 129 continue 130 } 131 gen.rules[toks[0]] = toks[1] 132 } 133 134 gen.printf(`// Automatically generated. DO NOT EDIT. 135 136 package %s 137 138 `, pkg) 139 140 return gen, nil 141 } 142 143 func (g *generator) generate() error { 144 var err error 145 146 for i := 0; i < len(g.doc.Components.Content); i += 2 { 147 key := g.doc.Components.Content[i] 148 val := g.doc.Components.Content[i+1] 149 err = g.genComponent(key, val) 150 if err != nil { 151 return fmt.Errorf("could not handle component %q: %w", key.Value, err) 152 } 153 154 } 155 156 for i := 0; i < len(g.doc.DataTypes.Content); i += 2 { 157 key := g.doc.DataTypes.Content[i] 158 val := g.doc.DataTypes.Content[i+1] 159 err = g.genDataType(key, val) 160 if err != nil { 161 return fmt.Errorf("could not handle datatype %q: %w", key.Value, err) 162 } 163 } 164 165 out, err := format.Source(g.buf.Bytes()) 166 if err != nil { 167 return fmt.Errorf("could not go/format generated code: %w", err) 168 } 169 170 _, err = g.w.Write(out) 171 if err != nil { 172 return fmt.Errorf("could not write generated code: %w", err) 173 } 174 175 return nil 176 } 177 178 func (g *generator) printf(format string, args ...any) { 179 fmt.Fprintf(g.buf, format, args...) 180 } 181 182 func (g *generator) genTypeName(typ string) string { 183 for k, v := range g.rules { 184 typ = strings.Replace(typ, k, v, 1) 185 } 186 187 return g.cxx2go(typ) 188 } 189 190 func (g *generator) cxx2go(typ string) string { 191 if v, ok := builtins[typ]; ok { 192 return v 193 } 194 195 if grps := stdArrayRe.FindStringSubmatch(typ); grps != nil { 196 return fmt.Sprintf("[%s]%s", grps[2], g.genTypeName(grps[1])) 197 } 198 199 return cxxMangle.Replace(typ) 200 } 201 202 func (g *generator) genComponent(knode, vnode *yaml.Node) error { 203 var ( 204 err error 205 name = knode.Value 206 typ = g.genTypeName(name) 207 doc = strings.TrimSpace(knode.HeadComment) 208 ) 209 210 handleDoc := func(doc string) string { 211 return strings.TrimSpace(strings.Replace(doc, "#", "", 1)) 212 } 213 214 doc = handleDoc(doc) 215 if doc != "" { 216 doc = "\n// " + doc 217 } 218 219 g.comps[name] = typ 220 g.printf(`// %s%s 221 type %s struct { 222 `, name, doc, typ, 223 ) 224 for i := 0; i < len(vnode.Content); i += 2 { 225 key := vnode.Content[i] 226 val := vnode.Content[i+1] 227 if key.Value == "ExtraCode" { 228 continue 229 } 230 doc := handleDoc(val.LineComment) 231 if doc != "" { 232 doc = "// " + doc 233 } 234 g.printf("\t%s %s%s\n", toTitle(key.Value), g.genTypeName(val.Value), doc) 235 } 236 237 g.printf("}\n\n") 238 239 return err 240 } 241 242 func (g *generator) genDataType(knode, vnode *yaml.Node) error { 243 var ( 244 err error 245 name = knode.Value 246 typ = g.genTypeName(name) 247 ) 248 249 g.types[name] = typ 250 251 var dt dtype 252 253 for i := 0; i < len(vnode.Content); i += 2 { 254 key := vnode.Content[i] 255 val := vnode.Content[i+1] 256 switch key.Value { 257 case "Description": 258 dt.descr = val.Value 259 case "Author": 260 dt.author = val.Value 261 case "Members": 262 dt.members, err = g.genMembers(val) 263 if err != nil { 264 return fmt.Errorf("could not handle data type members of %q: %w", 265 name, err, 266 ) 267 } 268 case "OneToOneRelations": 269 dt.one2oneRels, err = g.genMembers(val) 270 if err != nil { 271 return fmt.Errorf("could not handle data type 1-to-1 relations of %q: %w", 272 name, err, 273 ) 274 } 275 case "OneToManyRelations": 276 dt.one2manyRels, err = g.genMembers(val) 277 if err != nil { 278 return fmt.Errorf("could not handle data type 1-to-n relations of %q: %w", 279 name, err, 280 ) 281 } 282 case "ExtraCode": 283 err = val.Decode(&dt.extraCode) 284 if err != nil { 285 return fmt.Errorf("could not handle data type extracode of %q: %w", 286 name, err, 287 ) 288 } 289 case "ConstExtraCode": 290 err = val.Decode(&dt.constCode) 291 if err != nil { 292 return fmt.Errorf("could not handle data type const-extracode of %q: %w", 293 name, err, 294 ) 295 } 296 case "VectorMembers": 297 dt.vecmbrs, err = g.genMembers(val) 298 if err != nil { 299 return fmt.Errorf("could not handle data type vec-members of %q: %w", 300 name, err, 301 ) 302 } 303 default: 304 return fmt.Errorf("unknown data type field %q", key.Value) 305 } 306 } 307 308 doc := dt.descr 309 if doc != "" { 310 doc = "\n// " + dt.descr 311 } 312 g.printf(`// %s%s 313 type %s struct { 314 `, name, doc, typ, 315 ) 316 for _, m := range dt.members { 317 doc := "" 318 if m.Doc != "" { 319 doc = " // " + m.Doc 320 } 321 g.printf("\t%s %s%s\n", m.Name, m.Type, doc) 322 } 323 324 for _, m := range dt.one2oneRels { 325 doc := "" 326 if m.Doc != "" { 327 doc = " // " + m.Doc 328 } 329 g.printf("\t%s *%s%s\n", m.Name, m.Type, doc) 330 } 331 332 for _, m := range dt.one2manyRels { 333 doc := "" 334 if m.Doc != "" { 335 doc = " // " + m.Doc 336 } 337 g.printf("\t%s []*%s%s\n", m.Name, m.Type, doc) 338 } 339 340 for _, m := range dt.vecmbrs { 341 doc := "" 342 if m.Doc != "" { 343 doc = " // " + m.Doc 344 } 345 g.printf("\t%s []%s%s\n", m.Name, m.Type, doc) 346 } 347 g.printf("}\n\n") 348 349 return err 350 } 351 352 func (g *generator) genMembers(node *yaml.Node) ([]member, error) { 353 mbrs := make([]member, 0, len(node.Content)) 354 for _, elem := range node.Content { 355 mbr := g.genMember(strings.TrimSpace(elem.Value)) 356 mbr.Name = toTitle(mbr.Name) 357 mbrs = append(mbrs, mbr) 358 } 359 return mbrs, nil 360 } 361 362 func (g *generator) genMember(v string) member { 363 var mbr member 364 handleDoc := func(doc string) string { 365 return strings.TrimSpace(strings.Replace(doc, "//", "", 1)) 366 } 367 368 // remove spaces coming from builtins... 369 for _, cxx := range []struct { 370 k, v string 371 }{ 372 {"unsigned long long", "uint64"}, 373 {"unsigned long", "uint64"}, 374 {"long long", "int64"}, 375 {"unsigned int", "uint32"}, 376 {"unsigned", "uint32"}, 377 } { 378 if !strings.HasPrefix(v, cxx.k) { 379 continue 380 } 381 v = strings.Replace(v, cxx.k, cxx.v, 1) 382 } 383 384 for k, cxx := range builtins { 385 if !strings.HasPrefix(v, k) { 386 continue 387 } 388 mbr.Type = cxx 389 v = strings.TrimSpace(strings.Replace(v, k, "", 1)) 390 idx := strings.Index(v, " ") 391 mbr.Name = strings.TrimSpace(v[:idx]) 392 mbr.Doc = handleDoc(strings.TrimSpace(v[idx:])) 393 return mbr 394 } 395 396 if grps := stdArrayDocRe.FindStringSubmatch(v); grps != nil { 397 mbr.Type = fmt.Sprintf("[%s]%s", grps[2], g.genTypeName(grps[1])) 398 mbr.Name = grps[3] 399 mbr.Doc = handleDoc(grps[4]) 400 return mbr 401 } 402 403 idx := strings.Index(v, " ") 404 mbr.Type = g.genTypeName(strings.TrimSpace(v[:idx])) 405 v = strings.TrimSpace(v[idx:]) 406 idx = strings.Index(v, "//") 407 mbr.Name = strings.TrimSpace(v[:idx]) 408 mbr.Doc = handleDoc(strings.TrimSpace(v[idx:])) 409 410 return mbr 411 }