github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/internal/core/export/export.go (about) 1 // Copyright 2020 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 export 16 17 import ( 18 "fmt" 19 "math/rand" 20 21 "github.com/joomcode/cue/cue/ast" 22 "github.com/joomcode/cue/cue/ast/astutil" 23 "github.com/joomcode/cue/cue/errors" 24 "github.com/joomcode/cue/internal" 25 "github.com/joomcode/cue/internal/core/adt" 26 "github.com/joomcode/cue/internal/core/eval" 27 "github.com/joomcode/cue/internal/core/walk" 28 ) 29 30 const debug = false 31 32 type Profile struct { 33 Simplify bool 34 35 // Final reports incomplete errors as errors. 36 Final bool 37 38 // TakeDefaults is used in Value mode to drop non-default values. 39 TakeDefaults bool 40 41 ShowOptional bool 42 ShowDefinitions bool 43 44 // ShowHidden forces the inclusion of hidden fields when these would 45 // otherwise be omitted. Only hidden fields from the current package are 46 // included. 47 ShowHidden bool 48 ShowDocs bool 49 ShowAttributes bool 50 51 // ShowErrors treats errors as values and will not percolate errors up. 52 // 53 // TODO: convert this option to an error level instead, showing only 54 // errors below a certain severity. 55 ShowErrors bool 56 57 // Use unevaluated conjuncts for these error types 58 // IgnoreRecursive 59 60 // TODO: recurse over entire tree to determine transitive closure 61 // of what needs to be printed. 62 // IncludeDependencies bool 63 } 64 65 var Simplified = &Profile{ 66 Simplify: true, 67 ShowDocs: true, 68 } 69 70 var Final = &Profile{ 71 Simplify: true, 72 TakeDefaults: true, 73 Final: true, 74 } 75 76 var Raw = &Profile{ 77 ShowOptional: true, 78 ShowDefinitions: true, 79 ShowHidden: true, 80 ShowDocs: true, 81 } 82 83 var All = &Profile{ 84 Simplify: true, 85 ShowOptional: true, 86 ShowDefinitions: true, 87 ShowHidden: true, 88 ShowDocs: true, 89 ShowAttributes: true, 90 } 91 92 // Concrete 93 94 // Def exports v as a definition. 95 func Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) { 96 return All.Def(r, pkgID, v) 97 } 98 99 // Def exports v as a definition. 100 func (p *Profile) Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) { 101 e := newExporter(p, r, pkgID, v) 102 e.markUsedFeatures(v) 103 104 isDef := v.IsRecursivelyClosed() 105 if isDef { 106 e.inDefinition++ 107 } 108 109 expr := e.expr(v) 110 111 if isDef { 112 e.inDefinition-- 113 if v.Kind() == adt.StructKind { 114 expr = ast.NewStruct( 115 ast.Embed(ast.NewIdent("_#def")), 116 ast.NewIdent("_#def"), expr, 117 ) 118 } 119 } 120 return e.toFile(v, expr) 121 } 122 123 func Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) { 124 return Simplified.Expr(r, pkgID, n) 125 } 126 127 func (p *Profile) Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) { 128 e := newExporter(p, r, pkgID, nil) 129 e.markUsedFeatures(n) 130 131 return e.expr(n), nil 132 } 133 134 func (e *exporter) toFile(v *adt.Vertex, x ast.Expr) (*ast.File, errors.Error) { 135 f := &ast.File{} 136 137 pkgName := "" 138 pkg := &ast.Package{} 139 for _, c := range v.Conjuncts { 140 f, _ := c.Source().(*ast.File) 141 if f == nil { 142 continue 143 } 144 145 if _, name, _ := internal.PackageInfo(f); name != "" { 146 pkgName = name 147 } 148 149 if e.cfg.ShowDocs { 150 if doc := internal.FileComment(f); doc != nil { 151 ast.AddComment(pkg, doc) 152 } 153 } 154 } 155 156 if pkgName != "" { 157 pkg.Name = ast.NewIdent(pkgName) 158 f.Decls = append(f.Decls, pkg) 159 } 160 161 switch st := x.(type) { 162 case nil: 163 panic("null input") 164 165 case *ast.StructLit: 166 f.Decls = append(f.Decls, st.Elts...) 167 168 default: 169 f.Decls = append(f.Decls, &ast.EmbedDecl{Expr: x}) 170 } 171 if err := astutil.Sanitize(f); err != nil { 172 err := errors.Promote(err, "export") 173 return f, errors.Append(e.errs, err) 174 } 175 176 return f, nil 177 } 178 179 // File 180 181 func Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) { 182 return Simplified.Vertex(r, pkgID, n) 183 } 184 185 func (p *Profile) Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) { 186 e := exporter{ 187 ctx: eval.NewContext(r, nil), 188 cfg: p, 189 index: r, 190 pkgID: pkgID, 191 } 192 e.markUsedFeatures(n) 193 v := e.value(n, n.Conjuncts...) 194 195 return e.toFile(n, v) 196 } 197 198 func Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) { 199 return Simplified.Value(r, pkgID, n) 200 } 201 202 // Should take context. 203 func (p *Profile) Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) { 204 e := exporter{ 205 ctx: eval.NewContext(r, nil), 206 cfg: p, 207 index: r, 208 pkgID: pkgID, 209 } 210 e.markUsedFeatures(n) 211 v := e.value(n) 212 return v, e.errs 213 } 214 215 type exporter struct { 216 cfg *Profile // Make value todo 217 errs errors.Error 218 219 ctx *adt.OpContext 220 221 index adt.StringIndexer 222 rand *rand.Rand 223 224 // For resolving references. 225 stack []frame 226 227 inDefinition int // for close() wrapping. 228 229 // hidden label handling 230 pkgID string 231 hidden map[string]adt.Feature // adt.InvalidFeatures means more than one. 232 233 // If a used feature maps to an expression, it means it is assigned to a 234 // unique let expression. 235 usedFeature map[adt.Feature]adt.Expr 236 labelAlias map[adt.Expr]adt.Feature 237 valueAlias map[*ast.Alias]*ast.Alias 238 letAlias map[*ast.LetClause]*ast.LetClause 239 240 usedHidden map[string]bool 241 } 242 243 func newExporter(p *Profile, r adt.Runtime, pkgID string, v *adt.Vertex) *exporter { 244 return &exporter{ 245 cfg: p, 246 ctx: eval.NewContext(r, v), 247 index: r, 248 pkgID: pkgID, 249 } 250 } 251 252 func (e *exporter) markUsedFeatures(x adt.Expr) { 253 e.usedFeature = make(map[adt.Feature]adt.Expr) 254 255 w := &walk.Visitor{} 256 w.Before = func(n adt.Node) bool { 257 switch x := n.(type) { 258 case *adt.Vertex: 259 if !x.IsData() { 260 for _, c := range x.Conjuncts { 261 w.Elem(c.Elem()) 262 } 263 } 264 265 case *adt.DynamicReference: 266 if e.labelAlias == nil { 267 e.labelAlias = make(map[adt.Expr]adt.Feature) 268 } 269 // TODO: add preferred label. 270 e.labelAlias[x.Label] = adt.InvalidLabel 271 272 case *adt.LabelReference: 273 } 274 return true 275 } 276 277 w.Feature = func(f adt.Feature, src adt.Node) { 278 _, ok := e.usedFeature[f] 279 280 switch x := src.(type) { 281 case *adt.LetReference: 282 if !ok { 283 e.usedFeature[f] = x.X 284 } 285 286 default: 287 e.usedFeature[f] = nil 288 } 289 } 290 291 w.Elem(x) 292 } 293 294 func (e *exporter) getFieldAlias(f *ast.Field, name string) string { 295 a, ok := f.Label.(*ast.Alias) 296 if !ok { 297 a = &ast.Alias{ 298 Ident: ast.NewIdent(e.uniqueAlias(name)), 299 Expr: f.Label.(ast.Expr), 300 } 301 f.Label = a 302 } 303 return a.Ident.Name 304 } 305 306 func setFieldAlias(f *ast.Field, name string) { 307 if _, ok := f.Label.(*ast.Alias); !ok { 308 f.Label = &ast.Alias{ 309 Ident: ast.NewIdent(name), 310 Expr: f.Label.(ast.Expr), 311 } 312 } 313 } 314 315 func (e *exporter) markLets(n ast.Node) { 316 if n == nil { 317 return 318 } 319 ast.Walk(n, func(n ast.Node) bool { 320 switch v := n.(type) { 321 case *ast.StructLit: 322 e.markLetDecls(v.Elts) 323 case *ast.File: 324 e.markLetDecls(v.Decls) 325 326 case *ast.Field, 327 *ast.LetClause, 328 *ast.IfClause, 329 *ast.ForClause, 330 *ast.Comprehension: 331 return false 332 } 333 return true 334 }, nil) 335 } 336 337 func (e *exporter) markLetDecls(decls []ast.Decl) { 338 for _, d := range decls { 339 if let, ok := d.(*ast.LetClause); ok { 340 e.markLetAlias(let) 341 } 342 } 343 } 344 345 // markLetAlias inserts an uninitialized let clause into the current scope. 346 // It gets initialized upon first usage. 347 func (e *exporter) markLetAlias(x *ast.LetClause) { 348 // The created let clause is initialized upon first usage, and removed 349 // later if never referenced. 350 let := &ast.LetClause{} 351 352 if e.letAlias == nil { 353 e.letAlias = make(map[*ast.LetClause]*ast.LetClause) 354 } 355 e.letAlias[x] = let 356 357 scope := e.top().scope 358 scope.Elts = append(scope.Elts, let) 359 } 360 361 // In value mode, lets are only used if there wasn't an error. 362 func filterUnusedLets(s *ast.StructLit) { 363 k := 0 364 for i, d := range s.Elts { 365 if let, ok := d.(*ast.LetClause); ok && let.Expr == nil { 366 continue 367 } 368 s.Elts[k] = s.Elts[i] 369 k++ 370 } 371 s.Elts = s.Elts[:k] 372 } 373 374 // resolveLet actually parses the let expression. 375 // If there was no recorded let expression, it expands the expression in place. 376 func (e *exporter) resolveLet(x *adt.LetReference) ast.Expr { 377 letClause, _ := x.Src.Node.(*ast.LetClause) 378 let := e.letAlias[letClause] 379 380 switch { 381 case let == nil: 382 return e.expr(x.X) 383 384 case let.Expr == nil: 385 label := e.uniqueLetIdent(x.Label, x.X) 386 387 let.Ident = e.ident(label) 388 let.Expr = e.expr(x.X) 389 } 390 391 ident := ast.NewIdent(let.Ident.Name) 392 ident.Node = let 393 // TODO: set scope? 394 return ident 395 } 396 397 func (e *exporter) uniqueLetIdent(f adt.Feature, x adt.Expr) adt.Feature { 398 if e.usedFeature[f] == x { 399 return f 400 } 401 402 f, _ = e.uniqueFeature(f.IdentString(e.ctx)) 403 e.usedFeature[f] = x 404 return f 405 } 406 407 func (e *exporter) uniqueAlias(name string) string { 408 f := adt.MakeIdentLabel(e.ctx, name, "") 409 410 if _, ok := e.usedFeature[f]; !ok { 411 e.usedFeature[f] = nil 412 return name 413 } 414 415 _, name = e.uniqueFeature(f.IdentString(e.ctx)) 416 return name 417 } 418 419 // uniqueFeature returns a name for an identifier that uniquely identifies 420 // the given expression. If the preferred name is already taken, a new globally 421 // unique name of the form base_X ... base_XXXXXXXXXXXXXX is generated. 422 // 423 // It prefers short extensions over large ones, while ensuring the likelihood of 424 // fast termination is high. There are at least two digits to make it visually 425 // clearer this concerns a generated number. 426 // 427 func (e *exporter) uniqueFeature(base string) (f adt.Feature, name string) { 428 if e.rand == nil { 429 e.rand = rand.New(rand.NewSource(808)) 430 } 431 432 // Try the first few numbers in sequence. 433 for i := 1; i < 5; i++ { 434 name := fmt.Sprintf("%s_%01X", base, i) 435 f := adt.MakeIdentLabel(e.ctx, name, "") 436 if _, ok := e.usedFeature[f]; !ok { 437 e.usedFeature[f] = nil 438 return f, name 439 } 440 } 441 442 const mask = 0xff_ffff_ffff_ffff // max bits; stay clear of int64 overflow 443 const shift = 4 // rate of growth 444 digits := 1 445 for n := int64(0x10); ; n = int64(mask&((n<<shift)-1)) + 1 { 446 num := e.rand.Intn(int(n)-1) + 1 447 name := fmt.Sprintf("%[1]s_%0[2]*[3]X", base, digits, num) 448 f := adt.MakeIdentLabel(e.ctx, name, "") 449 if _, ok := e.usedFeature[f]; !ok { 450 e.usedFeature[f] = nil 451 return f, name 452 } 453 digits++ 454 } 455 } 456 457 type frame struct { 458 scope *ast.StructLit 459 460 docSources []adt.Conjunct 461 462 // For resolving dynamic fields. 463 field *ast.Field 464 labelExpr ast.Expr 465 upCount int32 // for off-by-one handling 466 467 // labeled fields 468 fields map[adt.Feature]entry 469 470 // field to new field 471 mapped map[adt.Node]ast.Node 472 } 473 474 type entry struct { 475 alias string 476 field *ast.Field 477 node ast.Node // How to reference. See astutil.Resolve 478 references []*ast.Ident 479 } 480 481 func (e *exporter) addField(label adt.Feature, f *ast.Field, n ast.Node) { 482 frame := e.top() 483 entry := frame.fields[label] 484 entry.field = f 485 entry.node = n 486 frame.fields[label] = entry 487 } 488 489 func (e *exporter) addEmbed(x ast.Expr) { 490 frame := e.top() 491 frame.scope.Elts = append(frame.scope.Elts, x) 492 } 493 494 func (e *exporter) pushFrame(conjuncts []adt.Conjunct) (s *ast.StructLit, saved []frame) { 495 saved = e.stack 496 s = &ast.StructLit{} 497 e.stack = append(e.stack, frame{ 498 scope: s, 499 mapped: map[adt.Node]ast.Node{}, 500 fields: map[adt.Feature]entry{}, 501 docSources: conjuncts, 502 }) 503 return s, saved 504 } 505 506 func (e *exporter) popFrame(saved []frame) { 507 top := e.stack[len(e.stack)-1] 508 509 for _, f := range top.fields { 510 node := f.node 511 if f.alias != "" && f.field != nil { 512 setFieldAlias(f.field, f.alias) 513 node = f.field 514 } 515 for _, r := range f.references { 516 r.Node = node 517 } 518 } 519 520 e.stack = saved 521 } 522 523 func (e *exporter) top() *frame { 524 return &(e.stack[len(e.stack)-1]) 525 } 526 527 func (e *exporter) frame(upCount int32) *frame { 528 for i := len(e.stack) - 1; i >= 0; i-- { 529 f := &(e.stack[i]) 530 if upCount <= (f.upCount - 1) { 531 return f 532 } 533 upCount -= f.upCount 534 } 535 if debug { 536 // This may be valid when exporting incomplete references. These are 537 // not yet handled though, so find a way to catch them when debugging 538 // printing of values that are supposed to be complete. 539 panic("unreachable reference") 540 } 541 542 return &frame{} 543 } 544 545 func (e *exporter) setDocs(x adt.Node) { 546 f := e.stack[len(e.stack)-1] 547 f.docSources = []adt.Conjunct{adt.MakeRootConjunct(nil, x)} 548 e.stack[len(e.stack)-1] = f 549 } 550 551 // func (e *Exporter) promise(upCount int32, f completeFunc) { 552 // e.todo = append(e.todo, f) 553 // } 554 555 func (e *exporter) errf(format string, args ...interface{}) *ast.BottomLit { 556 err := &exporterError{} 557 e.errs = errors.Append(e.errs, err) 558 return &ast.BottomLit{} 559 } 560 561 type errTODO errors.Error 562 563 type exporterError struct { 564 errTODO 565 }