cuelang.org/go@v0.10.1/internal/cmd/cue-ast-print/main.go (about) 1 // Copyright 2023 The 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 // cue-ast-print parses a CUE file and prints its syntax tree, for example: 16 // 17 // cue-ast-print file.cue 18 package main 19 20 import ( 21 "flag" 22 "os" 23 24 "fmt" 25 gotoken "go/token" 26 "io" 27 "reflect" 28 "strings" 29 30 "cuelang.org/go/cue/ast" 31 "cuelang.org/go/cue/parser" 32 "cuelang.org/go/cue/token" 33 ) 34 35 func main() { 36 flag.Parse() 37 args := flag.Args() 38 if len(args) != 1 { 39 // We could support multiple arguments or stdin if useful. 40 panic("expecting exactly one argument") 41 } 42 file, err := parser.ParseFile(args[0], nil, parser.ParseComments) 43 if err != nil { 44 panic(err) 45 } 46 debugPrint(os.Stdout, file) 47 } 48 49 func debugPrint(w io.Writer, node ast.Node) { 50 d := &debugPrinter{w: w} 51 d.value(reflect.ValueOf(node)) 52 d.newline() 53 } 54 55 type debugPrinter struct { 56 w io.Writer 57 level int 58 } 59 60 func (d *debugPrinter) printf(format string, args ...any) { 61 fmt.Fprintf(d.w, format, args...) 62 } 63 64 func (d *debugPrinter) newline() { 65 fmt.Fprintf(d.w, "\n%s", strings.Repeat("\t", d.level)) 66 } 67 68 var ( 69 typeTokenPos = reflect.TypeFor[token.Pos]() 70 typeTokenToken = reflect.TypeFor[token.Token]() 71 ) 72 73 func (d *debugPrinter) value(v reflect.Value) { 74 // Skip over interface types. 75 if v.Kind() == reflect.Interface { 76 v = v.Elem() 77 } 78 79 // Indirecting a nil interface/pointer gives a zero value; 80 // stop as calling reflect.Value.Type on an invalid type would panic. 81 if !v.IsValid() { 82 d.printf("nil") 83 return 84 } 85 // We print the original pointer type if there was one. 86 origType := v.Type() 87 v = reflect.Indirect(v) 88 89 t := v.Type() 90 switch t { 91 // Simple types which can stringify themselves. 92 case typeTokenPos, typeTokenToken: 93 d.printf("%s(%q)", t, v.Interface()) 94 return 95 } 96 97 switch t.Kind() { 98 default: 99 // We assume all other kinds are basic in practice, like string or bool. 100 if t.PkgPath() != "" { 101 // Mention defined and non-predeclared types, for clarity. 102 d.printf("%s(%#v)", t, v.Interface()) 103 } else { 104 d.printf("%#v", v.Interface()) 105 } 106 case reflect.Slice: 107 d.printf("%s{", origType) 108 d.level++ 109 for i := 0; i < v.Len(); i++ { 110 d.newline() 111 ev := v.Index(i) 112 d.value(ev) 113 } 114 d.level-- 115 d.newline() 116 d.printf("}") 117 case reflect.Struct: 118 d.printf("%s{", origType) 119 d.level++ 120 for i := 0; i < v.NumField(); i++ { 121 f := t.Field(i) 122 if !gotoken.IsExported(f.Name) { 123 continue 124 } 125 switch f.Name { 126 // These fields are cyclic, and they don't represent the syntax anyway. 127 case "Scope", "Node", "Unresolved": 128 continue 129 } 130 d.newline() 131 d.printf("%s: ", f.Name) 132 d.value(v.Field(i)) 133 } 134 val := v.Addr().Interface() 135 if val, ok := val.(ast.Node); ok { 136 // Comments attached to a node aren't a regular field, but are still useful. 137 // The majority of nodes won't have comments, so skip them when empty. 138 if comments := ast.Comments(val); len(comments) > 0 { 139 d.newline() 140 d.printf("Comments: ") 141 d.value(reflect.ValueOf(comments)) 142 } 143 } 144 d.level-- 145 d.newline() 146 d.printf("}") 147 } 148 }