blob: a3fd8a2a8197e236fd4a219ca8584333eb87ac44 [file] [log] [blame]
// Copyright 2019 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 cmd
import (
"strconv"
"strings"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/ast/astutil"
"cuelang.org/go/cue/parser"
"cuelang.org/go/cue/token"
)
func fix(f *ast.File) *ast.File {
// Rewrite block comments to regular comments.
ast.Walk(f, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.CommentGroup:
comments := []*ast.Comment{}
for _, c := range x.List {
s := c.Text
if !strings.HasPrefix(s, "/*") || !strings.HasSuffix(s, "*/") {
comments = append(comments, c)
continue
}
if x.Position > 0 {
// Moving to the end doesn't work, as it still
// may inject at a false line break position.
x.Position = 0
x.Doc = true
}
s = strings.TrimSpace(s[2 : len(s)-2])
for _, s := range strings.Split(s, "\n") {
for i := 0; i < 3; i++ {
if strings.HasPrefix(s, " ") || strings.HasPrefix(s, "*") {
s = s[1:]
}
}
comments = append(comments, &ast.Comment{Text: "// " + s})
}
}
x.List = comments
return false
}
return true
}, nil)
// Rewrite strings fields that are referenced.
referred := map[ast.Node]string{}
ast.Walk(f, func(n ast.Node) bool {
if i, ok := n.(*ast.Ident); ok {
str, err := ast.ParseIdent(i)
if err != nil {
return false
}
referred[i.Node] = str
}
return true
}, nil)
f = astutil.Apply(f, func(c astutil.Cursor) bool {
n := c.Node()
switch x := n.(type) {
case *ast.Field:
m, ok := referred[x.Value]
if !ok {
break
}
b, ok := x.Label.(*ast.BasicLit)
if !ok || b.Kind != token.STRING {
break
}
str, err := strconv.Unquote(b.Value)
if err != nil || str != m {
break
}
str, err = ast.QuoteIdent(str)
if err != nil {
return false
}
x.Label = astutil.CopyMeta(ast.NewIdent(str), x.Label).(ast.Label)
}
return true
}, nil).(*ast.File)
// Rewrite slice expression.
f = astutil.Apply(f, func(c astutil.Cursor) bool {
n := c.Node()
getVal := func(n ast.Expr) ast.Expr {
if n == nil {
return nil
}
if id, ok := n.(*ast.Ident); ok && id.Name == "_" {
return nil
}
return n
}
switch x := n.(type) {
case *ast.SliceExpr:
ast.SetRelPos(x.X, token.NoRelPos)
lo := getVal(x.Low)
hi := getVal(x.High)
if lo == nil { // a[:j]
lo = mustParseExpr("0")
astutil.CopyMeta(lo, x.Low)
}
if hi == nil { // a[i:]
hi = &ast.CallExpr{
Fun: ast.NewIdent("len"),
Args: []ast.Expr{x.X},
}
astutil.CopyMeta(lo, x.High)
}
if pkg := c.Import("list"); pkg != nil {
c.Replace(&ast.CallExpr{
Fun: &ast.SelectorExpr{X: pkg, Sel: ast.NewIdent("Slice")},
Args: []ast.Expr{x.X, lo, hi},
})
}
}
return true
}, nil).(*ast.File)
return f
}
func mustParseExpr(expr string) ast.Expr {
ex, err := parser.ParseExpr("fix", expr)
if err != nil {
panic(err)
}
return ex
}