diff --git a/cue/ast/astutil/file.go b/cue/ast/astutil/file.go new file mode 100644 index 000000000..e060b7119 --- /dev/null +++ b/cue/ast/astutil/file.go @@ -0,0 +1,38 @@ +// 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 astutil + +import ( + "cuelang.org/go/cue/ast" + "cuelang.org/go/cue/token" +) + +// ToFile converts an expression to a File. It will create an import section for +// any of the identifiers in x that refer to an import and will unshadow +// references as appropriate. +func ToFile(x ast.Expr) (*ast.File, error) { + var f *ast.File + if st, ok := x.(*ast.StructLit); ok { + f = &ast.File{Decls: st.Elts} + } else { + ast.SetRelPos(x, token.NoSpace) + f = &ast.File{Decls: []ast.Decl{&ast.EmbedDecl{Expr: x}}} + } + + if err := Sanitize(f); err != nil { + return nil, err + } + return f, nil +} diff --git a/cue/ast/astutil/file_test.go b/cue/ast/astutil/file_test.go new file mode 100644 index 000000000..a6aeb594b --- /dev/null +++ b/cue/ast/astutil/file_test.go @@ -0,0 +1,100 @@ +// 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 astutil_test + +import ( + "strings" + "testing" + + "cuelang.org/go/cue" + "cuelang.org/go/cue/ast" + "cuelang.org/go/cue/ast/astutil" + "cuelang.org/go/cue/format" + "cuelang.org/go/cue/token" + "github.com/google/go-cmp/cmp" +) + +func TestToFile(t *testing.T) { + mat := ast.NewIdent("math") + mat.Node = ast.NewImport(nil, "math") + pi := ast.NewSel(mat, "Pi") + + testCases := []struct { + desc string + expr ast.Expr + want string + }{{ + desc: "add import", + expr: ast.NewBinExpr(token.ADD, ast.NewLit(token.INT, "1"), pi), + want: "4.14159265358979323846264", + }, { + desc: "resolve unresolved within struct", + expr: ast.NewStruct( + ast.NewIdent("a"), ast.NewString("foo"), + ast.NewIdent("b"), ast.NewIdent("a"), + ), + want: ` +a: "foo" +b: "foo" +`, + }, { + desc: "unshadow", + expr: func() ast.Expr { + ident := ast.NewIdent("a") + ref := ast.NewIdent("a") + ref.Node = ident + + return ast.NewStruct( + ident, ast.NewString("bar"), + ast.NewIdent("c"), ast.NewStruct( + ast.NewIdent("a"), ast.NewString("foo"), + ast.NewIdent("b"), ref, // refers to outer `a`. + )) + }(), + want: ` +a: "bar" +c: { + a: "foo" + b: "bar" +} +`, + }} + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + f, err := astutil.ToFile(tc.expr) + if err != nil { + t.Fatal(err) + } + + var r cue.Runtime + + inst, err := r.CompileFile(f) + if err != nil { + t.Fatal(err) + } + + b, err := format.Node(inst.Value().Syntax()) + if err != nil { + t.Fatal(err) + } + + got := string(b) + want := strings.TrimLeft(tc.want, "\n") + if got != want { + t.Error(cmp.Diff(got, want)) + } + }) + } +}