this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cmd/cue-ast-print: allow reading stdin

It's useful to be able to pipe stdin into this command.
While we're about it, add a proper usage message
and make the output slightly more compact by:
- omitting a newline for empty slices and structs
- omitting the type name when it's implied by an enclosing
slice (as allowed in Go slice literals).

Also avoid the unneeded calls to `reflect.Value.Interface`: the
fmt package will do that automatically for us.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: I6339c4ef8d0af0f7c567301050a837350cb4de7f
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1199601
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>

+52 -21
+52 -21
internal/cmd/cue-ast-print/main.go
··· 19 19 20 20 import ( 21 21 "flag" 22 + "log" 22 23 "os" 23 24 24 25 "fmt" ··· 33 34 ) 34 35 35 36 func main() { 37 + flag.Usage = func() { 38 + fmt.Fprintf(os.Stderr, "usage: cue-ast-print [file.cue]\n") 39 + os.Exit(2) 40 + } 36 41 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") 42 + var filename string 43 + var src any 44 + switch flag.NArg() { 45 + case 0: 46 + filename = "<stdin>" 47 + data, err := io.ReadAll(os.Stdin) 48 + if err != nil { 49 + log.Fatal(err) 50 + } 51 + src = data 52 + case 1: 53 + filename = flag.Arg(0) 54 + default: 55 + flag.Usage() 41 56 } 42 - file, err := parser.ParseFile(args[0], nil, parser.ParseComments) 57 + file, err := parser.ParseFile(filename, src, parser.ParseComments) 43 58 if err != nil { 44 59 panic(err) 45 60 } ··· 48 63 49 64 func debugPrint(w io.Writer, node ast.Node) { 50 65 d := &debugPrinter{w: w} 51 - d.value(reflect.ValueOf(node)) 66 + d.value(reflect.ValueOf(node), nil) 52 67 d.newline() 53 68 } 54 69 ··· 70 85 typeTokenToken = reflect.TypeFor[token.Token]() 71 86 ) 72 87 73 - func (d *debugPrinter) value(v reflect.Value) { 88 + func (d *debugPrinter) value(v reflect.Value, impliedType reflect.Type) { 74 89 // Skip over interface types. 75 90 if v.Kind() == reflect.Interface { 76 91 v = v.Elem() ··· 90 105 switch t { 91 106 // Simple types which can stringify themselves. 92 107 case typeTokenPos, typeTokenToken: 93 - d.printf("%s(%q)", t, v.Interface()) 108 + d.printf("%s(%q)", t, v) 94 109 return 95 110 } 96 111 ··· 99 114 // We assume all other kinds are basic in practice, like string or bool. 100 115 if t.PkgPath() != "" { 101 116 // Mention defined and non-predeclared types, for clarity. 102 - d.printf("%s(%#v)", t, v.Interface()) 117 + d.printf("%s(%#v)", t, v) 103 118 } else { 104 - d.printf("%#v", v.Interface()) 119 + d.printf("%#v", v) 105 120 } 106 121 case reflect.Slice: 107 - d.printf("%s{", origType) 108 - d.level++ 109 - for i := 0; i < v.Len(); i++ { 122 + if origType != impliedType { 123 + d.printf("%s", origType) 124 + } 125 + d.printf("{") 126 + if v.Len() > 0 { 127 + d.level++ 128 + for i := 0; i < v.Len(); i++ { 129 + d.newline() 130 + ev := v.Index(i) 131 + // Note: a slice literal implies the type of its elements 132 + // so we can avoid mentioning the type 133 + // of each element if it matches. 134 + d.value(ev, t.Elem()) 135 + } 136 + d.level-- 110 137 d.newline() 111 - ev := v.Index(i) 112 - d.value(ev) 113 138 } 114 - d.level-- 115 - d.newline() 116 139 d.printf("}") 117 140 case reflect.Struct: 118 - d.printf("%s{", origType) 141 + if origType != impliedType { 142 + d.printf("%s", origType) 143 + } 144 + d.printf("{") 145 + printed := false 119 146 d.level++ 120 147 for i := 0; i < v.NumField(); i++ { 121 148 f := t.Field(i) ··· 127 154 case "Scope", "Node", "Unresolved": 128 155 continue 129 156 } 157 + printed = true 130 158 d.newline() 131 159 d.printf("%s: ", f.Name) 132 - d.value(v.Field(i)) 160 + d.value(v.Field(i), nil) 133 161 } 134 162 val := v.Addr().Interface() 135 163 if val, ok := val.(ast.Node); ok { 136 164 // Comments attached to a node aren't a regular field, but are still useful. 137 165 // The majority of nodes won't have comments, so skip them when empty. 138 166 if comments := ast.Comments(val); len(comments) > 0 { 167 + printed = true 139 168 d.newline() 140 169 d.printf("Comments: ") 141 - d.value(reflect.ValueOf(comments)) 170 + d.value(reflect.ValueOf(comments), nil) 142 171 } 143 172 } 144 173 d.level-- 145 - d.newline() 174 + if printed { 175 + d.newline() 176 + } 146 177 d.printf("}") 147 178 } 148 179 }