blob: feb7adebbcfd8a2b93c86132577380229fd3118e [file] [log] [blame]
// Copyright 2020 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package compile
import (
"cuelang.org/go/cue/errors"
"cuelang.org/go/internal/core/adt"
)
// This file contains predeclared builtins.
const supportedByLen = adt.StructKind | adt.BytesKind | adt.StringKind | adt.ListKind
var (
stringParam = adt.Param{Value: &adt.BasicType{K: adt.StringKind}}
structParam = adt.Param{Value: &adt.BasicType{K: adt.StructKind}}
listParam = adt.Param{Value: &adt.BasicType{K: adt.ListKind}}
intParam = adt.Param{Value: &adt.BasicType{K: adt.IntKind}}
)
var lenBuiltin = &adt.Builtin{
Name: "len",
Params: []adt.Param{{Value: &adt.BasicType{K: supportedByLen}}},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
v := args[0]
if x, ok := v.(*adt.Vertex); ok {
switch x.BaseValue.(type) {
case nil:
// This should not happen, but be defensive.
return c.NewErrf("unevaluated vertex")
case *adt.ListMarker:
return c.NewInt64(int64(len(x.Elems())), v)
case *adt.StructMarker:
n := 0
v, _ := v.(*adt.Vertex)
for _, a := range v.Arcs {
if a.Label.IsRegular() {
n++
}
}
return c.NewInt64(int64(n), v)
default:
v = x.Value()
}
}
switch x := v.(type) {
case *adt.Bytes:
return c.NewInt64(int64(len(x.B)), v)
case *adt.String:
return c.NewInt64(int64(len(x.Str)), v)
default:
k := x.Kind()
if k&supportedByLen == adt.BottomKind {
return c.NewErrf("invalid argument type %v", k)
}
b := c.NewErrf("incomplete argument %s (type %v)", c.Str(v), k)
b.Code = adt.IncompleteError
return b
}
},
}
var closeBuiltin = &adt.Builtin{
Name: "close",
Params: []adt.Param{structParam},
Result: adt.StructKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
s, ok := args[0].(*adt.Vertex)
if !ok {
return c.NewErrf("struct argument must be concrete")
}
if s.IsClosedStruct() {
return s
}
v := *s
// TODO(perf): do not copy the arc, but rather find a way to mark the
// calling nodeContext.
v.BaseValue = &adt.StructMarker{NeedClose: true}
return &v
},
}
var andBuiltin = &adt.Builtin{
Name: "and",
Params: []adt.Param{listParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
list := c.Elems(args[0])
if len(list) == 0 {
return &adt.Top{}
}
a := []adt.Value{}
for _, c := range list {
a = append(a, c)
}
return &adt.Conjunction{Values: a}
},
}
var orBuiltin = &adt.Builtin{
Name: "or",
Params: []adt.Param{listParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
d := []adt.Disjunct{}
for _, c := range c.Elems(args[0]) {
d = append(d, adt.Disjunct{Val: c, Default: false})
}
if len(d) == 0 {
// TODO(manifest): This should not be unconditionally incomplete,
// but it requires results from comprehensions and all to have
// some special status. Maybe this can be solved by having results
// of list comprehensions be open if they result from iterating over
// an open list or struct. This would actually be exactly what
// that means. The error here could then only add an incomplete
// status if the source is open.
return &adt.Bottom{
Code: adt.IncompleteError,
Err: errors.Newf(c.Pos(), "empty list in call to or"),
}
}
v := &adt.Vertex{}
// TODO: make a Disjunction.
v.AddConjunct(adt.MakeRootConjunct(nil,
&adt.DisjunctionExpr{Values: d, HasDefaults: false},
))
c.Unify(v, adt.Finalized)
return v
},
}
var divBuiltin = &adt.Builtin{
Name: "div",
Params: []adt.Param{intParam, intParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
const name = "argument to div builtin"
return intDivOp(c, (*adt.OpContext).IntDiv, name, args)
},
}
var modBuiltin = &adt.Builtin{
Name: "mod",
Params: []adt.Param{intParam, intParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
const name = "argument to mod builtin"
return intDivOp(c, (*adt.OpContext).IntMod, name, args)
},
}
var quoBuiltin = &adt.Builtin{
Name: "quo",
Params: []adt.Param{intParam, intParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
const name = "argument to quo builtin"
return intDivOp(c, (*adt.OpContext).IntQuo, name, args)
},
}
var remBuiltin = &adt.Builtin{
Name: "rem",
Params: []adt.Param{intParam, intParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
const name = "argument to rem builtin"
return intDivOp(c, (*adt.OpContext).IntRem, name, args)
},
}
type intFunc func(c *adt.OpContext, x, y *adt.Num) adt.Value
func intDivOp(c *adt.OpContext, fn intFunc, name string, args []adt.Value) adt.Value {
a := c.Num(args[0], name)
b := c.Num(args[1], name)
if c.HasErr() {
return nil
}
return fn(c, a, b)
}