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  }