| // Copyright 2026 The Go 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 specgen |
| |
| import ( |
| "bytes" |
| "cmp" |
| "fmt" |
| "reflect" |
| "regexp" |
| "simd/archsimd/_gen/specgen/specexpr" |
| "strings" |
| "text/template" |
| ) |
| |
| // specTemplate wraps a parsed text/template.Template, or a plain string if no |
| // template actions were present. |
| type specTemplate struct { |
| raw string |
| tmpl *template.Template |
| } |
| |
| var templateFuncs = template.FuncMap{ |
| "title": func(v any) string { |
| s := fmt.Sprint(v) |
| if len(s) == 0 { |
| return "" |
| } |
| return strings.ToTitle(s[:1]) + s[1:] |
| }, |
| "lt": func(a, b any) (bool, error) { |
| res, ok := compare(a, b) |
| if !ok { |
| return false, fmt.Errorf("incomparable types: %T and %T", a, b) |
| } |
| return res < 0, nil |
| }, |
| "le": func(a, b any) (bool, error) { |
| res, ok := compare(a, b) |
| if !ok { |
| return false, fmt.Errorf("incomparable types: %T and %T", a, b) |
| } |
| return res <= 0, nil |
| }, |
| "gt": func(a, b any) (bool, error) { |
| res, ok := compare(a, b) |
| if !ok { |
| return false, fmt.Errorf("incomparable types: %T and %T", a, b) |
| } |
| return res > 0, nil |
| }, |
| "ge": func(a, b any) (bool, error) { |
| res, ok := compare(a, b) |
| if !ok { |
| return false, fmt.Errorf("incomparable types: %T and %T", a, b) |
| } |
| return res >= 0, nil |
| }, |
| "eq": func(a, b any) bool { |
| if res, ok := compare(a, b); ok { |
| return res == 0 |
| } |
| return reflect.DeepEqual(a, b) |
| }, |
| "ne": func(a, b any) bool { |
| if res, ok := compare(a, b); ok { |
| return res != 0 |
| } |
| return !reflect.DeepEqual(a, b) |
| }, |
| } |
| |
| func toSpecNum(v any) (specexpr.Num, bool) { |
| switch v := v.(type) { |
| case specexpr.Num: |
| return v, true |
| case int: |
| return specexpr.Int(v), true |
| case int64: |
| return specexpr.Int(v), true |
| default: |
| return nil, false |
| } |
| } |
| |
| func compare(a, b any) (int, bool) { |
| if na, okA := toSpecNum(a); okA { |
| if nb, okB := toSpecNum(b); okB { |
| return na.Compare(nb) |
| } |
| } |
| if sa, okA := a.(string); okA { |
| if sb, okB := b.(string); okB { |
| return cmp.Compare(sa, sb), true |
| } |
| } |
| return 0, false |
| } |
| |
| // newSpecTemplate parses a spec template string. If tmpl does not contain "{{", |
| // it is treated as a raw string literal without template overhead. |
| func newSpecTemplate(tmpl string) (specTemplate, error) { |
| if !strings.Contains(tmpl, "{{") { |
| return specTemplate{raw: tmpl, tmpl: nil}, nil |
| } |
| |
| t, err := template.New("").Option("missingkey=error").Funcs(templateFuncs).Parse(tmpl) |
| if err != nil { |
| return specTemplate{}, err |
| } |
| return specTemplate{raw: tmpl, tmpl: t}, nil |
| } |
| |
| func (s *specTemplate) expand(data any) (string, error) { |
| if s.tmpl == nil { |
| return s.raw, nil |
| } |
| var buf bytes.Buffer |
| if err := s.tmpl.Execute(&buf, data); err != nil { |
| return "", err |
| } |
| return buf.String(), nil |
| } |
| |
| var reMultipleNewlines = regexp.MustCompile(`\n{3,}`) |
| |
| func cleanDocNewlines(s string) string { |
| s = reMultipleNewlines.ReplaceAllString(s, "\n\n") |
| trimmed := strings.TrimRight(s, " \t\n") |
| if trimmed == "" { |
| return "" |
| } |
| return trimmed + "\n" |
| } |
| |
| // expandNameAndDoc instantiates the API function name and doc comment for sFn |
| // using the solved variable bindings in b. |
| func (sFn *specFunc) expandNameAndDoc(ctx context, b *specexpr.Bindings) (name, doc string) { |
| data := make(map[string]any) |
| for v, val := range b.All() { |
| data[string(v)] = val |
| if s, ok := strings.CutPrefix(string(v), "$"); ok { |
| data[s] = val |
| } |
| } |
| |
| var err error |
| name, err = sFn.NameTmpl.expand(data) |
| if err != nil { |
| ctx.errorf("expanding function name template: %s", err) |
| name = sFn.Name |
| } |
| |
| data["Name"] = name |
| doc, err = sFn.Doc.expand(data) |
| if err != nil { |
| ctx.errorf("expanding doc template: %s", err) |
| doc = sFn.Doc.raw |
| } |
| |
| doc = cleanDocNewlines(doc) |
| |
| if name != sFn.Name { |
| doc = regexp.MustCompile(`\b`+regexp.QuoteMeta(sFn.Name)+`\b`).ReplaceAllLiteralString(doc, name) |
| } |
| |
| return name, doc |
| } |