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