Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 1 | // Copyright 2018 The 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 | |
Marcel van Lohuizen | 7b5d2fd | 2018-12-11 16:34:56 +0100 | [diff] [blame] | 15 | //go:generate go run gen.go |
Marcel van Lohuizen | d4847d9 | 2019-02-18 23:27:34 +0100 | [diff] [blame] | 16 | //go:generate goimports -w builtins.go |
Marcel van Lohuizen | 7b5d2fd | 2018-12-11 16:34:56 +0100 | [diff] [blame] | 17 | |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 18 | package cue |
| 19 | |
| 20 | import ( |
| 21 | "fmt" |
| 22 | "io" |
| 23 | "math/big" |
| 24 | "path" |
| 25 | "reflect" |
| 26 | "sort" |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 27 | "strings" |
| 28 | |
| 29 | "cuelang.org/go/cue/ast" |
Marcel van Lohuizen | 8bc02e5 | 2019-04-01 13:14:07 +0200 | [diff] [blame] | 30 | "cuelang.org/go/cue/parser" |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 31 | "github.com/cockroachdb/apd" |
| 32 | ) |
| 33 | |
| 34 | // A builtin is a builtin function or constant. |
| 35 | // |
| 36 | // A function may return and a constant may be any of the following types: |
| 37 | // |
| 38 | // error (translates to bottom) |
| 39 | // nil (translates to null) |
| 40 | // bool |
| 41 | // int* |
| 42 | // uint* |
| 43 | // float64 |
| 44 | // string |
| 45 | // *big.Float |
| 46 | // *big.Int |
| 47 | // |
| 48 | // For any of the above, including interface{} and these types recursively: |
| 49 | // []T |
| 50 | // map[string]T |
| 51 | // |
| 52 | type builtin struct { |
| 53 | baseValue |
| 54 | Name string |
| 55 | Params []kind |
| 56 | Result kind |
| 57 | Func func(c *callCtxt) |
| 58 | // Const interface{} |
Marcel van Lohuizen | 8bc02e5 | 2019-04-01 13:14:07 +0200 | [diff] [blame] | 59 | Const string |
| 60 | } |
| 61 | |
Marcel van Lohuizen | 5733336 | 2019-04-01 14:35:09 +0200 | [diff] [blame] | 62 | type builtinPkg struct { |
| 63 | native []*builtin |
| 64 | cue string |
| 65 | } |
| 66 | |
| 67 | func mustCompileBuiltins(ctx *context, p *builtinPkg) *structLit { |
Marcel van Lohuizen | 8bc02e5 | 2019-04-01 13:14:07 +0200 | [diff] [blame] | 68 | obj := &structLit{} |
Marcel van Lohuizen | 5733336 | 2019-04-01 14:35:09 +0200 | [diff] [blame] | 69 | for _, b := range p.native { |
Marcel van Lohuizen | 8bc02e5 | 2019-04-01 13:14:07 +0200 | [diff] [blame] | 70 | f := ctx.label(b.Name, false) // never starts with _ |
| 71 | // n := &node{baseValue: newBase(imp.Path)} |
| 72 | var v evaluated = b |
| 73 | if b.Const != "" { |
| 74 | v = mustParseConstBuiltin(ctx, b.Name, b.Const) |
| 75 | } |
| 76 | obj.arcs = append(obj.arcs, arc{feature: f, v: v}) |
| 77 | } |
| 78 | sort.Sort(obj) |
Marcel van Lohuizen | 5733336 | 2019-04-01 14:35:09 +0200 | [diff] [blame] | 79 | |
| 80 | // Parse builtin CUE |
| 81 | if p.cue != "" { |
| 82 | expr, err := parser.ParseExpr(ctx.index.fset, "<builtinPkg>", p.cue) |
| 83 | if err != nil { |
| 84 | fmt.Println(p.cue) |
| 85 | panic(err) |
| 86 | } |
| 87 | pkg := evalExpr(ctx.index, obj, expr).(*structLit) |
| 88 | for _, a := range pkg.arcs { |
| 89 | // Discard option status and attributes at top level. |
| 90 | // TODO: filter on capitalized fields? |
| 91 | obj.insertValue(ctx, a.feature, false, a.v, nil) |
| 92 | } |
| 93 | } |
| 94 | |
Marcel van Lohuizen | 8bc02e5 | 2019-04-01 13:14:07 +0200 | [diff] [blame] | 95 | return obj |
| 96 | } |
| 97 | |
| 98 | // newConstBuiltin parses and creates any CUE expression that does not have |
| 99 | // fields. |
| 100 | func mustParseConstBuiltin(ctx *context, name, val string) evaluated { |
| 101 | expr, err := parser.ParseExpr(ctx.index.fset, "<builtin:"+name+">", val) |
| 102 | if err != nil { |
| 103 | panic(err) |
| 104 | } |
| 105 | v := newVisitor(ctx.index, nil, nil, nil) |
| 106 | value := v.walk(expr) |
| 107 | return value.evalPartial(ctx) |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 108 | } |
| 109 | |
| 110 | var _ caller = &builtin{} |
| 111 | |
| 112 | var lenBuiltin = &builtin{ |
| 113 | Name: "len", |
| 114 | Params: []kind{stringKind | bytesKind | listKind | structKind}, |
| 115 | Result: intKind, |
| 116 | Func: func(c *callCtxt) { |
| 117 | v := c.value(0) |
| 118 | switch v.Kind() { |
| 119 | case StructKind: |
| 120 | s, _ := v.structVal(c.ctx) |
| 121 | c.ret = s.Len() |
| 122 | case ListKind: |
| 123 | i := 0 |
| 124 | iter, _ := v.List() |
| 125 | for ; iter.Next(); i++ { |
| 126 | } |
| 127 | c.ret = i |
| 128 | case BytesKind: |
| 129 | b, _ := v.Bytes() |
| 130 | c.ret = len(b) |
| 131 | case StringKind: |
| 132 | s, _ := v.String() |
| 133 | c.ret = len(s) |
| 134 | } |
| 135 | }, |
| 136 | } |
| 137 | |
| 138 | func (x *builtin) kind() kind { |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 139 | return lambdaKind |
| 140 | } |
| 141 | |
| 142 | func (x *builtin) evalPartial(ctx *context) evaluated { |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 143 | return x |
| 144 | } |
| 145 | |
| 146 | func (x *builtin) subsumesImpl(ctx *context, v value, mode subsumeMode) bool { |
| 147 | if y, ok := v.(*builtin); ok { |
| 148 | return x == y |
| 149 | } |
| 150 | return false |
| 151 | } |
| 152 | |
| 153 | func (x *builtin) call(ctx *context, src source, args ...evaluated) (ret value) { |
| 154 | if x.Func == nil { |
| 155 | return ctx.mkErr(x, "Builtin %q is not a function", x.Name) |
| 156 | } |
| 157 | if len(x.Params) != len(args) { |
| 158 | return ctx.mkErr(src, x, "number of arguments does not match (%d vs %d)", |
| 159 | len(x.Params), len(args)) |
| 160 | } |
| 161 | for i, a := range args { |
| 162 | if x.Params[i] != bottomKind { |
| 163 | if unifyType(x.Params[i], a.kind()) == bottomKind { |
| 164 | return ctx.mkErr(src, x, "argument %d requires type %v, found %v", i+1, x.Params[i], a.kind()) |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | call := callCtxt{src: src, ctx: ctx, args: args} |
| 169 | defer func() { |
| 170 | var errVal interface{} = call.err |
| 171 | if err := recover(); err != nil { |
| 172 | errVal = err |
| 173 | } |
| 174 | switch err := errVal.(type) { |
| 175 | case nil: |
| 176 | case *bottom: |
| 177 | ret = err |
| 178 | default: |
| 179 | ret = ctx.mkErr(src, x, "call error: %v", err) |
| 180 | } |
| 181 | }() |
| 182 | x.Func(&call) |
| 183 | return convert(ctx, x, call.ret) |
| 184 | } |
| 185 | |
| 186 | // callCtxt is passed to builtin implementations. |
| 187 | type callCtxt struct { |
| 188 | src source |
| 189 | ctx *context |
| 190 | args []evaluated |
| 191 | err error |
| 192 | ret interface{} |
| 193 | } |
| 194 | |
Marcel van Lohuizen | 8bc02e5 | 2019-04-01 13:14:07 +0200 | [diff] [blame] | 195 | var builtins = map[string]*structLit{} |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 196 | |
Marcel van Lohuizen | 5733336 | 2019-04-01 14:35:09 +0200 | [diff] [blame] | 197 | func initBuiltins(pkgs map[string]*builtinPkg) { |
Marcel van Lohuizen | 8bc02e5 | 2019-04-01 13:14:07 +0200 | [diff] [blame] | 198 | ctx := sharedIndex.newContext() |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 199 | for k, b := range pkgs { |
Marcel van Lohuizen | 8bc02e5 | 2019-04-01 13:14:07 +0200 | [diff] [blame] | 200 | e := mustCompileBuiltins(ctx, b) |
| 201 | builtins[k] = e |
| 202 | builtins["-/"+path.Base(k)] = e |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 203 | } |
| 204 | } |
| 205 | |
| 206 | func getBuiltinShorthandPkg(ctx *context, shorthand string) *structLit { |
| 207 | return getBuiltinPkg(ctx, "-/"+shorthand) |
| 208 | } |
| 209 | |
| 210 | func getBuiltinPkg(ctx *context, path string) *structLit { |
| 211 | p, ok := builtins[path] |
| 212 | if !ok { |
| 213 | return nil |
| 214 | } |
Marcel van Lohuizen | 8bc02e5 | 2019-04-01 13:14:07 +0200 | [diff] [blame] | 215 | return p |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | // do returns whether the call should be done. |
| 219 | func (c *callCtxt) do() bool { |
| 220 | return c.err == nil |
| 221 | } |
| 222 | |
| 223 | func (c *callCtxt) value(i int) Value { |
| 224 | return newValueRoot(c.ctx, c.args[i]) |
| 225 | } |
| 226 | |
| 227 | func (c *callCtxt) int(i int) int { return int(c.intValue(i, 64)) } |
| 228 | func (c *callCtxt) int8(i int) int8 { return int8(c.intValue(i, 8)) } |
| 229 | func (c *callCtxt) int16(i int) int16 { return int16(c.intValue(i, 16)) } |
| 230 | func (c *callCtxt) int32(i int) int32 { return int32(c.intValue(i, 32)) } |
| 231 | func (c *callCtxt) rune(i int) rune { return rune(c.intValue(i, 32)) } |
| 232 | func (c *callCtxt) int64(i int) int64 { return int64(c.intValue(i, 64)) } |
| 233 | |
| 234 | func (c *callCtxt) intValue(i, bits int) int64 { |
| 235 | x := newValueRoot(c.ctx, c.args[i]) |
| 236 | n, err := x.Int(nil) |
| 237 | if err != nil { |
| 238 | c.err = c.ctx.mkErr(c.src, "argument %d must be in int, found number", i) |
| 239 | return 0 |
| 240 | } |
| 241 | if n.BitLen() > bits { |
| 242 | c.err = c.ctx.mkErr(c.src, err, "argument %d out of range: has %d > %d bits", n.BitLen(), bits) |
| 243 | } |
| 244 | res, _ := x.Int64() |
| 245 | return res |
| 246 | } |
| 247 | |
| 248 | func (c *callCtxt) uint(i int) uint { return uint(c.uintValue(i, 64)) } |
| 249 | func (c *callCtxt) uint8(i int) uint8 { return uint8(c.uintValue(i, 8)) } |
| 250 | func (c *callCtxt) byte(i int) uint8 { return byte(c.uintValue(i, 8)) } |
| 251 | func (c *callCtxt) uint16(i int) uint16 { return uint16(c.uintValue(i, 16)) } |
| 252 | func (c *callCtxt) uint32(i int) uint32 { return uint32(c.uintValue(i, 32)) } |
| 253 | func (c *callCtxt) uint64(i int) uint64 { return uint64(c.uintValue(i, 64)) } |
| 254 | |
| 255 | func (c *callCtxt) uintValue(i, bits int) uint64 { |
| 256 | x := newValueRoot(c.ctx, c.args[i]) |
| 257 | n, err := x.Int(nil) |
| 258 | if err != nil { |
| 259 | c.err = c.ctx.mkErr(c.src, "argument %d must be an integer", i) |
| 260 | return 0 |
| 261 | } |
| 262 | if n.Sign() < 0 { |
| 263 | c.err = c.ctx.mkErr(c.src, "argument %d must be a positive integer", i) |
| 264 | return 0 |
| 265 | } |
| 266 | if n.BitLen() > bits { |
| 267 | c.err = c.ctx.mkErr(c.src, err, "argument %d out of range: has %d > %d bits", i, n.BitLen(), bits) |
| 268 | } |
| 269 | res, _ := x.Uint64() |
| 270 | return res |
| 271 | } |
| 272 | |
| 273 | func (c *callCtxt) float64(i int) float64 { |
| 274 | x := newValueRoot(c.ctx, c.args[i]) |
| 275 | res, err := x.Float64() |
| 276 | if err != nil { |
| 277 | c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) |
| 278 | return 0 |
| 279 | } |
| 280 | return res |
| 281 | } |
| 282 | |
| 283 | func (c *callCtxt) bigInt(i int) *big.Int { |
| 284 | x := newValueRoot(c.ctx, c.args[i]) |
| 285 | n, err := x.Int(nil) |
| 286 | if err != nil { |
| 287 | c.err = c.ctx.mkErr(c.src, "argument %d must be in int, found number", i) |
| 288 | return nil |
| 289 | } |
| 290 | return n |
| 291 | } |
| 292 | |
| 293 | func (c *callCtxt) bigFloat(i int) *big.Float { |
| 294 | x := newValueRoot(c.ctx, c.args[i]) |
| 295 | var mant big.Int |
| 296 | exp, err := x.MantExp(&mant) |
| 297 | if err != nil { |
| 298 | c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) |
| 299 | return nil |
| 300 | } |
| 301 | f := &big.Float{} |
| 302 | f.SetInt(&mant) |
| 303 | if exp != 0 { |
| 304 | var g big.Float |
| 305 | e := big.NewInt(int64(exp)) |
| 306 | f.Mul(f, g.SetInt(e.Exp(ten, e, nil))) |
| 307 | } |
| 308 | return f |
| 309 | } |
| 310 | |
| 311 | func (c *callCtxt) string(i int) string { |
| 312 | x := newValueRoot(c.ctx, c.args[i]) |
| 313 | v, err := x.String() |
| 314 | if err != nil { |
| 315 | c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) |
| 316 | return "" |
| 317 | } |
| 318 | return v |
| 319 | } |
| 320 | |
| 321 | func (c *callCtxt) bytes(i int) []byte { |
| 322 | x := newValueRoot(c.ctx, c.args[i]) |
| 323 | v, err := x.Bytes() |
| 324 | if err != nil { |
| 325 | c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) |
| 326 | return nil |
| 327 | } |
| 328 | return v |
| 329 | } |
| 330 | |
| 331 | func (c *callCtxt) reader(i int) io.Reader { |
| 332 | x := newValueRoot(c.ctx, c.args[i]) |
| 333 | // TODO: optimize for string and bytes cases |
| 334 | r, err := x.Reader() |
| 335 | if err != nil { |
| 336 | c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) |
| 337 | return nil |
| 338 | } |
| 339 | return r |
| 340 | } |
| 341 | |
| 342 | func (c *callCtxt) bool(i int) bool { |
| 343 | x := newValueRoot(c.ctx, c.args[i]) |
| 344 | b, err := x.Bool() |
| 345 | if err != nil { |
| 346 | c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) |
| 347 | return false |
| 348 | } |
| 349 | return b |
| 350 | } |
| 351 | |
| 352 | func (c *callCtxt) error(i int) error { |
| 353 | x := newValueRoot(c.ctx, c.args[i]) |
| 354 | return x.Err() |
| 355 | } |
| 356 | |
| 357 | func (c *callCtxt) list(i int) (a Iterator) { |
| 358 | x := newValueRoot(c.ctx, c.args[i]) |
| 359 | v, err := x.List() |
| 360 | if err != nil { |
| 361 | c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) |
| 362 | return Iterator{ctx: c.ctx} |
| 363 | } |
| 364 | return v |
| 365 | } |
| 366 | |
| 367 | func (c *callCtxt) strList(i int) (a []string) { |
| 368 | x := newValueRoot(c.ctx, c.args[i]) |
| 369 | v, err := x.List() |
| 370 | if err != nil { |
| 371 | c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err) |
| 372 | return nil |
| 373 | } |
| 374 | for i := 0; v.Next(); i++ { |
| 375 | str, err := v.Value().String() |
| 376 | if err != nil { |
| 377 | c.err = c.ctx.mkErr(c.src, err, "list element %d: %v", i, err) |
| 378 | } |
| 379 | a = append(a, str) |
| 380 | } |
| 381 | return a |
| 382 | } |
| 383 | |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 384 | func convert(ctx *context, src source, x interface{}) evaluated { |
| 385 | switch v := x.(type) { |
| 386 | case evaluated: |
| 387 | return v |
| 388 | case nil: |
| 389 | return &nullLit{src.base()} |
Marcel van Lohuizen | d4847d9 | 2019-02-18 23:27:34 +0100 | [diff] [blame] | 390 | case ast.Expr: |
| 391 | x := newVisitorCtx(ctx, nil, nil, nil) |
| 392 | return ctx.manifest(x.walk(v)) |
Marcel van Lohuizen | 17157ea | 2018-12-11 10:41:10 +0100 | [diff] [blame] | 393 | case error: |
| 394 | return ctx.mkErr(src, v.Error()) |
| 395 | case bool: |
| 396 | return &boolLit{src.base(), v} |
| 397 | case string: |
| 398 | return &stringLit{src.base(), v} |
| 399 | case []byte: |
| 400 | return &bytesLit{src.base(), v} |
| 401 | case int: |
| 402 | return toInt(ctx, src, int64(v)) |
| 403 | case int8: |
| 404 | return toInt(ctx, src, int64(v)) |
| 405 | case int16: |
| 406 | return toInt(ctx, src, int64(v)) |
| 407 | case int32: |
| 408 | return toInt(ctx, src, int64(v)) |
| 409 | case int64: |
| 410 | return toInt(ctx, src, int64(v)) |
| 411 | case uint: |
| 412 | return toUint(ctx, src, uint64(v)) |
| 413 | case uint8: |
| 414 | return toUint(ctx, src, uint64(v)) |
| 415 | case uint16: |
| 416 | return toUint(ctx, src, uint64(v)) |
| 417 | case uint32: |
| 418 | return toUint(ctx, src, uint64(v)) |
| 419 | case uint64: |
| 420 | return toUint(ctx, src, uint64(v)) |
| 421 | case float64: |
| 422 | r := newNum(src, floatKind) |
| 423 | r.v.SetString(fmt.Sprintf("%g", v)) |
| 424 | return r |
| 425 | case *big.Int: |
| 426 | n := newNum(src, intKind) |
| 427 | n.v.Coeff.Set(v) |
| 428 | if v.Sign() < 0 { |
| 429 | n.v.Coeff.Neg(&n.v.Coeff) |
| 430 | n.v.Negative = true |
| 431 | } |
| 432 | return n |
| 433 | case *big.Rat: |
| 434 | n := newNum(src, numKind) |
| 435 | ctx.Quo(&n.v, apd.NewWithBigInt(v.Num(), 0), apd.NewWithBigInt(v.Denom(), 0)) |
| 436 | if !v.IsInt() { |
| 437 | n.k = floatKind |
| 438 | } |
| 439 | return n |
| 440 | case *big.Float: |
| 441 | n := newNum(src, floatKind) |
| 442 | n.v.SetString(v.String()) |
| 443 | return n |
| 444 | case *apd.Decimal: |
| 445 | n := newNum(src, floatKind|intKind) |
| 446 | n.v.Set(v) |
| 447 | if !n.isInt(ctx) { |
| 448 | n.k = floatKind |
| 449 | } |
| 450 | return n |
| 451 | case reflect.Value: |
| 452 | if v.CanInterface() { |
| 453 | return convert(ctx, src, v.Interface()) |
| 454 | } |
| 455 | |
| 456 | default: |
| 457 | value := reflect.ValueOf(v) |
| 458 | switch value.Kind() { |
| 459 | case reflect.Ptr: |
| 460 | if value.IsNil() { |
| 461 | return &nullLit{src.base()} |
| 462 | } |
| 463 | return convert(ctx, src, value.Elem().Interface()) |
| 464 | case reflect.Struct: |
| 465 | obj := newStruct(src) |
| 466 | t := value.Type() |
| 467 | for i := 0; i < value.NumField(); i++ { |
| 468 | t := t.Field(i) |
| 469 | if t.PkgPath != "" { |
| 470 | continue |
| 471 | } |
| 472 | sub := convert(ctx, src, value.Field(i).Interface()) |
| 473 | // leave errors like we do during normal evaluation or do we |
| 474 | // want to return the error? |
| 475 | name := t.Name |
| 476 | for _, s := range []string{"cue", "json", "protobuf"} { |
| 477 | if tag, ok := t.Tag.Lookup(s); ok { |
| 478 | if p := strings.Index(tag, ","); p >= 0 { |
| 479 | tag = tag[:p] |
| 480 | } |
| 481 | if tag != "" { |
| 482 | name = tag |
| 483 | break |
| 484 | } |
| 485 | } |
| 486 | } |
| 487 | f := ctx.strLabel(name) |
| 488 | obj.arcs = append(obj.arcs, arc{feature: f, v: sub}) |
| 489 | } |
| 490 | sort.Sort(obj) |
| 491 | return obj |
| 492 | |
| 493 | case reflect.Map: |
| 494 | obj := newStruct(src) |
| 495 | t := value.Type() |
| 496 | if t.Key().Kind() != reflect.String { |
| 497 | return ctx.mkErr(src, "builtin map key not a string, but unsupported type %s", t.Key().String()) |
| 498 | } |
| 499 | keys := []string{} |
| 500 | for _, k := range value.MapKeys() { |
| 501 | keys = append(keys, k.String()) |
| 502 | } |
| 503 | sort.Strings(keys) |
| 504 | for _, k := range keys { |
| 505 | sub := convert(ctx, src, value.MapIndex(reflect.ValueOf(k)).Interface()) |
| 506 | // leave errors like we do during normal evaluation or do we |
| 507 | // want to return the error? |
| 508 | f := ctx.strLabel(k) |
| 509 | obj.arcs = append(obj.arcs, arc{feature: f, v: sub}) |
| 510 | } |
| 511 | sort.Sort(obj) |
| 512 | return obj |
| 513 | |
| 514 | case reflect.Slice, reflect.Array: |
| 515 | list := &list{baseValue: src.base()} |
| 516 | for i := 0; i < value.Len(); i++ { |
| 517 | x := convert(ctx, src, value.Index(i).Interface()) |
| 518 | if isBottom(x) { |
| 519 | return x |
| 520 | } |
| 521 | list.a = append(list.a, x) |
| 522 | } |
| 523 | list.initLit() |
| 524 | // There is no need to set the type of the list, as the list will |
| 525 | // be of fixed size and all elements will already have a defined |
| 526 | // value. |
| 527 | return list |
| 528 | } |
| 529 | } |
| 530 | return ctx.mkErr(src, "builtin returned unsupported type %T", x) |
| 531 | } |
| 532 | |
| 533 | func toInt(ctx *context, src source, x int64) evaluated { |
| 534 | n := newNum(src, intKind) |
| 535 | n.v.SetInt64(x) |
| 536 | return n |
| 537 | } |
| 538 | |
| 539 | func toUint(ctx *context, src source, x uint64) evaluated { |
| 540 | n := newNum(src, floatKind) |
| 541 | n.v.Coeff.SetUint64(x) |
| 542 | return n |
| 543 | } |