this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

all: move pkg/gen and internal/cmd/qgo to build-ignored files

These two are code generators which we need to be able to run.
One advantage of having them in their own package directories
used to be that gopls did not support build tags well at all,
so editing a file with `//go:build ignore` would show many errors.

In October 2022, gopls added built-in support for `//go:build ignore`,
since it's a very common pattern in cases like this.
We can now have these generators as standalone files.
Not only does that reduce the need for more directories,
but it also means `go install ./...` no longer installs the binaries
confusingly named `gen` and `qgo` in `go env GOBIN`.

While here, update the "generated by" comments in pkg source files,
as these files are no longer automatically generated since
https://cuelang.org/cl/536072, and the comment might confuse someone.
The `go run` example is also updated to use the standalone file.

Fixes #1869.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: I3976a613cc501ddaa47ad23afe0d5fd6df536267
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/552599
Reviewed-by: Marcel van Lohuizen <mpvl@gmail.com>
Unity-Result: CUEcueckoo <cueckoo@cuelang.org>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

+505 -502
+3 -1
internal/cmd/qgo/qgo.go pkg/qgo.go
··· 12 12 // See the License for the specific language governing permissions and 13 13 // limitations under the License. 14 14 15 + //go:build ignore 16 + 15 17 // qgo builds CUE builtin packages from Go packages. 16 18 package main 17 19 ··· 80 82 func main() { 81 83 flag.Parse() 82 84 83 - genLine = "// Generated with go run cuelang.org/go/internal/cmd/qgo " + strings.Join(os.Args[1:], " ") 85 + genLine = "// Originally generated with: go run qgo.go " + strings.Join(os.Args[1:], " ") 84 86 85 87 args := flag.Args() 86 88 if len(args) == 0 {
+3 -1
pkg/doc.go
··· 12 12 // See the License for the specific language governing permissions and 13 13 // limitations under the License. 14 14 15 - // Package pkg define CUE standard packages. 15 + // Package pkg defines CUE standard packages. 16 16 // 17 17 // Many of the standard packages are modeled after and generated from the Go 18 18 // core packages. The types, values, and functions are defined as their Go ··· 36 36 // in non-hermetic influences into configurations by using packages defined 37 37 // in the tool subdirectory. 38 38 package pkg 39 + 40 + //go:generate go run gen.go
+1 -1
pkg/encoding/hex/hex.go
··· 16 16 // Use of this source code is governed by a BSD-style 17 17 // license that can be found in the LICENSE file. 18 18 19 - // Generated with go run cuelang.org/go/internal/cmd/qgo -stripstr -exclude=Decode$,Encode$,EncodeToString,Dumper extract encoding/hex 19 + // Originally generated with: go run qgo.go -stripstr -exclude=Decode$,Encode$,EncodeToString,Dumper extract encoding/hex 20 20 21 21 package hex 22 22
+1 -1
pkg/encoding/json/json.go
··· 16 16 // Use of this source code is governed by a BSD-style 17 17 // license that can be found in the LICENSE file. 18 18 19 - // Generated with go run cuelang.org/go/internal/cmd/qgo -exclude=Compact,Indent,arshal$ extract encoding/json 19 + // Originally generated with: go run qgo.go -exclude=Compact,Indent,arshal$ extract encoding/json 20 20 21 21 package json 22 22
+491 -2
pkg/gen.go
··· 1 - package pkg 1 + // Copyright 2018 The CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + //go:build ignore 16 + 17 + // gen.go generates the pkg.go files inside the packages under the pkg directory. 18 + // 19 + // It takes the list of packages from the packages.txt. 20 + // 21 + // Be sure to also update an entry in pkg/pkg.go, if so desired. 22 + package main 23 + 24 + // TODO generate ../register.go too. 25 + 26 + import ( 27 + "bytes" 28 + _ "embed" 29 + "flag" 30 + "fmt" 31 + "go/ast" 32 + gobuild "go/build" 33 + "go/constant" 34 + "go/format" 35 + "go/parser" 36 + "go/printer" 37 + "go/token" 38 + "io/ioutil" 39 + "log" 40 + "math/big" 41 + "os" 42 + "path" 43 + "path/filepath" 44 + "strings" 45 + "text/template" 46 + 47 + "cuelang.org/go/cue" 48 + "cuelang.org/go/cue/build" 49 + "cuelang.org/go/cue/errors" 50 + cueformat "cuelang.org/go/cue/format" 51 + "cuelang.org/go/internal" 52 + "cuelang.org/go/internal/core/runtime" 53 + ) 54 + 55 + const genFile = "pkg.go" 56 + 57 + //go:embed packages.txt 58 + var packagesStr string 59 + 60 + var packages = strings.Fields(packagesStr) 61 + 62 + type headerParams struct { 63 + GoPkg string 64 + CUEPkg string 65 + 66 + PackageDoc string 67 + PackageDefs string 68 + } 69 + 70 + var header = template.Must(template.New("").Parse( 71 + `// Code generated by cuelang.org/go/pkg/gen. DO NOT EDIT. 72 + 73 + {{if .PackageDoc}} 74 + {{.PackageDoc -}} 75 + // {{.PackageDefs}} 76 + {{end -}} 77 + package {{.GoPkg}} 78 + 79 + {{if .CUEPkg -}} 80 + import ( 81 + "cuelang.org/go/internal/core/adt" 82 + "cuelang.org/go/pkg/internal" 83 + ) 84 + 85 + func init() { 86 + internal.Register({{printf "%q" .CUEPkg}}, pkg) 87 + } 88 + 89 + var _ = adt.TopKind // in case the adt package isn't used 90 + {{end}} 91 + `)) 92 + 93 + func main() { 94 + flag.Parse() 95 + log.SetFlags(log.Lshortfile) 96 + log.SetOutput(os.Stdout) 97 + 98 + for _, pkg := range packages { 99 + if pkg == "path" { 100 + // TODO remove this special case. Currently the path 101 + // pkg.go file cannot be generated automatically but that 102 + // will be possible when we can attach arbitrary signatures 103 + // to builtin functions. 104 + continue 105 + } 106 + if err := generate(pkg); err != nil { 107 + log.Fatalf("%s: %v", pkg, err) 108 + } 109 + } 110 + } 111 + 112 + type generator struct { 113 + dir string 114 + w *bytes.Buffer 115 + cuePkgPath string 116 + fset *token.FileSet 117 + first bool 118 + } 119 + 120 + func generate(cuePkgPath string) error { 121 + goPkgPath := path.Join("cuelang.org/go/pkg", cuePkgPath) 122 + pkg, err := gobuild.Import(goPkgPath, "", 0) 123 + if err != nil { 124 + return err 125 + } 126 + 127 + g := generator{ 128 + dir: pkg.Dir, 129 + cuePkgPath: cuePkgPath, 130 + w: &bytes.Buffer{}, 131 + fset: token.NewFileSet(), 132 + } 133 + 134 + params := headerParams{ 135 + GoPkg: pkg.Name, 136 + CUEPkg: cuePkgPath, 137 + } 138 + // As a special case, the "tool" package cannot be imported from CUE. 139 + skipRegister := params.CUEPkg == "tool" 140 + if skipRegister { 141 + params.CUEPkg = "" 142 + } 143 + 144 + if doc, err := os.ReadFile(filepath.Join(pkg.Dir, "doc.txt")); err == nil { 145 + defs, err := os.ReadFile(filepath.Join(pkg.Dir, pkg.Name+".cue")) 146 + if err != nil { 147 + return err 148 + } 149 + i := bytes.Index(defs, []byte("package "+pkg.Name)) 150 + defs = defs[i+len("package "+pkg.Name)+1:] 151 + defs = bytes.TrimRight(defs, "\n") 152 + defs = bytes.ReplaceAll(defs, []byte("\n"), []byte("\n//\t")) 153 + params.PackageDoc = string(doc) 154 + params.PackageDefs = string(defs) 155 + } 156 + 157 + if err := header.Execute(g.w, params); err != nil { 158 + return err 159 + } 160 + 161 + if !skipRegister { 162 + fmt.Fprintf(g.w, "var pkg = &internal.Package{\nNative: []*internal.Builtin{") 163 + g.first = true 164 + for _, filename := range pkg.GoFiles { 165 + if filename == genFile { 166 + continue 167 + } 168 + g.processGo(filepath.Join(pkg.Dir, filename)) 169 + } 170 + fmt.Fprintf(g.w, "},\n") 171 + if err := g.processCUE(); err != nil { 172 + return err 173 + } 174 + fmt.Fprintf(g.w, "}\n") 175 + } 176 + 177 + b, err := format.Source(g.w.Bytes()) 178 + if err != nil { 179 + b = g.w.Bytes() // write the unformatted source 180 + } 181 + 182 + filename := filepath.Join(pkg.Dir, genFile) 183 + 184 + if err := ioutil.WriteFile(filename, b, 0666); err != nil { 185 + return err 186 + } 187 + return nil 188 + } 189 + 190 + func (g *generator) sep() { 191 + if g.first { 192 + g.first = false 193 + return 194 + } 195 + fmt.Fprint(g.w, ", ") 196 + } 197 + 198 + // processCUE mixes in CUE definitions defined in the package directory. 199 + func (g *generator) processCUE() error { 200 + // Note: we avoid using the cue/load and the cuecontext packages 201 + // because they depend on the standard library which is what this 202 + // command is generating - cyclic dependencies are undesirable in general. 203 + ctx := newContext() 204 + val, err := loadCUEPackage(ctx, g.dir, g.cuePkgPath) 205 + if err != nil { 206 + if errors.Is(err, errNoCUEFiles) { 207 + return nil 208 + } 209 + errors.Print(os.Stderr, err, nil) 210 + return fmt.Errorf("error processing %s: %v", g.cuePkgPath, err) 211 + } 212 + 213 + v := val.Syntax(cue.Raw()) 214 + // fmt.Printf("%T\n", v) 215 + // fmt.Println(astinternal.DebugStr(v)) 216 + n := internal.ToExpr(v) 217 + b, err := cueformat.Node(n) 218 + if err != nil { 219 + return err 220 + } 221 + b = bytes.ReplaceAll(b, []byte("\n\n"), []byte("\n")) 222 + // body = strings.ReplaceAll(body, "\t", "") 223 + // TODO: escape backtick 224 + fmt.Fprintf(g.w, "CUE: `%s`,\n", string(b)) 225 + return nil 226 + } 227 + 228 + func (g *generator) processGo(filename string) error { 229 + f, err := parser.ParseFile(g.fset, filename, nil, parser.ParseComments) 230 + if err != nil { 231 + return err 232 + } 233 + 234 + for _, d := range f.Decls { 235 + switch x := d.(type) { 236 + case *ast.GenDecl: 237 + switch x.Tok { 238 + case token.CONST: 239 + for _, spec := range x.Specs { 240 + spec := spec.(*ast.ValueSpec) 241 + if ast.IsExported(spec.Names[0].Name) { 242 + g.genConst(spec) 243 + } 244 + } 245 + case token.VAR: 246 + continue 247 + case token.TYPE: 248 + // TODO: support type declarations. 249 + continue 250 + case token.IMPORT: 251 + continue 252 + default: 253 + panic(fmt.Errorf("gen %s: unexpected spec of type %s", filename, x.Tok)) 254 + } 255 + case *ast.FuncDecl: 256 + g.genFunc(x) 257 + } 258 + } 259 + return nil 260 + } 261 + 262 + func (g *generator) genConst(spec *ast.ValueSpec) { 263 + name := spec.Names[0].Name 264 + value := "" 265 + switch v := g.toValue(spec.Values[0]); v.Kind() { 266 + case constant.Bool, constant.Int, constant.String: 267 + // TODO: convert octal numbers 268 + value = v.ExactString() 269 + case constant.Float: 270 + var rat big.Rat 271 + rat.SetString(v.ExactString()) 272 + var float big.Float 273 + float.SetRat(&rat) 274 + value = float.Text('g', -1) 275 + default: 276 + fmt.Printf("Dropped entry %s.%s (%T: %v)\n", g.cuePkgPath, name, v.Kind(), v.ExactString()) 277 + return 278 + } 279 + g.sep() 280 + fmt.Fprintf(g.w, "{\nName: %q,\n Const: %q,\n}", name, value) 281 + } 282 + 283 + func (g *generator) toValue(x ast.Expr) constant.Value { 284 + switch x := x.(type) { 285 + case *ast.BasicLit: 286 + return constant.MakeFromLiteral(x.Value, x.Kind, 0) 287 + case *ast.BinaryExpr: 288 + return constant.BinaryOp(g.toValue(x.X), x.Op, g.toValue(x.Y)) 289 + case *ast.UnaryExpr: 290 + return constant.UnaryOp(x.Op, g.toValue(x.X), 0) 291 + default: 292 + panic(fmt.Errorf("%s: unsupported expression type %T: %#v", g.cuePkgPath, x, x)) 293 + } 294 + } 295 + 296 + func (g *generator) genFunc(x *ast.FuncDecl) { 297 + if x.Body == nil || !ast.IsExported(x.Name.Name) { 298 + return 299 + } 300 + types := []string{} 301 + if x.Type.Results != nil { 302 + for _, f := range x.Type.Results.List { 303 + if len(f.Names) > 0 { 304 + for range f.Names { 305 + types = append(types, g.goKind(f.Type)) 306 + } 307 + } else { 308 + types = append(types, g.goKind(f.Type)) 309 + } 310 + } 311 + } 312 + if x.Recv != nil { 313 + return 314 + } 315 + if n := len(types); n != 1 && (n != 2 || types[1] != "error") { 316 + fmt.Printf("Dropped func %s.%s: must have one return value or a value and an error %v\n", g.cuePkgPath, x.Name.Name, types) 317 + return 318 + } 319 + 320 + g.sep() 321 + fmt.Fprintf(g.w, "{\n") 322 + defer fmt.Fprintf(g.w, "}") 323 + 324 + fmt.Fprintf(g.w, "Name: %q,\n", x.Name.Name) 325 + 326 + args := []string{} 327 + vals := []string{} 328 + kind := []string{} 329 + for _, f := range x.Type.Params.List { 330 + for _, name := range f.Names { 331 + typ := strings.Title(g.goKind(f.Type)) 332 + argKind := g.goToCUE(f.Type) 333 + vals = append(vals, fmt.Sprintf("c.%s(%d)", typ, len(args))) 334 + args = append(args, name.Name) 335 + kind = append(kind, argKind) 336 + } 337 + } 338 + 339 + fmt.Fprintf(g.w, "Params: []internal.Param{\n") 340 + for _, k := range kind { 341 + fmt.Fprintf(g.w, "{Kind: %s},\n", k) 342 + } 343 + fmt.Fprintf(g.w, "\n},\n") 344 + 345 + expr := x.Type.Results.List[0].Type 346 + fmt.Fprintf(g.w, "Result: %s,\n", g.goToCUE(expr)) 347 + 348 + argList := strings.Join(args, ", ") 349 + valList := strings.Join(vals, ", ") 350 + init := "" 351 + if len(args) > 0 { 352 + init = fmt.Sprintf("%s := %s", argList, valList) 353 + } 2 354 3 - //go:generate go run ./gen 355 + fmt.Fprintf(g.w, "Func: func(c *internal.CallCtxt) {") 356 + defer fmt.Fprintln(g.w, "},") 357 + fmt.Fprintln(g.w) 358 + if init != "" { 359 + fmt.Fprintln(g.w, init) 360 + } 361 + fmt.Fprintln(g.w, "if c.Do() {") 362 + defer fmt.Fprintln(g.w, "}") 363 + if len(types) == 1 { 364 + fmt.Fprintf(g.w, "c.Ret = %s(%s)", x.Name.Name, argList) 365 + } else { 366 + fmt.Fprintf(g.w, "c.Ret, c.Err = %s(%s)", x.Name.Name, argList) 367 + } 368 + } 369 + 370 + func (g *generator) goKind(expr ast.Expr) string { 371 + if star, isStar := expr.(*ast.StarExpr); isStar { 372 + expr = star.X 373 + } 374 + w := &bytes.Buffer{} 375 + printer.Fprint(w, g.fset, expr) 376 + switch str := w.String(); str { 377 + case "big.Int": 378 + return "bigInt" 379 + case "big.Float": 380 + return "bigFloat" 381 + case "big.Rat": 382 + return "bigRat" 383 + case "adt.Bottom": 384 + return "error" 385 + case "internal.Decimal": 386 + return "decimal" 387 + case "internal.List": 388 + return "cueList" 389 + case "internal.Struct": 390 + return "struct" 391 + case "[]*internal.Decimal": 392 + return "decimalList" 393 + case "cue.Value": 394 + return "value" 395 + case "cue.List": 396 + return "list" 397 + case "[]string": 398 + return "stringList" 399 + case "[]byte": 400 + return "bytes" 401 + case "[]cue.Value": 402 + return "list" 403 + case "io.Reader": 404 + return "reader" 405 + case "time.Time": 406 + return "string" 407 + default: 408 + return str 409 + } 410 + } 411 + 412 + func (g *generator) goToCUE(expr ast.Expr) (cueKind string) { 413 + // TODO: detect list and structs types for return values. 414 + switch k := g.goKind(expr); k { 415 + case "error": 416 + cueKind += "adt.BottomKind" 417 + case "bool": 418 + cueKind += "adt.BoolKind" 419 + case "bytes", "reader": 420 + cueKind += "adt.BytesKind|adt.StringKind" 421 + case "string": 422 + cueKind += "adt.StringKind" 423 + case "int", "int8", "int16", "int32", "rune", "int64", 424 + "uint", "byte", "uint8", "uint16", "uint32", "uint64", 425 + "bigInt": 426 + cueKind += "adt.IntKind" 427 + case "float64", "bigRat", "bigFloat", "decimal": 428 + cueKind += "adt.NumKind" 429 + case "list", "decimalList", "stringList", "cueList": 430 + cueKind += "adt.ListKind" 431 + case "struct": 432 + cueKind += "adt.StructKind" 433 + case "value": 434 + // Must use callCtxt.value method for these types and resolve manually. 435 + cueKind += "adt.TopKind" // TODO: can be more precise 436 + default: 437 + switch { 438 + case strings.HasPrefix(k, "[]"): 439 + cueKind += "adt.ListKind" 440 + case strings.HasPrefix(k, "map["): 441 + cueKind += "adt.StructKind" 442 + default: 443 + // log.Println("Unknown type:", k) 444 + // Must use callCtxt.value method for these types and resolve manually. 445 + cueKind += "adt.TopKind" // TODO: can be more precise 446 + } 447 + } 448 + return cueKind 449 + } 450 + 451 + var errNoCUEFiles = errors.New("no CUE files in directory") 452 + 453 + // loadCUEPackage loads a CUE package as a value. We avoid using cue/load because 454 + // that depends on the standard library and as this generator is generating the standard 455 + // library, we don't want that cyclic dependency. 456 + // It only has to deal with the fairly limited subset of CUE packages that are 457 + // present inside pkg/.... 458 + func loadCUEPackage(ctx *cue.Context, dir string, pkgPath string) (cue.Value, error) { 459 + inst := &build.Instance{ 460 + PkgName: path.Base(pkgPath), 461 + Dir: dir, 462 + DisplayPath: pkgPath, 463 + ImportPath: pkgPath, 464 + } 465 + cuefiles, err := filepath.Glob(filepath.Join(dir, "*.cue")) 466 + if err != nil { 467 + return cue.Value{}, err 468 + } 469 + if len(cuefiles) == 0 { 470 + return cue.Value{}, errNoCUEFiles 471 + } 472 + for _, file := range cuefiles { 473 + if err := inst.AddFile(file, nil); err != nil { 474 + return cue.Value{}, err 475 + } 476 + } 477 + if err := inst.Complete(); err != nil { 478 + return cue.Value{}, err 479 + } 480 + vals, err := ctx.BuildInstances([]*build.Instance{inst}) 481 + if err != nil { 482 + return cue.Value{}, err 483 + } 484 + return vals[0], nil 485 + } 486 + 487 + // Avoid using cuecontext.New because that package imports 488 + // the entire stdlib which we are generating. 489 + func newContext() *cue.Context { 490 + r := runtime.New() 491 + return (*cue.Context)(r) 492 + }
-490
pkg/gen/gen.go
··· 1 - // Copyright 2018 The CUE Authors 2 - // 3 - // Licensed under the Apache License, Version 2.0 (the "License"); 4 - // you may not use this file except in compliance with the License. 5 - // You may obtain a copy of the License at 6 - // 7 - // http://www.apache.org/licenses/LICENSE-2.0 8 - // 9 - // Unless required by applicable law or agreed to in writing, software 10 - // distributed under the License is distributed on an "AS IS" BASIS, 11 - // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 - // See the License for the specific language governing permissions and 13 - // limitations under the License. 14 - 15 - // Package gen generates the pkg.go files inside the packages under the pkg directory. 16 - // 17 - // It takes the list of packages from the packages.txt. 18 - // 19 - // Be sure to also update an entry in pkg/pkg.go, if so desired. 20 - package main 21 - 22 - // TODO generate ../register.go too. 23 - 24 - import ( 25 - "bytes" 26 - _ "embed" 27 - "flag" 28 - "fmt" 29 - "go/ast" 30 - gobuild "go/build" 31 - "go/constant" 32 - "go/format" 33 - "go/parser" 34 - "go/printer" 35 - "go/token" 36 - "io/ioutil" 37 - "log" 38 - "math/big" 39 - "os" 40 - "path" 41 - "path/filepath" 42 - "strings" 43 - "text/template" 44 - 45 - "cuelang.org/go/cue" 46 - "cuelang.org/go/cue/build" 47 - "cuelang.org/go/cue/errors" 48 - cueformat "cuelang.org/go/cue/format" 49 - "cuelang.org/go/internal" 50 - "cuelang.org/go/internal/core/runtime" 51 - ) 52 - 53 - const genFile = "pkg.go" 54 - 55 - //go:embed packages.txt 56 - var packagesStr string 57 - 58 - var packages = strings.Fields(packagesStr) 59 - 60 - type headerParams struct { 61 - GoPkg string 62 - CUEPkg string 63 - 64 - PackageDoc string 65 - PackageDefs string 66 - } 67 - 68 - var header = template.Must(template.New("").Parse( 69 - `// Code generated by cuelang.org/go/pkg/gen. DO NOT EDIT. 70 - 71 - {{if .PackageDoc}} 72 - {{.PackageDoc -}} 73 - // {{.PackageDefs}} 74 - {{end -}} 75 - package {{.GoPkg}} 76 - 77 - {{if .CUEPkg -}} 78 - import ( 79 - "cuelang.org/go/internal/core/adt" 80 - "cuelang.org/go/pkg/internal" 81 - ) 82 - 83 - func init() { 84 - internal.Register({{printf "%q" .CUEPkg}}, pkg) 85 - } 86 - 87 - var _ = adt.TopKind // in case the adt package isn't used 88 - {{end}} 89 - `)) 90 - 91 - func main() { 92 - flag.Parse() 93 - log.SetFlags(log.Lshortfile) 94 - log.SetOutput(os.Stdout) 95 - 96 - for _, pkg := range packages { 97 - if pkg == "path" { 98 - // TODO remove this special case. Currently the path 99 - // pkg.go file cannot be generated automatically but that 100 - // will be possible when we can attach arbitrary signatures 101 - // to builtin functions. 102 - continue 103 - } 104 - if err := generate(pkg); err != nil { 105 - log.Fatalf("%s: %v", pkg, err) 106 - } 107 - } 108 - } 109 - 110 - type generator struct { 111 - dir string 112 - w *bytes.Buffer 113 - cuePkgPath string 114 - fset *token.FileSet 115 - first bool 116 - } 117 - 118 - func generate(cuePkgPath string) error { 119 - goPkgPath := path.Join("cuelang.org/go/pkg", cuePkgPath) 120 - pkg, err := gobuild.Import(goPkgPath, "", 0) 121 - if err != nil { 122 - return err 123 - } 124 - 125 - g := generator{ 126 - dir: pkg.Dir, 127 - cuePkgPath: cuePkgPath, 128 - w: &bytes.Buffer{}, 129 - fset: token.NewFileSet(), 130 - } 131 - 132 - params := headerParams{ 133 - GoPkg: pkg.Name, 134 - CUEPkg: cuePkgPath, 135 - } 136 - // As a special case, the "tool" package cannot be imported from CUE. 137 - skipRegister := params.CUEPkg == "tool" 138 - if skipRegister { 139 - params.CUEPkg = "" 140 - } 141 - 142 - if doc, err := os.ReadFile(filepath.Join(pkg.Dir, "doc.txt")); err == nil { 143 - defs, err := os.ReadFile(filepath.Join(pkg.Dir, pkg.Name+".cue")) 144 - if err != nil { 145 - return err 146 - } 147 - i := bytes.Index(defs, []byte("package "+pkg.Name)) 148 - defs = defs[i+len("package "+pkg.Name)+1:] 149 - defs = bytes.TrimRight(defs, "\n") 150 - defs = bytes.ReplaceAll(defs, []byte("\n"), []byte("\n//\t")) 151 - params.PackageDoc = string(doc) 152 - params.PackageDefs = string(defs) 153 - } 154 - 155 - if err := header.Execute(g.w, params); err != nil { 156 - return err 157 - } 158 - 159 - if !skipRegister { 160 - fmt.Fprintf(g.w, "var pkg = &internal.Package{\nNative: []*internal.Builtin{") 161 - g.first = true 162 - for _, filename := range pkg.GoFiles { 163 - if filename == genFile { 164 - continue 165 - } 166 - g.processGo(filepath.Join(pkg.Dir, filename)) 167 - } 168 - fmt.Fprintf(g.w, "},\n") 169 - if err := g.processCUE(); err != nil { 170 - return err 171 - } 172 - fmt.Fprintf(g.w, "}\n") 173 - } 174 - 175 - b, err := format.Source(g.w.Bytes()) 176 - if err != nil { 177 - b = g.w.Bytes() // write the unformatted source 178 - } 179 - 180 - filename := filepath.Join(pkg.Dir, genFile) 181 - 182 - if err := ioutil.WriteFile(filename, b, 0666); err != nil { 183 - return err 184 - } 185 - return nil 186 - } 187 - 188 - func (g *generator) sep() { 189 - if g.first { 190 - g.first = false 191 - return 192 - } 193 - fmt.Fprint(g.w, ", ") 194 - } 195 - 196 - // processCUE mixes in CUE definitions defined in the package directory. 197 - func (g *generator) processCUE() error { 198 - // Note: we avoid using the cue/load and the cuecontext packages 199 - // because they depend on the standard library which is what this 200 - // command is generating - cyclic dependencies are undesirable in general. 201 - ctx := newContext() 202 - val, err := loadCUEPackage(ctx, g.dir, g.cuePkgPath) 203 - if err != nil { 204 - if errors.Is(err, errNoCUEFiles) { 205 - return nil 206 - } 207 - errors.Print(os.Stderr, err, nil) 208 - return fmt.Errorf("error processing %s: %v", g.cuePkgPath, err) 209 - } 210 - 211 - v := val.Syntax(cue.Raw()) 212 - // fmt.Printf("%T\n", v) 213 - // fmt.Println(astinternal.DebugStr(v)) 214 - n := internal.ToExpr(v) 215 - b, err := cueformat.Node(n) 216 - if err != nil { 217 - return err 218 - } 219 - b = bytes.ReplaceAll(b, []byte("\n\n"), []byte("\n")) 220 - // body = strings.ReplaceAll(body, "\t", "") 221 - // TODO: escape backtick 222 - fmt.Fprintf(g.w, "CUE: `%s`,\n", string(b)) 223 - return nil 224 - } 225 - 226 - func (g *generator) processGo(filename string) error { 227 - f, err := parser.ParseFile(g.fset, filename, nil, parser.ParseComments) 228 - if err != nil { 229 - return err 230 - } 231 - 232 - for _, d := range f.Decls { 233 - switch x := d.(type) { 234 - case *ast.GenDecl: 235 - switch x.Tok { 236 - case token.CONST: 237 - for _, spec := range x.Specs { 238 - spec := spec.(*ast.ValueSpec) 239 - if ast.IsExported(spec.Names[0].Name) { 240 - g.genConst(spec) 241 - } 242 - } 243 - case token.VAR: 244 - continue 245 - case token.TYPE: 246 - // TODO: support type declarations. 247 - continue 248 - case token.IMPORT: 249 - continue 250 - default: 251 - panic(fmt.Errorf("gen %s: unexpected spec of type %s", filename, x.Tok)) 252 - } 253 - case *ast.FuncDecl: 254 - g.genFunc(x) 255 - } 256 - } 257 - return nil 258 - } 259 - 260 - func (g *generator) genConst(spec *ast.ValueSpec) { 261 - name := spec.Names[0].Name 262 - value := "" 263 - switch v := g.toValue(spec.Values[0]); v.Kind() { 264 - case constant.Bool, constant.Int, constant.String: 265 - // TODO: convert octal numbers 266 - value = v.ExactString() 267 - case constant.Float: 268 - var rat big.Rat 269 - rat.SetString(v.ExactString()) 270 - var float big.Float 271 - float.SetRat(&rat) 272 - value = float.Text('g', -1) 273 - default: 274 - fmt.Printf("Dropped entry %s.%s (%T: %v)\n", g.cuePkgPath, name, v.Kind(), v.ExactString()) 275 - return 276 - } 277 - g.sep() 278 - fmt.Fprintf(g.w, "{\nName: %q,\n Const: %q,\n}", name, value) 279 - } 280 - 281 - func (g *generator) toValue(x ast.Expr) constant.Value { 282 - switch x := x.(type) { 283 - case *ast.BasicLit: 284 - return constant.MakeFromLiteral(x.Value, x.Kind, 0) 285 - case *ast.BinaryExpr: 286 - return constant.BinaryOp(g.toValue(x.X), x.Op, g.toValue(x.Y)) 287 - case *ast.UnaryExpr: 288 - return constant.UnaryOp(x.Op, g.toValue(x.X), 0) 289 - default: 290 - panic(fmt.Errorf("%s: unsupported expression type %T: %#v", g.cuePkgPath, x, x)) 291 - } 292 - } 293 - 294 - func (g *generator) genFunc(x *ast.FuncDecl) { 295 - if x.Body == nil || !ast.IsExported(x.Name.Name) { 296 - return 297 - } 298 - types := []string{} 299 - if x.Type.Results != nil { 300 - for _, f := range x.Type.Results.List { 301 - if len(f.Names) > 0 { 302 - for range f.Names { 303 - types = append(types, g.goKind(f.Type)) 304 - } 305 - } else { 306 - types = append(types, g.goKind(f.Type)) 307 - } 308 - } 309 - } 310 - if x.Recv != nil { 311 - return 312 - } 313 - if n := len(types); n != 1 && (n != 2 || types[1] != "error") { 314 - fmt.Printf("Dropped func %s.%s: must have one return value or a value and an error %v\n", g.cuePkgPath, x.Name.Name, types) 315 - return 316 - } 317 - 318 - g.sep() 319 - fmt.Fprintf(g.w, "{\n") 320 - defer fmt.Fprintf(g.w, "}") 321 - 322 - fmt.Fprintf(g.w, "Name: %q,\n", x.Name.Name) 323 - 324 - args := []string{} 325 - vals := []string{} 326 - kind := []string{} 327 - for _, f := range x.Type.Params.List { 328 - for _, name := range f.Names { 329 - typ := strings.Title(g.goKind(f.Type)) 330 - argKind := g.goToCUE(f.Type) 331 - vals = append(vals, fmt.Sprintf("c.%s(%d)", typ, len(args))) 332 - args = append(args, name.Name) 333 - kind = append(kind, argKind) 334 - } 335 - } 336 - 337 - fmt.Fprintf(g.w, "Params: []internal.Param{\n") 338 - for _, k := range kind { 339 - fmt.Fprintf(g.w, "{Kind: %s},\n", k) 340 - } 341 - fmt.Fprintf(g.w, "\n},\n") 342 - 343 - expr := x.Type.Results.List[0].Type 344 - fmt.Fprintf(g.w, "Result: %s,\n", g.goToCUE(expr)) 345 - 346 - argList := strings.Join(args, ", ") 347 - valList := strings.Join(vals, ", ") 348 - init := "" 349 - if len(args) > 0 { 350 - init = fmt.Sprintf("%s := %s", argList, valList) 351 - } 352 - 353 - fmt.Fprintf(g.w, "Func: func(c *internal.CallCtxt) {") 354 - defer fmt.Fprintln(g.w, "},") 355 - fmt.Fprintln(g.w) 356 - if init != "" { 357 - fmt.Fprintln(g.w, init) 358 - } 359 - fmt.Fprintln(g.w, "if c.Do() {") 360 - defer fmt.Fprintln(g.w, "}") 361 - if len(types) == 1 { 362 - fmt.Fprintf(g.w, "c.Ret = %s(%s)", x.Name.Name, argList) 363 - } else { 364 - fmt.Fprintf(g.w, "c.Ret, c.Err = %s(%s)", x.Name.Name, argList) 365 - } 366 - } 367 - 368 - func (g *generator) goKind(expr ast.Expr) string { 369 - if star, isStar := expr.(*ast.StarExpr); isStar { 370 - expr = star.X 371 - } 372 - w := &bytes.Buffer{} 373 - printer.Fprint(w, g.fset, expr) 374 - switch str := w.String(); str { 375 - case "big.Int": 376 - return "bigInt" 377 - case "big.Float": 378 - return "bigFloat" 379 - case "big.Rat": 380 - return "bigRat" 381 - case "adt.Bottom": 382 - return "error" 383 - case "internal.Decimal": 384 - return "decimal" 385 - case "internal.List": 386 - return "cueList" 387 - case "internal.Struct": 388 - return "struct" 389 - case "[]*internal.Decimal": 390 - return "decimalList" 391 - case "cue.Value": 392 - return "value" 393 - case "cue.List": 394 - return "list" 395 - case "[]string": 396 - return "stringList" 397 - case "[]byte": 398 - return "bytes" 399 - case "[]cue.Value": 400 - return "list" 401 - case "io.Reader": 402 - return "reader" 403 - case "time.Time": 404 - return "string" 405 - default: 406 - return str 407 - } 408 - } 409 - 410 - func (g *generator) goToCUE(expr ast.Expr) (cueKind string) { 411 - // TODO: detect list and structs types for return values. 412 - switch k := g.goKind(expr); k { 413 - case "error": 414 - cueKind += "adt.BottomKind" 415 - case "bool": 416 - cueKind += "adt.BoolKind" 417 - case "bytes", "reader": 418 - cueKind += "adt.BytesKind|adt.StringKind" 419 - case "string": 420 - cueKind += "adt.StringKind" 421 - case "int", "int8", "int16", "int32", "rune", "int64", 422 - "uint", "byte", "uint8", "uint16", "uint32", "uint64", 423 - "bigInt": 424 - cueKind += "adt.IntKind" 425 - case "float64", "bigRat", "bigFloat", "decimal": 426 - cueKind += "adt.NumKind" 427 - case "list", "decimalList", "stringList", "cueList": 428 - cueKind += "adt.ListKind" 429 - case "struct": 430 - cueKind += "adt.StructKind" 431 - case "value": 432 - // Must use callCtxt.value method for these types and resolve manually. 433 - cueKind += "adt.TopKind" // TODO: can be more precise 434 - default: 435 - switch { 436 - case strings.HasPrefix(k, "[]"): 437 - cueKind += "adt.ListKind" 438 - case strings.HasPrefix(k, "map["): 439 - cueKind += "adt.StructKind" 440 - default: 441 - // log.Println("Unknown type:", k) 442 - // Must use callCtxt.value method for these types and resolve manually. 443 - cueKind += "adt.TopKind" // TODO: can be more precise 444 - } 445 - } 446 - return cueKind 447 - } 448 - 449 - var errNoCUEFiles = errors.New("no CUE files in directory") 450 - 451 - // loadCUEPackage loads a CUE package as a value. We avoid using cue/load because 452 - // that depends on the standard library and as this generator is generating the standard 453 - // library, we don't want that cyclic dependency. 454 - // It only has to deal with the fairly limited subset of CUE packages that are 455 - // present inside pkg/.... 456 - func loadCUEPackage(ctx *cue.Context, dir string, pkgPath string) (cue.Value, error) { 457 - inst := &build.Instance{ 458 - PkgName: path.Base(pkgPath), 459 - Dir: dir, 460 - DisplayPath: pkgPath, 461 - ImportPath: pkgPath, 462 - } 463 - cuefiles, err := filepath.Glob(filepath.Join(dir, "*.cue")) 464 - if err != nil { 465 - return cue.Value{}, err 466 - } 467 - if len(cuefiles) == 0 { 468 - return cue.Value{}, errNoCUEFiles 469 - } 470 - for _, file := range cuefiles { 471 - if err := inst.AddFile(file, nil); err != nil { 472 - return cue.Value{}, err 473 - } 474 - } 475 - if err := inst.Complete(); err != nil { 476 - return cue.Value{}, err 477 - } 478 - vals, err := ctx.BuildInstances([]*build.Instance{inst}) 479 - if err != nil { 480 - return cue.Value{}, err 481 - } 482 - return vals[0], nil 483 - } 484 - 485 - // Avoid using cuecontext.New because that package imports 486 - // the entire stdlib which we are generating. 487 - func newContext() *cue.Context { 488 - r := runtime.New() 489 - return (*cue.Context)(r) 490 - }
pkg/gen/packages.txt pkg/packages.txt
+1 -1
pkg/html/html.go
··· 16 16 // Use of this source code is governed by a BSD-style 17 17 // license that can be found in the LICENSE file. 18 18 19 - // Generated with go run cuelang.org/go/internal/cmd/qgo -stripstr extract html 19 + // Originally generated with: go run qgo.go -stripstr extract html 20 20 21 21 package html 22 22
+1 -1
pkg/math/big.go
··· 16 16 // Use of this source code is governed by a BSD-style 17 17 // license that can be found in the LICENSE file. 18 18 19 - // Generated with go run cuelang.org/go/internal/cmd/qgo -exclude= extract math/big 19 + // Originally generated with: go run qgo.go -exclude= extract math/big 20 20 21 21 package math 22 22
+1 -1
pkg/regexp/regexp.go
··· 16 16 // Use of this source code is governed by a BSD-style 17 17 // license that can be found in the LICENSE file. 18 18 19 - // Generated with go run cuelang.org/go/internal/cmd/qgo -exclude=Compile,Append,Reader,Match$ -stripstr extract regexp 19 + // Originally generated with: go run qgo.go -exclude=Compile,Append,Reader,Match$ -stripstr extract regexp 20 20 21 21 package regexp 22 22
+1 -1
pkg/strconv/strconv.go
··· 16 16 // Use of this source code is governed by a BSD-style 17 17 // license that can be found in the LICENSE file. 18 18 19 - // Generated with go run cuelang.org/go/internal/cmd/qgo -exclude=Append,Unquote,Itoa,CanBackquote,FormatComplex extract strconv 19 + // Originally generated with: go run qgo.go -exclude=Append,Unquote,Itoa,CanBackquote,FormatComplex extract strconv 20 20 21 21 package strconv 22 22
+1 -1
pkg/strings/strings.go
··· 16 16 // Use of this source code is governed by a BSD-style 17 17 // license that can be found in the LICENSE file. 18 18 19 - // Generated with go run cuelang.org/go/internal/cmd/qgo -exclude=Rune$,Func$,^Map$,Special$,EqualFold,Byte,Title$,ToValidUTF8,All$ extract strings 19 + // Originally generated with: go run qgo.go -exclude=Rune$,Func$,^Map$,Special$,EqualFold,Byte,Title$,ToValidUTF8,All$ extract strings 20 20 21 21 package strings 22 22
+1 -1
pkg/text/template/template.go
··· 16 16 // Use of this source code is governed by a BSD-style 17 17 // license that can be found in the LICENSE file. 18 18 19 - // Generated with go run cuelang.org/go/internal/cmd/qgo -exclude=Escaper$,Must,Parse -stripstr extract text/template 19 + // Originally generated with: go run qgo.go -exclude=Escaper$,Must,Parse -stripstr extract text/template 20 20 21 21 package template 22 22