Goコード生成

この記事では、Go言語のフレームワーク内でのコード生成の可能性を検討します。これにより、組み込みのリフレクションを部分的に置き換え、コンパイル段階でタイプセーフを失うことはありません。

Goプログラミング言語は、強力なコード生成ツールを提供します。 Goは一般化(ジェネリック)の欠如を非難することが非常に多く、これは実際問題になる可能性があります。 そして、ここでコード生成が救助に来ます。それは一見小さな操作ではかなり難しいですが、それでもかなり柔軟なツールです。 基本的な一般化のニーズをカバーする既製のコード生成ライブラリがすでに多数あります。 これは、「リファレンス」 ストリンガーであり、 ffjson備えたより便利なjsonenums です。強力なgenを使用すると、ユーザータイプの多くのforEachで利用できないアナログの追加など、Goに少し機能を追加できます。 それ以外は、genは独自のジェネレーターによって非常に簡単に拡張できます。 残念ながら、genは特定の型のメソッドを生成するコードに制限されています。

実際、私は良い人生からではなく、コード生成のトピックに触れることにしましたが、別の適切なソリューションを見つけることができなかった小さなタスクに直面したからです。



タスクは次のとおりです。定数のリストがあります。

type Color int const ( Green Color = iota Red Blue Black )
      
      





たとえば、パレットの出力用に、すべての色定数を含む配列(リスト)が必要です。

 Colors = [...]Color{Green, Red, Blue, Black}
      
      





同時に、Color型の定数の数を変更するときに要素の追加または削除を忘れる可能性を排除するために、Colorsが自動的に形成されるようにします。



主なツールは、次の標準パッケージです。

go / ast /

go /パーサー/

go /トークン/



これらのパッケージの助けを借りて、goソースコードを含むファイルのast( 抽象構文ツリー )を取得できます。 ASTは文字通り2行で取得されます。

 fset := token.NewFileSet() f, err := parser.ParseFile(fset, "", []byte(source), 0)
      
      





ParseFileの引数として、ファイルまたはテキストコンテンツへのパスを渡すことができます(詳細については、 https: //golang.org/pkg/go/parser/#ParseFileを参照してください )。 これで、変数fにastが含まれ、必要なコードを生成するために使用できます。

特定のタイプ(色)のすべての定数を含むリストを作成するには、astを調べて、定数を記述するノードを見つける必要があります。 これは、機能なしではありませんが、かなり簡単な方法で行われます。 実際、Goでは、型指定されていない定数またはiotaコンストラクトを介して自動インクリメントする定数のリストを定義できますこのような定数の場合、astの型は決定されず、値と型はコンパイル段階ですでに計算されます。 したがって、astを解析するときは構文を考慮する必要があります。

以下は、iotaを介した定数の定義を考慮したコード例です。



バイパス
 typeName := "Color" //       typ := "" //      ast consts := make([]string, 0) //     for _, decl := range f.Decls { //   , , ,   .. switch decl := decl.(type) { case *ast.GenDecl: switch decl.Tok { case token.CONST: //    for _, spec := range decl.Specs { vspec := spec.(*ast.ValueSpec) //     if vspec.Type == nil && len(vspec.Values) > 0 { //    "X = 1" //         //     ,       const typ = "" continue } if vspec.Type != nil { //"const Green Color" -    if ident, ok := vspec.Type.(*ast.Ident); ok { typ = ident.Name } else { continue } } if typ == typeName { //    ,      consts consts = append(consts, vspec.Names[0].Name) } } } } }
      
      







より詳細には、 ストリンガーパッケージで同様のコードがコメントされています。

現在は、既存のすべての色のリストを返す関数を生成します。

コード生成
 var constListTmpl = `//CODE GENERATED AUTOMATICALLY //THIS FILE SHOULD NOT BE EDITED BY HAND package {{.Package}} type {{.Name}}s []{{.Name}} func (c {{.Name}}s)List() []{{.Name}} { return []{{.Name}}{{"{"}}{{.List}}{{"}"}} } ` templateData := struct { Package string Name string List string }{ Package: "main", Name: typeName, List: strings.Join(consts, ", "), } t := template.Must(template.New("const-list").Parse(constListTmpl)) if err := t.Execute(os.Stdout, templateData); err != nil { fmt.Println(err) }
      
      









出力では、次の関数を取得します。

 type Colors []Color func (c Colors)List() []Color { return []Color{Green, Red, Blue, Black} }
      
      





関数の使用法:

 Colors{}.List()
      
      





リストの例https://play.golang.org/p/Mck9Y66Z1b



ストリンガージェネレーターに基づくconst_listジェネレーターを使用する準備ができました。



All Articles