github.com/solo-io/cue@v0.4.7/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/solo-io/cue/cue/ast" 22 "github.com/solo-io/cue/cue/ast/astutil" 23 "github.com/solo-io/cue/cue/errors" 24 "github.com/solo-io/cue/internal" 25 "github.com/solo-io/cue/internal/core/adt" 26 "github.com/solo-io/cue/internal/core/eval" 27 "github.com/solo-io/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 ShowErrors bool 53 // Use unevaluated conjuncts for these error types 54 // IgnoreRecursive 55 56 // TODO: recurse over entire tree to determine transitive closure 57 // of what needs to be printed. 58 // IncludeDependencies bool 59 } 60 61 var Simplified = &Profile{ 62 Simplify: true, 63 ShowDocs: true, 64 } 65 66 var Final = &Profile{ 67 Simplify: true, 68 TakeDefaults: true, 69 Final: true, 70 } 71 72 var Raw = &Profile{ 73 ShowOptional: true, 74 ShowDefinitions: true, 75 ShowHidden: true, 76 ShowDocs: true, 77 } 78 79 var All = &Profile{ 80 Simplify: true, 81 ShowOptional: true, 82 ShowDefinitions: true, 83 ShowHidden: true, 84 ShowDocs: true, 85 ShowAttributes: true, 86 } 87 88 // Concrete 89 90 // Def exports v as a definition. 91 func Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) { 92 return All.Def(r, pkgID, v) 93 } 94 95 // Def exports v as a definition. 96 func (p *Profile) Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) { 97 e := newExporter(p, r, pkgID, v) 98 e.markUsedFeatures(v) 99 100 isDef := v.IsRecursivelyClosed() 101 if isDef { 102 e.inDefinition++ 103 } 104 105 expr := e.expr(v) 106 107 if isDef { 108 e.inDefinition-- 109 if v.Kind() == adt.StructKind { 110 expr = ast.NewStruct( 111 ast.Embed(ast.NewIdent("_#def")), 112 ast.NewIdent("_#def"), expr, 113 ) 114 } 115 } 116 return e.toFile(v, expr) 117 } 118 119 func Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) { 120 return Simplified.Expr(r, pkgID, n) 121 } 122 123 func (p *Profile) Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) { 124 e := newExporter(p, r, pkgID, nil) 125 e.markUsedFeatures(n) 126 127 return e.expr(n), nil 128 } 129 130 func (e *exporter) toFile(v *adt.Vertex, x ast.Expr) (*ast.File, errors.Error) { 131 f := &ast.File{} 132 133 pkgName := "" 134 pkg := &ast.Package{} 135 for _, c := range v.Conjuncts { 136 f, _ := c.Source().(*ast.File) 137 if f == nil { 138 continue 139 } 140 141 if _, name, _ := internal.PackageInfo(f); name != "" { 142 pkgName = name 143 } 144 145 if e.cfg.ShowDocs { 146 if doc := internal.FileComment(f); doc != nil { 147 ast.AddComment(pkg, doc) 148 } 149 } 150 } 151 152 if pkgName != "" { 153 pkg.Name = ast.NewIdent(pkgName) 154 f.Decls = append(f.Decls, pkg) 155 } 156 157 switch st := x.(type) { 158 case nil: 159 panic("null input") 160 161 case *ast.StructLit: 162 f.Decls = append(f.Decls, st.Elts...) 163 164 default: 165 f.Decls = append(f.Decls, &ast.EmbedDecl{Expr: x}) 166 } 167 if err := astutil.Sanitize(f); err != nil { 168 err := errors.Promote(err, "export") 169 return f, errors.Append(e.errs, err) 170 } 171 172 return f, nil 173 } 174 175 // File 176 177 func Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) { 178 return Simplified.Vertex(r, pkgID, n) 179 } 180 181 func (p *Profile) Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) { 182 e := exporter{ 183 ctx: eval.NewContext(r, nil), 184 cfg: p, 185 index: r, 186 pkgID: pkgID, 187 } 188 e.markUsedFeatures(n) 189 v := e.value(n, n.Conjuncts...) 190 191 return e.toFile(n, v) 192 } 193 194 func Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) { 195 return Simplified.Value(r, pkgID, n) 196 } 197 198 // Should take context. 199 func (p *Profile) Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) { 200 e := exporter{ 201 ctx: eval.NewContext(r, nil), 202 cfg: p, 203 index: r, 204 pkgID: pkgID, 205 } 206 e.markUsedFeatures(n) 207 v := e.value(n) 208 return v, e.errs 209 } 210 211 type exporter struct { 212 cfg *Profile // Make value todo 213 errs errors.Error 214 215 ctx *adt.OpContext 216 217 index adt.StringIndexer 218 rand *rand.Rand 219 220 // For resolving up references. 221 stack []frame 222 223 inDefinition int // for close() wrapping. 224 225 // hidden label handling 226 pkgID string 227 hidden map[string]adt.Feature // adt.InvalidFeatures means more than one. 228 229 // If a used feature maps to an expression, it means it is assigned to a 230 // unique let expression. 231 usedFeature map[adt.Feature]adt.Expr 232 labelAlias map[adt.Expr]adt.Feature 233 valueAlias map[*ast.Alias]*ast.Alias 234 235 usedHidden map[string]bool 236 } 237 238 func newExporter(p *Profile, r adt.Runtime, pkgID string, v *adt.Vertex) *exporter { 239 return &exporter{ 240 cfg: p, 241 ctx: eval.NewContext(r, v), 242 index: r, 243 pkgID: pkgID, 244 } 245 } 246 247 func (e *exporter) markUsedFeatures(x adt.Expr) { 248 e.usedFeature = make(map[adt.Feature]adt.Expr) 249 250 w := &walk.Visitor{} 251 w.Before = func(n adt.Node) bool { 252 switch x := n.(type) { 253 case *adt.Vertex: 254 if !x.IsData() { 255 for _, c := range x.Conjuncts { 256 w.Expr(c.Expr()) 257 } 258 } 259 260 case *adt.DynamicReference: 261 if e.labelAlias == nil { 262 e.labelAlias = make(map[adt.Expr]adt.Feature) 263 } 264 // TODO: add preferred label. 265 e.labelAlias[x.Label] = adt.InvalidLabel 266 267 case *adt.LabelReference: 268 } 269 return true 270 } 271 272 w.Feature = func(f adt.Feature, src adt.Node) { 273 _, ok := e.usedFeature[f] 274 275 switch x := src.(type) { 276 case *adt.LetReference: 277 if !ok { 278 e.usedFeature[f] = x.X 279 } 280 281 default: 282 e.usedFeature[f] = nil 283 } 284 } 285 286 w.Expr(x) 287 } 288 289 func (e *exporter) getFieldAlias(f *ast.Field, name string) string { 290 a, ok := f.Label.(*ast.Alias) 291 if !ok { 292 a = &ast.Alias{ 293 Ident: ast.NewIdent(e.uniqueAlias(name)), 294 Expr: f.Label.(ast.Expr), 295 } 296 f.Label = a 297 } 298 return a.Ident.Name 299 } 300 301 func setFieldAlias(f *ast.Field, name string) { 302 if _, ok := f.Label.(*ast.Alias); !ok { 303 f.Label = &ast.Alias{ 304 Ident: ast.NewIdent(name), 305 Expr: f.Label.(ast.Expr), 306 } 307 } 308 } 309 310 // uniqueLetIdent returns a name for a let identifier that uniquely identifies 311 // the given expression. If the preferred name is already taken, a new globally 312 // unique name of the form base_X ... base_XXXXXXXXXXXXXX is generated. 313 // 314 // It prefers short extensions over large ones, while ensuring the likelihood of 315 // fast termination is high. There are at least two digits to make it visually 316 // clearer this concerns a generated number. 317 // 318 func (e exporter) uniqueLetIdent(f adt.Feature, x adt.Expr) adt.Feature { 319 if e.usedFeature[f] == x { 320 return f 321 } 322 323 f, _ = e.uniqueFeature(f.IdentString(e.ctx)) 324 e.usedFeature[f] = x 325 return f 326 } 327 328 func (e exporter) uniqueAlias(name string) string { 329 f := adt.MakeIdentLabel(e.ctx, name, "") 330 331 if _, ok := e.usedFeature[f]; !ok { 332 e.usedFeature[f] = nil 333 return name 334 } 335 336 _, name = e.uniqueFeature(f.IdentString(e.ctx)) 337 return name 338 } 339 340 func (e exporter) uniqueFeature(base string) (f adt.Feature, name string) { 341 if e.rand == nil { 342 e.rand = rand.New(rand.NewSource(808)) 343 } 344 345 const mask = 0xff_ffff_ffff_ffff // max bits; stay clear of int64 overflow 346 const shift = 4 // rate of growth 347 for n := int64(0x10); ; n = int64(mask&((n<<shift)-1)) + 1 { 348 num := e.rand.Intn(int(n)) 349 name := fmt.Sprintf("%s_%01X", base, num) 350 f := adt.MakeIdentLabel(e.ctx, name, "") 351 if _, ok := e.usedFeature[f]; !ok { 352 e.usedFeature[f] = nil 353 return f, name 354 } 355 } 356 } 357 358 type completeFunc func(scope *ast.StructLit, m adt.Node) 359 360 type frame struct { 361 scope *ast.StructLit 362 todo []completeFunc 363 364 docSources []adt.Conjunct 365 366 // For resolving dynamic fields. 367 field *ast.Field 368 labelExpr ast.Expr 369 upCount int32 // for off-by-one handling 370 371 // labeled fields 372 fields map[adt.Feature]entry 373 let map[adt.Expr]*ast.LetClause 374 375 // field to new field 376 mapped map[adt.Node]ast.Node 377 } 378 379 type entry struct { 380 alias string 381 field *ast.Field 382 node ast.Node // How to reference. See astutil.Resolve 383 references []*ast.Ident 384 } 385 386 func (e *exporter) addField(label adt.Feature, f *ast.Field, n ast.Node) { 387 frame := e.top() 388 entry := frame.fields[label] 389 entry.field = f 390 entry.node = n 391 frame.fields[label] = entry 392 } 393 394 func (e *exporter) addEmbed(x ast.Expr) { 395 frame := e.top() 396 frame.scope.Elts = append(frame.scope.Elts, x) 397 } 398 399 func (e *exporter) pushFrame(conjuncts []adt.Conjunct) (s *ast.StructLit, saved []frame) { 400 saved = e.stack 401 s = &ast.StructLit{} 402 e.stack = append(e.stack, frame{ 403 scope: s, 404 mapped: map[adt.Node]ast.Node{}, 405 fields: map[adt.Feature]entry{}, 406 docSources: conjuncts, 407 }) 408 return s, saved 409 } 410 411 func (e *exporter) popFrame(saved []frame) { 412 top := e.stack[len(e.stack)-1] 413 414 for _, f := range top.fields { 415 node := f.node 416 if f.alias != "" && f.field != nil { 417 setFieldAlias(f.field, f.alias) 418 node = f.field 419 } 420 for _, r := range f.references { 421 r.Node = node 422 } 423 } 424 425 e.stack = saved 426 } 427 428 func (e *exporter) top() *frame { 429 return &(e.stack[len(e.stack)-1]) 430 } 431 432 func (e *exporter) frame(upCount int32) *frame { 433 for i := len(e.stack) - 1; i >= 0; i-- { 434 f := &(e.stack[i]) 435 if upCount <= (f.upCount - 1) { 436 return f 437 } 438 upCount -= f.upCount 439 } 440 if debug { 441 // This may be valid when exporting incomplete references. These are 442 // not yet handled though, so find a way to catch them when debugging 443 // printing of values that are supposed to be complete. 444 panic("unreachable reference") 445 } 446 447 return &frame{} 448 } 449 450 func (e *exporter) setDocs(x adt.Node) { 451 f := e.stack[len(e.stack)-1] 452 f.docSources = []adt.Conjunct{adt.MakeRootConjunct(nil, x)} 453 e.stack[len(e.stack)-1] = f 454 } 455 456 // func (e *Exporter) promise(upCount int32, f completeFunc) { 457 // e.todo = append(e.todo, f) 458 // } 459 460 func (e *exporter) errf(format string, args ...interface{}) *ast.BottomLit { 461 err := &exporterError{} 462 e.errs = errors.Append(e.errs, err) 463 return &ast.BottomLit{} 464 } 465 466 type errTODO errors.Error 467 468 type exporterError struct { 469 errTODO 470 }