github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/cue/format/simplify.go (about) 1 // Copyright 2019 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package format 16 17 import ( 18 "strconv" 19 20 "github.com/joomcode/cue/cue/ast" 21 "github.com/joomcode/cue/cue/ast/astutil" 22 "github.com/joomcode/cue/internal" 23 ) 24 25 // labelSimplifier rewrites string labels to identifiers if 26 // no identifiers will subsequently bind to the exposed label. 27 // In other words, string labels are only replaced if this does 28 // not change the semantics of the CUE code. 29 type labelSimplifier struct { 30 parent *labelSimplifier 31 scope map[string]bool 32 } 33 34 func (s *labelSimplifier) processDecls(decls []ast.Decl) { 35 sc := labelSimplifier{parent: s, scope: map[string]bool{}} 36 for _, d := range decls { 37 switch x := d.(type) { 38 case *ast.Field: 39 ast.Walk(x.Label, sc.markStrings, nil) 40 } 41 } 42 43 for _, d := range decls { 44 switch x := d.(type) { 45 case *ast.Field: 46 ast.Walk(x.Value, sc.markReferences, nil) 47 default: 48 ast.Walk(x, sc.markReferences, nil) 49 } 50 } 51 52 for _, d := range decls { 53 switch x := d.(type) { 54 case *ast.Field: 55 x.Label = astutil.Apply(x.Label, sc.replace, nil).(ast.Label) 56 } 57 } 58 } 59 60 func (s *labelSimplifier) markReferences(n ast.Node) bool { 61 // Record strings at this level. 62 switch x := n.(type) { 63 case *ast.File: 64 s.processDecls(x.Decls) 65 return false 66 67 case *ast.StructLit: 68 s.processDecls(x.Elts) 69 return false 70 71 case *ast.SelectorExpr: 72 ast.Walk(x.X, s.markReferences, nil) 73 return false 74 75 case *ast.Ident: 76 for c := s; c != nil; c = c.parent { 77 if _, ok := c.scope[x.Name]; ok { 78 c.scope[x.Name] = false 79 break 80 } 81 } 82 } 83 return true 84 } 85 86 func (s *labelSimplifier) markStrings(n ast.Node) bool { 87 switch x := n.(type) { 88 case *ast.BasicLit: 89 str, err := strconv.Unquote(x.Value) 90 if err != nil || !ast.IsValidIdent(str) || internal.IsDefOrHidden(str) { 91 return false 92 } 93 s.scope[str] = true 94 95 case *ast.Ident: 96 s.scope[x.Name] = true 97 98 case *ast.ListLit, *ast.Interpolation: 99 return false 100 } 101 return true 102 } 103 104 func (s *labelSimplifier) replace(c astutil.Cursor) bool { 105 switch x := c.Node().(type) { 106 case *ast.BasicLit: 107 str, err := strconv.Unquote(x.Value) 108 if err == nil && s.scope[str] && !internal.IsDefOrHidden(str) { 109 c.Replace(ast.NewIdent(str)) 110 } 111 } 112 return true 113 }