github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/internal/core/export/value.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 "strings" 20 21 "github.com/joomcode/cue/cue/ast" 22 "github.com/joomcode/cue/cue/ast/astutil" 23 "github.com/joomcode/cue/cue/literal" 24 "github.com/joomcode/cue/cue/token" 25 "github.com/joomcode/cue/internal/core/adt" 26 ) 27 28 func (e *exporter) bareValue(v adt.Value) ast.Expr { 29 switch x := v.(type) { 30 case *adt.Vertex: 31 return e.vertex(x) 32 case adt.Value: 33 a := &adt.Vertex{BaseValue: x} 34 return e.vertex(a) 35 default: 36 panic("unreachable") 37 } 38 // TODO: allow a Value context wrapper. 39 } 40 41 // TODO: if the original value was a single reference, we could replace the 42 // value with a reference in graph mode. 43 44 func (e *exporter) vertex(n *adt.Vertex) (result ast.Expr) { 45 var attrs []*ast.Attribute 46 if e.cfg.ShowAttributes { 47 attrs = ExtractDeclAttrs(n) 48 } 49 50 s, saved := e.pushFrame(n.Conjuncts) 51 e.top().upCount++ 52 defer func() { 53 e.top().upCount-- 54 e.popFrame(saved) 55 }() 56 57 for _, c := range n.Conjuncts { 58 e.markLets(c.Expr().Source()) 59 } 60 61 switch x := n.BaseValue.(type) { 62 case nil: 63 // bare 64 case *adt.StructMarker: 65 result = e.structComposite(n, attrs) 66 67 case *adt.ListMarker: 68 if e.showArcs(n) || attrs != nil { 69 result = e.structComposite(n, attrs) 70 } else { 71 result = e.listComposite(n) 72 } 73 74 case *adt.Bottom: 75 switch { 76 case e.cfg.ShowErrors && x.ChildError: 77 // TODO(perf): use precompiled arc statistics 78 if len(n.Arcs) > 0 && n.Arcs[0].Label.IsInt() && !e.showArcs(n) && attrs == nil { 79 result = e.listComposite(n) 80 } else { 81 result = e.structComposite(n, attrs) 82 } 83 84 case !x.IsIncomplete() || len(n.Conjuncts) == 0 || e.cfg.Final: 85 result = e.bottom(x) 86 } 87 88 case adt.Value: 89 if e.showArcs(n) || attrs != nil { 90 result = e.structComposite(n, attrs) 91 } else { 92 result = e.value(x, n.Conjuncts...) 93 } 94 95 default: 96 panic("unknown value") 97 } 98 if result == nil { 99 // fall back to expression mode 100 a := []ast.Expr{} 101 for _, c := range n.Conjuncts { 102 a = append(a, e.expr(c.Elem())) 103 } 104 result = ast.NewBinExpr(token.AND, a...) 105 } 106 107 if len(s.Elts) > 0 { 108 filterUnusedLets(s) 109 } 110 if result != s && len(s.Elts) > 0 { 111 // There are used let expressions within a non-struct. 112 // For now we just fall back to the original expressions. 113 result = e.adt(n, n.Conjuncts) 114 } 115 116 return result 117 } 118 119 // TODO: do something more principled. Best would be to have a similar 120 // mechanism in ast.Ident as others do. 121 func stripRefs(x ast.Expr) ast.Expr { 122 ast.Walk(x, nil, func(n ast.Node) { 123 switch x := n.(type) { 124 case *ast.Ident: 125 switch x.Node.(type) { 126 case *ast.ImportSpec: 127 default: 128 x.Node = nil 129 } 130 } 131 }) 132 return x 133 } 134 135 func (e *exporter) value(n adt.Value, a ...adt.Conjunct) (result ast.Expr) { 136 if e.cfg.TakeDefaults { 137 n = adt.Default(n) 138 } 139 // Evaluate arc if needed? 140 141 // if e.concrete && !adt.IsConcrete(n.Value) { 142 // return e.errf("non-concrete value: %v", e.bareValue(n.Value)) 143 // } 144 145 switch x := n.(type) { 146 case *adt.Bottom: 147 result = e.bottom(x) 148 149 case *adt.Null: 150 result = e.null(x) 151 152 case *adt.Bool: 153 result = e.bool(x) 154 155 case *adt.Num: 156 result = e.num(x, a) 157 158 case *adt.String: 159 result = e.string(x, a) 160 161 case *adt.Bytes: 162 result = e.bytes(x, a) 163 164 case *adt.BasicType: 165 result = e.basicType(x) 166 167 case *adt.Top: 168 result = ast.NewIdent("_") 169 170 case *adt.BoundValue: 171 result = e.boundValue(x) 172 173 case *adt.Builtin: 174 result = e.builtin(x) 175 176 case *adt.BuiltinValidator: 177 result = e.builtinValidator(x) 178 179 case *adt.Vertex: 180 result = e.vertex(x) 181 182 case *adt.Conjunction: 183 switch len(x.Values) { 184 case 0: 185 return ast.NewIdent("_") 186 case 1: 187 if e.cfg.Simplify { 188 return e.expr(x.Values[0]) 189 } 190 return e.bareValue(x.Values[0]) 191 } 192 193 a := []adt.Value{} 194 b := boundSimplifier{e: e} 195 for _, v := range x.Values { 196 if !e.cfg.Simplify || !b.add(v) { 197 a = append(a, v) 198 } 199 } 200 201 result = b.expr(e.ctx) 202 if result == nil { 203 a = x.Values 204 } 205 206 for _, x := range a { 207 result = wrapBin(result, e.bareValue(x), adt.AndOp) 208 } 209 210 case *adt.Disjunction: 211 a := []ast.Expr{} 212 for i, v := range x.Values { 213 var expr ast.Expr 214 if e.cfg.Simplify { 215 expr = e.bareValue(v) 216 } else { 217 expr = e.expr(v) 218 } 219 if i < x.NumDefaults { 220 expr = &ast.UnaryExpr{Op: token.MUL, X: expr} 221 } 222 a = append(a, expr) 223 } 224 result = ast.NewBinExpr(token.OR, a...) 225 226 default: 227 panic(fmt.Sprintf("unsupported type %T", x)) 228 } 229 230 // TODO: Add comments from original. 231 232 return result 233 } 234 235 func (e *exporter) bottom(n *adt.Bottom) *ast.BottomLit { 236 err := &ast.BottomLit{} 237 if x := n.Err; x != nil { 238 msg := x.Error() 239 comment := &ast.Comment{Text: "// " + msg} 240 err.AddComment(&ast.CommentGroup{ 241 Line: true, 242 Position: 2, 243 List: []*ast.Comment{comment}, 244 }) 245 } 246 return err 247 } 248 249 func (e *exporter) null(n *adt.Null) *ast.BasicLit { 250 return &ast.BasicLit{Kind: token.NULL, Value: "null"} 251 } 252 253 func (e *exporter) bool(n *adt.Bool) (b *ast.BasicLit) { 254 return ast.NewBool(n.B) 255 } 256 257 func extractBasic(a []adt.Conjunct) *ast.BasicLit { 258 for _, v := range a { 259 if b, ok := v.Source().(*ast.BasicLit); ok { 260 return &ast.BasicLit{Kind: b.Kind, Value: b.Value} 261 } 262 } 263 return nil 264 } 265 266 func (e *exporter) num(n *adt.Num, orig []adt.Conjunct) *ast.BasicLit { 267 // TODO: take original formatting into account. 268 if b := extractBasic(orig); b != nil { 269 return b 270 } 271 kind := token.FLOAT 272 if n.K&adt.IntKind != 0 { 273 kind = token.INT 274 } 275 s := n.X.String() 276 if kind == token.FLOAT && !strings.ContainsAny(s, "eE.") { 277 s += "." 278 } 279 return &ast.BasicLit{Kind: kind, Value: s} 280 } 281 282 func (e *exporter) string(n *adt.String, orig []adt.Conjunct) *ast.BasicLit { 283 // TODO: take original formatting into account. 284 if b := extractBasic(orig); b != nil { 285 return b 286 } 287 s := literal.String.WithOptionalTabIndent(len(e.stack)).Quote(n.Str) 288 return &ast.BasicLit{ 289 Kind: token.STRING, 290 Value: s, 291 } 292 } 293 294 func (e *exporter) bytes(n *adt.Bytes, orig []adt.Conjunct) *ast.BasicLit { 295 // TODO: take original formatting into account. 296 if b := extractBasic(orig); b != nil { 297 return b 298 } 299 s := literal.Bytes.WithOptionalTabIndent(len(e.stack)).Quote(string(n.B)) 300 return &ast.BasicLit{ 301 Kind: token.STRING, 302 Value: s, 303 } 304 } 305 306 func (e *exporter) basicType(n *adt.BasicType) ast.Expr { 307 // TODO: allow multi-bit types? 308 return ast.NewIdent(n.K.String()) 309 } 310 311 func (e *exporter) boundValue(n *adt.BoundValue) ast.Expr { 312 return &ast.UnaryExpr{Op: n.Op.Token(), X: e.value(n.Value)} 313 } 314 315 func (e *exporter) builtin(x *adt.Builtin) ast.Expr { 316 if x.Package == 0 { 317 return ast.NewIdent(x.Name) 318 } 319 spec := ast.NewImport(nil, x.Package.StringValue(e.index)) 320 info, _ := astutil.ParseImportSpec(spec) 321 ident := ast.NewIdent(info.Ident) 322 ident.Node = spec 323 return ast.NewSel(ident, x.Name) 324 } 325 326 func (e *exporter) builtinValidator(n *adt.BuiltinValidator) ast.Expr { 327 call := ast.NewCall(e.builtin(n.Builtin)) 328 for _, a := range n.Args { 329 call.Args = append(call.Args, e.value(a)) 330 } 331 return call 332 } 333 334 func (e *exporter) listComposite(v *adt.Vertex) ast.Expr { 335 l := &ast.ListLit{} 336 for _, a := range v.Arcs { 337 if !a.Label.IsInt() { 338 continue 339 } 340 elem := e.vertex(a) 341 342 docs := ExtractDoc(a) 343 ast.SetComments(elem, docs) 344 345 l.Elts = append(l.Elts, elem) 346 } 347 m, ok := v.BaseValue.(*adt.ListMarker) 348 if !e.cfg.TakeDefaults && ok && m.IsOpen { 349 ellipsis := &ast.Ellipsis{} 350 typ := &adt.Vertex{ 351 Parent: v, 352 Label: adt.AnyIndex, 353 } 354 v.MatchAndInsert(e.ctx, typ) 355 typ.Finalize(e.ctx) 356 if typ.Kind() != adt.TopKind { 357 ellipsis.Type = e.value(typ) 358 } 359 360 l.Elts = append(l.Elts, ellipsis) 361 } 362 return l 363 } 364 365 func (e exporter) showArcs(v *adt.Vertex) bool { 366 p := e.cfg 367 if !p.ShowHidden && !p.ShowDefinitions { 368 return false 369 } 370 for _, a := range v.Arcs { 371 switch { 372 case a.Label.IsDef() && p.ShowDefinitions: 373 return true 374 case a.Label.IsHidden() && p.ShowHidden: 375 return true 376 } 377 } 378 return false 379 } 380 381 func (e *exporter) structComposite(v *adt.Vertex, attrs []*ast.Attribute) ast.Expr { 382 s := e.top().scope 383 384 showRegular := false 385 switch x := v.BaseValue.(type) { 386 case *adt.StructMarker: 387 showRegular = true 388 case *adt.ListMarker: 389 // As lists may be long, put them at the end. 390 defer e.addEmbed(e.listComposite(v)) 391 case *adt.Bottom: 392 if !e.cfg.ShowErrors || !x.ChildError { 393 // Should not be reachable, but just in case. The output will be 394 // correct. 395 e.addEmbed(e.value(x)) 396 return s 397 } 398 // Always also show regular fields, even when list, as we are in 399 // debugging mode. 400 showRegular = true 401 // TODO(perf): do something better 402 for _, a := range v.Arcs { 403 if a.Label.IsInt() { 404 defer e.addEmbed(e.listComposite(v)) 405 break 406 } 407 } 408 409 case adt.Value: 410 e.addEmbed(e.value(x)) 411 } 412 413 for _, a := range attrs { 414 s.Elts = append(s.Elts, a) 415 } 416 417 p := e.cfg 418 for _, label := range VertexFeatures(e.ctx, v) { 419 show := false 420 switch label.Typ() { 421 case adt.StringLabel: 422 show = showRegular 423 case adt.IntLabel: 424 continue 425 case adt.DefinitionLabel: 426 show = p.ShowDefinitions 427 case adt.HiddenLabel, adt.HiddenDefinitionLabel: 428 show = p.ShowHidden && label.PkgID(e.ctx) == e.pkgID 429 } 430 if !show { 431 continue 432 } 433 434 f := &ast.Field{Label: e.stringLabel(label)} 435 436 e.addField(label, f, f.Value) 437 438 if label.IsDef() { 439 e.inDefinition++ 440 } 441 442 arc := v.Lookup(label) 443 switch { 444 case arc == nil: 445 if !p.ShowOptional { 446 continue 447 } 448 f.Optional = token.NoSpace.Pos() 449 450 arc = &adt.Vertex{Label: label} 451 v.MatchAndInsert(e.ctx, arc) 452 if len(arc.Conjuncts) == 0 { 453 continue 454 } 455 456 // fall back to expression mode. 457 f.Value = stripRefs(e.expr(arc)) 458 459 // TODO: remove use of stripRefs. 460 // f.Value = e.expr(arc) 461 462 default: 463 f.Value = e.vertex(arc) 464 } 465 466 if label.IsDef() { 467 e.inDefinition-- 468 } 469 470 if p.ShowAttributes { 471 f.Attrs = ExtractFieldAttrs(arc) 472 } 473 474 if p.ShowDocs { 475 docs := ExtractDoc(arc) 476 ast.SetComments(f, docs) 477 } 478 479 s.Elts = append(s.Elts, f) 480 } 481 482 return s 483 }