Skip to content

Commit

Permalink
internal/core/runtime: expand runtime.Compile signature to accomodate…
Browse files Browse the repository at this point in the history
… non-builtins

Widen runtime.Compile signature to accomodate external interpreters
that return more complex structures than a builtin. One example
would be interpreters that fill in concrete data. A more immediate
use case is for Wasm where we want the interpreter to be able to
refer to other declarations, for example functions with arguments
of types defined in another package.

Also, when processing a field, an external interpreter often needs
access to the struct that contains the field in order to resolve
identifiers specified in the extern attribute at the correct scope.
Add a parameter to runtime.Compile which represents this struct.

Additionally, modify Interpreter to take a *Runtime argument in
NewCompiler.  This is needed because if the external interpreter
needs to generate synthetic code, this code has to use the same
runtime as the compiler.

Change-Id: Ia28690f48122573364c420541f95edcf552449e6
Signed-off-by: Aram Hăvărneanu <aram@mgk.ro>
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1173962
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@gmail.com>
  • Loading branch information
4ad committed Feb 12, 2024
1 parent b460e71 commit 063b8c1
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 17 deletions.
4 changes: 2 additions & 2 deletions cue/interpreter/wasm/wasm.go
Expand Up @@ -44,7 +44,7 @@ func (i *interpreter) Kind() string {

// NewCompiler returns a Wasm compiler that services the specified
// build.Instance.
func (i *interpreter) NewCompiler(b *build.Instance) (coreruntime.Compiler, errors.Error) {
func (i *interpreter) NewCompiler(b *build.Instance, _ *coreruntime.Runtime) (coreruntime.Compiler, errors.Error) {
return &compiler{
b: b,
instances: make(map[string]*instance),
Expand All @@ -64,7 +64,7 @@ type compiler struct {
// Compile searches for a Wasm function described by the given `@extern`
// attribute and returns it as an [adt.Builtin] with the given function
// name.
func (c *compiler) Compile(funcName string, a *internal.Attr) (*adt.Builtin, errors.Error) {
func (c *compiler) Compile(funcName string, _ adt.Value, a *internal.Attr) (adt.Expr, errors.Error) {
file, err := fileName(a)
if err != nil {
return nil, errors.Promote(err, "invalid attribute")
Expand Down
31 changes: 18 additions & 13 deletions internal/core/runtime/extern.go
Expand Up @@ -40,19 +40,22 @@ func (r *Runtime) SetInterpreter(i Interpreter) {
// Interpreter defines an entrypoint for creating per-package interpreters.
type Interpreter interface {
// NewCompiler creates a compiler for b and reports any errors.
NewCompiler(b *build.Instance) (Compiler, errors.Error)
NewCompiler(b *build.Instance, r *Runtime) (Compiler, errors.Error)

// Kind returns the string to be used in the file-level @extern attribute.
Kind() string
}

// A Compiler composes an adt.Builtin for an external function implementation.
// A Compiler fills in an adt.Expr for fields marked with `@extern(kind)`.
type Compiler interface {
// Compile creates a builtin for the given function name and attribute.
// funcName is the name of the function to compile, taken from altName in
// @extern(name=altName), or from the field name if that's not defined.
// Other than "name", the fields in a are implementation specific.
Compile(funcName string, a *internal.Attr) (*adt.Builtin, errors.Error)
// Compile creates an adt.Expr (usually a builtin) for the
// given external named resource (usually a function). name
// is the name of the resource to compile, taken from altName
// in `@extern(name=altName)`, or from the field name if that's
// not defined. Scope is the struct that contains the field.
// Other than "name", the fields in a are implementation
// specific.
Compile(name string, scope adt.Value, a *internal.Attr) (adt.Expr, errors.Error)
}

// injectImplementations modifies v to include implementations of functions
Expand All @@ -72,7 +75,7 @@ func (r *Runtime) injectImplementations(b *build.Instance, v *adt.Vertex) (errs
}

for _, c := range v.Conjuncts {
d.decorateConjunct(c.Elem())
d.decorateConjunct(c.Elem(), v)
}

return d.errs
Expand Down Expand Up @@ -215,7 +218,7 @@ func (d *externDecorator) initCompiler(kind string, pos token.Pos) (ok bool, err
return false, errors.Newf(pos, "no interpreter defined for %q", kind)
}

c, err := x.NewCompiler(d.pkg)
c, err := x.NewCompiler(d.pkg, d.runtime)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -289,14 +292,16 @@ func (d *externDecorator) markExternFieldAttr(kind string, decls []ast.Decl) (er
return errs
}

func (d *externDecorator) decorateConjunct(e adt.Elem) {
w := walk.Visitor{Before: d.processADTNode}
func (d *externDecorator) decorateConjunct(e adt.Elem, scope *adt.Vertex) {
w := walk.Visitor{Before: func(n adt.Node) bool {
return d.processADTNode(n, scope)
}}
w.Elem(e)
}

// processADTNode injects a builtin conjunct into n if n is an adt.Field and
// has a marked ast.Field associated with it.
func (d *externDecorator) processADTNode(n adt.Node) bool {
func (d *externDecorator) processADTNode(n adt.Node, scope *adt.Vertex) bool {
f, ok := n.(*adt.Field)
if !ok {
return true
Expand Down Expand Up @@ -324,7 +329,7 @@ func (d *externDecorator) processADTNode(n adt.Node) bool {
name = str
}

b, err := c.Compile(name, &attr)
b, err := c.Compile(name, scope, &attr)
if err != nil {
err = errors.Newf(info.attr.Pos(), "can't load from external module: %v", err)
d.errs = errors.Append(d.errs, err)
Expand Down
40 changes: 38 additions & 2 deletions internal/core/runtime/extern_test.go
Expand Up @@ -19,6 +19,7 @@ import (
"strconv"
"testing"

"cuelang.org/go/cue"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/errors"
Expand All @@ -27,6 +28,7 @@ import (
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/core/runtime"
"cuelang.org/go/internal/cuetxtar"
"cuelang.org/go/internal/value"
)

func Test(t *testing.T) {
Expand Down Expand Up @@ -56,17 +58,19 @@ type interpreterFake struct {

func (i *interpreterFake) Kind() string { return "test" }

func (i *interpreterFake) NewCompiler(b *build.Instance) (runtime.Compiler, errors.Error) {
func (i *interpreterFake) NewCompiler(b *build.Instance, r *runtime.Runtime) (runtime.Compiler, errors.Error) {
switch b.PkgName {
case "failinit":
return nil, errors.Newf(token.NoPos, "TEST: fail initialization")
case "nullinit":
return nil, nil
case "scopetest":
return newCompilerFake(b, r)
}
return i, nil
}

func (i *interpreterFake) Compile(funcName string, a *internal.Attr) (*adt.Builtin, errors.Error) {
func (i *interpreterFake) Compile(funcName string, _ adt.Value, a *internal.Attr) (adt.Expr, errors.Error) {
if ok, _ := a.Flag(1, "fail"); ok {
return nil, errors.Newf(token.NoPos, "TEST: fail compilation")
}
Expand Down Expand Up @@ -94,3 +98,35 @@ func (i *interpreterFake) Compile(funcName string, a *internal.Attr) (*adt.Built
Result: adt.IntKind,
}, nil
}

type compilerFake struct {
runtime *runtime.Runtime
b *build.Instance
}

func newCompilerFake(b *build.Instance, r *runtime.Runtime) (runtime.Compiler, errors.Error) {
return &compilerFake{
runtime: r,
b: b,
}, nil
}

func (c *compilerFake) Compile(name string, scope adt.Value, a *internal.Attr) (adt.Expr, errors.Error) {
typStr, err := a.String(0)
if err != nil {
return nil, errors.Promote(err, "test")
}

call := &adt.CallExpr{Fun: &adt.Builtin{
Result: adt.TopKind,
Func: func(opctx *adt.OpContext, args []adt.Value) adt.Expr {
cuectx := (*cue.Context)(c.runtime)
scope := value.Make(opctx, scope)

typ := cuectx.CompileString(typStr, cue.Scope(scope))
_, ityp := value.ToInternal(typ)
return ityp
},
}}
return call, nil
}
27 changes: 27 additions & 0 deletions internal/core/runtime/testdata/scope.txtar
@@ -0,0 +1,27 @@
-- cue.mod/modules.cue --
-- file1.cue --
@extern("test")

package scopetest

#Bar: float
#Baz: {
#Quux: string
spam: eggs: bool
}

foo: _ @extern(int)
bar: _ @extern(#Bar)
baz: {
quux: _ @extern(#Baz.#Quux)
eggs: _ @extern(#Baz.spam.eggs)
}
-- out/extern --
{
foo: int
bar: float
baz: {
quux: string
eggs: bool
}
}

0 comments on commit 063b8c1

Please sign in to comment.