blob: a2dce511d1547e5c69e094d6ca7c3a8af3c64fd2 [file] [log] [blame]
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +01001// 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 Lohuizen7b5d2fd2018-12-11 16:34:56 +010015//go:generate go run gen.go
Marcel van Lohuizend4847d92019-02-18 23:27:34 +010016//go:generate goimports -w builtins.go
Marcel van Lohuizen7b5d2fd2018-12-11 16:34:56 +010017
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010018package cue
19
20import (
21 "fmt"
22 "io"
23 "math/big"
24 "path"
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010025 "sort"
Marcel van Lohuizenf8132852019-04-26 12:16:18 +020026 "strings"
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010027
Marcel van Lohuizena460fe82019-04-26 10:20:51 +020028 "cuelang.org/go/cue/errors"
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +020029 "cuelang.org/go/cue/parser"
Marcel van Lohuizenf8132852019-04-26 12:16:18 +020030 "cuelang.org/go/internal"
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +010031)
32
33// A builtin is a builtin function or constant.
34//
35// A function may return and a constant may be any of the following types:
36//
37// error (translates to bottom)
38// nil (translates to null)
39// bool
40// int*
41// uint*
42// float64
43// string
44// *big.Float
45// *big.Int
46//
47// For any of the above, including interface{} and these types recursively:
48// []T
49// map[string]T
50//
51type builtin struct {
52 baseValue
53 Name string
54 Params []kind
55 Result kind
56 Func func(c *callCtxt)
57 // Const interface{}
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +020058 Const string
59}
60
Marcel van Lohuizen57333362019-04-01 14:35:09 +020061type builtinPkg struct {
62 native []*builtin
63 cue string
64}
65
Marcel van Lohuizen46103912019-04-26 22:23:38 +020066func mustCompileBuiltins(ctx *context, p *builtinPkg, name string) *structLit {
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +020067 obj := &structLit{}
Marcel van Lohuizen57333362019-04-01 14:35:09 +020068 for _, b := range p.native {
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +020069 f := ctx.label(b.Name, false) // never starts with _
70 // n := &node{baseValue: newBase(imp.Path)}
71 var v evaluated = b
72 if b.Const != "" {
73 v = mustParseConstBuiltin(ctx, b.Name, b.Const)
74 }
75 obj.arcs = append(obj.arcs, arc{feature: f, v: v})
76 }
77 sort.Sort(obj)
Marcel van Lohuizen57333362019-04-01 14:35:09 +020078
79 // Parse builtin CUE
80 if p.cue != "" {
Marcel van Lohuizen46103912019-04-26 22:23:38 +020081 expr, err := parser.ParseExpr(ctx.index.fset, name, p.cue)
Marcel van Lohuizen57333362019-04-01 14:35:09 +020082 if err != nil {
83 fmt.Println(p.cue)
84 panic(err)
85 }
86 pkg := evalExpr(ctx.index, obj, expr).(*structLit)
87 for _, a := range pkg.arcs {
88 // Discard option status and attributes at top level.
89 // TODO: filter on capitalized fields?
90 obj.insertValue(ctx, a.feature, false, a.v, nil)
91 }
92 }
93
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +020094 return obj
95}
96
97// newConstBuiltin parses and creates any CUE expression that does not have
98// fields.
99func mustParseConstBuiltin(ctx *context, name, val string) evaluated {
100 expr, err := parser.ParseExpr(ctx.index.fset, "<builtin:"+name+">", val)
101 if err != nil {
102 panic(err)
103 }
104 v := newVisitor(ctx.index, nil, nil, nil)
105 value := v.walk(expr)
106 return value.evalPartial(ctx)
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100107}
108
109var _ caller = &builtin{}
110
111var lenBuiltin = &builtin{
112 Name: "len",
113 Params: []kind{stringKind | bytesKind | listKind | structKind},
114 Result: intKind,
115 Func: func(c *callCtxt) {
116 v := c.value(0)
117 switch v.Kind() {
118 case StructKind:
119 s, _ := v.structVal(c.ctx)
120 c.ret = s.Len()
121 case ListKind:
122 i := 0
123 iter, _ := v.List()
124 for ; iter.Next(); i++ {
125 }
126 c.ret = i
127 case BytesKind:
128 b, _ := v.Bytes()
129 c.ret = len(b)
130 case StringKind:
131 s, _ := v.String()
132 c.ret = len(s)
133 }
134 },
135}
136
Marcel van Lohuizena460fe82019-04-26 10:20:51 +0200137var andBuiltin = &builtin{
138 Name: "and",
139 Params: []kind{listKind},
140 Result: intKind,
141 Func: func(c *callCtxt) {
142 iter := c.list(0)
143 if !iter.Next() {
144 c.ret = &top{baseValue{c.src}}
145 return
146 }
147 u := iter.Value().path.v
148 for iter.Next() {
149 u = mkBin(c.ctx, c.src.Pos(), opUnify, u, iter.Value().path.v)
150 }
151 c.ret = u
152 },
153}
154
155var orBuiltin = &builtin{
156 Name: "or",
157 Params: []kind{stringKind | bytesKind | listKind | structKind},
158 Result: intKind,
159 Func: func(c *callCtxt) {
160 iter := c.list(0)
161 d := []dValue{}
162 for iter.Next() {
163 d = append(d, dValue{iter.Value().path.v, false})
164 }
Marcel van Lohuizen94d845d2019-05-10 00:28:03 +0200165 c.ret = &disjunction{baseValue{c.src}, d, false}
Marcel van Lohuizena460fe82019-04-26 10:20:51 +0200166 if len(d) == 0 {
167 c.ret = errors.New("empty or")
168 }
169 },
170}
171
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100172func (x *builtin) kind() kind {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100173 return lambdaKind
174}
175
176func (x *builtin) evalPartial(ctx *context) evaluated {
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100177 return x
178}
179
180func (x *builtin) subsumesImpl(ctx *context, v value, mode subsumeMode) bool {
181 if y, ok := v.(*builtin); ok {
182 return x == y
183 }
184 return false
185}
186
187func (x *builtin) call(ctx *context, src source, args ...evaluated) (ret value) {
188 if x.Func == nil {
189 return ctx.mkErr(x, "Builtin %q is not a function", x.Name)
190 }
191 if len(x.Params) != len(args) {
192 return ctx.mkErr(src, x, "number of arguments does not match (%d vs %d)",
193 len(x.Params), len(args))
194 }
195 for i, a := range args {
196 if x.Params[i] != bottomKind {
197 if unifyType(x.Params[i], a.kind()) == bottomKind {
198 return ctx.mkErr(src, x, "argument %d requires type %v, found %v", i+1, x.Params[i], a.kind())
199 }
200 }
201 }
202 call := callCtxt{src: src, ctx: ctx, args: args}
203 defer func() {
204 var errVal interface{} = call.err
205 if err := recover(); err != nil {
206 errVal = err
207 }
208 switch err := errVal.(type) {
209 case nil:
210 case *bottom:
211 ret = err
212 default:
213 ret = ctx.mkErr(src, x, "call error: %v", err)
214 }
215 }()
216 x.Func(&call)
Marcel van Lohuizena460fe82019-04-26 10:20:51 +0200217 if e, ok := call.ret.(value); ok {
218 return e
219 }
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100220 return convert(ctx, x, call.ret)
221}
222
223// callCtxt is passed to builtin implementations.
224type callCtxt struct {
225 src source
226 ctx *context
227 args []evaluated
228 err error
229 ret interface{}
230}
231
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +0200232var builtins = map[string]*structLit{}
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100233
Marcel van Lohuizen57333362019-04-01 14:35:09 +0200234func initBuiltins(pkgs map[string]*builtinPkg) {
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +0200235 ctx := sharedIndex.newContext()
Marcel van Lohuizen928bfa32019-04-26 12:16:18 +0200236 keys := []string{}
237 for k := range pkgs {
238 keys = append(keys, k)
239 }
240 sort.Strings(keys)
241 for _, k := range keys {
242 b := pkgs[k]
Marcel van Lohuizen46103912019-04-26 22:23:38 +0200243 e := mustCompileBuiltins(ctx, b, k)
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +0200244 builtins[k] = e
245 builtins["-/"+path.Base(k)] = e
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100246 }
247}
248
249func getBuiltinShorthandPkg(ctx *context, shorthand string) *structLit {
250 return getBuiltinPkg(ctx, "-/"+shorthand)
251}
252
253func getBuiltinPkg(ctx *context, path string) *structLit {
254 p, ok := builtins[path]
255 if !ok {
256 return nil
257 }
Marcel van Lohuizen8bc02e52019-04-01 13:14:07 +0200258 return p
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100259}
260
Marcel van Lohuizenf8132852019-04-26 12:16:18 +0200261func init() {
262 internal.UnifyBuiltin = func(val interface{}, kind string) interface{} {
263 v := val.(Value)
264 ctx := v.ctx()
265
266 p := strings.Split(kind, ".")
267 pkg, name := p[0], p[1]
268 s := getBuiltinPkg(ctx, pkg)
269 if s == nil {
270 return v
271 }
272 a := s.lookup(ctx, ctx.label(name, false))
273 if a.v == nil {
274 return v
275 }
276
277 return v.Unify(newValueRoot(ctx, a.v.evalPartial(ctx)))
278 }
279}
280
Marcel van Lohuizen17157ea2018-12-11 10:41:10 +0100281// do returns whether the call should be done.
282func (c *callCtxt) do() bool {
283 return c.err == nil
284}
285
286func (c *callCtxt) value(i int) Value {
287 return newValueRoot(c.ctx, c.args[i])
288}
289
290func (c *callCtxt) int(i int) int { return int(c.intValue(i, 64)) }
291func (c *callCtxt) int8(i int) int8 { return int8(c.intValue(i, 8)) }
292func (c *callCtxt) int16(i int) int16 { return int16(c.intValue(i, 16)) }
293func (c *callCtxt) int32(i int) int32 { return int32(c.intValue(i, 32)) }
294func (c *callCtxt) rune(i int) rune { return rune(c.intValue(i, 32)) }
295func (c *callCtxt) int64(i int) int64 { return int64(c.intValue(i, 64)) }
296
297func (c *callCtxt) intValue(i, bits int) int64 {
298 x := newValueRoot(c.ctx, c.args[i])
299 n, err := x.Int(nil)
300 if err != nil {
301 c.err = c.ctx.mkErr(c.src, "argument %d must be in int, found number", i)
302 return 0
303 }
304 if n.BitLen() > bits {
305 c.err = c.ctx.mkErr(c.src, err, "argument %d out of range: has %d > %d bits", n.BitLen(), bits)
306 }
307 res, _ := x.Int64()
308 return res
309}
310
311func (c *callCtxt) uint(i int) uint { return uint(c.uintValue(i, 64)) }
312func (c *callCtxt) uint8(i int) uint8 { return uint8(c.uintValue(i, 8)) }
313func (c *callCtxt) byte(i int) uint8 { return byte(c.uintValue(i, 8)) }
314func (c *callCtxt) uint16(i int) uint16 { return uint16(c.uintValue(i, 16)) }
315func (c *callCtxt) uint32(i int) uint32 { return uint32(c.uintValue(i, 32)) }
316func (c *callCtxt) uint64(i int) uint64 { return uint64(c.uintValue(i, 64)) }
317
318func (c *callCtxt) uintValue(i, bits int) uint64 {
319 x := newValueRoot(c.ctx, c.args[i])
320 n, err := x.Int(nil)
321 if err != nil {
322 c.err = c.ctx.mkErr(c.src, "argument %d must be an integer", i)
323 return 0
324 }
325 if n.Sign() < 0 {
326 c.err = c.ctx.mkErr(c.src, "argument %d must be a positive integer", i)
327 return 0
328 }
329 if n.BitLen() > bits {
330 c.err = c.ctx.mkErr(c.src, err, "argument %d out of range: has %d > %d bits", i, n.BitLen(), bits)
331 }
332 res, _ := x.Uint64()
333 return res
334}
335
336func (c *callCtxt) float64(i int) float64 {
337 x := newValueRoot(c.ctx, c.args[i])
338 res, err := x.Float64()
339 if err != nil {
340 c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err)
341 return 0
342 }
343 return res
344}
345
346func (c *callCtxt) bigInt(i int) *big.Int {
347 x := newValueRoot(c.ctx, c.args[i])
348 n, err := x.Int(nil)
349 if err != nil {
350 c.err = c.ctx.mkErr(c.src, "argument %d must be in int, found number", i)
351 return nil
352 }
353 return n
354}
355
356func (c *callCtxt) bigFloat(i int) *big.Float {
357 x := newValueRoot(c.ctx, c.args[i])
358 var mant big.Int
359 exp, err := x.MantExp(&mant)
360 if err != nil {
361 c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err)
362 return nil
363 }
364 f := &big.Float{}
365 f.SetInt(&mant)
366 if exp != 0 {
367 var g big.Float
368 e := big.NewInt(int64(exp))
369 f.Mul(f, g.SetInt(e.Exp(ten, e, nil)))
370 }
371 return f
372}
373
374func (c *callCtxt) string(i int) string {
375 x := newValueRoot(c.ctx, c.args[i])
376 v, err := x.String()
377 if err != nil {
378 c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err)
379 return ""
380 }
381 return v
382}
383
384func (c *callCtxt) bytes(i int) []byte {
385 x := newValueRoot(c.ctx, c.args[i])
386 v, err := x.Bytes()
387 if err != nil {
388 c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err)
389 return nil
390 }
391 return v
392}
393
394func (c *callCtxt) reader(i int) io.Reader {
395 x := newValueRoot(c.ctx, c.args[i])
396 // TODO: optimize for string and bytes cases
397 r, err := x.Reader()
398 if err != nil {
399 c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err)
400 return nil
401 }
402 return r
403}
404
405func (c *callCtxt) bool(i int) bool {
406 x := newValueRoot(c.ctx, c.args[i])
407 b, err := x.Bool()
408 if err != nil {
409 c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err)
410 return false
411 }
412 return b
413}
414
415func (c *callCtxt) error(i int) error {
416 x := newValueRoot(c.ctx, c.args[i])
417 return x.Err()
418}
419
420func (c *callCtxt) list(i int) (a Iterator) {
421 x := newValueRoot(c.ctx, c.args[i])
422 v, err := x.List()
423 if err != nil {
424 c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err)
425 return Iterator{ctx: c.ctx}
426 }
427 return v
428}
429
430func (c *callCtxt) strList(i int) (a []string) {
431 x := newValueRoot(c.ctx, c.args[i])
432 v, err := x.List()
433 if err != nil {
434 c.err = c.ctx.mkErr(c.src, err, "invalid argument %d: %v", i, err)
435 return nil
436 }
437 for i := 0; v.Next(); i++ {
438 str, err := v.Value().String()
439 if err != nil {
440 c.err = c.ctx.mkErr(c.src, err, "list element %d: %v", i, err)
441 }
442 a = append(a, str)
443 }
444 return a
445}