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  }