cuelang.org/go@v0.13.0/internal/pkg/builtin.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 pkg
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  
    21  	"cuelang.org/go/cue/errors"
    22  	"cuelang.org/go/cue/parser"
    23  	"cuelang.org/go/internal"
    24  	"cuelang.org/go/internal/core/adt"
    25  	"cuelang.org/go/internal/core/compile"
    26  	"cuelang.org/go/internal/core/convert"
    27  )
    28  
    29  // A Builtin is a Builtin function or constant.
    30  //
    31  // A function may return and a constant may be any of the following types:
    32  //
    33  //	error (translates to bottom)
    34  //	nil   (translates to null)
    35  //	bool
    36  //	int*
    37  //	uint*
    38  //	float64
    39  //	string
    40  //	*big.Float
    41  //	*big.Int
    42  //
    43  //	For any of the above, including interface{} and these types recursively:
    44  //	[]T
    45  //	map[string]T
    46  type Builtin struct {
    47  	Name        string
    48  	Pkg         adt.Feature
    49  	Params      []Param
    50  	Result      adt.Kind
    51  	NonConcrete bool
    52  	Func        func(c *CallCtxt)
    53  	Const       string
    54  }
    55  
    56  type Param struct {
    57  	Kind  adt.Kind
    58  	Value adt.Value // input constraint (may be nil)
    59  }
    60  
    61  type Package struct {
    62  	Native []*Builtin
    63  	CUE    string
    64  }
    65  
    66  func (p *Package) MustCompile(ctx *adt.OpContext, importPath string) *adt.Vertex {
    67  	obj := &adt.Vertex{}
    68  	pkgLabel := ctx.StringLabel(importPath)
    69  	st := &adt.StructLit{}
    70  	if len(p.Native) > 0 {
    71  		obj.AddConjunct(adt.MakeRootConjunct(nil, st))
    72  	}
    73  	for _, b := range p.Native {
    74  		b.Pkg = pkgLabel
    75  
    76  		f := ctx.StringLabel(b.Name) // never starts with _
    77  		// n := &node{baseValue: newBase(imp.Path)}
    78  		var v adt.Expr
    79  		if b.Const != "" {
    80  			v = mustParseConstBuiltin(ctx, b.Name, b.Const)
    81  		} else {
    82  			v = ToBuiltin(b)
    83  		}
    84  		st.Decls = append(st.Decls, &adt.Field{
    85  			Label: f,
    86  			Value: v,
    87  		})
    88  	}
    89  
    90  	// Parse builtin CUE
    91  	if p.CUE != "" {
    92  		expr, err := parser.ParseExpr(importPath, p.CUE)
    93  		if err != nil {
    94  			panic(fmt.Errorf("could not parse %v: %v", p.CUE, err))
    95  		}
    96  		c, err := compile.Expr(nil, ctx.Runtime, importPath, expr)
    97  		if err != nil {
    98  			panic(fmt.Errorf("could compile parse %v: %v", p.CUE, err))
    99  		}
   100  		obj.AddConjunct(c)
   101  	}
   102  
   103  	// We could compile lazily, but this is easier for debugging.
   104  	obj.Finalize(ctx)
   105  	if err := obj.Err(ctx); err != nil {
   106  		panic(err.Err)
   107  	}
   108  
   109  	return obj
   110  }
   111  
   112  // ToBuiltin converts a Builtin into an adt.Builtin.
   113  func ToBuiltin(b *Builtin) *adt.Builtin {
   114  	params := make([]adt.Param, len(b.Params))
   115  	for i, p := range b.Params {
   116  		params[i].Value = p.Value
   117  		if params[i].Value == nil {
   118  			params[i].Value = &adt.BasicType{K: p.Kind}
   119  		}
   120  	}
   121  
   122  	x := &adt.Builtin{
   123  		Params:      params,
   124  		Result:      b.Result,
   125  		NonConcrete: b.NonConcrete,
   126  		Package:     b.Pkg,
   127  		Name:        b.Name,
   128  	}
   129  	x.Func = func(call *adt.CallContext) (ret adt.Expr) {
   130  		ctx := call.OpContext()
   131  		args := call.Args()
   132  
   133  		// call, _ := ctx.Source().(*ast.CallExpr)
   134  		c := &CallCtxt{
   135  			CallContext: call,
   136  			ctx:         ctx,
   137  			args:        args,
   138  			builtin:     b,
   139  		}
   140  		defer func() {
   141  			var errVal interface{} = c.Err
   142  			if err := recover(); err != nil {
   143  				errVal = err
   144  			}
   145  			ret = processErr(c, errVal, ret)
   146  		}()
   147  		b.Func(c)
   148  		switch v := c.Ret.(type) {
   149  		case nil:
   150  			// Validators may return a nil in case validation passes.
   151  			return nil
   152  		case *adt.Bottom:
   153  			// deal with API limitation: catch nil interface issue.
   154  			if v != nil {
   155  				return v
   156  			}
   157  			return nil
   158  		case adt.Value:
   159  			return v
   160  		case Bottomer:
   161  			// deal with API limitation: catch nil interface issue.
   162  			if b := v.Bottom(); b != nil {
   163  				return b
   164  			}
   165  			return nil
   166  		}
   167  		if c.Err != nil {
   168  			if _, ok := c.Err.(ValidationError); !ok || c.ctx.IsValidator {
   169  				return nil
   170  			}
   171  		}
   172  		return convert.GoValueToValue(ctx, c.Ret, true)
   173  	}
   174  	return x
   175  }
   176  
   177  // newConstBuiltin parses and creates any CUE expression that does not have
   178  // fields.
   179  func mustParseConstBuiltin(ctx adt.Runtime, name, val string) adt.Expr {
   180  	expr, err := parser.ParseExpr("<builtin:"+name+">", val)
   181  	if err != nil {
   182  		panic(err)
   183  	}
   184  	c, err := compile.Expr(nil, ctx, "_", expr)
   185  	if err != nil {
   186  		panic(err)
   187  	}
   188  	return c.Expr()
   189  
   190  }
   191  
   192  func (x *Builtin) name(ctx *adt.OpContext) string {
   193  	if x.Pkg == 0 {
   194  		return x.Name
   195  	}
   196  	return fmt.Sprintf("%s.%s", x.Pkg.StringValue(ctx), x.Name)
   197  }
   198  
   199  func processErr(call *CallCtxt, errVal interface{}, ret adt.Expr) adt.Expr {
   200  	ctx := call.ctx
   201  	switch err := errVal.(type) {
   202  	case nil:
   203  	case ValidationError:
   204  		if call.ctx.IsValidator {
   205  			ret = err.B
   206  		}
   207  	case *adt.Bottom:
   208  		ret = err
   209  	case *callError:
   210  		ret = err.b
   211  	case *json.MarshalerError:
   212  		if err, ok := err.Err.(Bottomer); ok {
   213  			if b := err.Bottom(); b != nil {
   214  				ret = b
   215  			}
   216  		}
   217  	case Bottomer:
   218  		ret = wrapCallErr(call, err.Bottom())
   219  
   220  	case errors.Error:
   221  		// Convert lists of errors to a combined Bottom error.
   222  		if list := errors.Errors(err); len(list) != 0 && list[0] != errVal {
   223  			var errs *adt.Bottom
   224  			for _, err := range list {
   225  				if b, ok := processErr(call, err, nil).(*adt.Bottom); ok {
   226  					errs = adt.CombineErrors(nil, errs, b)
   227  				}
   228  			}
   229  			if errs != nil {
   230  				return errs
   231  			}
   232  		}
   233  
   234  		ret = wrapCallErr(call, &adt.Bottom{Err: err})
   235  	case error:
   236  		if call.Err == internal.ErrIncomplete {
   237  			err := ctx.NewErrf("incomplete value")
   238  			err.Code = adt.IncompleteError
   239  			ret = err
   240  		} else {
   241  			// TODO: store the underlying error explicitly
   242  			ret = wrapCallErr(call, &adt.Bottom{Err: errors.Promote(err, "")})
   243  		}
   244  	default:
   245  		// Likely a string passed to panic.
   246  		ret = wrapCallErr(call, &adt.Bottom{
   247  			Err: errors.Newf(call.Pos(), "%s", err),
   248  		})
   249  	}
   250  	return ret
   251  }