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