Skip to content

Commit

Permalink
internal/core/adt: pass CallContext to builtin functions
Browse files Browse the repository at this point in the history
This is the next step in the refactor. This gives
functions access to the low-level expressions, which
in turn gives finer control over evaluation order,
which is important for some functions that validate
schema.

In general, this gives more flexibility as to what kind
of information is pass and when it is evaluated.

Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: Id5128951a670f60a2b4e7d039806413d3059836f
Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1208022
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Reviewed-by: Matthew Sackman <matthew@cue.works>
  • Loading branch information
mpvl committed Jan 31, 2025
1 parent 2d65404 commit 5991735
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 17 deletions.
4 changes: 3 additions & 1 deletion cue/interpreter/wasm/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ func generateCallThatReturnsBuiltin(name string, scope adt.Value, args []string,
call := &adt.CallExpr{Fun: &adt.Builtin{
Result: adt.TopKind,
Name: name,
Func: func(opctx *adt.OpContext, _ []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
opctx := call.OpContext()

scope := value.Make(opctx, scope)
sig := compileStringsInScope(args, scope)
args, result := splitLast(sig)
Expand Down
18 changes: 18 additions & 0 deletions internal/core/adt/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ type CallContext struct {
isValidator bool
}

func (c *CallContext) OpContext() *OpContext {
return c.ctx
}

func (c *CallContext) Pos() token.Pos {
var src ast.Node
switch {
Expand Down Expand Up @@ -57,3 +61,17 @@ func (c *CallContext) AddPositions(err *ValueError) {
err.AddPosition(v)
}
}

// Args return the pre-evaluated arguments. This function is only used for
// transitioning and will be removed at some point. Use [CallContext.Value]
// instead.
func (c *CallContext) Args() []Value {
return c.args
}

// Exprs return the unevaluated argument expressions. This function is only used
// for transitioning and will be removed at some point. Use [CallContext.Expr]
// instead. (TODO)
func (c *CallContext) Exprs() []Expr {
return c.call.Args
}
10 changes: 6 additions & 4 deletions internal/core/adt/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -1544,7 +1544,7 @@ func (x *CallExpr) evaluate(c *OpContext, state combinedFlags) Value {
if !call.builtin.checkArgs(c, pos(x), len(x.Args)) {
return nil
}
return f.RawFunc(c, x.Args)
return f.RawFunc(call)
}

case *BuiltinValidator:
Expand Down Expand Up @@ -1637,15 +1637,17 @@ type Builtin struct {
// arguments. By default, all arguments are checked to be concrete.
NonConcrete bool

Func func(c *OpContext, args []Value) Expr
Func func(call *CallContext) 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
//
// TODO: consider merging Func and RawFunc into a single field again.
RawFunc func(call *CallContext) Value

Package Feature
Name string
Expand Down Expand Up @@ -1773,7 +1775,7 @@ func (x *Builtin) call(call *CallContext) Expr {

saved := c.IsValidator
c.IsValidator = call.isValidator
ret := x.Func(c, call.args)
ret := x.Func(call)
c.IsValidator = saved

return ret
Expand Down
40 changes: 32 additions & 8 deletions internal/core/compile/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ 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 {
Func: func(call *adt.CallContext) adt.Expr {
c := call.OpContext()
args := call.Args()

v := args[0]
if x, ok := v.(*adt.Vertex); ok {
x.LockArcs = true
Expand Down Expand Up @@ -82,7 +85,10 @@ var closeBuiltin = &adt.Builtin{
Name: "close",
Params: []adt.Param{structParam},
Result: adt.StructKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
c := call.OpContext()
args := call.Args()

s, ok := args[0].(*adt.Vertex)
if !ok {
return c.NewErrf("struct argument must be concrete")
Expand Down Expand Up @@ -110,7 +116,10 @@ var andBuiltin = &adt.Builtin{
Name: "and",
Params: []adt.Param{listParam},
Result: adt.IntKind,
RawFunc: func(c *adt.OpContext, args []adt.Expr) adt.Value {
RawFunc: func(call *adt.CallContext) adt.Value {
c := call.OpContext()
args := call.Exprs()

// Pass through the cycle information from evaluating the first argument.
v := c.EvaluateKeepState(args[0])
list := c.RawElems(v)
Expand All @@ -130,7 +139,10 @@ var orBuiltin = &adt.Builtin{
Params: []adt.Param{listParam},
Result: adt.IntKind,
NonConcrete: true,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
c := call.OpContext()
args := call.Args()

d := []adt.Disjunct{}
for _, c := range c.RawElems(args[0]) {
d = append(d, adt.Disjunct{Val: c, Default: false})
Expand Down Expand Up @@ -165,7 +177,10 @@ var divBuiltin = &adt.Builtin{
Name: "div",
Params: []adt.Param{intParam, intParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
c := call.OpContext()
args := call.Args()

const name = "argument to div builtin"

return intDivOp(c, (*adt.OpContext).IntDiv, name, args)
Expand All @@ -176,7 +191,10 @@ var modBuiltin = &adt.Builtin{
Name: "mod",
Params: []adt.Param{intParam, intParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
c := call.OpContext()
args := call.Args()

const name = "argument to mod builtin"

return intDivOp(c, (*adt.OpContext).IntMod, name, args)
Expand All @@ -187,7 +205,10 @@ var quoBuiltin = &adt.Builtin{
Name: "quo",
Params: []adt.Param{intParam, intParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
c := call.OpContext()
args := call.Args()

const name = "argument to quo builtin"

return intDivOp(c, (*adt.OpContext).IntQuo, name, args)
Expand All @@ -198,7 +219,10 @@ var remBuiltin = &adt.Builtin{
Name: "rem",
Params: []adt.Param{intParam, intParam},
Result: adt.IntKind,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
c := call.OpContext()
args := call.Args()

const name = "argument to rem builtin"

return intDivOp(c, (*adt.OpContext).IntRem, name, args)
Expand Down
10 changes: 8 additions & 2 deletions internal/core/compile/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ var matchNBuiltin = &adt.Builtin{
Params: []adt.Param{topParam, intParam, listParam}, // varargs
Result: adt.BoolKind,
NonConcrete: true,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
c := call.OpContext()
args := call.Args()

if !c.IsValidator {
return c.NewErrf("matchN is a validator and should not be used as a function")
}
Expand Down Expand Up @@ -89,7 +92,10 @@ var matchIfBuiltin = &adt.Builtin{
Params: []adt.Param{topParam, topParam, topParam, topParam},
Result: adt.BoolKind,
NonConcrete: true,
Func: func(c *adt.OpContext, args []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
c := call.OpContext()
args := call.Args()

if !c.IsValidator {
return c.NewErrf("matchIf is a validator and should not be used as a function")
}
Expand Down
4 changes: 3 additions & 1 deletion internal/core/runtime/extern_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ func (c *compilerFake) Compile(name string, scope adt.Value, a *internal.Attr) (

call := &adt.CallExpr{Fun: &adt.Builtin{
Result: adt.TopKind,
Func: func(opctx *adt.OpContext, args []adt.Value) adt.Expr {
Func: func(call *adt.CallContext) adt.Expr {
opctx := call.OpContext()

cuectx := (*cue.Context)(c.runtime)
scope := value.Make(opctx, scope)

Expand Down
5 changes: 4 additions & 1 deletion internal/pkg/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ func ToBuiltin(b *Builtin) *adt.Builtin {
Package: b.Pkg,
Name: b.Name,
}
x.Func = func(ctx *adt.OpContext, args []adt.Value) (ret adt.Expr) {
x.Func = func(call *adt.CallContext) (ret adt.Expr) {
ctx := call.OpContext()
args := call.Args()

// call, _ := ctx.Source().(*ast.CallExpr)
c := &CallCtxt{
ctx: ctx,
Expand Down

0 comments on commit 5991735

Please sign in to comment.