github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/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/joomcode/cue/cue/ast"
    22  	"github.com/joomcode/cue/cue/ast/astutil"
    23  	"github.com/joomcode/cue/cue/errors"
    24  	"github.com/joomcode/cue/internal"
    25  	"github.com/joomcode/cue/internal/core/adt"
    26  	"github.com/joomcode/cue/internal/core/eval"
    27  	"github.com/joomcode/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  	//
    53  	// TODO: convert this option to an error level instead, showing only
    54  	// errors below a certain severity.
    55  	ShowErrors bool
    56  
    57  	// Use unevaluated conjuncts for these error types
    58  	// IgnoreRecursive
    59  
    60  	// TODO: recurse over entire tree to determine transitive closure
    61  	// of what needs to be printed.
    62  	// IncludeDependencies bool
    63  }
    64  
    65  var Simplified = &Profile{
    66  	Simplify: true,
    67  	ShowDocs: true,
    68  }
    69  
    70  var Final = &Profile{
    71  	Simplify:     true,
    72  	TakeDefaults: true,
    73  	Final:        true,
    74  }
    75  
    76  var Raw = &Profile{
    77  	ShowOptional:    true,
    78  	ShowDefinitions: true,
    79  	ShowHidden:      true,
    80  	ShowDocs:        true,
    81  }
    82  
    83  var All = &Profile{
    84  	Simplify:        true,
    85  	ShowOptional:    true,
    86  	ShowDefinitions: true,
    87  	ShowHidden:      true,
    88  	ShowDocs:        true,
    89  	ShowAttributes:  true,
    90  }
    91  
    92  // Concrete
    93  
    94  // Def exports v as a definition.
    95  func Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) {
    96  	return All.Def(r, pkgID, v)
    97  }
    98  
    99  // Def exports v as a definition.
   100  func (p *Profile) Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) {
   101  	e := newExporter(p, r, pkgID, v)
   102  	e.markUsedFeatures(v)
   103  
   104  	isDef := v.IsRecursivelyClosed()
   105  	if isDef {
   106  		e.inDefinition++
   107  	}
   108  
   109  	expr := e.expr(v)
   110  
   111  	if isDef {
   112  		e.inDefinition--
   113  		if v.Kind() == adt.StructKind {
   114  			expr = ast.NewStruct(
   115  				ast.Embed(ast.NewIdent("_#def")),
   116  				ast.NewIdent("_#def"), expr,
   117  			)
   118  		}
   119  	}
   120  	return e.toFile(v, expr)
   121  }
   122  
   123  func Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) {
   124  	return Simplified.Expr(r, pkgID, n)
   125  }
   126  
   127  func (p *Profile) Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) {
   128  	e := newExporter(p, r, pkgID, nil)
   129  	e.markUsedFeatures(n)
   130  
   131  	return e.expr(n), nil
   132  }
   133  
   134  func (e *exporter) toFile(v *adt.Vertex, x ast.Expr) (*ast.File, errors.Error) {
   135  	f := &ast.File{}
   136  
   137  	pkgName := ""
   138  	pkg := &ast.Package{}
   139  	for _, c := range v.Conjuncts {
   140  		f, _ := c.Source().(*ast.File)
   141  		if f == nil {
   142  			continue
   143  		}
   144  
   145  		if _, name, _ := internal.PackageInfo(f); name != "" {
   146  			pkgName = name
   147  		}
   148  
   149  		if e.cfg.ShowDocs {
   150  			if doc := internal.FileComment(f); doc != nil {
   151  				ast.AddComment(pkg, doc)
   152  			}
   153  		}
   154  	}
   155  
   156  	if pkgName != "" {
   157  		pkg.Name = ast.NewIdent(pkgName)
   158  		f.Decls = append(f.Decls, pkg)
   159  	}
   160  
   161  	switch st := x.(type) {
   162  	case nil:
   163  		panic("null input")
   164  
   165  	case *ast.StructLit:
   166  		f.Decls = append(f.Decls, st.Elts...)
   167  
   168  	default:
   169  		f.Decls = append(f.Decls, &ast.EmbedDecl{Expr: x})
   170  	}
   171  	if err := astutil.Sanitize(f); err != nil {
   172  		err := errors.Promote(err, "export")
   173  		return f, errors.Append(e.errs, err)
   174  	}
   175  
   176  	return f, nil
   177  }
   178  
   179  // File
   180  
   181  func Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) {
   182  	return Simplified.Vertex(r, pkgID, n)
   183  }
   184  
   185  func (p *Profile) Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) {
   186  	e := exporter{
   187  		ctx:   eval.NewContext(r, nil),
   188  		cfg:   p,
   189  		index: r,
   190  		pkgID: pkgID,
   191  	}
   192  	e.markUsedFeatures(n)
   193  	v := e.value(n, n.Conjuncts...)
   194  
   195  	return e.toFile(n, v)
   196  }
   197  
   198  func Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) {
   199  	return Simplified.Value(r, pkgID, n)
   200  }
   201  
   202  // Should take context.
   203  func (p *Profile) Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) {
   204  	e := exporter{
   205  		ctx:   eval.NewContext(r, nil),
   206  		cfg:   p,
   207  		index: r,
   208  		pkgID: pkgID,
   209  	}
   210  	e.markUsedFeatures(n)
   211  	v := e.value(n)
   212  	return v, e.errs
   213  }
   214  
   215  type exporter struct {
   216  	cfg  *Profile // Make value todo
   217  	errs errors.Error
   218  
   219  	ctx *adt.OpContext
   220  
   221  	index adt.StringIndexer
   222  	rand  *rand.Rand
   223  
   224  	// For resolving references.
   225  	stack []frame
   226  
   227  	inDefinition int // for close() wrapping.
   228  
   229  	// hidden label handling
   230  	pkgID  string
   231  	hidden map[string]adt.Feature // adt.InvalidFeatures means more than one.
   232  
   233  	// If a used feature maps to an expression, it means it is assigned to a
   234  	// unique let expression.
   235  	usedFeature map[adt.Feature]adt.Expr
   236  	labelAlias  map[adt.Expr]adt.Feature
   237  	valueAlias  map[*ast.Alias]*ast.Alias
   238  	letAlias    map[*ast.LetClause]*ast.LetClause
   239  
   240  	usedHidden map[string]bool
   241  }
   242  
   243  func newExporter(p *Profile, r adt.Runtime, pkgID string, v *adt.Vertex) *exporter {
   244  	return &exporter{
   245  		cfg:   p,
   246  		ctx:   eval.NewContext(r, v),
   247  		index: r,
   248  		pkgID: pkgID,
   249  	}
   250  }
   251  
   252  func (e *exporter) markUsedFeatures(x adt.Expr) {
   253  	e.usedFeature = make(map[adt.Feature]adt.Expr)
   254  
   255  	w := &walk.Visitor{}
   256  	w.Before = func(n adt.Node) bool {
   257  		switch x := n.(type) {
   258  		case *adt.Vertex:
   259  			if !x.IsData() {
   260  				for _, c := range x.Conjuncts {
   261  					w.Elem(c.Elem())
   262  				}
   263  			}
   264  
   265  		case *adt.DynamicReference:
   266  			if e.labelAlias == nil {
   267  				e.labelAlias = make(map[adt.Expr]adt.Feature)
   268  			}
   269  			// TODO: add preferred label.
   270  			e.labelAlias[x.Label] = adt.InvalidLabel
   271  
   272  		case *adt.LabelReference:
   273  		}
   274  		return true
   275  	}
   276  
   277  	w.Feature = func(f adt.Feature, src adt.Node) {
   278  		_, ok := e.usedFeature[f]
   279  
   280  		switch x := src.(type) {
   281  		case *adt.LetReference:
   282  			if !ok {
   283  				e.usedFeature[f] = x.X
   284  			}
   285  
   286  		default:
   287  			e.usedFeature[f] = nil
   288  		}
   289  	}
   290  
   291  	w.Elem(x)
   292  }
   293  
   294  func (e *exporter) getFieldAlias(f *ast.Field, name string) string {
   295  	a, ok := f.Label.(*ast.Alias)
   296  	if !ok {
   297  		a = &ast.Alias{
   298  			Ident: ast.NewIdent(e.uniqueAlias(name)),
   299  			Expr:  f.Label.(ast.Expr),
   300  		}
   301  		f.Label = a
   302  	}
   303  	return a.Ident.Name
   304  }
   305  
   306  func setFieldAlias(f *ast.Field, name string) {
   307  	if _, ok := f.Label.(*ast.Alias); !ok {
   308  		f.Label = &ast.Alias{
   309  			Ident: ast.NewIdent(name),
   310  			Expr:  f.Label.(ast.Expr),
   311  		}
   312  	}
   313  }
   314  
   315  func (e *exporter) markLets(n ast.Node) {
   316  	if n == nil {
   317  		return
   318  	}
   319  	ast.Walk(n, func(n ast.Node) bool {
   320  		switch v := n.(type) {
   321  		case *ast.StructLit:
   322  			e.markLetDecls(v.Elts)
   323  		case *ast.File:
   324  			e.markLetDecls(v.Decls)
   325  
   326  		case *ast.Field,
   327  			*ast.LetClause,
   328  			*ast.IfClause,
   329  			*ast.ForClause,
   330  			*ast.Comprehension:
   331  			return false
   332  		}
   333  		return true
   334  	}, nil)
   335  }
   336  
   337  func (e *exporter) markLetDecls(decls []ast.Decl) {
   338  	for _, d := range decls {
   339  		if let, ok := d.(*ast.LetClause); ok {
   340  			e.markLetAlias(let)
   341  		}
   342  	}
   343  }
   344  
   345  // markLetAlias inserts an uninitialized let clause into the current scope.
   346  // It gets initialized upon first usage.
   347  func (e *exporter) markLetAlias(x *ast.LetClause) {
   348  	// The created let clause is initialized upon first usage, and removed
   349  	// later if never referenced.
   350  	let := &ast.LetClause{}
   351  
   352  	if e.letAlias == nil {
   353  		e.letAlias = make(map[*ast.LetClause]*ast.LetClause)
   354  	}
   355  	e.letAlias[x] = let
   356  
   357  	scope := e.top().scope
   358  	scope.Elts = append(scope.Elts, let)
   359  }
   360  
   361  // In value mode, lets are only used if there wasn't an error.
   362  func filterUnusedLets(s *ast.StructLit) {
   363  	k := 0
   364  	for i, d := range s.Elts {
   365  		if let, ok := d.(*ast.LetClause); ok && let.Expr == nil {
   366  			continue
   367  		}
   368  		s.Elts[k] = s.Elts[i]
   369  		k++
   370  	}
   371  	s.Elts = s.Elts[:k]
   372  }
   373  
   374  // resolveLet actually parses the let expression.
   375  // If there was no recorded let expression, it expands the expression in place.
   376  func (e *exporter) resolveLet(x *adt.LetReference) ast.Expr {
   377  	letClause, _ := x.Src.Node.(*ast.LetClause)
   378  	let := e.letAlias[letClause]
   379  
   380  	switch {
   381  	case let == nil:
   382  		return e.expr(x.X)
   383  
   384  	case let.Expr == nil:
   385  		label := e.uniqueLetIdent(x.Label, x.X)
   386  
   387  		let.Ident = e.ident(label)
   388  		let.Expr = e.expr(x.X)
   389  	}
   390  
   391  	ident := ast.NewIdent(let.Ident.Name)
   392  	ident.Node = let
   393  	// TODO: set scope?
   394  	return ident
   395  }
   396  
   397  func (e *exporter) uniqueLetIdent(f adt.Feature, x adt.Expr) adt.Feature {
   398  	if e.usedFeature[f] == x {
   399  		return f
   400  	}
   401  
   402  	f, _ = e.uniqueFeature(f.IdentString(e.ctx))
   403  	e.usedFeature[f] = x
   404  	return f
   405  }
   406  
   407  func (e *exporter) uniqueAlias(name string) string {
   408  	f := adt.MakeIdentLabel(e.ctx, name, "")
   409  
   410  	if _, ok := e.usedFeature[f]; !ok {
   411  		e.usedFeature[f] = nil
   412  		return name
   413  	}
   414  
   415  	_, name = e.uniqueFeature(f.IdentString(e.ctx))
   416  	return name
   417  }
   418  
   419  // uniqueFeature returns a name for an identifier that uniquely identifies
   420  // the given expression. If the preferred name is already taken, a new globally
   421  // unique name of the form base_X ... base_XXXXXXXXXXXXXX is generated.
   422  //
   423  // It prefers short extensions over large ones, while ensuring the likelihood of
   424  // fast termination is high. There are at least two digits to make it visually
   425  // clearer this concerns a generated number.
   426  //
   427  func (e *exporter) uniqueFeature(base string) (f adt.Feature, name string) {
   428  	if e.rand == nil {
   429  		e.rand = rand.New(rand.NewSource(808))
   430  	}
   431  
   432  	// Try the first few numbers in sequence.
   433  	for i := 1; i < 5; i++ {
   434  		name := fmt.Sprintf("%s_%01X", base, i)
   435  		f := adt.MakeIdentLabel(e.ctx, name, "")
   436  		if _, ok := e.usedFeature[f]; !ok {
   437  			e.usedFeature[f] = nil
   438  			return f, name
   439  		}
   440  	}
   441  
   442  	const mask = 0xff_ffff_ffff_ffff // max bits; stay clear of int64 overflow
   443  	const shift = 4                  // rate of growth
   444  	digits := 1
   445  	for n := int64(0x10); ; n = int64(mask&((n<<shift)-1)) + 1 {
   446  		num := e.rand.Intn(int(n)-1) + 1
   447  		name := fmt.Sprintf("%[1]s_%0[2]*[3]X", base, digits, num)
   448  		f := adt.MakeIdentLabel(e.ctx, name, "")
   449  		if _, ok := e.usedFeature[f]; !ok {
   450  			e.usedFeature[f] = nil
   451  			return f, name
   452  		}
   453  		digits++
   454  	}
   455  }
   456  
   457  type frame struct {
   458  	scope *ast.StructLit
   459  
   460  	docSources []adt.Conjunct
   461  
   462  	// For resolving dynamic fields.
   463  	field     *ast.Field
   464  	labelExpr ast.Expr
   465  	upCount   int32 // for off-by-one handling
   466  
   467  	// labeled fields
   468  	fields map[adt.Feature]entry
   469  
   470  	// field to new field
   471  	mapped map[adt.Node]ast.Node
   472  }
   473  
   474  type entry struct {
   475  	alias      string
   476  	field      *ast.Field
   477  	node       ast.Node // How to reference. See astutil.Resolve
   478  	references []*ast.Ident
   479  }
   480  
   481  func (e *exporter) addField(label adt.Feature, f *ast.Field, n ast.Node) {
   482  	frame := e.top()
   483  	entry := frame.fields[label]
   484  	entry.field = f
   485  	entry.node = n
   486  	frame.fields[label] = entry
   487  }
   488  
   489  func (e *exporter) addEmbed(x ast.Expr) {
   490  	frame := e.top()
   491  	frame.scope.Elts = append(frame.scope.Elts, x)
   492  }
   493  
   494  func (e *exporter) pushFrame(conjuncts []adt.Conjunct) (s *ast.StructLit, saved []frame) {
   495  	saved = e.stack
   496  	s = &ast.StructLit{}
   497  	e.stack = append(e.stack, frame{
   498  		scope:      s,
   499  		mapped:     map[adt.Node]ast.Node{},
   500  		fields:     map[adt.Feature]entry{},
   501  		docSources: conjuncts,
   502  	})
   503  	return s, saved
   504  }
   505  
   506  func (e *exporter) popFrame(saved []frame) {
   507  	top := e.stack[len(e.stack)-1]
   508  
   509  	for _, f := range top.fields {
   510  		node := f.node
   511  		if f.alias != "" && f.field != nil {
   512  			setFieldAlias(f.field, f.alias)
   513  			node = f.field
   514  		}
   515  		for _, r := range f.references {
   516  			r.Node = node
   517  		}
   518  	}
   519  
   520  	e.stack = saved
   521  }
   522  
   523  func (e *exporter) top() *frame {
   524  	return &(e.stack[len(e.stack)-1])
   525  }
   526  
   527  func (e *exporter) frame(upCount int32) *frame {
   528  	for i := len(e.stack) - 1; i >= 0; i-- {
   529  		f := &(e.stack[i])
   530  		if upCount <= (f.upCount - 1) {
   531  			return f
   532  		}
   533  		upCount -= f.upCount
   534  	}
   535  	if debug {
   536  		// This may be valid when exporting incomplete references. These are
   537  		// not yet handled though, so find a way to catch them when debugging
   538  		// printing of values that are supposed to be complete.
   539  		panic("unreachable reference")
   540  	}
   541  
   542  	return &frame{}
   543  }
   544  
   545  func (e *exporter) setDocs(x adt.Node) {
   546  	f := e.stack[len(e.stack)-1]
   547  	f.docSources = []adt.Conjunct{adt.MakeRootConjunct(nil, x)}
   548  	e.stack[len(e.stack)-1] = f
   549  }
   550  
   551  // func (e *Exporter) promise(upCount int32, f completeFunc) {
   552  // 	e.todo = append(e.todo, f)
   553  // }
   554  
   555  func (e *exporter) errf(format string, args ...interface{}) *ast.BottomLit {
   556  	err := &exporterError{}
   557  	e.errs = errors.Append(e.errs, err)
   558  	return &ast.BottomLit{}
   559  }
   560  
   561  type errTODO errors.Error
   562  
   563  type exporterError struct {
   564  	errTODO
   565  }