cuelang.org/go@v0.13.0/internal/core/compile/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 compile
    16  
    17  import (
    18  	"cuelang.org/go/cue/errors"
    19  	"cuelang.org/go/internal"
    20  	"cuelang.org/go/internal/core/adt"
    21  )
    22  
    23  // This file contains predeclared builtins.
    24  
    25  const supportedByLen = adt.StructKind | adt.BytesKind | adt.StringKind | adt.ListKind
    26  
    27  var (
    28  	structParam = adt.Param{Value: &adt.BasicType{K: adt.StructKind}}
    29  	listParam   = adt.Param{Value: &adt.BasicType{K: adt.ListKind}}
    30  	intParam    = adt.Param{Value: &adt.BasicType{K: adt.IntKind}}
    31  	topParam    = adt.Param{Value: &adt.BasicType{K: adt.TopKind}}
    32  )
    33  
    34  var lenBuiltin = &adt.Builtin{
    35  	Name:   "len",
    36  	Params: []adt.Param{{Value: &adt.BasicType{K: supportedByLen}}},
    37  	Result: adt.IntKind,
    38  	Func: func(call *adt.CallContext) adt.Expr {
    39  		c := call.OpContext()
    40  		args := call.Args()
    41  
    42  		v := args[0]
    43  		if x, ok := v.(*adt.Vertex); ok {
    44  			x.LockArcs = true
    45  			switch x.BaseValue.(type) {
    46  			case nil:
    47  				// This should not happen, but be defensive.
    48  				return c.NewErrf("unevaluated vertex")
    49  			case *adt.ListMarker:
    50  				return c.NewInt64(int64(len(x.Elems())), v)
    51  
    52  			case *adt.StructMarker:
    53  				n := 0
    54  				v, _ := v.(*adt.Vertex)
    55  				for _, a := range v.Arcs {
    56  					if a.Label.IsRegular() && a.IsDefined(c) {
    57  						n++
    58  					}
    59  				}
    60  				return c.NewInt64(int64(n), v)
    61  
    62  			default:
    63  				v = x.Value()
    64  			}
    65  		}
    66  
    67  		switch x := v.(type) {
    68  		case *adt.Bytes:
    69  			return c.NewInt64(int64(len(x.B)), v)
    70  		case *adt.String:
    71  			return c.NewInt64(int64(len(x.Str)), v)
    72  		default:
    73  			k := x.Kind()
    74  			if k&supportedByLen == adt.BottomKind {
    75  				return c.NewErrf("invalid argument type %v", k)
    76  			}
    77  			b := c.NewErrf("incomplete argument %s (type %v)", v, k)
    78  			b.Code = adt.IncompleteError
    79  			return b
    80  		}
    81  	},
    82  }
    83  
    84  var closeBuiltin = &adt.Builtin{
    85  	Name:   "close",
    86  	Params: []adt.Param{structParam},
    87  	Result: adt.StructKind,
    88  	Func: func(call *adt.CallContext) adt.Expr {
    89  		c := call.OpContext()
    90  		args := call.Args()
    91  
    92  		s, ok := args[0].(*adt.Vertex)
    93  		if !ok {
    94  			return c.NewErrf("struct argument must be concrete")
    95  		}
    96  		var v *adt.Vertex
    97  		if c.Version == internal.DevVersion {
    98  			// TODO(evalv3) this is a rather convoluted and inefficient way to
    99  			// accomplish signaling vertex should be closed. In most cases, it
   100  			// would suffice to set IsClosed in the CloseInfo. However, that
   101  			// does not cover all code paths. Consider simplifying this.
   102  			v = c.Wrap(s, c.CloseInfo())
   103  			v.ClosedNonRecursive = true
   104  		} else {
   105  			if m, ok := s.BaseValue.(*adt.StructMarker); ok && m.NeedClose {
   106  				return s
   107  			}
   108  			v = s.Clone()
   109  			v.BaseValue = &adt.StructMarker{NeedClose: true}
   110  		}
   111  		return v
   112  	},
   113  }
   114  
   115  var andBuiltin = &adt.Builtin{
   116  	Name:   "and",
   117  	Params: []adt.Param{listParam},
   118  	Result: adt.IntKind,
   119  	RawFunc: func(call *adt.CallContext) adt.Value {
   120  		c := call.OpContext()
   121  		arg := call.Expr(0)
   122  
   123  		list := c.RawElems(arg)
   124  		if len(list) == 0 {
   125  			return &adt.Top{}
   126  		}
   127  		a := []adt.Value{}
   128  		for _, c := range list {
   129  			a = append(a, c)
   130  		}
   131  		return &adt.Conjunction{Values: a}
   132  	},
   133  }
   134  
   135  var orBuiltin = &adt.Builtin{
   136  	Name:        "or",
   137  	Params:      []adt.Param{listParam},
   138  	Result:      adt.IntKind,
   139  	NonConcrete: true,
   140  	Func: func(call *adt.CallContext) adt.Expr {
   141  		c := call.OpContext()
   142  		args := call.Args()
   143  
   144  		d := []adt.Disjunct{}
   145  		for _, c := range c.RawElems(args[0]) {
   146  			d = append(d, adt.Disjunct{Val: c, Default: false})
   147  		}
   148  		if len(d) == 0 {
   149  			// TODO(manifest): This should not be unconditionally incomplete,
   150  			// but it requires results from comprehensions and all to have
   151  			// some special status. Maybe this can be solved by having results
   152  			// of list comprehensions be open if they result from iterating over
   153  			// an open list or struct. This would actually be exactly what
   154  			// that means. The error here could then only add an incomplete
   155  			// status if the source is open.
   156  			return &adt.Bottom{
   157  				Code: adt.IncompleteError,
   158  				// TODO: get and set Vertex
   159  				Err: errors.Newf(c.Pos(), "empty list in call to or"),
   160  			}
   161  		}
   162  		v := &adt.Vertex{}
   163  		// TODO: make a Disjunction.
   164  		closeInfo := c.CloseInfo()
   165  		v.AddConjunct(adt.MakeConjunct(nil,
   166  			&adt.DisjunctionExpr{Values: d, HasDefaults: false},
   167  			closeInfo,
   168  		))
   169  		v.CompleteArcs(c)
   170  		return v
   171  	},
   172  }
   173  
   174  var divBuiltin = &adt.Builtin{
   175  	Name:   "div",
   176  	Params: []adt.Param{intParam, intParam},
   177  	Result: adt.IntKind,
   178  	Func: func(call *adt.CallContext) adt.Expr {
   179  		c := call.OpContext()
   180  		args := call.Args()
   181  
   182  		const name = "argument to div builtin"
   183  
   184  		return intDivOp(c, (*adt.OpContext).IntDiv, name, args)
   185  	},
   186  }
   187  
   188  var modBuiltin = &adt.Builtin{
   189  	Name:   "mod",
   190  	Params: []adt.Param{intParam, intParam},
   191  	Result: adt.IntKind,
   192  	Func: func(call *adt.CallContext) adt.Expr {
   193  		c := call.OpContext()
   194  		args := call.Args()
   195  
   196  		const name = "argument to mod builtin"
   197  
   198  		return intDivOp(c, (*adt.OpContext).IntMod, name, args)
   199  	},
   200  }
   201  
   202  var quoBuiltin = &adt.Builtin{
   203  	Name:   "quo",
   204  	Params: []adt.Param{intParam, intParam},
   205  	Result: adt.IntKind,
   206  	Func: func(call *adt.CallContext) adt.Expr {
   207  		c := call.OpContext()
   208  		args := call.Args()
   209  
   210  		const name = "argument to quo builtin"
   211  
   212  		return intDivOp(c, (*adt.OpContext).IntQuo, name, args)
   213  	},
   214  }
   215  
   216  var remBuiltin = &adt.Builtin{
   217  	Name:   "rem",
   218  	Params: []adt.Param{intParam, intParam},
   219  	Result: adt.IntKind,
   220  	Func: func(call *adt.CallContext) adt.Expr {
   221  		c := call.OpContext()
   222  		args := call.Args()
   223  
   224  		const name = "argument to rem builtin"
   225  
   226  		return intDivOp(c, (*adt.OpContext).IntRem, name, args)
   227  	},
   228  }
   229  
   230  type intFunc func(c *adt.OpContext, x, y *adt.Num) adt.Value
   231  
   232  func intDivOp(c *adt.OpContext, fn intFunc, name string, args []adt.Value) adt.Value {
   233  	a := c.Num(args[0], name)
   234  	b := c.Num(args[1], name)
   235  
   236  	if c.HasErr() {
   237  		return nil
   238  	}
   239  
   240  	return fn(c, a, b)
   241  }