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 }