cuelang.org/go@v0.10.1/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/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  	structParam = adt.Param{Value: &adt.BasicType{K: adt.StructKind}}
    28  	listParam   = adt.Param{Value: &adt.BasicType{K: adt.ListKind}}
    29  	intParam    = adt.Param{Value: &adt.BasicType{K: adt.IntKind}}
    30  )
    31  
    32  var lenBuiltin = &adt.Builtin{
    33  	Name:   "len",
    34  	Params: []adt.Param{{Value: &adt.BasicType{K: supportedByLen}}},
    35  	Result: adt.IntKind,
    36  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
    37  		v := args[0]
    38  		if x, ok := v.(*adt.Vertex); ok {
    39  			x.LockArcs = true
    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() && a.IsDefined(c) {
    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 m, ok := s.BaseValue.(*adt.StructMarker); ok && m.NeedClose {
    89  			return s
    90  		}
    91  		v := s.Clone()
    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.RawElems(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.RawElems(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  		closeInfo := c.CloseInfo()
   141  		v.AddConjunct(adt.MakeConjunct(nil,
   142  			&adt.DisjunctionExpr{Values: d, HasDefaults: false},
   143  			closeInfo,
   144  		))
   145  		v.CompleteArcs(c)
   146  		return v
   147  	},
   148  }
   149  
   150  var divBuiltin = &adt.Builtin{
   151  	Name:   "div",
   152  	Params: []adt.Param{intParam, intParam},
   153  	Result: adt.IntKind,
   154  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   155  		const name = "argument to div builtin"
   156  
   157  		return intDivOp(c, (*adt.OpContext).IntDiv, name, args)
   158  	},
   159  }
   160  
   161  var modBuiltin = &adt.Builtin{
   162  	Name:   "mod",
   163  	Params: []adt.Param{intParam, intParam},
   164  	Result: adt.IntKind,
   165  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   166  		const name = "argument to mod builtin"
   167  
   168  		return intDivOp(c, (*adt.OpContext).IntMod, name, args)
   169  	},
   170  }
   171  
   172  var quoBuiltin = &adt.Builtin{
   173  	Name:   "quo",
   174  	Params: []adt.Param{intParam, intParam},
   175  	Result: adt.IntKind,
   176  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   177  		const name = "argument to quo builtin"
   178  
   179  		return intDivOp(c, (*adt.OpContext).IntQuo, name, args)
   180  	},
   181  }
   182  
   183  var remBuiltin = &adt.Builtin{
   184  	Name:   "rem",
   185  	Params: []adt.Param{intParam, intParam},
   186  	Result: adt.IntKind,
   187  	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
   188  		const name = "argument to rem builtin"
   189  
   190  		return intDivOp(c, (*adt.OpContext).IntRem, name, args)
   191  	},
   192  }
   193  
   194  type intFunc func(c *adt.OpContext, x, y *adt.Num) adt.Value
   195  
   196  func intDivOp(c *adt.OpContext, fn intFunc, name string, args []adt.Value) adt.Value {
   197  	a := c.Num(args[0], name)
   198  	b := c.Num(args[1], name)
   199  
   200  	if c.HasErr() {
   201  		return nil
   202  	}
   203  
   204  	return fn(c, a, b)
   205  }