github.com/solo-io/cue@v0.4.7/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  	"github.com/solo-io/cue/cue/errors"
    19  	"github.com/solo-io/cue/internal/core/adt"
    20  )
    21  
    22  // This file contains predeclared builtins.
    23  
    24  const supportedByLen = adt.StructKind | adt.BytesKind | adt.StringKind | adt.ListKind
    25  
    26  var (
    27  	stringParam = adt.Param{Value: &adt.BasicType{K: adt.StringKind}}
    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  )
    32  
    33  var lenBuiltin = &adt.Builtin{
    34  	Name:   "len",
    35  	Params: []adt.Param{{Value: &adt.BasicType{K: supportedByLen}}},
    36  	Result: adt.IntKind,
    37  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
    38  		v := args[0]
    39  		if x, ok := v.(*adt.Vertex); ok {
    40  			switch x.BaseValue.(type) {
    41  			case nil:
    42  				// This should not happen, but be defensive.
    43  				return c.NewErrf("unevaluated vertex")
    44  			case *adt.ListMarker:
    45  				return c.NewInt64(int64(len(x.Elems())), v)
    46  
    47  			case *adt.StructMarker:
    48  				n := 0
    49  				v, _ := v.(*adt.Vertex)
    50  				for _, a := range v.Arcs {
    51  					if a.Label.IsRegular() {
    52  						n++
    53  					}
    54  				}
    55  				return c.NewInt64(int64(n), v)
    56  
    57  			default:
    58  				v = x.Value()
    59  			}
    60  		}
    61  
    62  		switch x := v.(type) {
    63  		case *adt.Bytes:
    64  			return c.NewInt64(int64(len(x.B)), v)
    65  		case *adt.String:
    66  			return c.NewInt64(int64(len(x.Str)), v)
    67  		default:
    68  			k := x.Kind()
    69  			if k&supportedByLen == adt.BottomKind {
    70  				return c.NewErrf("invalid argument type %v", k)
    71  			}
    72  			b := c.NewErrf("incomplete argument %s (type %v)", v, k)
    73  			b.Code = adt.IncompleteError
    74  			return b
    75  		}
    76  	},
    77  }
    78  
    79  var closeBuiltin = &adt.Builtin{
    80  	Name:   "close",
    81  	Params: []adt.Param{structParam},
    82  	Result: adt.StructKind,
    83  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
    84  		s, ok := args[0].(*adt.Vertex)
    85  		if !ok {
    86  			return c.NewErrf("struct argument must be concrete")
    87  		}
    88  		if s.IsClosedStruct() {
    89  			return s
    90  		}
    91  		v := *s
    92  		// TODO(perf): do not copy the arc, but rather find a way to mark the
    93  		// calling nodeContext.
    94  		v.BaseValue = &adt.StructMarker{NeedClose: true}
    95  		return &v
    96  	},
    97  }
    98  
    99  var andBuiltin = &adt.Builtin{
   100  	Name:   "and",
   101  	Params: []adt.Param{listParam},
   102  	Result: adt.IntKind,
   103  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   104  		list := c.Elems(args[0])
   105  		if len(list) == 0 {
   106  			return &adt.Top{}
   107  		}
   108  		a := []adt.Value{}
   109  		for _, c := range list {
   110  			a = append(a, c)
   111  		}
   112  		return &adt.Conjunction{Values: a}
   113  	},
   114  }
   115  
   116  var orBuiltin = &adt.Builtin{
   117  	Name:   "or",
   118  	Params: []adt.Param{listParam},
   119  	Result: adt.IntKind,
   120  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   121  		d := []adt.Disjunct{}
   122  		for _, c := range c.Elems(args[0]) {
   123  			d = append(d, adt.Disjunct{Val: c, Default: false})
   124  		}
   125  		if len(d) == 0 {
   126  			// TODO(manifest): This should not be unconditionally incomplete,
   127  			// but it requires results from comprehensions and all to have
   128  			// some special status. Maybe this can be solved by having results
   129  			// of list comprehensions be open if they result from iterating over
   130  			// an open list or struct. This would actually be exactly what
   131  			// that means. The error here could then only add an incomplete
   132  			// status if the source is open.
   133  			return &adt.Bottom{
   134  				Code: adt.IncompleteError,
   135  				Err:  errors.Newf(c.Pos(), "empty list in call to or"),
   136  			}
   137  		}
   138  		v := &adt.Vertex{}
   139  		// TODO: make a Disjunction.
   140  		v.AddConjunct(adt.MakeRootConjunct(nil,
   141  			&adt.DisjunctionExpr{Values: d, HasDefaults: false},
   142  		))
   143  		c.Unify(v, adt.Finalized)
   144  		return v
   145  	},
   146  }
   147  
   148  var divBuiltin = &adt.Builtin{
   149  	Name:   "div",
   150  	Params: []adt.Param{intParam, intParam},
   151  	Result: adt.IntKind,
   152  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   153  		const name = "argument to div builtin"
   154  
   155  		return intDivOp(c, (*adt.OpContext).IntDiv, name, args)
   156  	},
   157  }
   158  
   159  var modBuiltin = &adt.Builtin{
   160  	Name:   "mod",
   161  	Params: []adt.Param{intParam, intParam},
   162  	Result: adt.IntKind,
   163  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   164  		const name = "argument to mod builtin"
   165  
   166  		return intDivOp(c, (*adt.OpContext).IntMod, name, args)
   167  	},
   168  }
   169  
   170  var quoBuiltin = &adt.Builtin{
   171  	Name:   "quo",
   172  	Params: []adt.Param{intParam, intParam},
   173  	Result: adt.IntKind,
   174  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   175  		const name = "argument to quo builtin"
   176  
   177  		return intDivOp(c, (*adt.OpContext).IntQuo, name, args)
   178  	},
   179  }
   180  
   181  var remBuiltin = &adt.Builtin{
   182  	Name:   "rem",
   183  	Params: []adt.Param{intParam, intParam},
   184  	Result: adt.IntKind,
   185  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   186  		const name = "argument to rem builtin"
   187  
   188  		return intDivOp(c, (*adt.OpContext).IntRem, name, args)
   189  	},
   190  }
   191  
   192  type intFunc func(c *adt.OpContext, x, y *adt.Num) adt.Value
   193  
   194  func intDivOp(c *adt.OpContext, fn intFunc, name string, args []adt.Value) adt.Value {
   195  	a := c.Num(args[0], name)
   196  	b := c.Num(args[1], name)
   197  
   198  	if c.HasErr() {
   199  		return nil
   200  	}
   201  
   202  	return fn(c, a, b)
   203  }