github.com/lmittmann/w3@v0.20.0/internal/fourbyte/gen.go (about)

     1  //go:build ignore
     2  
     3  package main
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"fmt"
     9  	"os"
    10  	"slices"
    11  	"strings"
    12  	"sync"
    13  	"text/template"
    14  
    15  	"github.com/lmittmann/w3"
    16  )
    17  
    18  func main() {
    19  	var (
    20  		errFuncs, errEvents error
    21  		wg                  sync.WaitGroup
    22  	)
    23  
    24  	wg.Add(2)
    25  	go func() {
    26  		defer wg.Done()
    27  		errFuncs = genFuncs("funcs.txt", "funcs.go")
    28  	}()
    29  	go func() {
    30  		defer wg.Done()
    31  		errEvents = genEvents("events.txt", "events.go")
    32  	}()
    33  
    34  	wg.Wait()
    35  	if errFuncs != nil {
    36  		fmt.Printf("error generating functions: %v\n", errFuncs)
    37  	}
    38  	if errEvents != nil {
    39  		fmt.Printf("error generating events: %v\n", errEvents)
    40  	}
    41  	if errFuncs != nil || errEvents != nil {
    42  		os.Exit(1)
    43  	}
    44  }
    45  
    46  func genFuncs(fn, goFn string) error {
    47  	// open function definitions
    48  	f, err := os.Open(fn)
    49  	if err != nil {
    50  		return err
    51  	}
    52  	defer f.Close()
    53  
    54  	// parse function definitions from file
    55  	var functions []function
    56  	knownIdentifiers := make(map[[4]byte]struct{})
    57  
    58  	scanner := bufio.NewScanner(f)
    59  	for i := 0; scanner.Scan(); i++ {
    60  		line := scanner.Text()
    61  		tokens := strings.Split(line, "\t")
    62  		if len(tokens) == 1 && strings.HasSuffix(tokens[0], ")") {
    63  			tokens = append(tokens, "") // no returns
    64  		}
    65  		if len(tokens) != 2 {
    66  			return fmt.Errorf("line %d: invalid line %q", i, line)
    67  		}
    68  
    69  		fn, err := w3.NewFunc(tokens[0], tokens[1])
    70  		if err != nil {
    71  			return fmt.Errorf("line %d: %v (%q)", i, err, line)
    72  		}
    73  
    74  		if _, ok := knownIdentifiers[fn.Selector]; ok {
    75  			return fmt.Errorf("line %d: duplicate function selector %q", i, line)
    76  		}
    77  		knownIdentifiers[fn.Selector] = struct{}{}
    78  
    79  		functions = append(functions, function{
    80  			Selector:  fn.Selector,
    81  			Signature: tokens[0],
    82  			Returns:   tokens[1],
    83  		})
    84  	}
    85  	if err := scanner.Err(); err != nil {
    86  		return fmt.Errorf("scan lines: %v", err)
    87  	}
    88  
    89  	// make sure function definitions stay in alphabetical order
    90  	slices.SortFunc(functions, func(a, b function) int {
    91  		return strings.Compare(strings.ToLower(a.Signature), strings.ToLower(b.Signature))
    92  	})
    93  	f, err = os.OpenFile(fn, os.O_WRONLY, 0644)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	defer f.Close()
    98  
    99  	for _, fn := range functions {
   100  		if _, err := f.WriteString(strings.TrimSpace(fn.Signature+"\t"+fn.Returns) + "\n"); err != nil {
   101  			return err
   102  		}
   103  	}
   104  
   105  	// generate go file
   106  	goF, err := os.Create(goFn)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	defer goF.Close()
   111  
   112  	slices.SortFunc(functions, func(a, b function) int {
   113  		return bytes.Compare(a.Selector[:], b.Selector[:])
   114  	})
   115  
   116  	if err := tmplFuncs.Execute(goF, &model{Functions: functions}); err != nil {
   117  		return fmt.Errorf("execute template: %v", err)
   118  	}
   119  
   120  	return nil
   121  }
   122  
   123  func genEvents(fn, goFn string) error {
   124  	// open event definitions
   125  	f, err := os.Open(fn)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	defer f.Close()
   130  
   131  	var events []event
   132  	knownTopic0s := make(map[[32]byte]struct{})
   133  	scanner := bufio.NewScanner(f)
   134  	for i := 0; scanner.Scan(); i++ {
   135  		line := scanner.Text()
   136  
   137  		evt, err := w3.NewEvent(line)
   138  		if err != nil {
   139  			return fmt.Errorf("line %d: %v (%q)", i, err, line)
   140  		}
   141  
   142  		if _, ok := knownTopic0s[evt.Topic0]; ok {
   143  			return fmt.Errorf("line %d: duplicate function selector %q", i, line)
   144  		}
   145  		knownTopic0s[evt.Topic0] = struct{}{}
   146  
   147  		events = append(events, event{
   148  			Topic0:    evt.Topic0,
   149  			Signature: line,
   150  		})
   151  	}
   152  	if err := scanner.Err(); err != nil {
   153  		return fmt.Errorf("scan lines: %v", err)
   154  	}
   155  
   156  	// make sure event definitions stay in alphabetical order
   157  	slices.SortFunc(events, func(a, b event) int {
   158  		return strings.Compare(strings.ToLower(a.Signature), strings.ToLower(b.Signature))
   159  	})
   160  	f, err = os.OpenFile(fn, os.O_WRONLY, 0644)
   161  	if err != nil {
   162  		return err
   163  	}
   164  	defer f.Close()
   165  
   166  	for _, evt := range events {
   167  		if _, err := f.WriteString(evt.Signature + "\n"); err != nil {
   168  			return err
   169  		}
   170  	}
   171  
   172  	// generate go file
   173  	goF, err := os.Create(goFn)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	defer goF.Close()
   178  
   179  	slices.SortFunc(events, func(a, b event) int {
   180  		return bytes.Compare(a.Topic0[:], b.Topic0[:])
   181  	})
   182  
   183  	if err := tmplEvents.Execute(goF, &model{Events: events}); err != nil {
   184  		return fmt.Errorf("execute template: %v", err)
   185  	}
   186  
   187  	return nil
   188  
   189  }
   190  
   191  type model struct {
   192  	Functions []function
   193  	Events    []event
   194  }
   195  
   196  type function struct {
   197  	Selector  [4]byte
   198  	Signature string
   199  	Returns   string
   200  }
   201  
   202  type event struct {
   203  	Topic0    [32]byte
   204  	Signature string
   205  }
   206  
   207  var (
   208  	tmplFuncs = template.Must(template.New("funcs").Parse(`// Code generated by "go generate"; DO NOT EDIT.
   209  package fourbyte
   210  
   211  import "github.com/lmittmann/w3"
   212  
   213  var functions = map[[4]byte]*w3.Func{
   214  	{{- range .Functions }}
   215  	{{ printf "{0x%02x, 0x%02x, 0x%02x, 0x%02x}: w3.MustNewFunc(%q, %q)"
   216  		(index .Selector 0)
   217  		(index .Selector 1)
   218  		(index .Selector 2)
   219  		(index .Selector 3)
   220  		.Signature
   221  		.Returns
   222  	}},
   223  	{{- end }}
   224  }
   225  `))
   226  
   227  	tmplEvents = template.Must(template.New("events").Parse(`// Code generated by "go generate"; DO NOT EDIT.
   228  package fourbyte
   229  
   230  import "github.com/lmittmann/w3"
   231  
   232  var events = map[[32]byte]*w3.Event{
   233  	{{- range .Events }}
   234  	{{ printf "{0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x}: w3.MustNewEvent(%q)"
   235  		(index .Topic0 0)
   236  		(index .Topic0 1)
   237  		(index .Topic0 2)
   238  		(index .Topic0 3)
   239  		(index .Topic0 4)
   240  		(index .Topic0 5)
   241  		(index .Topic0 6)
   242  		(index .Topic0 7)
   243  		(index .Topic0 8)
   244  		(index .Topic0 9)
   245  		(index .Topic0 10)
   246  		(index .Topic0 11)
   247  		(index .Topic0 12)
   248  		(index .Topic0 13)
   249  		(index .Topic0 14)
   250  		(index .Topic0 15)
   251  		(index .Topic0 16)
   252  		(index .Topic0 17)
   253  		(index .Topic0 18)
   254  		(index .Topic0 19)
   255  		(index .Topic0 20)
   256  		(index .Topic0 21)
   257  		(index .Topic0 22)
   258  		(index .Topic0 23)
   259  		(index .Topic0 24)
   260  		(index .Topic0 25)
   261  		(index .Topic0 26)
   262  		(index .Topic0 27)
   263  		(index .Topic0 28)
   264  		(index .Topic0 29)
   265  		(index .Topic0 30)
   266  		(index .Topic0 31)
   267  		.Signature
   268  	}},
   269  	{{- end }}
   270  }
   271  `))
   272  )