1
2
3
4
5 package specgen
6
7 import (
8 "bytes"
9 "cmp"
10 "fmt"
11 "reflect"
12 "regexp"
13 "simd/archsimd/_gen/specgen/specexpr"
14 "strings"
15 "text/template"
16 )
17
18
19
20 type specTemplate struct {
21 raw string
22 tmpl *template.Template
23 }
24
25 var templateFuncs = template.FuncMap{
26 "title": func(v any) string {
27 s := fmt.Sprint(v)
28 if len(s) == 0 {
29 return ""
30 }
31 return strings.ToTitle(s[:1]) + s[1:]
32 },
33 "lt": func(a, b any) (bool, error) {
34 res, ok := compare(a, b)
35 if !ok {
36 return false, fmt.Errorf("incomparable types: %T and %T", a, b)
37 }
38 return res < 0, nil
39 },
40 "le": func(a, b any) (bool, error) {
41 res, ok := compare(a, b)
42 if !ok {
43 return false, fmt.Errorf("incomparable types: %T and %T", a, b)
44 }
45 return res <= 0, nil
46 },
47 "gt": func(a, b any) (bool, error) {
48 res, ok := compare(a, b)
49 if !ok {
50 return false, fmt.Errorf("incomparable types: %T and %T", a, b)
51 }
52 return res > 0, nil
53 },
54 "ge": func(a, b any) (bool, error) {
55 res, ok := compare(a, b)
56 if !ok {
57 return false, fmt.Errorf("incomparable types: %T and %T", a, b)
58 }
59 return res >= 0, nil
60 },
61 "eq": func(a, b any) bool {
62 if res, ok := compare(a, b); ok {
63 return res == 0
64 }
65 return reflect.DeepEqual(a, b)
66 },
67 "ne": func(a, b any) bool {
68 if res, ok := compare(a, b); ok {
69 return res != 0
70 }
71 return !reflect.DeepEqual(a, b)
72 },
73 }
74
75 func toSpecNum(v any) (specexpr.Num, bool) {
76 switch v := v.(type) {
77 case specexpr.Num:
78 return v, true
79 case int:
80 return specexpr.Int(v), true
81 case int64:
82 return specexpr.Int(v), true
83 default:
84 return nil, false
85 }
86 }
87
88 func compare(a, b any) (int, bool) {
89 if na, okA := toSpecNum(a); okA {
90 if nb, okB := toSpecNum(b); okB {
91 return na.Compare(nb)
92 }
93 }
94 if sa, okA := a.(string); okA {
95 if sb, okB := b.(string); okB {
96 return cmp.Compare(sa, sb), true
97 }
98 }
99 return 0, false
100 }
101
102
103
104 func newSpecTemplate(tmpl string) (specTemplate, error) {
105 if !strings.Contains(tmpl, "{{") {
106 return specTemplate{raw: tmpl, tmpl: nil}, nil
107 }
108
109 t, err := template.New("").Option("missingkey=error").Funcs(templateFuncs).Parse(tmpl)
110 if err != nil {
111 return specTemplate{}, err
112 }
113 return specTemplate{raw: tmpl, tmpl: t}, nil
114 }
115
116 func (s *specTemplate) expand(data any) (string, error) {
117 if s.tmpl == nil {
118 return s.raw, nil
119 }
120 var buf bytes.Buffer
121 if err := s.tmpl.Execute(&buf, data); err != nil {
122 return "", err
123 }
124 return buf.String(), nil
125 }
126
127 var reMultipleNewlines = regexp.MustCompile(`\n{3,}`)
128
129 func cleanDocNewlines(s string) string {
130 s = reMultipleNewlines.ReplaceAllString(s, "\n\n")
131 trimmed := strings.TrimRight(s, " \t\n")
132 if trimmed == "" {
133 return ""
134 }
135 return trimmed + "\n"
136 }
137
138
139
140 func (sFn *specFunc) expandNameAndDoc(ctx context, b *specexpr.Bindings) (name, doc string) {
141 data := make(map[string]any)
142 for v, val := range b.All() {
143 data[string(v)] = val
144 if s, ok := strings.CutPrefix(string(v), "$"); ok {
145 data[s] = val
146 }
147 }
148
149 var err error
150 name, err = sFn.NameTmpl.expand(data)
151 if err != nil {
152 ctx.errorf("expanding function name template: %s", err)
153 name = sFn.Name
154 }
155
156 data["Name"] = name
157 doc, err = sFn.Doc.expand(data)
158 if err != nil {
159 ctx.errorf("expanding doc template: %s", err)
160 doc = sFn.Doc.raw
161 }
162
163 doc = cleanDocNewlines(doc)
164
165 if name != sFn.Name {
166 doc = regexp.MustCompile(`\b`+regexp.QuoteMeta(sFn.Name)+`\b`).ReplaceAllLiteralString(doc, name)
167 }
168
169 return name, doc
170 }
171
View as plain text