blob: bf9500a8b84d9a9985292c09df18cd145d5b0777 [file] [log] [blame]
// 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 export
import (
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/ast/astutil"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal/core/adt"
)
func (e *exporter) bareValue(v adt.Value) ast.Expr {
// TODO: allow a Value context wrapper.
a := &adt.Vertex{Value: v}
return e.vertex(a)
}
// TODO: if the original value was a single reference, we could replace the
// value with a reference in graph mode.
func (e *exporter) vertex(n *adt.Vertex) (result ast.Expr) {
switch n.Value.(type) {
case nil:
// bare
case *adt.StructMarker:
result = e.structComposite(n)
case *adt.ListMarker:
result = e.listComposite(n)
default:
result = e.value(n.Value, n.Conjuncts...)
}
return result
}
func (e *exporter) value(n adt.Value, a ...adt.Conjunct) (result ast.Expr) {
// Evaluate arc if needed?
// if e.concrete && !adt.IsConcrete(n.Value) {
// return e.errf("non-concrete value: %v", e.bareValue(n.Value))
// }
switch x := n.(type) {
case *adt.Bottom:
result = e.bottom(x)
case *adt.Null:
result = e.null(x)
case *adt.Bool:
result = e.bool(x)
case *adt.Num:
result = e.num(x, a)
case *adt.String:
result = e.string(x, a)
case *adt.Bytes:
result = e.bytes(x, a)
case *adt.BasicType:
result = e.basicType(x)
case *adt.Top:
result = ast.NewIdent("_")
case *adt.BoundValue:
result = e.boundValue(x)
case *adt.BuiltinValidator:
result = e.builtinValidator(x)
case *adt.Vertex:
result = e.vertex(x)
case *adt.Conjunction:
if len(x.Values) == 0 {
result = ast.NewIdent("_")
break
}
a := []ast.Expr{}
b := boundSimplifier{e: e}
for _, v := range x.Values {
if !e.cfg.Simplify || !b.add(v) {
a = append(a, e.bareValue(v))
}
}
if !e.cfg.Simplify {
return ast.NewBinExpr(token.AND, a...)
}
result = b.expr(e.ctx) // e.bareValue(x.Values[0])
for _, c := range a {
result = &ast.BinaryExpr{X: result, Op: token.AND, Y: c}
}
}
// TODO: Add comments from original.
return result
}
func (e *exporter) bottom(n *adt.Bottom) *ast.BottomLit {
err := &ast.BottomLit{}
if x := n.Err; x != nil {
msg := x.Error()
// if len(x.sub) > 0 {
// buf := strings.Builder{}
// for i, b := range x.sub {
// if i > 0 {
// buf.WriteString("; ")
// buf.WriteString(b.msg())
// }
// }
// msg = buf.String()
// }
comment := &ast.Comment{Text: "// " + msg}
err.AddComment(&ast.CommentGroup{
Line: true,
Position: 2,
List: []*ast.Comment{comment},
})
}
return err
}
func (e *exporter) null(n *adt.Null) *ast.BasicLit {
return &ast.BasicLit{Kind: token.NULL, Value: "null"}
}
func (e *exporter) bool(n *adt.Bool) (b *ast.BasicLit) {
return ast.NewBool(n.B)
}
func extractBasic(a []adt.Conjunct) *ast.BasicLit {
for _, v := range a {
if b, ok := v.Source().(*ast.BasicLit); ok {
return &ast.BasicLit{Kind: b.Kind, Value: b.Value}
}
}
return nil
}
func (e *exporter) num(n *adt.Num, orig []adt.Conjunct) *ast.BasicLit {
// TODO: take original formatting into account.
if b := extractBasic(orig); b != nil {
return b
}
kind := token.FLOAT
if n.K&adt.IntKind != 0 {
kind = token.INT
}
return &ast.BasicLit{Kind: kind, Value: n.X.String()}
}
func (e *exporter) string(n *adt.String, orig []adt.Conjunct) *ast.BasicLit {
// TODO: take original formatting into account.
if b := extractBasic(orig); b != nil {
return b
}
return &ast.BasicLit{
Kind: token.STRING,
Value: quote(n.Str, '"'),
}
}
func (e *exporter) bytes(n *adt.Bytes, orig []adt.Conjunct) *ast.BasicLit {
// TODO: take original formatting into account.
if b := extractBasic(orig); b != nil {
return b
}
return &ast.BasicLit{
Kind: token.STRING,
Value: quote(string(n.B), '\''),
}
}
func (e *exporter) basicType(n *adt.BasicType) ast.Expr {
// TODO: allow multi-bit types?
return ast.NewIdent(n.K.String())
}
func (e *exporter) boundValue(n *adt.BoundValue) ast.Expr {
return &ast.UnaryExpr{Op: n.Op.Token(), X: e.value(n.Value)}
}
func (e *exporter) builtin(x *adt.Builtin) ast.Expr {
if x.Package == 0 {
return ast.NewIdent(x.Name)
}
spec := ast.NewImport(nil, x.Package.StringValue(e.index))
info, _ := astutil.ParseImportSpec(spec)
ident := ast.NewIdent(info.Ident)
ident.Node = spec
return ast.NewSel(ident, x.Name)
}
func (e *exporter) builtinValidator(n *adt.BuiltinValidator) ast.Expr {
call := ast.NewCall(e.builtin(n.Builtin))
for _, a := range n.Args {
call.Args = append(call.Args, e.value(a))
}
return call
}
func (e *exporter) listComposite(v *adt.Vertex) ast.Expr {
l := &ast.ListLit{}
for _, a := range v.Arcs {
if !a.Label.IsInt() {
continue
}
elem := e.vertex(a)
docs := ExtractDoc(a)
ast.SetComments(elem, docs)
l.Elts = append(l.Elts, elem)
}
return l
}
func (e *exporter) structComposite(v *adt.Vertex) ast.Expr {
s := &ast.StructLit{}
saved := e.stack
e.stack = append(e.stack, frame{scope: s})
defer func() { e.stack = saved }()
for _, a := range e.sortedArcs(v) {
if a.Label.IsDef() || a.Label.IsHidden() {
continue
}
f := &ast.Field{
Label: e.stringLabel(a.Label),
Value: e.vertex(a),
Attrs: ExtractFieldAttrs(a.Conjuncts),
}
docs := ExtractDoc(a)
ast.SetComments(f, docs)
s.Elts = append(s.Elts, f)
}
return s
}