mirror of
				https://github.com/gonum/gonum.git
				synced 2025-11-01 02:52:49 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			364 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright ©2014 The Gonum Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // +build ignore
 | |
| 
 | |
| package main
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"go/format"
 | |
| 	"log"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"text/template"
 | |
| )
 | |
| 
 | |
| type Unit struct {
 | |
| 	Name          string
 | |
| 	Receiver      string
 | |
| 	Offset        int    // From normal (for example, mass base unit is kg, not kg)
 | |
| 	PrintString   string // print string for the unit (kg for mass)
 | |
| 	ExtraConstant []Constant
 | |
| 	Suffix        string
 | |
| 	Singular      string
 | |
| 	TypeComment   string // Text to comment the type
 | |
| 	Dimensions    []Dimension
 | |
| 	ErForm        string //For Xxxer interface
 | |
| }
 | |
| 
 | |
| type Dimension struct {
 | |
| 	Name  string
 | |
| 	Power int
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	TimeName   string = "TimeDim"
 | |
| 	LengthName string = "LengthDim"
 | |
| 	MassName   string = "MassDim"
 | |
| )
 | |
| 
 | |
| type Constant struct {
 | |
| 	Name  string
 | |
| 	Value string
 | |
| }
 | |
| 
 | |
| type Prefix struct {
 | |
| 	Name  string
 | |
| 	Power int
 | |
| }
 | |
| 
 | |
| var Prefixes = []Prefix{
 | |
| 	{
 | |
| 		Name:  "Yotta",
 | |
| 		Power: 24,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Zetta",
 | |
| 		Power: 21,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Exa",
 | |
| 		Power: 18,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Peta",
 | |
| 		Power: 15,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Tera",
 | |
| 		Power: 12,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Giga",
 | |
| 		Power: 9,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Mega",
 | |
| 		Power: 6,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Kilo",
 | |
| 		Power: 3,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Hecto",
 | |
| 		Power: 2,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Deca",
 | |
| 		Power: 1,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "",
 | |
| 		Power: 0,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Deci",
 | |
| 		Power: -1,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Centi",
 | |
| 		Power: -2,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Milli",
 | |
| 		Power: -3,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Micro",
 | |
| 		Power: -6,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Nano",
 | |
| 		Power: -9,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Pico",
 | |
| 		Power: -12,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Femto",
 | |
| 		Power: -15,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Atto",
 | |
| 		Power: -18,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Zepto",
 | |
| 		Power: -21,
 | |
| 	},
 | |
| 	{
 | |
| 		Name:  "Yocto",
 | |
| 		Power: -24,
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var Units = []Unit{
 | |
| 	{
 | |
| 		Name:        "Mass",
 | |
| 		Receiver:    "m",
 | |
| 		Offset:      -3,
 | |
| 		PrintString: "kg",
 | |
| 		Suffix:      "gram",
 | |
| 		Singular:    "Gram",
 | |
| 		TypeComment: "Mass represents a mass in kilograms",
 | |
| 		Dimensions: []Dimension{
 | |
| 			{
 | |
| 				Name:  MassName,
 | |
| 				Power: 1,
 | |
| 			},
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		Name:        "Length",
 | |
| 		Receiver:    "l",
 | |
| 		PrintString: "m",
 | |
| 		Suffix:      "meter",
 | |
| 		Singular:    "Meter",
 | |
| 		TypeComment: "Length represents a length in meters",
 | |
| 		Dimensions: []Dimension{
 | |
| 			{
 | |
| 				Name:  LengthName,
 | |
| 				Power: 1,
 | |
| 			},
 | |
| 		},
 | |
| 	},
 | |
| 	{
 | |
| 		Name:        "Time",
 | |
| 		Receiver:    "t",
 | |
| 		PrintString: "s",
 | |
| 		Suffix:      "second",
 | |
| 		Singular:    "Second",
 | |
| 		TypeComment: "Time represents a time in seconds",
 | |
| 		ExtraConstant: []Constant{
 | |
| 			{
 | |
| 				Name:  "Hour",
 | |
| 				Value: "3600",
 | |
| 			},
 | |
| 			{
 | |
| 				Name:  "Minute",
 | |
| 				Value: "60",
 | |
| 			},
 | |
| 		},
 | |
| 		Dimensions: []Dimension{
 | |
| 			{
 | |
| 				Name:  TimeName,
 | |
| 				Power: 1,
 | |
| 			},
 | |
| 		},
 | |
| 		ErForm: "Timer",
 | |
| 	},
 | |
| }
 | |
| 
 | |
| var gopath string
 | |
| var unitPkgPath string
 | |
| 
 | |
| func init() {
 | |
| 	gopath = os.Getenv("GOPATH")
 | |
| 	if gopath == "" {
 | |
| 		log.Fatal("no gopath")
 | |
| 	}
 | |
| 
 | |
| 	unitPkgPath = filepath.Join(gopath, "src", "gonum.org", "v1", "gonum", "unit")
 | |
| }
 | |
| 
 | |
| // Generate generates a file for each of the units
 | |
| func main() {
 | |
| 	for _, unit := range Units {
 | |
| 		generate(unit)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| const headerTemplate = `// Code generated by "go generate gonum.org/v1/gonum/unit”; DO NOT EDIT.
 | |
| 
 | |
| // Copyright ©2014 The Gonum Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package unit
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"math"
 | |
| )
 | |
| 
 | |
| // {{.TypeComment}}
 | |
| type {{.Name}} float64
 | |
| `
 | |
| 
 | |
| var header = template.Must(template.New("header").Parse(headerTemplate))
 | |
| 
 | |
| const constTemplate = `
 | |
| const(
 | |
| 	{{$unit := .Unit}}
 | |
| 	{{range $unit.ExtraConstant}} {{.Name}} {{$unit.Name}} = {{.Value}}
 | |
| 	{{end}}
 | |
| 	{{$prefixes := .Prefixes}}
 | |
| 	{{range $prefixes}} {{if .Name}} {{.Name}}{{$unit.Suffix}} {{else}} {{$unit.Singular}} {{end}} {{$unit.Name}} = {{if .Power}} 1e{{.Power}} {{else}} 1.0 {{end}}
 | |
| 	{{end}}
 | |
| )
 | |
| `
 | |
| 
 | |
| var prefix = template.Must(template.New("prefix").Parse(constTemplate))
 | |
| 
 | |
| const methodTemplate = `
 | |
| // Unit converts the {{.Name}} to a *Unit
 | |
| func ({{.Receiver}} {{.Name}}) Unit() *Unit {
 | |
| 	return New(float64({{.Receiver}}), Dimensions{
 | |
| 		{{range .Dimensions}} {{.Name}}: {{.Power}},
 | |
| 		{{end}}
 | |
| 		})
 | |
| }
 | |
| 
 | |
| // {{.Name}} allows {{.Name}} to implement a {{if .ErForm}}{{.ErForm}}{{else}}{{.Name}}er{{end}} interface
 | |
| func ({{.Receiver}} {{.Name}}) {{.Name}}() {{.Name}} {
 | |
| 	return {{.Receiver}}
 | |
| }
 | |
| 
 | |
| // From converts the unit into the receiver. From returns an
 | |
| // error if there is a mismatch in dimension
 | |
| func ({{.Receiver}} *{{.Name}}) From(u Uniter) error {
 | |
| 	if !DimensionsMatch(u, {{.Singular}}){
 | |
| 		*{{.Receiver}} = {{.Name}}(math.NaN())
 | |
| 		return errors.New("Dimension mismatch")
 | |
| 	}
 | |
| 	*{{.Receiver}} = {{.Name}}(u.Unit().Value())
 | |
| 	return nil
 | |
| }
 | |
| `
 | |
| 
 | |
| var methods = template.Must(template.New("methods").Parse(methodTemplate))
 | |
| 
 | |
| const formatTemplate = `
 | |
| func ({{.Receiver}} {{.Name}}) Format(fs fmt.State, c rune) {
 | |
| 	switch c {
 | |
| 	case 'v':
 | |
| 		if fs.Flag('#') {
 | |
| 			fmt.Fprintf(fs, "%T(%v)", {{.Receiver}}, float64({{.Receiver}}))
 | |
| 			return
 | |
| 		}
 | |
| 		fallthrough
 | |
| 	case 'e', 'E', 'f', 'F', 'g', 'G':
 | |
| 		p, pOk := fs.Precision()
 | |
| 		w, wOk := fs.Width()
 | |
| 		switch {
 | |
| 		case pOk && wOk:
 | |
| 			fmt.Fprintf(fs, "%*.*"+string(c), w, p, float64({{.Receiver}}))
 | |
| 		case pOk:
 | |
| 			fmt.Fprintf(fs, "%.*"+string(c), p, float64({{.Receiver}}))
 | |
| 		case wOk:
 | |
| 			fmt.Fprintf(fs, "%*"+string(c), w, float64({{.Receiver}}))
 | |
| 		default:
 | |
| 			fmt.Fprintf(fs, "%"+string(c), float64({{.Receiver}}))
 | |
| 		}
 | |
| 		fmt.Fprint(fs, " {{.PrintString}}")
 | |
| 	default:
 | |
| 		fmt.Fprintf(fs, "%%!%c(%T=%g {{.PrintString}})", c, {{.Receiver}}, float64({{.Receiver}}))
 | |
| 	}
 | |
| }
 | |
| `
 | |
| 
 | |
| var form = template.Must(template.New("format").Parse(formatTemplate))
 | |
| 
 | |
| func generate(unit Unit) {
 | |
| 	lowerName := strings.ToLower(unit.Name)
 | |
| 	filename := filepath.Join(unitPkgPath, lowerName+".go")
 | |
| 	f, err := os.Create(filename)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 	defer f.Close()
 | |
| 
 | |
| 	// Need to define new prefixes because text/template can't do math.
 | |
| 	// Need to do math because kilogram = 1 not 10^3
 | |
| 
 | |
| 	prefixes := make([]Prefix, len(Prefixes))
 | |
| 	for i, p := range Prefixes {
 | |
| 		prefixes[i].Name = p.Name
 | |
| 		prefixes[i].Power = p.Power + unit.Offset
 | |
| 	}
 | |
| 
 | |
| 	data := struct {
 | |
| 		Prefixes []Prefix
 | |
| 		Unit     Unit
 | |
| 	}{
 | |
| 		prefixes,
 | |
| 		unit,
 | |
| 	}
 | |
| 
 | |
| 	buf := bytes.NewBuffer(make([]byte, 0))
 | |
| 
 | |
| 	err = header.Execute(buf, unit)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	err = prefix.Execute(buf, data)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	err = methods.Execute(buf, unit)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	err = form.Execute(buf, unit)
 | |
| 	if err != nil {
 | |
| 		log.Fatal(err)
 | |
| 	}
 | |
| 
 | |
| 	b, err := format.Source(buf.Bytes())
 | |
| 	if err != nil {
 | |
| 		f.Write(buf.Bytes()) // This is here to debug bad format
 | |
| 		log.Fatalf("error formatting: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	f.Write(b)
 | |
| }
 | 
