tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/p1am/internal/cmd/gen_defines/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"go/format"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"path/filepath"
    10  	"regexp"
    11  	"strings"
    12  	"text/template"
    13  )
    14  
    15  var tmpl = template.Must(template.New("main").Parse(`package p1am
    16  
    17  //go:generate go run ./internal/cmd/gen_defines
    18  
    19  type ModuleProps struct {
    20  	ModuleID                                 uint32
    21  	DI, DO, AI, AO, Status, Config, DataSize byte
    22  	Name                                     string
    23  }
    24  
    25  var modules = []ModuleProps{
    26  {{.MDB -}}
    27  }
    28  
    29  var defaultConfig = map[uint32][]byte{
    30  	{{range .Configs -}}
    31  	0x{{.ID}}: // {{.Name}}
    32  	{{index $.DefaultConfigs .Name}},
    33  	{{end}}
    34  }
    35  
    36  {{range .Defines}}
    37  const {{.Name}} = {{.Value}}{{.Comment -}}
    38  {{end}}
    39  `))
    40  
    41  func findLibrary() string {
    42  	home, err := os.UserHomeDir()
    43  	if err != nil {
    44  		log.Fatal(err)
    45  	}
    46  	for _, dir := range []string{
    47  		"Documents/Arduino",
    48  		"Arduino",
    49  	} {
    50  		dir = filepath.Join(home, dir, "libraries/P1AM/src")
    51  		if _, err := os.Stat(dir); err == nil {
    52  			return dir
    53  		}
    54  	}
    55  	return ""
    56  }
    57  
    58  func definitions(path string, delim string) []string {
    59  	data, err := ioutil.ReadFile(path)
    60  	if err != nil {
    61  		log.Fatal(err)
    62  	}
    63  	return strings.Split(string(data), delim)
    64  }
    65  
    66  var (
    67  	mdbRE    = regexp.MustCompile(`(?s)mdb\[\] = \{\s*(.+)\}`)
    68  	configRE = regexp.MustCompile(`(?s)const char (.*?)\[\] = (.+)`)
    69  	caseRE   = regexp.MustCompile(`(?s)case 0x([^:]+):\s+return \(char\*\)(.+)`)
    70  	defineRE = regexp.MustCompile(`(?ms)^\s*#define (\S+)\s+(\d+|0x[0-9a-fA-F]+)(\s+.*?)?\s*$`)
    71  )
    72  
    73  func main() {
    74  	base := findLibrary()
    75  	if base == "" {
    76  		log.Fatal("can't find Arduino library")
    77  	}
    78  	var data = struct {
    79  		MDB            string
    80  		DefaultConfigs map[string]string
    81  		Configs        []struct {
    82  			ID   string
    83  			Name string
    84  		}
    85  		Defines []struct {
    86  			Name    string
    87  			Value   string
    88  			Comment string
    89  		}
    90  	}{
    91  		DefaultConfigs: make(map[string]string),
    92  	}
    93  	for _, line := range definitions(filepath.Join(base, "Module_List.h"), ";") {
    94  		if matches := mdbRE.FindStringSubmatch(line); matches != nil {
    95  			data.MDB = regexp.MustCompile(`}\s*//`).ReplaceAllString(matches[1], `}, //`)
    96  		}
    97  		if matches := configRE.FindStringSubmatch(line); matches != nil {
    98  			data.DefaultConfigs[matches[1]] = matches[2]
    99  		}
   100  	}
   101  
   102  	for _, line := range definitions(filepath.Join(base, "P1AM.cpp"), ";") {
   103  		if matches := caseRE.FindStringSubmatch(line); matches != nil {
   104  			data.Configs = append(data.Configs, struct{ ID, Name string }{
   105  				ID:   matches[1],
   106  				Name: matches[2],
   107  			})
   108  		}
   109  	}
   110  
   111  	for _, line := range definitions(filepath.Join(base, "defines.h"), "\n") {
   112  		if matches := defineRE.FindStringSubmatch(line); matches != nil {
   113  			data.Defines = append(data.Defines, struct{ Name, Value, Comment string }{
   114  				Name:    matches[1],
   115  				Value:   matches[2],
   116  				Comment: matches[3],
   117  			})
   118  		}
   119  	}
   120  
   121  	var buf bytes.Buffer
   122  	if err := tmpl.Execute(&buf, &data); err != nil {
   123  		log.Fatal(err)
   124  	}
   125  	formatted, err := format.Source(buf.Bytes())
   126  	if err != nil {
   127  		log.Printf("failed to compile %s", buf.Bytes())
   128  		log.Fatal(err)
   129  	}
   130  	if err := ioutil.WriteFile("defines.go", formatted, 0666); err != nil {
   131  		log.Fatal(err)
   132  	}
   133  }