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