Skip to content

Commit

Permalink
internal/core/adt: add RawFunc
Browse files Browse the repository at this point in the history
This prepares for both adding new buitlins (such as
the proposed numExist et. al.) as well as
adjusting some exiting ones, like `and`.

This CL is supposed to be a no-op (aside from adding
the functionality) and we separate it out to make
future diffs smaller. We will test RawFunc itself
with the respective builtins.

The issue with `and`, for instance, is that it
"weaves" in partially evaluated expressions into
existing evaluation. In come cases this may lead
to cycles. To prevent this, there needs to be a
back channel from the function to the evaluator.
Only the function can know exactly which cycle
information is needed.

Other uses are functions like `numExists` or any
other builtin that needs to operate on CUE expressions
rather than values.

Issue #943

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: I32ef92bfdc2a8318b00801bc067df4a073a10a73
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1202442
Reviewed-by: Matthew Sackman <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
Unity-Result: CUE porcuepine <[email protected]>
  • Loading branch information
mpvl committed Oct 14, 2024
1 parent 4e8e027 commit cf2f66e
Showing 1 changed file with 34 additions and 10 deletions.
44 changes: 34 additions & 10 deletions internal/core/adt/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,12 @@ func (x *CallExpr) evaluate(c *OpContext, state combinedFlags) Value {
switch f := fun.(type) {
case *Builtin:
b = f
if f.RawFunc != nil {
if !b.checkArgs(c, pos(x), len(x.Args)) {
return nil
}
return f.RawFunc(c, x.Args)
}

case *BuiltinValidator:
// We allow a validator that takes no arguments except the validated
Expand Down Expand Up @@ -1605,6 +1611,14 @@ type Builtin struct {

Func func(c *OpContext, args []Value) Expr

// RawFunc gives low-level control to CUE's internals for builtins.
// It should be used when fine control over the evaluation process is
// needed. Note that RawFuncs are responsible for returning a Value. This
// gives them fine control over how exactly such value gets evaluated.
// A RawFunc may pass CycleInfo, errors and other information through
// the Context.
RawFunc func(c *OpContext, args []Expr) Value

Package Feature
Name string
}
Expand Down Expand Up @@ -1665,23 +1679,33 @@ func bottom(v Value) *Bottom {
return b
}

func (x *Builtin) call(c *OpContext, p token.Pos, validate bool, args []Value) Expr {
fun := x // right now always x.
if len(args) > len(x.Params) {
func (x *Builtin) checkArgs(c *OpContext, p token.Pos, numArgs int) bool {
if numArgs > len(x.Params) {
c.addErrf(0, p,
"too many arguments in call to %v (have %d, want %d)",
fun, len(args), len(x.Params))
return nil
x, numArgs, len(x.Params))
return false
}
for i := len(args); i < len(x.Params); i++ {
v := x.Params[i].Default()
if numArgs < len(x.Params) {
// Assume that all subsequent params have a default as well.
v := x.Params[numArgs].Default()
if v == nil {
c.addErrf(0, p,
"not enough arguments in call to %v (have %d, want %d)",
fun, len(args), len(x.Params))
return nil
x, numArgs, len(x.Params))
return false
}
args = append(args, v)
}
return true
}

func (x *Builtin) call(c *OpContext, p token.Pos, validate bool, args []Value) Expr {
fun := x // right now always x.
if !x.checkArgs(c, p, len(args)) {
return nil
}
for i := len(args); i < len(x.Params); i++ {
args = append(args, x.Params[i].Default())
}
for i, a := range args {
if x.Params[i].Kind() == BottomKind {
Expand Down

0 comments on commit cf2f66e

Please sign in to comment.