2015-07-25 3 views
21

Следующий тест пытается использовать AST для добавления полей в структуру. Поля добавляются правильно, но комментарии добавляются не в порядок. Я понимаю, что эта позиция, возможно, потребуется указать вручную, но я до сих пор нарисовал пустой ответ.Комментарии после завершения заказа после добавления товара в Go AST

Вот неудачу тест: http://play.golang.org/p/RID4N30FZK

Вот код:

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

Я подозреваю, что вам необходимо обновить [Комментировать карту] (http://golang.org/pkg/go/ast/#NewCommentMap), чтобы это работало правильно. –

+2

Здесь вы можете увидеть некоторые детали фактических и ожидаемых деревьев: https://play.golang.org/p/qv63Hu1xmP благодаря https://golang.org/pkg/go/ast/#Fprint. Основные отличия, которые я вижу, это 'Slash',' NamePos' и 'Obj' не установлены. Я пытался возиться с позициями, но не мог понять это правильно ... – HectorJ

+1

У меня есть тупость ... Похоже, что есть какое-то другое бухгалтерию, которое нужно сделать, поскольку я смог получить Slash и NamePos для соответствия (несмотря на 100%): http://play.golang.org/p/pQodZncMjA - и даже добавление в AddLine и CommentMap, похоже, не помогает: http: //play.golang. org/p/GGj2eDwDF- –

ответ

4

Я считаю, что я получил его на работу. Как указано в моем комментарии выше, основные моменты, необходимые являются:

  1. В частности установить расположение буфера в том числе Slash и NamePos
  2. Использование token.File.AddLine для добавления новых строк в определенных сдвигах (рассчитанные с использованием позиции из пункта 1)
  3. Overallocate источник буфера так token.File.Position (используется printer.Printer и token.File.Addline не выходят из строя проверки дальности на буфер источника

Co де:

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) 
} 

Пример: http://play.golang.org/p/_q1xh3giHm

Для Пункт (3), также важно установить все overallocated байт пространства (0x20), так что принтер не жалуется на нуль байт при их обработке.

+0

Я думаю, вы его взломали! Отлично сработано. Я уверен, что буду отвечать за вопросы, поскольку добавляю больше функциональности, но это отличный ответ. Благодаря! –

Смежные вопросы