Starten Sie die Codegenerierung

  • Tutorial
In diesem Artikel möchte ich einige der Möglichkeiten der Codegenerierung im Rahmen der Sprache Go betrachten, die die eingebaute Reflektion teilweise ersetzen und die Typensicherheit in der Kompilierungsphase nicht verlieren können.
Die Programmiersprache Go bietet leistungsstarke Tools zur Codegenerierung. Sehr oft wird Go für das Fehlen von Verallgemeinerungen (Generika) verantwortlich gemacht, und dies kann in der Tat ein Problem sein. Und hier hilft die Codegenerierung, die für kleine Routineoperationen auf den ersten Blick eher schwierig ist, aber dennoch ein recht flexibles Werkzeug darstellt. Es gibt bereits eine Reihe vorgefertigter Codegenerierungsbibliotheken, die die grundlegenden Generalisierungsanforderungen abdecken. Diese „Referenz» der Stringer und nützliches jsonenums mit ffjson Einem leistungsfähigen GenAußerdem können Sie Go ein wenig Funktionalität hinzufügen, einschließlich eines Analogons von forEach, das für viele benutzerdefinierte Typen nicht ausreicht. Alles andere ist durch eigene Generatoren recht einfach erweiterbar. Leider ist gen auf Codegenerierungsmethoden für bestimmte Typen beschränkt.
Eigentlich habe ich beschlossen, das Thema der Code-Generierung nicht aus einem guten Leben heraus anzusprechen, sondern weil ich vor einer kleinen Aufgabe stand, für die ich keine andere passende Lösung finden konnte.

Die Aufgabe ist die folgende, es gibt eine Liste von Konstanten:
type Color int
const (
	Green Color = iota
	Red
	Blue
	Black
)

Sie benötigen ein Array (Liste), das alle Farbkonstanten enthält, z. B. für die Ausgabe in der Palette.
Colors = [...]Color{Green, Red, Blue, Black}

Gleichzeitig möchte ich, dass Farben automatisch gebildet werden, damit nicht vergessen wird, ein Element hinzuzufügen oder zu entfernen, wenn die Anzahl der Konstanten vom Typ Farbe geändert wird.

Die wichtigsten Tools sind die folgenden Standardpakete:
go / ast /
go / parser /
go / token /

Mit diesen Paketen können wir ast ( abstrakte Syntaxstruktur ) von jeder Datei mit go-Quellcode erhalten. AST wir bekommen buchstäblich in zwei Zeilen:
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "", []byte(source), 0)

Als Argumente für ParseFile können Sie entweder den Pfad zur Datei oder den Textinhalt übergeben (Details finden Sie unter https://golang.org/pkg/go/parser/#ParseFile ). Jetzt enthält die Variable f ast, mit dem der erforderliche Code generiert werden kann.
Um eine Liste mit allen Konstanten eines bestimmten Typs (Farbe) zu erstellen, müssen Sie ast durchgehen und die Knoten finden, die die Konstanten beschreiben. Dies geschieht in eher trivialer Weise, wenn auch nicht ohne Merkmale. Tatsache ist, dass Sie mit Go nicht typisierte Konstanten oder eine Liste von Konstanten mit automatischer Inkrementierung über das iota- Konstrukt definieren können . Daher müssen Sie beim Parsen von ast die Syntax berücksichtigen.
Unten sehen Sie ein Codebeispiel, das die Definition von Konstanten über iota berücksichtigt.

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


Genauer gesagt wird ein ähnlicher Code im String- Paket kommentiert .
Jetzt muss noch eine Funktion generiert werden, die eine Liste aller vorhandenen Farben zurückgibt.
Code-Generierung

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



Am Ausgang erhalten wir folgende Funktion:
type Colors []Color
func (c Colors)List() []Color {
	return []Color{Green, Red, Blue, Black}
}

Funktionsnutzung:
Colors{}.List()

Beispielprotokoll https://play.golang.org/p/Mck9Y66Z1b

Gebrauchsfertiger const_list- Generator basierend auf dem String- Generator.

Jetzt auch beliebt: