github.com/solo-io/cue@v0.4.7/internal/core/export/export.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  	"math/rand"
    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/errors"
    24  	"github.com/solo-io/cue/internal"
    25  	"github.com/solo-io/cue/internal/core/adt"
    26  	"github.com/solo-io/cue/internal/core/eval"
    27  	"github.com/solo-io/cue/internal/core/walk"
    28  )
    29  
    30  const debug = false
    31  
    32  type Profile struct {
    33  	Simplify bool
    34  
    35  	// Final reports incomplete errors as errors.
    36  	Final bool
    37  
    38  	// TakeDefaults is used in Value mode to drop non-default values.
    39  	TakeDefaults bool
    40  
    41  	ShowOptional    bool
    42  	ShowDefinitions bool
    43  
    44  	// ShowHidden forces the inclusion of hidden fields when these would
    45  	// otherwise be omitted. Only hidden fields from the current package are
    46  	// included.
    47  	ShowHidden     bool
    48  	ShowDocs       bool
    49  	ShowAttributes bool
    50  
    51  	// ShowErrors treats errors as values and will not percolate errors up.
    52  	ShowErrors bool
    53  	// Use unevaluated conjuncts for these error types
    54  	// IgnoreRecursive
    55  
    56  	// TODO: recurse over entire tree to determine transitive closure
    57  	// of what needs to be printed.
    58  	// IncludeDependencies bool
    59  }
    60  
    61  var Simplified = &Profile{
    62  	Simplify: true,
    63  	ShowDocs: true,
    64  }
    65  
    66  var Final = &Profile{
    67  	Simplify:     true,
    68  	TakeDefaults: true,
    69  	Final:        true,
    70  }
    71  
    72  var Raw = &Profile{
    73  	ShowOptional:    true,
    74  	ShowDefinitions: true,
    75  	ShowHidden:      true,
    76  	ShowDocs:        true,
    77  }
    78  
    79  var All = &Profile{
    80  	Simplify:        true,
    81  	ShowOptional:    true,
    82  	ShowDefinitions: true,
    83  	ShowHidden:      true,
    84  	ShowDocs:        true,
    85  	ShowAttributes:  true,
    86  }
    87  
    88  // Concrete
    89  
    90  // Def exports v as a definition.
    91  func Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) {
    92  	return All.Def(r, pkgID, v)
    93  }
    94  
    95  // Def exports v as a definition.
    96  func (p *Profile) Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) {
    97  	e := newExporter(p, r, pkgID, v)
    98  	e.markUsedFeatures(v)
    99  
   100  	isDef := v.IsRecursivelyClosed()
   101  	if isDef {
   102  		e.inDefinition++
   103  	}
   104  
   105  	expr := e.expr(v)
   106  
   107  	if isDef {
   108  		e.inDefinition--
   109  		if v.Kind() == adt.StructKind {
   110  			expr = ast.NewStruct(
   111  				ast.Embed(ast.NewIdent("_#def")),
   112  				ast.NewIdent("_#def"), expr,
   113  			)
   114  		}
   115  	}
   116  	return e.toFile(v, expr)
   117  }
   118  
   119  func Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) {
   120  	return Simplified.Expr(r, pkgID, n)
   121  }
   122  
   123  func (p *Profile) Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) {
   124  	e := newExporter(p, r, pkgID, nil)
   125  	e.markUsedFeatures(n)
   126  
   127  	return e.expr(n), nil
   128  }
   129  
   130  func (e *exporter) toFile(v *adt.Vertex, x ast.Expr) (*ast.File, errors.Error) {
   131  	f := &ast.File{}
   132  
   133  	pkgName := ""
   134  	pkg := &ast.Package{}
   135  	for _, c := range v.Conjuncts {
   136  		f, _ := c.Source().(*ast.File)
   137  		if f == nil {
   138  			continue
   139  		}
   140  
   141  		if _, name, _ := internal.PackageInfo(f); name != "" {
   142  			pkgName = name
   143  		}
   144  
   145  		if e.cfg.ShowDocs {
   146  			if doc := internal.FileComment(f); doc != nil {
   147  				ast.AddComment(pkg, doc)
   148  			}
   149  		}
   150  	}
   151  
   152  	if pkgName != "" {
   153  		pkg.Name = ast.NewIdent(pkgName)
   154  		f.Decls = append(f.Decls, pkg)
   155  	}
   156  
   157  	switch st := x.(type) {
   158  	case nil:
   159  		panic("null input")
   160  
   161  	case *ast.StructLit:
   162  		f.Decls = append(f.Decls, st.Elts...)
   163  
   164  	default:
   165  		f.Decls = append(f.Decls, &ast.EmbedDecl{Expr: x})
   166  	}
   167  	if err := astutil.Sanitize(f); err != nil {
   168  		err := errors.Promote(err, "export")
   169  		return f, errors.Append(e.errs, err)
   170  	}
   171  
   172  	return f, nil
   173  }
   174  
   175  // File
   176  
   177  func Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) {
   178  	return Simplified.Vertex(r, pkgID, n)
   179  }
   180  
   181  func (p *Profile) Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) {
   182  	e := exporter{
   183  		ctx:   eval.NewContext(r, nil),
   184  		cfg:   p,
   185  		index: r,
   186  		pkgID: pkgID,
   187  	}
   188  	e.markUsedFeatures(n)
   189  	v := e.value(n, n.Conjuncts...)
   190  
   191  	return e.toFile(n, v)
   192  }
   193  
   194  func Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) {
   195  	return Simplified.Value(r, pkgID, n)
   196  }
   197  
   198  // Should take context.
   199  func (p *Profile) Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) {
   200  	e := exporter{
   201  		ctx:   eval.NewContext(r, nil),
   202  		cfg:   p,
   203  		index: r,
   204  		pkgID: pkgID,
   205  	}
   206  	e.markUsedFeatures(n)
   207  	v := e.value(n)
   208  	return v, e.errs
   209  }
   210  
   211  type exporter struct {
   212  	cfg  *Profile // Make value todo
   213  	errs errors.Error
   214  
   215  	ctx *adt.OpContext
   216  
   217  	index adt.StringIndexer
   218  	rand  *rand.Rand
   219  
   220  	// For resolving up references.
   221  	stack []frame
   222  
   223  	inDefinition int // for close() wrapping.
   224  
   225  	// hidden label handling
   226  	pkgID  string
   227  	hidden map[string]adt.Feature // adt.InvalidFeatures means more than one.
   228  
   229  	// If a used feature maps to an expression, it means it is assigned to a
   230  	// unique let expression.
   231  	usedFeature map[adt.Feature]adt.Expr
   232  	labelAlias  map[adt.Expr]adt.Feature
   233  	valueAlias  map[*ast.Alias]*ast.Alias
   234  
   235  	usedHidden map[string]bool
   236  }
   237  
   238  func newExporter(p *Profile, r adt.Runtime, pkgID string, v *adt.Vertex) *exporter {
   239  	return &exporter{
   240  		cfg:   p,
   241  		ctx:   eval.NewContext(r, v),
   242  		index: r,
   243  		pkgID: pkgID,
   244  	}
   245  }
   246  
   247  func (e *exporter) markUsedFeatures(x adt.Expr) {
   248  	e.usedFeature = make(map[adt.Feature]adt.Expr)
   249  
   250  	w := &walk.Visitor{}
   251  	w.Before = func(n adt.Node) bool {
   252  		switch x := n.(type) {
   253  		case *adt.Vertex:
   254  			if !x.IsData() {
   255  				for _, c := range x.Conjuncts {
   256  					w.Expr(c.Expr())
   257  				}
   258  			}
   259  
   260  		case *adt.DynamicReference:
   261  			if e.labelAlias == nil {
   262  				e.labelAlias = make(map[adt.Expr]adt.Feature)
   263  			}
   264  			// TODO: add preferred label.
   265  			e.labelAlias[x.Label] = adt.InvalidLabel
   266  
   267  		case *adt.LabelReference:
   268  		}
   269  		return true
   270  	}
   271  
   272  	w.Feature = func(f adt.Feature, src adt.Node) {
   273  		_, ok := e.usedFeature[f]
   274  
   275  		switch x := src.(type) {
   276  		case *adt.LetReference:
   277  			if !ok {
   278  				e.usedFeature[f] = x.X
   279  			}
   280  
   281  		default:
   282  			e.usedFeature[f] = nil
   283  		}
   284  	}
   285  
   286  	w.Expr(x)
   287  }
   288  
   289  func (e *exporter) getFieldAlias(f *ast.Field, name string) string {
   290  	a, ok := f.Label.(*ast.Alias)
   291  	if !ok {
   292  		a = &ast.Alias{
   293  			Ident: ast.NewIdent(e.uniqueAlias(name)),
   294  			Expr:  f.Label.(ast.Expr),
   295  		}
   296  		f.Label = a
   297  	}
   298  	return a.Ident.Name
   299  }
   300  
   301  func setFieldAlias(f *ast.Field, name string) {
   302  	if _, ok := f.Label.(*ast.Alias); !ok {
   303  		f.Label = &ast.Alias{
   304  			Ident: ast.NewIdent(name),
   305  			Expr:  f.Label.(ast.Expr),
   306  		}
   307  	}
   308  }
   309  
   310  // uniqueLetIdent returns a name for a let identifier that uniquely identifies
   311  // the given expression. If the preferred name is already taken, a new globally
   312  // unique name of the form base_X ... base_XXXXXXXXXXXXXX is generated.
   313  //
   314  // It prefers short extensions over large ones, while ensuring the likelihood of
   315  // fast termination is high. There are at least two digits to make it visually
   316  // clearer this concerns a generated number.
   317  //
   318  func (e exporter) uniqueLetIdent(f adt.Feature, x adt.Expr) adt.Feature {
   319  	if e.usedFeature[f] == x {
   320  		return f
   321  	}
   322  
   323  	f, _ = e.uniqueFeature(f.IdentString(e.ctx))
   324  	e.usedFeature[f] = x
   325  	return f
   326  }
   327  
   328  func (e exporter) uniqueAlias(name string) string {
   329  	f := adt.MakeIdentLabel(e.ctx, name, "")
   330  
   331  	if _, ok := e.usedFeature[f]; !ok {
   332  		e.usedFeature[f] = nil
   333  		return name
   334  	}
   335  
   336  	_, name = e.uniqueFeature(f.IdentString(e.ctx))
   337  	return name
   338  }
   339  
   340  func (e exporter) uniqueFeature(base string) (f adt.Feature, name string) {
   341  	if e.rand == nil {
   342  		e.rand = rand.New(rand.NewSource(808))
   343  	}
   344  
   345  	const mask = 0xff_ffff_ffff_ffff // max bits; stay clear of int64 overflow
   346  	const shift = 4                  // rate of growth
   347  	for n := int64(0x10); ; n = int64(mask&((n<<shift)-1)) + 1 {
   348  		num := e.rand.Intn(int(n))
   349  		name := fmt.Sprintf("%s_%01X", base, num)
   350  		f := adt.MakeIdentLabel(e.ctx, name, "")
   351  		if _, ok := e.usedFeature[f]; !ok {
   352  			e.usedFeature[f] = nil
   353  			return f, name
   354  		}
   355  	}
   356  }
   357  
   358  type completeFunc func(scope *ast.StructLit, m adt.Node)
   359  
   360  type frame struct {
   361  	scope *ast.StructLit
   362  	todo  []completeFunc
   363  
   364  	docSources []adt.Conjunct
   365  
   366  	// For resolving dynamic fields.
   367  	field     *ast.Field
   368  	labelExpr ast.Expr
   369  	upCount   int32 // for off-by-one handling
   370  
   371  	// labeled fields
   372  	fields map[adt.Feature]entry
   373  	let    map[adt.Expr]*ast.LetClause
   374  
   375  	// field to new field
   376  	mapped map[adt.Node]ast.Node
   377  }
   378  
   379  type entry struct {
   380  	alias      string
   381  	field      *ast.Field
   382  	node       ast.Node // How to reference. See astutil.Resolve
   383  	references []*ast.Ident
   384  }
   385  
   386  func (e *exporter) addField(label adt.Feature, f *ast.Field, n ast.Node) {
   387  	frame := e.top()
   388  	entry := frame.fields[label]
   389  	entry.field = f
   390  	entry.node = n
   391  	frame.fields[label] = entry
   392  }
   393  
   394  func (e *exporter) addEmbed(x ast.Expr) {
   395  	frame := e.top()
   396  	frame.scope.Elts = append(frame.scope.Elts, x)
   397  }
   398  
   399  func (e *exporter) pushFrame(conjuncts []adt.Conjunct) (s *ast.StructLit, saved []frame) {
   400  	saved = e.stack
   401  	s = &ast.StructLit{}
   402  	e.stack = append(e.stack, frame{
   403  		scope:      s,
   404  		mapped:     map[adt.Node]ast.Node{},
   405  		fields:     map[adt.Feature]entry{},
   406  		docSources: conjuncts,
   407  	})
   408  	return s, saved
   409  }
   410  
   411  func (e *exporter) popFrame(saved []frame) {
   412  	top := e.stack[len(e.stack)-1]
   413  
   414  	for _, f := range top.fields {
   415  		node := f.node
   416  		if f.alias != "" && f.field != nil {
   417  			setFieldAlias(f.field, f.alias)
   418  			node = f.field
   419  		}
   420  		for _, r := range f.references {
   421  			r.Node = node
   422  		}
   423  	}
   424  
   425  	e.stack = saved
   426  }
   427  
   428  func (e *exporter) top() *frame {
   429  	return &(e.stack[len(e.stack)-1])
   430  }
   431  
   432  func (e *exporter) frame(upCount int32) *frame {
   433  	for i := len(e.stack) - 1; i >= 0; i-- {
   434  		f := &(e.stack[i])
   435  		if upCount <= (f.upCount - 1) {
   436  			return f
   437  		}
   438  		upCount -= f.upCount
   439  	}
   440  	if debug {
   441  		// This may be valid when exporting incomplete references. These are
   442  		// not yet handled though, so find a way to catch them when debugging
   443  		// printing of values that are supposed to be complete.
   444  		panic("unreachable reference")
   445  	}
   446  
   447  	return &frame{}
   448  }
   449  
   450  func (e *exporter) setDocs(x adt.Node) {
   451  	f := e.stack[len(e.stack)-1]
   452  	f.docSources = []adt.Conjunct{adt.MakeRootConjunct(nil, x)}
   453  	e.stack[len(e.stack)-1] = f
   454  }
   455  
   456  // func (e *Exporter) promise(upCount int32, f completeFunc) {
   457  // 	e.todo = append(e.todo, f)
   458  // }
   459  
   460  func (e *exporter) errf(format string, args ...interface{}) *ast.BottomLit {
   461  	err := &exporterError{}
   462  	e.errs = errors.Append(e.errs, err)
   463  	return &ast.BottomLit{}
   464  }
   465  
   466  type errTODO errors.Error
   467  
   468  type exporterError struct {
   469  	errTODO
   470  }