intrinsic-gen: Additional functionality
* Add new template utilty functions, including the ability to split out multiple files.
* Add basic printing of the semantic overloads.
* Add a pointer from the overload to the function
* Change TemplateArguments from a list of FQN to a list of interface{} (any). This is required as once the overload is permutated, some arguments will need to hold integers.
This will be used by the test generator.
Bug: tint:832
Change-Id: Idbfbe85e52489b31850cbb0ee7430bb4b021530e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/53046
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/intrinsic_table.inl b/src/intrinsic_table.inl
index 966f835..87be0fd 100644
--- a/src/intrinsic_table.inl
+++ b/src/intrinsic_table.inl
@@ -14,6 +14,11 @@
////////////////////////////////////////////////////////////////////////////////
// File generated by tools/intrinsic-gen
+// using the template:
+// src/intrinsic_table.inl.tmpl
+// and the intrinsic defintion file:
+// src/intrinsics.def
+//
// Do not modify this file directly
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/sem/intrinsic_type.cc b/src/sem/intrinsic_type.cc
index 727a57c..7ba427f 100644
--- a/src/sem/intrinsic_type.cc
+++ b/src/sem/intrinsic_type.cc
@@ -14,6 +14,11 @@
////////////////////////////////////////////////////////////////////////////////
// File generated by tools/intrinsic-gen
+// using the template:
+// src/sem/intrinsic_type.cc.tmpl
+// and the intrinsic defintion file:
+// src/intrinsics.def
+//
// Do not modify this file directly
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/sem/intrinsic_type.h b/src/sem/intrinsic_type.h
index c8c02dc..f0d951f 100644
--- a/src/sem/intrinsic_type.h
+++ b/src/sem/intrinsic_type.h
@@ -14,6 +14,11 @@
////////////////////////////////////////////////////////////////////////////////
// File generated by tools/intrinsic-gen
+// using the template:
+// src/sem/intrinsic_type.h.tmpl
+// and the intrinsic defintion file:
+// src/intrinsics.def
+//
// Do not modify this file directly
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/sem/parameter_usage.cc b/src/sem/parameter_usage.cc
index 9a6263c..74b32b6 100644
--- a/src/sem/parameter_usage.cc
+++ b/src/sem/parameter_usage.cc
@@ -14,6 +14,11 @@
////////////////////////////////////////////////////////////////////////////////
// File generated by tools/intrinsic-gen
+// using the template:
+// src/sem/parameter_usage.cc.tmpl
+// and the intrinsic defintion file:
+// src/intrinsics.def
+//
// Do not modify this file directly
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/sem/parameter_usage.h b/src/sem/parameter_usage.h
index 3c20c76..79ab693 100644
--- a/src/sem/parameter_usage.h
+++ b/src/sem/parameter_usage.h
@@ -14,6 +14,11 @@
////////////////////////////////////////////////////////////////////////////////
// File generated by tools/intrinsic-gen
+// using the template:
+// src/sem/parameter_usage.h.tmpl
+// and the intrinsic defintion file:
+// src/intrinsics.def
+//
// Do not modify this file directly
////////////////////////////////////////////////////////////////////////////////
diff --git a/tools/src/cmd/intrinsic-gen/gen/generate.go b/tools/src/cmd/intrinsic-gen/gen/generate.go
index 50ad354..6dad43b 100644
--- a/tools/src/cmd/intrinsic-gen/gen/generate.go
+++ b/tools/src/cmd/intrinsic-gen/gen/generate.go
@@ -27,46 +27,74 @@
type generator struct {
s *sem.Sem
+ t *template.Template
cached struct {
intrinsicTable *IntrinsicTable // lazily built by intrinsicTable()
}
}
+// WriteFile is a function that Generate() may call to emit a new file from a
+// template.
+// relpath is the relative path from the currently executing template.
+// content is the file content to write.
+type WriteFile func(relpath, content string) error
+
// Generate executes the template tmpl using the provided semantic
// information, writing the output to w.
// See https://golang.org/pkg/text/template/ for documentation on the template
// syntax.
-func Generate(s *sem.Sem, tmpl string, w io.Writer) error {
+func Generate(s *sem.Sem, tmpl string, w io.Writer, writeFile WriteFile) error {
g := generator{s: s}
- return g.generate(tmpl, w)
+ return g.generate(tmpl, w, writeFile)
}
-func (g *generator) generate(tmpl string, w io.Writer) error {
- t, err := template.
- New("<template>").
- Funcs(map[string]interface{}{
- "Map": newMap,
- "Iterate": iterate,
- "Title": strings.Title,
- "PascalCase": pascalCase,
- "SplitDisplayName": splitDisplayName,
- "IsTemplateTypeParam": is(&sem.TemplateTypeParam{}),
- "IsTemplateNumberParam": is(&sem.TemplateNumberParam{}),
- "IsTemplateEnumParam": is(&sem.TemplateEnumParam{}),
- "IsFirstIn": isFirstIn,
- "IsLastIn": isLastIn,
- "IntrinsicTable": g.intrinsicTable,
- }).
- Option("missingkey=error").
+func (g *generator) generate(tmpl string, w io.Writer, writeFile WriteFile) error {
+ t, err := template.New("<template>").Funcs(map[string]interface{}{
+ "Map": newMap,
+ "Iterate": iterate,
+ "Title": strings.Title,
+ "PascalCase": pascalCase,
+ "SplitDisplayName": splitDisplayName,
+ "HasPrefix": strings.HasPrefix,
+ "HasSuffix": strings.HasSuffix,
+ "IsEnumEntry": is(sem.EnumEntry{}),
+ "IsEnumMatcher": is(sem.EnumMatcher{}),
+ "IsFQN": is(sem.FullyQualifiedName{}),
+ "IsInt": is(1),
+ "IsTemplateEnumParam": is(sem.TemplateEnumParam{}),
+ "IsTemplateNumberParam": is(sem.TemplateNumberParam{}),
+ "IsTemplateTypeParam": is(sem.TemplateTypeParam{}),
+ "IsType": is(sem.Type{}),
+ "IsFirstIn": isFirstIn,
+ "IsLastIn": isLastIn,
+ "IntrinsicTable": g.intrinsicTable,
+ "Eval": g.eval,
+ "WriteFile": func(relpath, content string) (string, error) { return "", writeFile(relpath, content) },
+ }).Option("missingkey=error").
Parse(tmpl)
if err != nil {
return err
}
+ g.t = t
return t.Execute(w, map[string]interface{}{
"Sem": g.s,
})
}
+// eval executes the sub-template with the given name and argument, returning
+// the generated output
+func (g *generator) eval(template string, arg interface{}) (string, error) {
+ target := g.t.Lookup(template)
+ if target == nil {
+ return "", fmt.Errorf("template '%v' not found", template)
+ }
+ sb := strings.Builder{}
+ if err := target.Execute(&sb, arg); err != nil {
+ return "", fmt.Errorf("while evaluating '%v': %v", template, err)
+ }
+ return sb.String(), nil
+}
+
// intrinsicTable lazily calls and returns the result of buildIntrinsicTable(),
// caching the result for repeated calls.
func (g *generator) intrinsicTable() (*IntrinsicTable, error) {
@@ -103,7 +131,8 @@
func is(ty interface{}) func(interface{}) bool {
rty := reflect.TypeOf(ty)
return func(v interface{}) bool {
- return reflect.TypeOf(v) == rty
+ ty := reflect.TypeOf(v)
+ return ty == rty || ty == reflect.PtrTo(rty)
}
}
diff --git a/tools/src/cmd/intrinsic-gen/gen/intrinsic_table.go b/tools/src/cmd/intrinsic-gen/gen/intrinsic_table.go
index e5c7244..84fa3ee 100644
--- a/tools/src/cmd/intrinsic-gen/gen/intrinsic_table.go
+++ b/tools/src/cmd/intrinsic-gen/gen/intrinsic_table.go
@@ -327,7 +327,7 @@
}
out := []int{idx}
for _, arg := range fqn.TemplateArguments {
- indices, err := b.collectMatcherIndices(arg)
+ indices, err := b.collectMatcherIndices(arg.(sem.FullyQualifiedName))
if err != nil {
return nil, err
}
diff --git a/tools/src/cmd/intrinsic-gen/main.go b/tools/src/cmd/intrinsic-gen/main.go
index 8271fc1..b6577ee 100644
--- a/tools/src/cmd/intrinsic-gen/main.go
+++ b/tools/src/cmd/intrinsic-gen/main.go
@@ -80,18 +80,19 @@
}
// Recursively find all the template files in the <tint>/src directory
- srcDir := filepath.Join(projectRoot, "src")
- files, err := glob.Scan(srcDir, glob.MustParseConfig(`{
- "paths": [{"include": [ "**.tmpl" ]}]
+ files, err := glob.Scan(projectRoot, glob.MustParseConfig(`{
+ "paths": [{"include": [
+ "src/**.tmpl"
+ ]}]
}`))
if err != nil {
return err
}
// For each template file...
- for _, tmplPath := range files {
+ for _, relTmplPath := range files {
// Make tmplPath absolute
- tmplPath := filepath.Join(srcDir, tmplPath)
+ tmplPath := filepath.Join(projectRoot, relTmplPath)
// Read the template file
tmpl, err := ioutil.ReadFile(tmplPath)
@@ -99,31 +100,49 @@
return fmt.Errorf("failed to open '%v': %w", tmplPath, err)
}
- // Write the common file header
- sb := strings.Builder{}
- sb.WriteString(header)
+ // Create or update the file at relpath if the file content has changed
+ // relpath is a path relative to the template
+ writeFile := func(relpath, body string) error {
+ // Write the common file header
+ sb := strings.Builder{}
+ sb.WriteString(fmt.Sprintf(header, relTmplPath, defProjectRelPath))
+ sb.WriteString(body)
+ content := sb.String()
+ abspath := filepath.Join(filepath.Dir(tmplPath), relpath)
+ return writeFileIfChanged(abspath, content)
+ }
// Write the content generated using the template and semantic info
- if err := gen.Generate(sem, string(tmpl), &sb); err != nil {
+ sb := strings.Builder{}
+ if err := gen.Generate(sem, string(tmpl), &sb, writeFile); err != nil {
return fmt.Errorf("while processing '%v': %w", tmplPath, err)
}
- // Create or update the output file if the content has not changed
- filePath := strings.TrimSuffix(tmplPath, ".tmpl")
- if err := writeFileIfChanged(filePath, sb.String()); err != nil {
- return fmt.Errorf("failed to write '%v': %w", filePath, err)
+ if body := sb.String(); body != "" {
+ _, tmplFileName := filepath.Split(tmplPath)
+ outFileName := strings.TrimSuffix(tmplFileName, ".tmpl")
+ if err := writeFile(outFileName, body); err != nil {
+ return err
+ }
}
}
return nil
}
+// writes content to path if the file has changed
func writeFileIfChanged(path, content string) error {
existing, err := ioutil.ReadFile(path)
if err == nil && string(existing) == content {
return nil // Not changed
}
- return ioutil.WriteFile(path, []byte(content), 0666)
+ if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
+ return fmt.Errorf("failed to create directory for '%v': %w", path, err)
+ }
+ if err := ioutil.WriteFile(path, []byte(content), 0666); err != nil {
+ return fmt.Errorf("failed to write file '%v': %w", path, err)
+ }
+ return nil
}
const header = `// Copyright 2021 The Tint Authors.
@@ -142,6 +161,11 @@
////////////////////////////////////////////////////////////////////////////////
// File generated by tools/intrinsic-gen
+// using the template:
+// %v
+// and the intrinsic defintion file:
+// %v
+//
// Do not modify this file directly
////////////////////////////////////////////////////////////////////////////////
diff --git a/tools/src/cmd/intrinsic-gen/resolver/resolve.go b/tools/src/cmd/intrinsic-gen/resolver/resolve.go
index 05d14e1..0f1a1e9 100644
--- a/tools/src/cmd/intrinsic-gen/resolver/resolve.go
+++ b/tools/src/cmd/intrinsic-gen/resolver/resolve.go
@@ -246,6 +246,7 @@
// Construct the semantic overload and append it to the function
overload := &sem.Overload{
Decl: a,
+ Function: f,
Parameters: make([]sem.Parameter, len(a.Parameters)),
TemplateParams: templateParams,
}
@@ -335,7 +336,7 @@
fqn := sem.FullyQualifiedName{
Target: target,
- TemplateArguments: make([]sem.FullyQualifiedName, len(arg.TemplateArgs)),
+ TemplateArguments: make([]interface{}, len(arg.TemplateArgs)),
}
for i, a := range arg.TemplateArgs {
arg, err := r.fullyQualifiedName(s, a)
diff --git a/tools/src/cmd/intrinsic-gen/sem/sem.go b/tools/src/cmd/intrinsic-gen/sem/sem.go
index cb8f383..b7b49c5 100644
--- a/tools/src/cmd/intrinsic-gen/sem/sem.go
+++ b/tools/src/cmd/intrinsic-gen/sem/sem.go
@@ -15,6 +15,8 @@
package sem
import (
+ "fmt"
+
"dawn.googlesource.com/tint/tools/src/cmd/intrinsic-gen/ast"
)
@@ -68,6 +70,14 @@
IsInternal bool // True if this entry is not part of the WGSL grammar
}
+// Format implements the fmt.Formatter interface
+func (e EnumEntry) Format(w fmt.State, verb rune) {
+ if e.IsInternal {
+ fmt.Fprint(w, "[[internal]] ")
+ }
+ fmt.Fprint(w, e.Name)
+}
+
// Type declares a type
type Type struct {
TemplateParams []TemplateParam
@@ -120,6 +130,7 @@
// Overload describes a single overload of a function
type Overload struct {
Decl ast.FunctionDecl
+ Function *Function
TemplateParams []TemplateParam
OpenTypes []*TemplateTypeParam
OpenNumbers []TemplateParam
@@ -127,16 +138,65 @@
Parameters []Parameter
}
+// Format implements the fmt.Formatter interface
+func (o Overload) Format(w fmt.State, verb rune) {
+ fmt.Fprintf(w, "fn %v", o.Function.Name)
+ if len(o.TemplateParams) > 0 {
+ fmt.Fprintf(w, "<")
+ for i, t := range o.TemplateParams {
+ if i > 0 {
+ fmt.Fprint(w, ", ")
+ }
+ fmt.Fprintf(w, "%v", t)
+ }
+ fmt.Fprintf(w, ">")
+ }
+ fmt.Fprint(w, "(")
+ for i, p := range o.Parameters {
+ if i > 0 {
+ fmt.Fprint(w, ", ")
+ }
+ fmt.Fprintf(w, "%v", p)
+ }
+ fmt.Fprint(w, ")")
+ if o.ReturnType != nil {
+ fmt.Fprintf(w, " -> %v", o.ReturnType)
+ }
+}
+
// Parameter describes a single parameter of a function overload
type Parameter struct {
Name string
Type FullyQualifiedName
}
+// Format implements the fmt.Formatter interface
+func (p Parameter) Format(w fmt.State, verb rune) {
+ if p.Name != "" {
+ fmt.Fprintf(w, "%v: ", p.Name)
+ }
+ fmt.Fprintf(w, "%v", p.Type)
+}
+
// FullyQualifiedName is the usage of a Type, TypeMatcher or TemplateTypeParam
type FullyQualifiedName struct {
Target Named
- TemplateArguments []FullyQualifiedName
+ TemplateArguments []interface{}
+}
+
+// Format implements the fmt.Formatter interface
+func (f FullyQualifiedName) Format(w fmt.State, verb rune) {
+ fmt.Fprint(w, f.Target.GetName())
+ if len(f.TemplateArguments) > 0 {
+ fmt.Fprintf(w, "<")
+ for i, t := range f.TemplateArguments {
+ if i > 0 {
+ fmt.Fprint(w, ", ")
+ }
+ fmt.Fprintf(w, "%v", t)
+ }
+ fmt.Fprintf(w, ">")
+ }
}
// TemplateParam is a TemplateEnumParam, TemplateTypeParam or TemplateNumberParam