github.com/teddydd/sh@v2.6.4+incompatible/cmd/shfmt/json.go (about) 1 // Copyright (c) 2017, Daniel Martà <mvdan@mvdan.cc> 2 // See LICENSE for licensing information 3 4 package main 5 6 import ( 7 "encoding/json" 8 "go/ast" 9 "io" 10 "reflect" 11 12 "mvdan.cc/sh/syntax" 13 ) 14 15 func writeJSON(w io.Writer, f *syntax.File, pretty bool) error { 16 val := reflect.ValueOf(f) 17 v, _ := recurse(val, val) 18 enc := json.NewEncoder(w) 19 if pretty { 20 enc.SetIndent("", "\t") 21 } 22 return enc.Encode(v) 23 } 24 25 func recurse(val, valPtr reflect.Value) (interface{}, string) { 26 switch val.Kind() { 27 case reflect.Ptr: 28 elem := val.Elem() 29 if !elem.IsValid() { 30 return nil, "" 31 } 32 return recurse(elem, val) 33 case reflect.Interface: 34 if val.IsNil() { 35 return nil, "" 36 } 37 v, tname := recurse(val.Elem(), val) 38 m := v.(map[string]interface{}) 39 m["Type"] = tname 40 return m, "" 41 case reflect.Struct: 42 m := make(map[string]interface{}, val.NumField()+1) 43 typ := val.Type() 44 for i := 0; i < val.NumField(); i++ { 45 ftyp := typ.Field(i) 46 if ftyp.Type.Name() == "Pos" { 47 continue 48 } 49 if !ast.IsExported(ftyp.Name) { 50 continue 51 } 52 fval := val.Field(i) 53 v, _ := recurse(fval, fval) 54 switch ftyp.Name { 55 case "StmtList": 56 // inline their fields 57 for name, v := range v.(map[string]interface{}) { 58 m[name] = v 59 } 60 default: 61 m[ftyp.Name] = v 62 } 63 } 64 // use valPtr to find the method, as methods are defined on the 65 // pointer values. 66 if posMethod := valPtr.MethodByName("Pos"); posMethod.IsValid() { 67 m["Pos"] = translatePos(posMethod.Call(nil)[0]) 68 } 69 if posMethod := valPtr.MethodByName("End"); posMethod.IsValid() { 70 m["End"] = translatePos(posMethod.Call(nil)[0]) 71 } 72 return m, typ.Name() 73 case reflect.Slice: 74 l := make([]interface{}, val.Len()) 75 for i := 0; i < val.Len(); i++ { 76 elem := val.Index(i) 77 l[i], _ = recurse(elem.Addr(), elem) 78 } 79 return l, "" 80 default: 81 return val.Interface(), "" 82 } 83 } 84 85 func translatePos(val reflect.Value) map[string]interface{} { 86 return map[string]interface{}{ 87 "Offset": val.MethodByName("Offset").Call(nil)[0].Uint(), 88 "Line": val.MethodByName("Line").Call(nil)[0].Uint(), 89 "Col": val.MethodByName("Col").Call(nil)[0].Uint(), 90 } 91 }