hatmill/internal/codegen/codegen.go

217 lines
4.8 KiB
Go
Raw Normal View History

2019-03-24 19:28:36 +00:00
package main
import (
"bytes"
2019-04-09 04:10:02 +00:00
"encoding/json"
"flag"
2019-03-24 19:28:36 +00:00
"fmt"
"go/format"
"io/ioutil"
"log"
"strings"
)
func identifier(s string) string {
words := strings.Split(s, "-")
for i, word := range words {
words[i] = strings.Title(word)
}
return strings.Join(words, "")
}
2019-04-23 03:49:46 +00:00
type AttribTypeInfo struct {
2019-08-31 16:46:54 +00:00
Template AttribTemplate
2019-04-23 03:49:46 +00:00
}
2019-04-23 02:23:56 +00:00
2019-08-31 16:46:54 +00:00
type AttribTemplate func(identifier, name string) string
2019-04-23 03:49:46 +00:00
2019-08-31 16:46:54 +00:00
func simpleTemplate(paramType, toType string) AttribTemplate {
return func(identifier, name string) string {
return fmt.Sprintf(
`func %s(value %s) hatmill.Attrib {
return hatmill.Attrib{
Key: "%s",
Value: %s(value),
}
}
`, identifier, paramType, name, toType)
}
}
2019-08-31 16:46:54 +00:00
func boolTemplate(identifier, name string) string {
return fmt.Sprintf(
2020-05-25 21:18:17 +00:00
`func %s(showAttrib bool) hatmill.Attrib {
if !showAttrib {
return hatmill.Attrib{}
}
2019-04-23 03:49:46 +00:00
return hatmill.Attrib{
2019-04-27 21:26:59 +00:00
Key: "%s",
2019-04-23 03:49:46 +00:00
}
}
2019-08-31 16:46:54 +00:00
`, identifier, name)
}
var attribTypes = map[string]AttribTypeInfo{
"string": {
Template: simpleTemplate("string", "String"),
},
"bool": {
Template: boolTemplate,
2019-04-23 03:49:46 +00:00
},
2019-04-27 02:49:10 +00:00
"explicit bool": {
2019-08-31 16:46:54 +00:00
Template: simpleTemplate("bool", "Bool"),
2019-04-27 02:49:10 +00:00
},
2019-04-23 03:49:46 +00:00
"int": {
2019-08-31 16:46:54 +00:00
Template: simpleTemplate("int", "Int"),
2019-04-23 03:49:46 +00:00
},
2019-04-23 04:12:12 +00:00
"float": {
2019-08-31 16:46:54 +00:00
Template: simpleTemplate("float32", "Float"),
2019-04-23 04:12:12 +00:00
},
"space list": {
2019-08-31 16:46:54 +00:00
Template: simpleTemplate("...string", "SpaceList"),
},
"comma list": {
2019-08-31 16:46:54 +00:00
Template: simpleTemplate("...string", "CommaList"),
},
}
2019-03-24 19:28:36 +00:00
2019-04-23 03:49:46 +00:00
// Def represents a top-level definition and its required imports.
type Def struct {
2019-08-31 16:30:17 +00:00
Source string
2019-04-23 02:23:56 +00:00
}
2019-04-23 03:49:46 +00:00
type Spec interface {
Generate() Def
2019-03-24 19:28:36 +00:00
}
2019-04-23 03:49:46 +00:00
type AttribSpec struct {
2019-08-27 22:59:59 +00:00
Name string `json:"name"`
Type string `json:"type"`
2019-04-23 02:23:56 +00:00
}
2019-04-09 04:10:02 +00:00
2019-04-23 03:49:46 +00:00
func (spec AttribSpec) Generate() Def {
2019-04-27 21:26:59 +00:00
name := spec.Name
ident := identifier(spec.Name)
2019-08-27 22:59:59 +00:00
comment := fmt.Sprintf("// %s creates a \"%s\" attribute\n", ident, name)
2019-04-23 03:49:46 +00:00
template := attribTypes[spec.Type].Template
return Def{
2019-08-31 16:46:54 +00:00
Source: comment + template(ident, name),
2019-04-23 03:49:46 +00:00
}
}
2019-04-23 02:23:56 +00:00
type ElemSpec struct {
2019-08-27 22:59:59 +00:00
Name string `json:"name"`
Void bool `json:"void"`
}
2019-04-23 03:49:46 +00:00
func (spec ElemSpec) Generate() Def {
2019-04-09 04:10:02 +00:00
const (
2019-04-27 21:26:59 +00:00
parentTemplate = `func %s(attribs ...hatmill.Attrib) func(children ...hatmill.Term) hatmill.ParentElement {
return func(children ...hatmill.Term) hatmill.ParentElement {
return hatmill.ParentElement{
VoidElement: hatmill.VoidElement{
2019-04-27 21:26:59 +00:00
TagName: "%s",
Attribs: attribs,
},
Children: children,
}
}
}
`
2019-04-27 21:26:59 +00:00
voidTemplate = `func %s(attribs ...hatmill.Attrib) hatmill.VoidElement {
return hatmill.VoidElement{
2019-04-27 21:26:59 +00:00
TagName: "%s",
Attribs: attribs,
}
}
`
2019-04-09 04:10:02 +00:00
)
2019-04-27 21:26:59 +00:00
name := spec.Name
ident := identifier(spec.Name)
2019-08-27 22:59:59 +00:00
comment := fmt.Sprintf("// %s creates a <%s> element.\n", ident, name)
2019-04-09 04:10:02 +00:00
template := parentTemplate
2019-04-23 03:49:46 +00:00
if spec.Void {
2019-04-09 04:10:02 +00:00
template = voidTemplate
}
2019-04-23 03:49:46 +00:00
return Def{
2019-08-27 22:59:59 +00:00
Source: comment + fmt.Sprintf(template, ident, name),
2019-04-23 03:49:46 +00:00
}
2019-03-24 19:28:36 +00:00
}
2019-04-23 03:49:46 +00:00
func Render(defs []Def, pkgName string) ([]byte, error) {
2019-04-23 02:23:56 +00:00
buf := new(bytes.Buffer)
buf.WriteString(`// GENERATED BY git.codemonkeysoftware.net/b/hatmill/internal/codegen
2019-04-23 02:23:56 +00:00
// DO NOT EDIT!
`)
2019-04-23 03:49:46 +00:00
fmt.Fprintln(buf, "package ", pkgName)
buf.WriteString(`import "git.codemonkeysoftware.net/b/hatmill"
2019-04-23 03:49:46 +00:00
`)
for _, def := range defs {
buf.WriteString(def.Source)
2019-04-23 02:23:56 +00:00
}
2019-04-23 03:49:46 +00:00
formatted, err := format.Source(buf.Bytes())
if err != nil {
err = fmt.Errorf("generated invalid code: %s\nSource code:\n%s", err, buf)
}
return formatted, err
2019-04-23 02:23:56 +00:00
}
2019-04-23 03:49:46 +00:00
func WriteCodeFile(specs []Spec, path, pkgName string) error {
var defs []Def
for _, spec := range specs {
defs = append(defs, spec.Generate())
}
b, err := Render(defs, pkgName)
2019-04-03 03:42:16 +00:00
if err != nil {
return err
}
2019-04-23 02:23:56 +00:00
return ioutil.WriteFile(path, b, 0644)
2019-04-03 03:42:16 +00:00
}
2019-03-24 19:28:36 +00:00
func main() {
2019-04-09 04:10:02 +00:00
inputPath := flag.String("input", "", "JSON input file")
2019-04-03 03:42:16 +00:00
elemPath := flag.String("elemfile", "", "generated element .go file")
elemPkg := flag.String("elempkg", "", "generated element package name")
attribPath := flag.String("attribfile", "", "generated attribute .go file")
attribPkg := flag.String("attribpkg", "", "generated attribute package name")
2019-04-09 04:10:02 +00:00
flag.Parse()
2019-04-09 04:10:02 +00:00
input, err := ioutil.ReadFile(*inputPath)
if err != nil {
log.Fatal(err)
}
2019-04-23 03:49:46 +00:00
var specs struct {
AttribSpecs []AttribSpec `json:"attributes"`
ElemSpecs []ElemSpec `json:"elements"`
}
2019-04-23 02:23:56 +00:00
err = json.Unmarshal(input, &specs)
2019-04-09 04:10:02 +00:00
if err != nil {
log.Fatal(err)
}
2019-04-23 03:49:46 +00:00
var attribSpecs []Spec
for _, spec := range specs.AttribSpecs {
attribSpecs = append(attribSpecs, spec)
}
err = WriteCodeFile(attribSpecs, *attribPath, *attribPkg)
2019-04-03 03:42:16 +00:00
if err != nil {
log.Fatal(err)
}
2019-04-23 03:49:46 +00:00
var elemSpecs []Spec
for _, spec := range specs.ElemSpecs {
elemSpecs = append(elemSpecs, spec)
}
err = WriteCodeFile(elemSpecs, *elemPath, *elemPkg)
2019-04-03 03:42:16 +00:00
if err != nil {
log.Fatal(err)
}
2019-04-23 03:49:46 +00:00
2019-03-24 19:28:36 +00:00
}