generate.go 6.98 KB
Newer Older
Eric Myhre's avatar
Eric Myhre committed
1 2 3
package gengo

import (
4
	"fmt"
Eric Myhre's avatar
Eric Myhre committed
5 6 7 8 9 10 11
	"io"
	"os"
	"path/filepath"

	"github.com/ipld/go-ipld-prime/schema"
)

12 13 14 15
// Generate takes a typesystem and the adjunct config for codegen,
// and emits generated code in the given path with the given package name.
//
// All of the files produced will match the pattern "ipldsch.*.gen.go".
Eric Myhre's avatar
Eric Myhre committed
16
func Generate(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctCfg) {
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
	// Emit fixed bits.
	withFile(filepath.Join(pth, "ipldsch.minima.gen.go"), func(f io.Writer) {
		EmitInternalEnums(pkgName, f)
	})

	// Local helper function for applying generation logic to each type.
	//  We will end up doing this more than once because in this layout, more than one file contains part of the story for each type.
	applyToEachType := func(fn func(tg TypeGenerator, w io.Writer), f io.Writer) {
		// FIXME: the order of this iteration is not stable, and it should be, because it affects determinism of the output.
		for _, typ := range ts.GetTypes() {
			switch t2 := typ.(type) {
			case *schema.TypeBool:
				fn(NewBoolReprBoolGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeInt:
				fn(NewIntReprIntGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeFloat:
				fn(NewFloatReprFloatGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeString:
				fn(NewStringReprStringGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeBytes:
				fn(NewBytesReprBytesGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeLink:
				fn(NewLinkReprLinkGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeStruct:
				switch t2.RepresentationStrategy().(type) {
				case schema.StructRepresentation_Map:
					fn(NewStructReprMapGenerator(pkgName, t2, adjCfg), f)
				case schema.StructRepresentation_Tuple:
					fn(NewStructReprTupleGenerator(pkgName, t2, adjCfg), f)
				case schema.StructRepresentation_Stringjoin:
					fn(NewStructReprStringjoinGenerator(pkgName, t2, adjCfg), f)
				default:
					panic("unrecognized struct representation strategy")
				}
			case *schema.TypeMap:
				fn(NewMapReprMapGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeList:
				fn(NewListReprListGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeUnion:
				switch t2.RepresentationStrategy().(type) {
				case schema.UnionRepresentation_Keyed:
					fn(NewUnionReprKeyedGenerator(pkgName, t2, adjCfg), f)
				case schema.UnionRepresentation_Kinded:
					fn(NewUnionReprKindedGenerator(pkgName, t2, adjCfg), f)
				default:
					panic("unrecognized union representation strategy")
				}
			default:
				panic("add more type switches here :)")
			}
		}
	}

	// Emit a file with the type table, and the golang type defns for each type.
	withFile(filepath.Join(pth, "ipldsch.types.gen.go"), func(f io.Writer) {
		// Emit headers, import statements, etc.
		fmt.Fprintf(f, "package %s\n\n", pkgName)
		fmt.Fprintf(f, doNotEditComment+"\n\n")

		// Emit the type table.
		EmitTypeTable(pkgName, ts, adjCfg, f)

		// Emit the type defns matching the schema types.
		fmt.Fprintf(f, "\n// --- type definitions follow ---\n\n")
		applyToEachType(func(tg TypeGenerator, w io.Writer) {
			tg.EmitNativeType(w)
			fmt.Fprintf(f, "\n")
		}, f)

	})

	// Emit a file with all the Node/NodeBuilder/NodeAssembler boilerplate.
	//  Also includes typedefs for representation-level data.
	//  Also includes the MaybeT boilerplate.
	withFile(filepath.Join(pth, "ipldsch.satisfaction.gen.go"), func(f io.Writer) {
		// Emit headers, import statements, etc.
		fmt.Fprintf(f, "package %s\n\n", pkgName)
		fmt.Fprintf(f, doNotEditComment+"\n\n")
		fmt.Fprintf(f, "import (\n")
		fmt.Fprintf(f, "\tipld \"github.com/ipld/go-ipld-prime\"\n")        // referenced everywhere.
		fmt.Fprintf(f, "\t\"github.com/ipld/go-ipld-prime/node/mixins\"\n") // referenced by node implementation guts.
		fmt.Fprintf(f, "\t\"github.com/ipld/go-ipld-prime/schema\"\n")      // referenced by maybes (and surprisingly little else).
		fmt.Fprintf(f, ")\n\n")

		// For each type, we'll emit... everything except the native type, really.
		applyToEachType(func(tg TypeGenerator, w io.Writer) {
			tg.EmitNativeAccessors(w)
			tg.EmitNativeBuilder(w)
			tg.EmitNativeMaybe(w)
			EmitNode(tg, w)
			tg.EmitTypedNodeMethodType(w)
			tg.EmitTypedNodeMethodRepresentation(w)

			nrg := tg.GetRepresentationNodeGen()
			EmitNode(nrg, w)

			fmt.Fprintf(f, "\n")
		}, f)
	})
}

// GenerateSplayed is like Generate, but emits a differnet pattern of files.
// GenerateSplayed emits many more individual files than Generate.
//
// This function should be considered deprecated and may be removed in the future.
func GenerateSplayed(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctCfg) {
Eric Myhre's avatar
Eric Myhre committed
123 124 125 126 127 128 129 130 131 132
	// Emit fixed bits.
	withFile(filepath.Join(pth, "minima.go"), func(f io.Writer) {
		EmitInternalEnums(pkgName, f)
	})

	// Emit a file for each type.
	for _, typ := range ts.GetTypes() {
		withFile(filepath.Join(pth, "t"+typ.Name().String()+".go"), func(f io.Writer) {
			EmitFileHeader(pkgName, f)
			switch t2 := typ.(type) {
133
			case *schema.TypeBool:
Eric Myhre's avatar
Eric Myhre committed
134
				EmitEntireType(NewBoolReprBoolGenerator(pkgName, t2, adjCfg), f)
135
			case *schema.TypeInt:
Eric Myhre's avatar
Eric Myhre committed
136
				EmitEntireType(NewIntReprIntGenerator(pkgName, t2, adjCfg), f)
137
			case *schema.TypeFloat:
Eric Myhre's avatar
Eric Myhre committed
138
				EmitEntireType(NewFloatReprFloatGenerator(pkgName, t2, adjCfg), f)
139
			case *schema.TypeString:
Eric Myhre's avatar
Eric Myhre committed
140
				EmitEntireType(NewStringReprStringGenerator(pkgName, t2, adjCfg), f)
141
			case *schema.TypeBytes:
Eric Myhre's avatar
Eric Myhre committed
142
				EmitEntireType(NewBytesReprBytesGenerator(pkgName, t2, adjCfg), f)
143
			case *schema.TypeLink:
Eric Myhre's avatar
Eric Myhre committed
144
				EmitEntireType(NewLinkReprLinkGenerator(pkgName, t2, adjCfg), f)
145
			case *schema.TypeStruct:
Eric Myhre's avatar
Eric Myhre committed
146 147 148
				switch t2.RepresentationStrategy().(type) {
				case schema.StructRepresentation_Map:
					EmitEntireType(NewStructReprMapGenerator(pkgName, t2, adjCfg), f)
149 150
				case schema.StructRepresentation_Tuple:
					EmitEntireType(NewStructReprTupleGenerator(pkgName, t2, adjCfg), f)
151 152
				case schema.StructRepresentation_Stringjoin:
					EmitEntireType(NewStructReprStringjoinGenerator(pkgName, t2, adjCfg), f)
Eric Myhre's avatar
Eric Myhre committed
153 154 155
				default:
					panic("unrecognized struct representation strategy")
				}
156
			case *schema.TypeMap:
157
				EmitEntireType(NewMapReprMapGenerator(pkgName, t2, adjCfg), f)
158
			case *schema.TypeList:
Eric Myhre's avatar
Eric Myhre committed
159
				EmitEntireType(NewListReprListGenerator(pkgName, t2, adjCfg), f)
160
			case *schema.TypeUnion:
161 162 163
				switch t2.RepresentationStrategy().(type) {
				case schema.UnionRepresentation_Keyed:
					EmitEntireType(NewUnionReprKeyedGenerator(pkgName, t2, adjCfg), f)
164 165
				case schema.UnionRepresentation_Kinded:
					EmitEntireType(NewUnionReprKindedGenerator(pkgName, t2, adjCfg), f)
166 167 168
				default:
					panic("unrecognized union representation strategy")
				}
Eric Myhre's avatar
Eric Myhre committed
169 170 171 172 173
			default:
				panic("add more type switches here :)")
			}
		})
	}
174 175 176

	// Emit the unified type table.
	withFile(filepath.Join(pth, "typeTable.go"), func(f io.Writer) {
177 178
		fmt.Fprintf(f, "package %s\n\n", pkgName)
		fmt.Fprintf(f, doNotEditComment+"\n\n")
179 180
		EmitTypeTable(pkgName, ts, adjCfg, f)
	})
Eric Myhre's avatar
Eric Myhre committed
181 182 183 184 185 186 187 188 189 190
}

func withFile(filename string, fn func(io.Writer)) {
	f, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	fn(f)
}