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
15package compile
16
17import (
18	"cuelang.org/go/cue/errors"
19	"cuelang.org/go/internal/core/adt"
20)
21
22// This file contains predeclared builtins.
23
24const supportedByLen = adt.StructKind | adt.BytesKind | adt.StringKind | adt.ListKind
25
26var (
27	stringParam = adt.Param{Value: &adt.BasicType{K: adt.StringKind}}
28	structParam = adt.Param{Value: &adt.BasicType{K: adt.StructKind}}
29	listParam   = adt.Param{Value: &adt.BasicType{K: adt.ListKind}}
30	intParam    = adt.Param{Value: &adt.BasicType{K: adt.IntKind}}
31)
32
33var lenBuiltin = &adt.Builtin{
34	Name:   "len",
35	Params: []adt.Param{{Value: &adt.BasicType{K: supportedByLen}}},
36	Result: adt.IntKind,
37	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
38		v := args[0]
39		if x, ok := v.(*adt.Vertex); ok {
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() {
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
79var 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 s.IsClosedStruct() {
89			return s
90		}
91		v := *s
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
99var 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.Elems(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
116var 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.Elems(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		v.AddConjunct(adt.MakeRootConjunct(nil,
141			&adt.DisjunctionExpr{Values: d, HasDefaults: false},
142		))
143		c.Unify(v, adt.Finalized)
144		return v
145	},
146}
147
148var divBuiltin = &adt.Builtin{
149	Name:   "div",
150	Params: []adt.Param{intParam, intParam},
151	Result: adt.IntKind,
152	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
153		const name = "argument to div builtin"
154
155		return intDivOp(c, (*adt.OpContext).IntDiv, name, args)
156	},
157}
158
159var modBuiltin = &adt.Builtin{
160	Name:   "mod",
161	Params: []adt.Param{intParam, intParam},
162	Result: adt.IntKind,
163	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
164		const name = "argument to mod builtin"
165
166		return intDivOp(c, (*adt.OpContext).IntMod, name, args)
167	},
168}
169
170var quoBuiltin = &adt.Builtin{
171	Name:   "quo",
172	Params: []adt.Param{intParam, intParam},
173	Result: adt.IntKind,
174	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
175		const name = "argument to quo builtin"
176
177		return intDivOp(c, (*adt.OpContext).IntQuo, name, args)
178	},
179}
180
181var remBuiltin = &adt.Builtin{
182	Name:   "rem",
183	Params: []adt.Param{intParam, intParam},
184	Result: adt.IntKind,
185	Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
186		const name = "argument to rem builtin"
187
188		return intDivOp(c, (*adt.OpContext).IntRem, name, args)
189	},
190}
191
192type intFunc func(c *adt.OpContext, x, y *adt.Num) adt.Value
193
194func intDivOp(c *adt.OpContext, fn intFunc, name string, args []adt.Value) adt.Value {
195	a := c.Num(args[0], name)
196	b := c.Num(args[1], name)
197
198	if c.HasErr() {
199		return nil
200	}
201
202	return fn(c, a, b)
203}
204