2015-07-25 8 views
21

Aşağıdaki sınama, bir yapıya alan eklemek için AST kullanmayı dener. Alanlar doğru şekilde eklenir, ancak yorumlar sırayla eklenir. Pozisyonu manuel olarak belirtmek zorunda kalabilirim, ama şimdiye kadar boş bir cevap buldum. İşte http://play.golang.org/p/RID4N30FZKYorumlar AST'ye öğe ekledikten sonra yapılan yorumlar yorumsuz: AST

kod::

İşte başarısız bir test var

package generator 

import (
    "bytes" 
    "fmt" 
    "go/ast" 
    "go/parser" 
    "go/printer" 
    "go/token" 
    "testing" 
) 

func TestAst(t *testing.T) { 

    source := `package a 

// B comment 
type B struct { 
    // C comment 
    C string 
}` 

    fset := token.NewFileSet() 
    file, err := parser.ParseFile(fset, "", []byte(source), parser.ParseComments) 
    if err != nil { 
     t.Error(err) 
    } 

    v := &visitor{ 
     file: file, 
    } 
    ast.Walk(v, file) 

    var output []byte 
    buf := bytes.NewBuffer(output) 
    if err := printer.Fprint(buf, fset, file); err != nil { 
     t.Error(err) 
    } 

    expected := `package a 

// B comment 
type B struct { 
    // C comment 
    C string 
    // D comment 
    D int 
    // E comment 
    E float64 
} 
` 

    if buf.String() != expected { 
     t.Error(fmt.Sprintf("Test failed. Expected:\n%s\nGot:\n%s", expected, buf.String())) 
    } 

    /* 
    actual output = `package a 

// B comment 
type B struct { 
    // C comment 
    // D comment 
    // E comment 
    C string 
    D int 
    E float64 
} 
` 
    */ 

} 

type visitor struct { 
    file *ast.File 
} 

func (v *visitor) Visit(node ast.Node) (w ast.Visitor) { 

    if node == nil { 
     return v 
    } 

    switch n := node.(type) { 
    case *ast.GenDecl: 
     if n.Tok != token.TYPE { 
      break 
     } 
     ts := n.Specs[0].(*ast.TypeSpec) 
     if ts.Name.Name == "B" { 
      fields := ts.Type.(*ast.StructType).Fields 
      addStructField(fields, v.file, "int", "D", "D comment") 
      addStructField(fields, v.file, "float64", "E", "E comment") 
     } 
    } 

    return v 
} 

func addStructField(fields *ast.FieldList, file *ast.File, typ string, name string, comment string) { 
    c := &ast.Comment{Text: fmt.Sprint("// ", comment)} 
    cg := &ast.CommentGroup{List: []*ast.Comment{c}} 
    f := &ast.Field{ 
     Doc: cg, 
     Names: []*ast.Ident{ast.NewIdent(name)}, 
     Type: ast.NewIdent(typ), 
    } 
    fields.List = append(fields.List, f) 
    file.Comments = append(file.Comments, cg) 
} 
+1

Doğru çalışması için, [Yorum Haritası] 'nı (http://golang.org/pkg/go/ast/#NewCommentMap) güncellemeniz gerektiğinden şüpheleniyorum. –

+2

Burada gerçek ve beklenen ağaçların bazı ayrıntılarını görebilirsiniz: https://play.golang.org/p/qv63Hu1xmP https://golang.org/pkg/go/ast/#Fprint. Gördüğüm en önemli farklar "Slash", "NamePos" ve "Obj" değerleri değil. Pozisyonlarla uğraşmaya çalıştım, ama doğru anlayamadım ... – HectorJ

+1

Bu beni güldürdü ... Yapması gereken bir tür başka kitaplık var gibi görünüyor, Slash elde edebildiğim gibi ve NamePos (buna rağmen 100 ile dengelenerek): http://play.golang.org/p/pQodZncMjA - ve hatta AddLine ve CommentMap ekleyerek yardımcı olmuyor: http: //play.golang. org/p/GGj2eDwDF- –

cevap

4

ben çalışmak için onu aldık inanıyoruz. Yukarıda benim yorum da belirtildiği üzere, gerekli olan ana noktalar şunlardır:

  1. Özellikle Slash ve NamePos dahil tampon konumlarını ayarlamak
  2. Kullanım token.File.AddLine (madde 1 den pozisyonları kullanılarak hesaplanmıştır) belirli kayıklıklarında yeni satırlar eklemek
  3. Overallocate çok token.File.Position (kaynak tamponu ile aralığı kontroller başarısız yoktur printer.Printer ve token.File.Addline tarafından kullanılan kaynak tamponu

Co. de:

package main 

import (
    "bytes" 
    "fmt" 
    "go/ast" 
    "go/parser" 
    "go/printer" 
    "go/token" 
    "testing" 
) 

func main() { 
    tests := []testing.InternalTest{{"TestAst", TestAst}} 
    matchAll := func(t string, pat string) (bool, error) { return true, nil } 
    testing.Main(matchAll, tests, nil, nil) 
} 

func TestAst(t *testing.T) { 

    source := `package a 

// B comment 
type B struct { 
    // C comment 
    C string 
}` 

    buffer := make([]byte, 1024, 1024) 
    for idx,_ := range buffer { 
     buffer[idx] = 0x20 
    } 
    copy(buffer[:], source) 
    fset := token.NewFileSet() 
    file, err := parser.ParseFile(fset, "", buffer, parser.ParseComments) 
    if err != nil { 
     t.Error(err) 
    } 

    v := &visitor{ 
     file: file, 
     fset: fset, 
    } 
    ast.Walk(v, file) 

    var output []byte 
    buf := bytes.NewBuffer(output) 
    if err := printer.Fprint(buf, fset, file); err != nil { 
     t.Error(err) 
    } 

    expected := `package a 

// B comment 
type B struct { 
    // C comment 
    C string 
    // D comment 
    D int 
    // E comment 
    E float64 
} 
` 
    if buf.String() != expected { 
     t.Error(fmt.Sprintf("Test failed. Expected:\n%s\nGot:\n%s", expected, buf.String())) 
    } 

} 

type visitor struct { 
    file *ast.File 
    fset *token.FileSet 
} 

func (v *visitor) Visit(node ast.Node) (w ast.Visitor) { 

    if node == nil { 
     return v 
    } 

    switch n := node.(type) { 
    case *ast.GenDecl: 
     if n.Tok != token.TYPE { 
      break 
     } 
     ts := n.Specs[0].(*ast.TypeSpec) 
     if ts.Name.Name == "B" { 
      fields := ts.Type.(*ast.StructType).Fields 
      addStructField(v.fset, fields, v.file, "int", "D", "D comment") 
      addStructField(v.fset, fields, v.file, "float64", "E", "E comment") 
     } 
    } 

    return v 
} 

func addStructField(fset *token.FileSet, fields *ast.FieldList, file *ast.File, typ string, name string, comment string) { 
    prevField := fields.List[fields.NumFields()-1] 

    c := &ast.Comment{Text: fmt.Sprint("// ", comment), Slash: prevField.End() + 1} 
    cg := &ast.CommentGroup{List: []*ast.Comment{c}} 
    o := ast.NewObj(ast.Var, name) 
    f := &ast.Field{ 
     Doc: cg, 
     Names: []*ast.Ident{&ast.Ident{Name: name, Obj: o, NamePos: cg.End() + 1}}, 
    } 
    o.Decl = f 
    f.Type = &ast.Ident{Name: typ, NamePos: f.Names[0].End() + 1} 

    fset.File(c.End()).AddLine(int(c.End())) 
    fset.File(f.End()).AddLine(int(f.End())) 

    fields.List = append(fields.List, f) 
    file.Comments = append(file.Comments, cg) 
} 

Örnek: Yazıcı boş şikayetçi kalmaması, aynı zamanda önemlidir http://play.golang.org/p/_q1xh3giHm

Item (3) için boşluk (0x20) tüm overallocated bayt ayarlamak için Onları işlerken baytlar.

+0

Sanırım onu ​​kırdın! Aferin. Daha fazla işlevsellik ekledikçe soruları takip edeceğime eminim, ama bu harika bir cevap. Teşekkürler! –