aboutsummaryrefslogblamecommitdiffstats
path: root/core/vm/sqlvm/ast/printer.go
blob: ac4c43fe04376eff6d72cb3842fceb8ba1ece9a5 (plain) (tree)
1
2
3
4
5
6
7
8
9



             
            
                 
                 
                 
                 
                      

 
























                                                                        
                                                                  
                                              


                                                     
                     
                                                        


                                     

                                
                                    
                                                                
                 

                                        
                                    
         
                             
 

                                                 
                                                                        

                                    
                                                                        

                                     
                                                                        



                                  







                                                               
                 

                                             




                                                                                       
                 


                                                        
         


























                                                                                             














                                                                                 





                                                              
                                     







                                                                         
                 
                                                  









                                                                                         
                 


                                                        
         
                                                                     


                                     
                                                                
                                                  
 
                                                                
 
package ast

import (
    "fmt"
    "io"
    "reflect"
    "strconv"
    "strings"
    "unicode"
    "unicode/utf8"
)

func formatBytes(b []byte) string {
    if utf8.Valid(b) {
        for r, i, size := rune(0), 0, 0; i < len(b); i += size {
            r, size = utf8.DecodeRune(b[i:])
            if !unicode.IsPrint(r) {
                return strconv.Quote(string(b))
            }
        }
        return string(b)
    }
    return fmt.Sprintf("%v", b)
}

func formatString(s string) string {
    if utf8.ValidString(s) {
        for _, r := range s {
            if !unicode.IsPrint(r) {
                return strconv.Quote(s)
            }
        }
        return s
    }
    return fmt.Sprintf("%v", []byte(s))
}

func printAST(w io.Writer, n interface{}, s []byte, prefix string,
    detail bool, depth int) (int, error) {

    indent := strings.Repeat(prefix, depth)
    indentLong := strings.Repeat(prefix, depth+1)
    if n == nil {
        return fmt.Fprintf(w, "%snil\n", indent)
    }
    typeOf := reflect.TypeOf(n)
    valueOf := reflect.ValueOf(n)
    kind := typeOf.Kind()
    if kind == reflect.Ptr {
        if valueOf.IsNil() {
            return fmt.Fprintf(w, "%snil\n", indent)
        }
        valueOf = valueOf.Elem()
        typeOf = typeOf.Elem()
        kind = typeOf.Kind()
    }
    name := typeOf.Name()

    if stringer, ok := n.(fmt.Stringer); ok {
        s := stringer.String()
        return fmt.Fprintf(w, "%s%s\n", indent, formatString(s))
    }
    if s, ok := n.(string); ok {
        return fmt.Fprintf(w, "%s%s\n", indent, formatString(s))
    }
    if bs, ok := n.([]byte); ok {
        return fmt.Fprintf(w, "%s%s\n", indent, formatBytes(bs))
    }
    if kind == reflect.Slice {
        l := valueOf.Len()
        if l == 0 {
            return fmt.Fprintf(w, "%s[]\n", indent)
        }

        var bytesWritten int
        b, err := fmt.Fprintf(w, "%s[\n", indent)
        bytesWritten += b
        if err != nil {
            return bytesWritten, err
        }
        for i := 0; i < l; i++ {
            v := valueOf.Index(i)
            b, err = printAST(w, v.Interface(), s, prefix, detail, depth+1)
            bytesWritten += b
            if err != nil {
                return bytesWritten, err
            }
        }
        b, err = fmt.Fprintf(w, "%s]\n", indent)
        bytesWritten += b
        return bytesWritten, err
    }
    if kind == reflect.Struct {
        type field struct {
            name  string
            value interface{}
        }
        var fields []field
        var collect func(reflect.Type, reflect.Value)
        collect = func(typeOf reflect.Type, valueOf reflect.Value) {
            l := typeOf.NumField()
            for i := 0; i < l; i++ {
                if !detail && typeOf.Field(i).Tag.Get("print") == "-" {
                    continue
                }
                if typeOf.Field(i).Anonymous {
                    embeddedInterface := valueOf.Field(i).Interface()
                    embeddedTypeOf := reflect.TypeOf(embeddedInterface)
                    embeddedValueOf := reflect.ValueOf(embeddedInterface)
                    collect(embeddedTypeOf, embeddedValueOf)
                    continue
                }
                fields = append(fields, field{
                    name:  typeOf.Field(i).Name,
                    value: valueOf.Field(i).Interface(),
                })
            }
        }
        collect(typeOf, valueOf)

        var position string
        if node, ok := n.(Node); ok {
            begin := node.GetPosition()
            length := node.GetLength()
            if node.HasPosition() {
                end := begin + length - 1
                token := s[begin : begin+length]
                position = fmt.Sprintf("%d-%d %s",
                    begin, end, strconv.Quote(string(token)))
            } else {
                position = "no position info"
            }
        }

        var bytesWritten int
        b, err := fmt.Fprintf(w, "%s%s", indent, name)
        bytesWritten += b
        if err != nil {
            return bytesWritten, err
        }
        if len(fields) == 0 {
            b, err = fmt.Fprintf(w, " {}  // %s\n", position)
            bytesWritten += b
            return bytesWritten, err
        }
        b, err = fmt.Fprintf(w, " {  // %s\n", position)
        bytesWritten += b
        if err != nil {
            return bytesWritten, err
        }
        for i := 0; i < len(fields); i++ {
            b, err = fmt.Fprintf(w, "%s%s:\n", indentLong, fields[i].name)
            bytesWritten += b
            if err != nil {
                return bytesWritten, err
            }
            b, err = printAST(w, fields[i].value, s, prefix, detail, depth+2)
            bytesWritten += b
            if err != nil {
                return bytesWritten, err
            }
        }
        b, err = fmt.Fprintf(w, "%s}\n", indent)
        bytesWritten += b
        return bytesWritten, err
    }
    return fmt.Fprintf(w, "%s%+v\n", indent, valueOf.Interface())
}

// PrintAST prints AST for debugging.
func PrintAST(output io.Writer, node interface{}, source []byte,
    indent string, detail bool) (int, error) {

    return printAST(output, node, source, indent, detail, 0)
}