cuelang.org/go@v0.10.1/internal/pkg/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 pkg 16 17 import ( 18 "encoding/json" 19 "fmt" 20 21 "cuelang.org/go/cue/errors" 22 "cuelang.org/go/cue/parser" 23 "cuelang.org/go/internal" 24 "cuelang.org/go/internal/core/adt" 25 "cuelang.org/go/internal/core/compile" 26 "cuelang.org/go/internal/core/convert" 27 ) 28 29 // A Builtin is a Builtin function or constant. 30 // 31 // A function may return and a constant may be any of the following types: 32 // 33 // error (translates to bottom) 34 // nil (translates to null) 35 // bool 36 // int* 37 // uint* 38 // float64 39 // string 40 // *big.Float 41 // *big.Int 42 // 43 // For any of the above, including interface{} and these types recursively: 44 // []T 45 // map[string]T 46 type Builtin struct { 47 Name string 48 Pkg adt.Feature 49 Params []Param 50 Result adt.Kind 51 Func func(c *CallCtxt) 52 Const string 53 } 54 55 type Param struct { 56 Kind adt.Kind 57 Value adt.Value // input constraint (may be nil) 58 } 59 60 type Package struct { 61 Native []*Builtin 62 CUE string 63 } 64 65 func (p *Package) MustCompile(ctx *adt.OpContext, importPath string) *adt.Vertex { 66 obj := &adt.Vertex{} 67 pkgLabel := ctx.StringLabel(importPath) 68 st := &adt.StructLit{} 69 if len(p.Native) > 0 { 70 obj.AddConjunct(adt.MakeRootConjunct(nil, st)) 71 } 72 for _, b := range p.Native { 73 b.Pkg = pkgLabel 74 75 f := ctx.StringLabel(b.Name) // never starts with _ 76 // n := &node{baseValue: newBase(imp.Path)} 77 var v adt.Expr 78 if b.Const != "" { 79 v = mustParseConstBuiltin(ctx, b.Name, b.Const) 80 } else { 81 v = ToBuiltin(b) 82 } 83 st.Decls = append(st.Decls, &adt.Field{ 84 Label: f, 85 Value: v, 86 }) 87 } 88 89 // Parse builtin CUE 90 if p.CUE != "" { 91 expr, err := parser.ParseExpr(importPath, p.CUE) 92 if err != nil { 93 panic(fmt.Errorf("could not parse %v: %v", p.CUE, err)) 94 } 95 c, err := compile.Expr(nil, ctx.Runtime, importPath, expr) 96 if err != nil { 97 panic(fmt.Errorf("could compile parse %v: %v", p.CUE, err)) 98 } 99 obj.AddConjunct(c) 100 } 101 102 // We could compile lazily, but this is easier for debugging. 103 obj.Finalize(ctx) 104 if err := obj.Err(ctx); err != nil { 105 panic(err.Err) 106 } 107 108 return obj 109 } 110 111 // ToBuiltin converts a Builtin into an adt.Builtin. 112 func ToBuiltin(b *Builtin) *adt.Builtin { 113 params := make([]adt.Param, len(b.Params)) 114 for i, p := range b.Params { 115 params[i].Value = p.Value 116 if params[i].Value == nil { 117 params[i].Value = &adt.BasicType{K: p.Kind} 118 } 119 } 120 121 x := &adt.Builtin{ 122 Params: params, 123 Result: b.Result, 124 Package: b.Pkg, 125 Name: b.Name, 126 } 127 x.Func = func(ctx *adt.OpContext, args []adt.Value) (ret adt.Expr) { 128 // call, _ := ctx.Source().(*ast.CallExpr) 129 c := &CallCtxt{ 130 ctx: ctx, 131 args: args, 132 builtin: b, 133 } 134 defer func() { 135 var errVal interface{} = c.Err 136 if err := recover(); err != nil { 137 errVal = err 138 } 139 ret = processErr(c, errVal, ret) 140 }() 141 b.Func(c) 142 switch v := c.Ret.(type) { 143 case nil: 144 // Validators may return a nil in case validation passes. 145 return nil 146 case *adt.Bottom: 147 // deal with API limitation: catch nil interface issue. 148 if v != nil { 149 return v 150 } 151 return nil 152 case adt.Value: 153 return v 154 case Bottomer: 155 // deal with API limitation: catch nil interface issue. 156 if b := v.Bottom(); b != nil { 157 return b 158 } 159 return nil 160 } 161 if c.Err != nil { 162 if _, ok := c.Err.(ValidationError); !ok || c.ctx.IsValidator { 163 return nil 164 } 165 } 166 return convert.GoValueToValue(ctx, c.Ret, true) 167 } 168 return x 169 } 170 171 // newConstBuiltin parses and creates any CUE expression that does not have 172 // fields. 173 func mustParseConstBuiltin(ctx adt.Runtime, name, val string) adt.Expr { 174 expr, err := parser.ParseExpr("<builtin:"+name+">", val) 175 if err != nil { 176 panic(err) 177 } 178 c, err := compile.Expr(nil, ctx, "_", expr) 179 if err != nil { 180 panic(err) 181 } 182 return c.Expr() 183 184 } 185 186 func (x *Builtin) name(ctx *adt.OpContext) string { 187 if x.Pkg == 0 { 188 return x.Name 189 } 190 return fmt.Sprintf("%s.%s", x.Pkg.StringValue(ctx), x.Name) 191 } 192 193 func processErr(call *CallCtxt, errVal interface{}, ret adt.Expr) adt.Expr { 194 ctx := call.ctx 195 switch err := errVal.(type) { 196 case nil: 197 case ValidationError: 198 if call.ctx.IsValidator { 199 ret = err.B 200 } 201 case *adt.Bottom: 202 ret = err 203 case *callError: 204 ret = err.b 205 case *json.MarshalerError: 206 if err, ok := err.Err.(Bottomer); ok { 207 if b := err.Bottom(); b != nil { 208 ret = b 209 } 210 } 211 case Bottomer: 212 ret = wrapCallErr(call, err.Bottom()) 213 214 case errors.Error: 215 // Convert lists of errors to a combined Bottom error. 216 if list := errors.Errors(err); len(list) != 0 && list[0] != errVal { 217 var errs *adt.Bottom 218 for _, err := range list { 219 if b, ok := processErr(call, err, nil).(*adt.Bottom); ok { 220 errs = adt.CombineErrors(nil, errs, b) 221 } 222 } 223 if errs != nil { 224 return errs 225 } 226 } 227 228 ret = wrapCallErr(call, &adt.Bottom{Err: err}) 229 case error: 230 if call.Err == internal.ErrIncomplete { 231 err := ctx.NewErrf("incomplete value") 232 err.Code = adt.IncompleteError 233 ret = err 234 } else { 235 // TODO: store the underlying error explicitly 236 ret = wrapCallErr(call, &adt.Bottom{Err: errors.Promote(err, "")}) 237 } 238 default: 239 // Likely a string passed to panic. 240 ret = wrapCallErr(call, &adt.Bottom{ 241 Err: errors.Newf(call.Pos(), "%s", err), 242 }) 243 } 244 return ret 245 }