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  }