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 }