mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-18 17:35:30 +00:00
tools: Shuffle 'intrinsic-gen' tooling
Rename to 'gen', so that more templating can be added without having a confusing name. Can now be run with './tools/run gen' Move the bulk of the intrinsic-gen logic to `tools/src/tint/intrinsic` Change-Id: I750989a5aa86272c10c2ad37adffe7def11c61f2 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/97141 Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
committed by
Dawn LUCI CQ
parent
62c58a076c
commit
cde5009be3
350
tools/src/tint/intrinsic/ast/ast.go
Normal file
350
tools/src/tint/intrinsic/ast/ast.go
Normal file
@@ -0,0 +1,350 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package ast defines AST nodes that are produced by the Tint intrinsic
|
||||
// definition parser
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/tok"
|
||||
)
|
||||
|
||||
// AST is the parsed syntax tree of the intrinsic definition file
|
||||
type AST struct {
|
||||
Enums []EnumDecl
|
||||
Types []TypeDecl
|
||||
Matchers []MatcherDecl
|
||||
Builtins []IntrinsicDecl
|
||||
Constructors []IntrinsicDecl
|
||||
Converters []IntrinsicDecl
|
||||
Operators []IntrinsicDecl
|
||||
}
|
||||
|
||||
func (a AST) String() string {
|
||||
sb := strings.Builder{}
|
||||
for _, e := range a.Enums {
|
||||
fmt.Fprintf(&sb, "%v", e)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
for _, p := range a.Types {
|
||||
fmt.Fprintf(&sb, "%v", p)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
for _, m := range a.Matchers {
|
||||
fmt.Fprintf(&sb, "%v", m)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
for _, b := range a.Builtins {
|
||||
fmt.Fprintf(&sb, "%v", b)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
for _, o := range a.Constructors {
|
||||
fmt.Fprintf(&sb, "%v", o)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
for _, o := range a.Converters {
|
||||
fmt.Fprintf(&sb, "%v", o)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
for _, o := range a.Operators {
|
||||
fmt.Fprintf(&sb, "%v", o)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// EnumDecl describes an enumerator
|
||||
type EnumDecl struct {
|
||||
Source tok.Source
|
||||
Name string
|
||||
Entries []EnumEntry
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (e EnumDecl) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprintf(w, "enum %v {\n", e.Name)
|
||||
for _, e := range e.Entries {
|
||||
fmt.Fprintf(w, " %v\n", e)
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
}
|
||||
|
||||
// EnumEntry describes an entry in a enumerator
|
||||
type EnumEntry struct {
|
||||
Source tok.Source
|
||||
Name string
|
||||
Attributes Attributes
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (e EnumEntry) Format(w fmt.State, verb rune) {
|
||||
if len(e.Attributes) > 0 {
|
||||
fmt.Fprintf(w, "%v %v", e.Attributes, e.Name)
|
||||
} else {
|
||||
fmt.Fprint(w, e.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// MatcherDecl describes a matcher declaration
|
||||
type MatcherDecl struct {
|
||||
Source tok.Source
|
||||
Name string
|
||||
Options MatcherOptions
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (m MatcherDecl) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprintf(w, "match %v", m.Name)
|
||||
fmt.Fprintf(w, ": ")
|
||||
m.Options.Format(w, verb)
|
||||
}
|
||||
|
||||
// IntrinsicKind is either a Builtin, Operator, Constructor or Converter
|
||||
type IntrinsicKind string
|
||||
|
||||
const (
|
||||
// Builtin is a builtin function (max, fract, etc).
|
||||
// Declared with 'fn'.
|
||||
Builtin IntrinsicKind = "builtin"
|
||||
// Operator is a unary or binary operator.
|
||||
// Declared with 'op'.
|
||||
Operator IntrinsicKind = "operator"
|
||||
// Constructor is a type constructor function.
|
||||
// Declared with 'ctor'.
|
||||
Constructor IntrinsicKind = "constructor"
|
||||
// Converter is a type conversion function.
|
||||
// Declared with 'conv'.
|
||||
Converter IntrinsicKind = "converter"
|
||||
)
|
||||
|
||||
// IntrinsicDecl describes a builtin or operator declaration
|
||||
type IntrinsicDecl struct {
|
||||
Source tok.Source
|
||||
Kind IntrinsicKind
|
||||
Name string
|
||||
Attributes Attributes
|
||||
TemplateParams TemplateParams
|
||||
Parameters Parameters
|
||||
ReturnType *TemplatedName
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (i IntrinsicDecl) Format(w fmt.State, verb rune) {
|
||||
switch i.Kind {
|
||||
case Builtin:
|
||||
fmt.Fprintf(w, "fn ")
|
||||
case Operator:
|
||||
fmt.Fprintf(w, "op ")
|
||||
case Constructor:
|
||||
fmt.Fprintf(w, "ctor ")
|
||||
case Converter:
|
||||
fmt.Fprintf(w, "conv ")
|
||||
}
|
||||
fmt.Fprintf(w, "%v", i.Name)
|
||||
i.TemplateParams.Format(w, verb)
|
||||
i.Parameters.Format(w, verb)
|
||||
if i.ReturnType != nil {
|
||||
fmt.Fprintf(w, " -> ")
|
||||
i.ReturnType.Format(w, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// Parameters is a list of parameter
|
||||
type Parameters []Parameter
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (l Parameters) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprintf(w, "(")
|
||||
for i, p := range l {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(w, ", ")
|
||||
}
|
||||
p.Attributes.Format(w, verb)
|
||||
p.Format(w, verb)
|
||||
}
|
||||
fmt.Fprintf(w, ")")
|
||||
}
|
||||
|
||||
// Parameter describes a single parameter of a function
|
||||
type Parameter struct {
|
||||
Source tok.Source
|
||||
Attributes Attributes
|
||||
Name string // Optional
|
||||
Type TemplatedName
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (p Parameter) Format(w fmt.State, verb rune) {
|
||||
if p.Name != "" {
|
||||
fmt.Fprintf(w, "%v: ", p.Name)
|
||||
}
|
||||
p.Type.Format(w, verb)
|
||||
}
|
||||
|
||||
// MatcherOptions is a list of TemplatedName
|
||||
type MatcherOptions TemplatedNames
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (o MatcherOptions) Format(w fmt.State, verb rune) {
|
||||
for i, mo := range o {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(w, " | ")
|
||||
}
|
||||
mo.Format(w, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// TemplatedNames is a list of TemplatedName
|
||||
// Example:
|
||||
// a<b>, c<d, e>
|
||||
type TemplatedNames []TemplatedName
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (l TemplatedNames) Format(w fmt.State, verb rune) {
|
||||
for i, n := range l {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(w, ", ")
|
||||
}
|
||||
n.Format(w, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// TemplatedName is an identifier with optional templated arguments
|
||||
// Example:
|
||||
// vec<N, T>
|
||||
type TemplatedName struct {
|
||||
Source tok.Source
|
||||
Name string
|
||||
TemplateArgs TemplatedNames
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (t TemplatedName) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprintf(w, "%v", t.Name)
|
||||
if len(t.TemplateArgs) > 0 {
|
||||
fmt.Fprintf(w, "<")
|
||||
t.TemplateArgs.Format(w, verb)
|
||||
fmt.Fprintf(w, ">")
|
||||
}
|
||||
}
|
||||
|
||||
// TypeDecl describes a type declaration
|
||||
type TypeDecl struct {
|
||||
Source tok.Source
|
||||
Attributes Attributes
|
||||
Name string
|
||||
TemplateParams TemplateParams
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (p TypeDecl) Format(w fmt.State, verb rune) {
|
||||
if len(p.Attributes) > 0 {
|
||||
p.Attributes.Format(w, verb)
|
||||
fmt.Fprintf(w, " type %v", p.Name)
|
||||
}
|
||||
fmt.Fprintf(w, "type %v", p.Name)
|
||||
p.TemplateParams.Format(w, verb)
|
||||
}
|
||||
|
||||
// TemplateParams is a list of TemplateParam
|
||||
// Example:
|
||||
// <A, B : TyB>
|
||||
type TemplateParams []TemplateParam
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (p TemplateParams) Format(w fmt.State, verb rune) {
|
||||
if len(p) > 0 {
|
||||
fmt.Fprintf(w, "<")
|
||||
for i, tp := range p {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(w, ", ")
|
||||
}
|
||||
tp.Format(w, verb)
|
||||
}
|
||||
fmt.Fprintf(w, ">")
|
||||
}
|
||||
}
|
||||
|
||||
// TemplateParam describes a template parameter with optional type
|
||||
// Example:
|
||||
// <Name>
|
||||
// <Name: Type>
|
||||
type TemplateParam struct {
|
||||
Source tok.Source
|
||||
Name string
|
||||
Type TemplatedName // Optional
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (t TemplateParam) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprintf(w, "%v", t.Name)
|
||||
if t.Type.Name != "" {
|
||||
fmt.Fprintf(w, " : ")
|
||||
t.Type.Format(w, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// Attributes is a list of Attribute
|
||||
// Example:
|
||||
// [[a(x), b(y)]]
|
||||
type Attributes []Attribute
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (l Attributes) Format(w fmt.State, verb rune) {
|
||||
for _, d := range l {
|
||||
fmt.Fprint(w, "@")
|
||||
d.Format(w, verb)
|
||||
fmt.Fprint(w, " ")
|
||||
}
|
||||
}
|
||||
|
||||
// Take looks up the attribute with the given name. If the attribute is found
|
||||
// it is removed from the Attributes list and returned, otherwise nil is
|
||||
// returned and the Attributes are not altered.
|
||||
func (l *Attributes) Take(name string) *Attribute {
|
||||
for i, a := range *l {
|
||||
if a.Name == name {
|
||||
*l = append((*l)[:i], (*l)[i+1:]...)
|
||||
return &a
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attribute describes a single attribute
|
||||
// Example:
|
||||
// @a(x)
|
||||
type Attribute struct {
|
||||
Source tok.Source
|
||||
Name string
|
||||
Values []string
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (d Attribute) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprintf(w, "%v", d.Name)
|
||||
if len(d.Values) > 0 {
|
||||
fmt.Fprintf(w, "(")
|
||||
for i, v := range d.Values {
|
||||
if i > 0 {
|
||||
fmt.Fprint(w, ", ")
|
||||
}
|
||||
fmt.Fprintf(w, "%v", v)
|
||||
}
|
||||
fmt.Fprintf(w, ")")
|
||||
}
|
||||
}
|
||||
465
tools/src/tint/intrinsic/gen/gen.go
Normal file
465
tools/src/tint/intrinsic/gen/gen.go
Normal file
@@ -0,0 +1,465 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// Package gen holds types and helpers for generating templated code from the
|
||||
/// intrinsic.def file.
|
||||
///
|
||||
/// Used by tools/src/cmd/gen/main.go
|
||||
package gen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/list"
|
||||
"dawn.googlesource.com/dawn/tools/src/lut"
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/sem"
|
||||
)
|
||||
|
||||
// IntrinsicTable holds data specific to the intrinsic_table.inl.tmpl template
|
||||
type IntrinsicTable struct {
|
||||
// The semantic info
|
||||
Sem *sem.Sem
|
||||
|
||||
// TMatchers are all the sem.TemplateType, sem.Type and sem.TypeMatchers.
|
||||
// These are all implemented by classes deriving from tint::TypeMatcher
|
||||
TMatchers []sem.Named
|
||||
TMatcherIndex map[sem.Named]int // [object -> index] in TMatcher
|
||||
|
||||
// NMatchers are all the sem.TemplateNumber and sem.EnumMatchers.
|
||||
// These are all implemented by classes deriving from tint::NumberMatcher
|
||||
NMatchers []sem.Named
|
||||
NMatcherIndex map[sem.Named]int // [object -> index] in NMatchers
|
||||
|
||||
MatcherIndices []int // kMatcherIndices table content
|
||||
TemplateTypes []TemplateType // kTemplateTypes table content
|
||||
TemplateNumbers []TemplateNumber // kTemplateNumbers table content
|
||||
Parameters []Parameter // kParameters table content
|
||||
Overloads []Overload // kOverloads table content
|
||||
Builtins []Intrinsic // kBuiltins table content
|
||||
UnaryOperators []Intrinsic // kUnaryOperators table content
|
||||
BinaryOperators []Intrinsic // kBinaryOperators table content
|
||||
ConstructorsAndConverters []Intrinsic // kConstructorsAndConverters table content
|
||||
}
|
||||
|
||||
// TemplateType is used to create the C++ TemplateTypeInfo structure
|
||||
type TemplateType struct {
|
||||
// Name of the template type (e.g. 'T')
|
||||
Name string
|
||||
// Optional type matcher constraint.
|
||||
// Either an index in Matchers::type, or -1
|
||||
MatcherIndex int
|
||||
}
|
||||
|
||||
// TemplateNumber is used to create the C++ TemplateNumberInfo structure
|
||||
type TemplateNumber struct {
|
||||
// Name of the template number (e.g. 'N')
|
||||
Name string
|
||||
// Optional type matcher constraint.
|
||||
// Either an index in Matchers::type, or -1
|
||||
MatcherIndex int
|
||||
}
|
||||
|
||||
// Parameter is used to create the C++ ParameterInfo structure
|
||||
type Parameter struct {
|
||||
// The parameter usage (parameter name)
|
||||
Usage string
|
||||
|
||||
// Index into IntrinsicTable.MatcherIndices, beginning the list of matchers
|
||||
// required to match the parameter type. The matcher indices index
|
||||
// into IntrinsicTable::TMatchers and / or IntrinsicTable::NMatchers.
|
||||
// These indices are consumed by the matchers themselves.
|
||||
// The first index is always a TypeMatcher.
|
||||
MatcherIndicesOffset *int
|
||||
}
|
||||
|
||||
// Overload is used to create the C++ OverloadInfo structure
|
||||
type Overload struct {
|
||||
// Total number of parameters for the overload
|
||||
NumParameters int
|
||||
// Total number of template types for the overload
|
||||
NumTemplateTypes int
|
||||
// Total number of template numbers for the overload
|
||||
NumTemplateNumbers int
|
||||
// Index to the first template type in IntrinsicTable.TemplateTypes
|
||||
TemplateTypesOffset *int
|
||||
// Index to the first template number in IntrinsicTable.TemplateNumbers
|
||||
TemplateNumbersOffset *int
|
||||
// Index to the first parameter in IntrinsicTable.Parameters
|
||||
ParametersOffset *int
|
||||
// Index into IntrinsicTable.MatcherIndices, beginning the list of matchers
|
||||
// required to match the return type. The matcher indices index
|
||||
// into IntrinsicTable::TMatchers and / or IntrinsicTable::NMatchers.
|
||||
// These indices are consumed by the matchers themselves.
|
||||
// The first index is always a TypeMatcher.
|
||||
ReturnMatcherIndicesOffset *int
|
||||
// StageUses describes the stages an overload can be used in
|
||||
CanBeUsedInStage sem.StageUses
|
||||
// True if the overload is marked as deprecated
|
||||
IsDeprecated bool
|
||||
// The kind of overload
|
||||
Kind string
|
||||
// The function name used to evaluate the overload at shader-creation time
|
||||
ConstEvalFunction string
|
||||
}
|
||||
|
||||
// Intrinsic is used to create the C++ IntrinsicInfo structure
|
||||
type Intrinsic struct {
|
||||
Name string
|
||||
OverloadDescriptions []string
|
||||
NumOverloads int
|
||||
OverloadsOffset *int
|
||||
}
|
||||
|
||||
// Helper for building the IntrinsicTable
|
||||
type IntrinsicTableBuilder struct {
|
||||
// The output of the builder
|
||||
IntrinsicTable
|
||||
|
||||
// Lookup tables.
|
||||
// These are packed (compressed) once all the entries have been added.
|
||||
lut struct {
|
||||
matcherIndices lut.LUT
|
||||
templateTypes lut.LUT
|
||||
templateNumbers lut.LUT
|
||||
parameters lut.LUT
|
||||
overloads lut.LUT
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for building a single overload
|
||||
type overloadBuilder struct {
|
||||
*IntrinsicTableBuilder
|
||||
// Maps TemplateParam to index in templateTypes
|
||||
templateTypeIndex map[sem.TemplateParam]int
|
||||
// Maps TemplateParam to index in templateNumbers
|
||||
templateNumberIndex map[sem.TemplateParam]int
|
||||
// Template types used by the overload
|
||||
templateTypes []TemplateType
|
||||
// Template numbers used by the overload
|
||||
templateNumbers []TemplateNumber
|
||||
// All parameters declared by the overload
|
||||
parameters []Parameter
|
||||
// Index into IntrinsicTable.MatcherIndices, beginning the list of matchers
|
||||
// required to match the return type. The matcher indices index
|
||||
// into IntrinsicTable::TMatchers and / or IntrinsicTable::NMatchers.
|
||||
// These indices are consumed by the matchers themselves.
|
||||
// The first index is always a TypeMatcher.
|
||||
returnTypeMatcherIndicesOffset *int
|
||||
}
|
||||
|
||||
// layoutMatchers assigns each of the TMatchers and NMatchers a unique index
|
||||
// in the C++ Matchers::type and Matchers::number arrays, respectively.
|
||||
func (b *IntrinsicTableBuilder) layoutMatchers(s *sem.Sem) {
|
||||
// First MaxTemplateTypes of TMatchers are template types
|
||||
b.TMatchers = make([]sem.Named, s.MaxTemplateTypes)
|
||||
for _, m := range s.Types {
|
||||
b.TMatcherIndex[m] = len(b.TMatchers)
|
||||
b.TMatchers = append(b.TMatchers, m)
|
||||
}
|
||||
for _, m := range s.TypeMatchers {
|
||||
b.TMatcherIndex[m] = len(b.TMatchers)
|
||||
b.TMatchers = append(b.TMatchers, m)
|
||||
}
|
||||
|
||||
// First MaxTemplateNumbers of NMatchers are template numbers
|
||||
b.NMatchers = make([]sem.Named, s.MaxTemplateNumbers)
|
||||
for _, m := range s.EnumMatchers {
|
||||
b.NMatcherIndex[m] = len(b.NMatchers)
|
||||
b.NMatchers = append(b.NMatchers, m)
|
||||
}
|
||||
}
|
||||
|
||||
// buildOverload constructs an Overload for a sem.Overload
|
||||
func (b *IntrinsicTableBuilder) buildOverload(o *sem.Overload) (Overload, error) {
|
||||
ob := overloadBuilder{
|
||||
IntrinsicTableBuilder: b,
|
||||
templateTypeIndex: map[sem.TemplateParam]int{},
|
||||
templateNumberIndex: map[sem.TemplateParam]int{},
|
||||
}
|
||||
|
||||
if err := ob.buildTemplateTypes(o); err != nil {
|
||||
return Overload{}, err
|
||||
}
|
||||
if err := ob.buildTemplateNumbers(o); err != nil {
|
||||
return Overload{}, err
|
||||
}
|
||||
if err := ob.buildParameters(o); err != nil {
|
||||
return Overload{}, err
|
||||
}
|
||||
if err := ob.buildReturnType(o); err != nil {
|
||||
return Overload{}, err
|
||||
}
|
||||
|
||||
return Overload{
|
||||
NumParameters: len(ob.parameters),
|
||||
NumTemplateTypes: len(ob.templateTypes),
|
||||
NumTemplateNumbers: len(ob.templateNumbers),
|
||||
TemplateTypesOffset: b.lut.templateTypes.Add(ob.templateTypes),
|
||||
TemplateNumbersOffset: b.lut.templateNumbers.Add(ob.templateNumbers),
|
||||
ParametersOffset: b.lut.parameters.Add(ob.parameters),
|
||||
ReturnMatcherIndicesOffset: ob.returnTypeMatcherIndicesOffset,
|
||||
CanBeUsedInStage: o.CanBeUsedInStage,
|
||||
IsDeprecated: o.IsDeprecated,
|
||||
Kind: string(o.Decl.Kind),
|
||||
ConstEvalFunction: o.ConstEvalFunction,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// buildTemplateTypes constructs the TemplateTypes used by the overload, populating
|
||||
// b.templateTypes
|
||||
func (b *overloadBuilder) buildTemplateTypes(o *sem.Overload) error {
|
||||
b.templateTypes = make([]TemplateType, len(o.TemplateTypes))
|
||||
for i, t := range o.TemplateTypes {
|
||||
b.templateTypeIndex[t] = i
|
||||
matcherIndex := -1
|
||||
if t.Type != nil {
|
||||
var err error
|
||||
matcherIndex, err = b.matcherIndex(t.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.templateTypes[i] = TemplateType{
|
||||
Name: t.Name,
|
||||
MatcherIndex: matcherIndex,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildTemplateNumbers constructs the TemplateNumbers used by the overload, populating
|
||||
// b.templateNumbers
|
||||
func (b *overloadBuilder) buildTemplateNumbers(o *sem.Overload) error {
|
||||
b.templateNumbers = make([]TemplateNumber, len(o.TemplateNumbers))
|
||||
for i, t := range o.TemplateNumbers {
|
||||
b.templateNumberIndex[t] = i
|
||||
matcherIndex := -1
|
||||
if e, ok := t.(*sem.TemplateEnumParam); ok && e.Matcher != nil {
|
||||
var err error
|
||||
matcherIndex, err = b.matcherIndex(e.Matcher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
b.templateNumbers[i] = TemplateNumber{
|
||||
Name: t.GetName(),
|
||||
MatcherIndex: matcherIndex,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildParameters constructs the Parameters used by the overload, populating
|
||||
// b.parameters
|
||||
func (b *overloadBuilder) buildParameters(o *sem.Overload) error {
|
||||
b.parameters = make([]Parameter, len(o.Parameters))
|
||||
for i, p := range o.Parameters {
|
||||
indices, err := b.collectMatcherIndices(p.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.parameters[i] = Parameter{
|
||||
Usage: p.Name,
|
||||
MatcherIndicesOffset: b.lut.matcherIndices.Add(indices),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildParameters calculates the matcher indices required to match the
|
||||
// overload's return type (if the overload has a return value), possibly
|
||||
// populating b.returnTypeMatcherIndicesOffset
|
||||
func (b *overloadBuilder) buildReturnType(o *sem.Overload) error {
|
||||
if o.ReturnType != nil {
|
||||
indices, err := b.collectMatcherIndices(*o.ReturnType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.returnTypeMatcherIndicesOffset = b.lut.matcherIndices.Add(indices)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// matcherIndex returns the index of TMatcher or NMatcher in
|
||||
// IntrinsicTable.TMatcher or IntrinsicTable.NMatcher, respectively.
|
||||
func (b *overloadBuilder) matcherIndex(n sem.Named) (int, error) {
|
||||
switch n := n.(type) {
|
||||
case *sem.Type, *sem.TypeMatcher:
|
||||
if i, ok := b.TMatcherIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("matcherIndex missing entry for %v %T", n.GetName(), n)
|
||||
case *sem.TemplateTypeParam:
|
||||
if i, ok := b.templateTypeIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("templateTypeIndex missing entry for %v %T", n.Name, n)
|
||||
case *sem.EnumMatcher:
|
||||
if i, ok := b.NMatcherIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("matcherIndex missing entry for %v %T", n.GetName(), n)
|
||||
case *sem.TemplateEnumParam:
|
||||
if i, ok := b.templateNumberIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("templateNumberIndex missing entry for %v %T", n, n)
|
||||
case *sem.TemplateNumberParam:
|
||||
if i, ok := b.templateNumberIndex[n]; ok {
|
||||
return i, nil
|
||||
}
|
||||
return 0, fmt.Errorf("templateNumberIndex missing entry for %v %T", n, n)
|
||||
default:
|
||||
return 0, fmt.Errorf("overload.matcherIndex() does not handle %v %T", n, n)
|
||||
}
|
||||
}
|
||||
|
||||
// collectMatcherIndices returns the full list of matcher indices required to
|
||||
// match the fully-qualified-name. For names that have do not have templated
|
||||
// arguments, collectMatcherIndices() will return a single TMatcher index.
|
||||
// For names that do have templated arguments, collectMatcherIndices() returns
|
||||
// a list of type matcher indices, starting with the target of the fully
|
||||
// qualified name, then followed by each of the template arguments from left to
|
||||
// right. Note that template arguments may themselves have template arguments,
|
||||
// and so collectMatcherIndices() may call itself.
|
||||
// The order of returned matcher indices is always the order of the fully
|
||||
// qualified name as read from left to right.
|
||||
// For example, calling collectMatcherIndices() for the fully qualified name:
|
||||
// A<B<C, D>, E<F, G<H>, I>
|
||||
// Would return the matcher indices:
|
||||
// A, B, C, D, E, F, G, H, I
|
||||
func (b *overloadBuilder) collectMatcherIndices(fqn sem.FullyQualifiedName) ([]int, error) {
|
||||
idx, err := b.matcherIndex(fqn.Target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := []int{idx}
|
||||
for _, arg := range fqn.TemplateArguments {
|
||||
indices, err := b.collectMatcherIndices(arg.(sem.FullyQualifiedName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, indices...)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// BuildIntrinsicTable builds the IntrinsicTable from the semantic info
|
||||
func BuildIntrinsicTable(s *sem.Sem) (*IntrinsicTable, error) {
|
||||
b := IntrinsicTableBuilder{
|
||||
IntrinsicTable: IntrinsicTable{
|
||||
Sem: s,
|
||||
TMatcherIndex: map[sem.Named]int{},
|
||||
NMatcherIndex: map[sem.Named]int{},
|
||||
},
|
||||
}
|
||||
b.lut.matcherIndices = lut.New(list.Wrap(&b.MatcherIndices))
|
||||
b.lut.templateTypes = lut.New(list.Wrap(&b.TemplateTypes))
|
||||
b.lut.templateNumbers = lut.New(list.Wrap(&b.TemplateNumbers))
|
||||
b.lut.parameters = lut.New(list.Wrap(&b.Parameters))
|
||||
b.lut.overloads = lut.New(list.Wrap(&b.Overloads))
|
||||
|
||||
b.layoutMatchers(s)
|
||||
|
||||
for _, intrinsics := range []struct {
|
||||
in []*sem.Intrinsic
|
||||
out *[]Intrinsic
|
||||
}{
|
||||
{s.Builtins, &b.Builtins},
|
||||
{s.UnaryOperators, &b.UnaryOperators},
|
||||
{s.BinaryOperators, &b.BinaryOperators},
|
||||
{s.ConstructorsAndConverters, &b.ConstructorsAndConverters},
|
||||
} {
|
||||
out := make([]Intrinsic, len(intrinsics.in))
|
||||
for i, f := range intrinsics.in {
|
||||
overloads := make([]Overload, len(f.Overloads))
|
||||
overloadDescriptions := make([]string, len(f.Overloads))
|
||||
for i, o := range f.Overloads {
|
||||
overloadDescriptions[i] = fmt.Sprint(o.Decl)
|
||||
var err error
|
||||
if overloads[i], err = b.buildOverload(o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
out[i] = Intrinsic{
|
||||
Name: f.Name,
|
||||
OverloadDescriptions: overloadDescriptions,
|
||||
NumOverloads: len(overloads),
|
||||
OverloadsOffset: b.lut.overloads.Add(overloads),
|
||||
}
|
||||
}
|
||||
*intrinsics.out = out
|
||||
}
|
||||
|
||||
b.lut.matcherIndices.Compact()
|
||||
b.lut.templateTypes.Compact()
|
||||
b.lut.templateNumbers.Compact()
|
||||
b.lut.parameters.Compact()
|
||||
b.lut.overloads.Compact()
|
||||
|
||||
return &b.IntrinsicTable, nil
|
||||
}
|
||||
|
||||
// SplitDisplayName splits displayName into parts, where text wrapped in {}
|
||||
// braces are not quoted and the rest is quoted. This is used to help process
|
||||
// the string value of the [[display()]] decoration. For example:
|
||||
// SplitDisplayName("vec{N}<{T}>")
|
||||
// would return the strings:
|
||||
// [`"vec"`, `N`, `"<"`, `T`, `">"`]
|
||||
func SplitDisplayName(displayName string) []string {
|
||||
parts := []string{}
|
||||
pending := strings.Builder{}
|
||||
for _, r := range displayName {
|
||||
switch r {
|
||||
case '{':
|
||||
if pending.Len() > 0 {
|
||||
parts = append(parts, fmt.Sprintf(`"%v"`, pending.String()))
|
||||
pending.Reset()
|
||||
}
|
||||
case '}':
|
||||
if pending.Len() > 0 {
|
||||
parts = append(parts, pending.String())
|
||||
pending.Reset()
|
||||
}
|
||||
default:
|
||||
pending.WriteRune(r)
|
||||
}
|
||||
}
|
||||
if pending.Len() > 0 {
|
||||
parts = append(parts, fmt.Sprintf(`"%v"`, pending.String()))
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
// IsAbstract returns true if the FullyQualifiedName refers to an abstract
|
||||
// numeric type
|
||||
func IsAbstract(fqn sem.FullyQualifiedName) bool {
|
||||
switch fqn.Target.GetName() {
|
||||
case "ia", "fa":
|
||||
return true
|
||||
case "vec":
|
||||
return IsAbstract(fqn.TemplateArguments[1].(sem.FullyQualifiedName))
|
||||
case "mat":
|
||||
return IsAbstract(fqn.TemplateArguments[2].(sem.FullyQualifiedName))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDeclarable returns false if the FullyQualifiedName refers to an abstract
|
||||
// numeric type, or if it starts with a leading underscore.
|
||||
func IsDeclarable(fqn sem.FullyQualifiedName) bool {
|
||||
return !IsAbstract(fqn) && !strings.HasPrefix(fqn.Target.GetName(), "_")
|
||||
}
|
||||
383
tools/src/tint/intrinsic/gen/permutate.go
Normal file
383
tools/src/tint/intrinsic/gen/permutate.go
Normal file
@@ -0,0 +1,383 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gen
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/fileutils"
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/sem"
|
||||
)
|
||||
|
||||
// Permuter generates permutations of intrinsic overloads
|
||||
type Permuter struct {
|
||||
sem *sem.Sem
|
||||
allTypes []sem.FullyQualifiedName
|
||||
}
|
||||
|
||||
// NewPermuter returns a new initialized Permuter
|
||||
func NewPermuter(s *sem.Sem) (*Permuter, error) {
|
||||
// allTypes are the list of FQNs that are used for unconstrained types
|
||||
allTypes := []sem.FullyQualifiedName{}
|
||||
for _, ty := range s.Types {
|
||||
if len(ty.TemplateParams) > 0 {
|
||||
// Ignore aggregate types for now.
|
||||
// TODO(bclayton): Support a limited set of aggregate types
|
||||
continue
|
||||
}
|
||||
allTypes = append(allTypes, sem.FullyQualifiedName{Target: ty})
|
||||
}
|
||||
return &Permuter{
|
||||
sem: s,
|
||||
allTypes: allTypes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Permutation describes a single permutation of an overload
|
||||
type Permutation struct {
|
||||
sem.Overload // The permutated overload signature
|
||||
Desc string // Description of the overload
|
||||
Hash string // Hash of the overload
|
||||
}
|
||||
|
||||
// Permute generates a set of permutations for the given intrinsic overload
|
||||
func (p *Permuter) Permute(overload *sem.Overload) ([]Permutation, error) {
|
||||
state := permutationState{
|
||||
Permuter: p,
|
||||
templateTypes: map[sem.TemplateParam]sem.FullyQualifiedName{},
|
||||
templateNumbers: map[sem.TemplateParam]interface{}{},
|
||||
parameters: map[int]sem.FullyQualifiedName{},
|
||||
}
|
||||
|
||||
out := []Permutation{}
|
||||
|
||||
// Map of hash to permutation description. Used to detect collisions.
|
||||
hashes := map[string]string{}
|
||||
|
||||
// permutate appends a permutation to out.
|
||||
// permutate may be chained to generate N-dimensional permutations.
|
||||
permutate := func() error {
|
||||
o := sem.Overload{
|
||||
Decl: overload.Decl,
|
||||
Intrinsic: overload.Intrinsic,
|
||||
CanBeUsedInStage: overload.CanBeUsedInStage,
|
||||
}
|
||||
for i, p := range overload.Parameters {
|
||||
ty := state.parameters[i]
|
||||
if !validate(ty, &o.CanBeUsedInStage) {
|
||||
return nil
|
||||
}
|
||||
o.Parameters = append(o.Parameters, sem.Parameter{
|
||||
Name: p.Name,
|
||||
Type: ty,
|
||||
IsConst: p.IsConst,
|
||||
})
|
||||
}
|
||||
if overload.ReturnType != nil {
|
||||
retTys, err := state.permutateFQN(*overload.ReturnType)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while permutating return type: %w", err)
|
||||
}
|
||||
if len(retTys) != 1 {
|
||||
return fmt.Errorf("result type not pinned")
|
||||
}
|
||||
o.ReturnType = &retTys[0]
|
||||
}
|
||||
desc := fmt.Sprint(o)
|
||||
hash := sha256.Sum256([]byte(desc))
|
||||
const hashLength = 6
|
||||
shortHash := hex.EncodeToString(hash[:])[:hashLength]
|
||||
out = append(out, Permutation{
|
||||
Overload: o,
|
||||
Desc: desc,
|
||||
Hash: shortHash,
|
||||
})
|
||||
|
||||
// Check for hash collisions
|
||||
if existing, collision := hashes[shortHash]; collision {
|
||||
return fmt.Errorf("hash '%v' collision between %v and %v\nIncrease hashLength in %v",
|
||||
shortHash, existing, desc, fileutils.GoSourcePath())
|
||||
}
|
||||
hashes[shortHash] = desc
|
||||
return nil
|
||||
}
|
||||
for i, param := range overload.Parameters {
|
||||
i, param := i, param // Capture iterator values for anonymous function
|
||||
next := permutate // Permutation chaining
|
||||
permutate = func() error {
|
||||
permutations, err := state.permutateFQN(param.Type)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while processing parameter %v: %w", i, err)
|
||||
}
|
||||
if len(permutations) == 0 {
|
||||
return fmt.Errorf("parameter %v has no permutations", i)
|
||||
}
|
||||
for _, fqn := range permutations {
|
||||
state.parameters[i] = fqn
|
||||
if err := next(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for _, t := range overload.TemplateParams {
|
||||
next := permutate // Permutation chaining
|
||||
switch t := t.(type) {
|
||||
case *sem.TemplateTypeParam:
|
||||
types := p.allTypes
|
||||
if t.Type != nil {
|
||||
var err error
|
||||
types, err = state.permutateFQN(sem.FullyQualifiedName{Target: t.Type})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while permutating template types: %w", err)
|
||||
}
|
||||
}
|
||||
if len(types) == 0 {
|
||||
return nil, fmt.Errorf("template type %v has no permutations", t.Name)
|
||||
}
|
||||
permutate = func() error {
|
||||
for _, ty := range types {
|
||||
state.templateTypes[t] = ty
|
||||
if err := next(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sem.TemplateEnumParam:
|
||||
var permutations []sem.FullyQualifiedName
|
||||
var err error
|
||||
if t.Matcher != nil {
|
||||
permutations, err = state.permutateFQN(sem.FullyQualifiedName{Target: t.Matcher})
|
||||
} else {
|
||||
permutations, err = state.permutateFQN(sem.FullyQualifiedName{Target: t.Enum})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while permutating template numbers: %w", err)
|
||||
}
|
||||
if len(permutations) == 0 {
|
||||
return nil, fmt.Errorf("template type %v has no permutations", t.Name)
|
||||
}
|
||||
permutate = func() error {
|
||||
for _, n := range permutations {
|
||||
state.templateNumbers[t] = n
|
||||
if err := next(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sem.TemplateNumberParam:
|
||||
// Currently all open numbers are used for vector / matrices
|
||||
permutations := []int{2, 3, 4}
|
||||
permutate = func() error {
|
||||
for _, n := range permutations {
|
||||
state.templateNumbers[t] = n
|
||||
if err := next(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := permutate(); err != nil {
|
||||
return nil, fmt.Errorf("%v %v %w\nState: %v", overload.Decl.Source, overload.Decl, err, state)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type permutationState struct {
|
||||
*Permuter
|
||||
templateTypes map[sem.TemplateParam]sem.FullyQualifiedName
|
||||
templateNumbers map[sem.TemplateParam]interface{}
|
||||
parameters map[int]sem.FullyQualifiedName
|
||||
}
|
||||
|
||||
func (s permutationState) String() string {
|
||||
sb := &strings.Builder{}
|
||||
sb.WriteString("Template types:\n")
|
||||
for ct, ty := range s.templateTypes {
|
||||
fmt.Fprintf(sb, " %v: %v\n", ct.GetName(), ty)
|
||||
}
|
||||
sb.WriteString("Template numbers:\n")
|
||||
for cn, v := range s.templateNumbers {
|
||||
fmt.Fprintf(sb, " %v: %v\n", cn.GetName(), v)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (s *permutationState) permutateFQN(in sem.FullyQualifiedName) ([]sem.FullyQualifiedName, error) {
|
||||
args := append([]interface{}{}, in.TemplateArguments...)
|
||||
out := []sem.FullyQualifiedName{}
|
||||
|
||||
// permutate appends a permutation to out.
|
||||
// permutate may be chained to generate N-dimensional permutations.
|
||||
var permutate func() error
|
||||
|
||||
switch target := in.Target.(type) {
|
||||
case *sem.Type:
|
||||
permutate = func() error {
|
||||
out = append(out, sem.FullyQualifiedName{Target: in.Target, TemplateArguments: args})
|
||||
args = append([]interface{}{}, in.TemplateArguments...)
|
||||
return nil
|
||||
}
|
||||
case sem.TemplateParam:
|
||||
if ty, ok := s.templateTypes[target]; ok {
|
||||
permutate = func() error {
|
||||
out = append(out, ty)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("'%v' was not found in templateTypes", target.GetName())
|
||||
}
|
||||
case *sem.TypeMatcher:
|
||||
permutate = func() error {
|
||||
for _, ty := range target.Types {
|
||||
out = append(out, sem.FullyQualifiedName{Target: ty})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sem.EnumMatcher:
|
||||
permutate = func() error {
|
||||
for _, o := range target.Options {
|
||||
if !o.IsInternal {
|
||||
out = append(out, sem.FullyQualifiedName{Target: o})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case *sem.Enum:
|
||||
permutate = func() error {
|
||||
for _, e := range target.Entries {
|
||||
if !e.IsInternal {
|
||||
out = append(out, sem.FullyQualifiedName{Target: e})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled target type: %T", in.Target)
|
||||
}
|
||||
|
||||
for i, arg := range in.TemplateArguments {
|
||||
i := i // Capture iterator value for anonymous functions
|
||||
next := permutate // Permutation chaining
|
||||
switch arg := arg.(type) {
|
||||
case sem.FullyQualifiedName:
|
||||
switch target := arg.Target.(type) {
|
||||
case sem.TemplateParam:
|
||||
if ty, ok := s.templateTypes[target]; ok {
|
||||
args[i] = ty
|
||||
} else if num, ok := s.templateNumbers[target]; ok {
|
||||
args[i] = num
|
||||
} else {
|
||||
return nil, fmt.Errorf("'%v' was not found in templateTypes or templateNumbers", target.GetName())
|
||||
}
|
||||
default:
|
||||
perms, err := s.permutateFQN(arg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while processing template argument %v: %v", i, err)
|
||||
}
|
||||
if len(perms) == 0 {
|
||||
return nil, fmt.Errorf("template argument %v has no permutations", i)
|
||||
}
|
||||
permutate = func() error {
|
||||
for _, f := range perms {
|
||||
args[i] = f
|
||||
if err := next(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("permutateFQN() unhandled template argument type: %T", arg)
|
||||
}
|
||||
}
|
||||
|
||||
if err := permutate(); err != nil {
|
||||
return nil, fmt.Errorf("while processing fully qualified name '%v': %w", in.Target.GetName(), err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func validate(fqn sem.FullyQualifiedName, uses *sem.StageUses) bool {
|
||||
if strings.HasPrefix(fqn.Target.GetName(), "_") {
|
||||
return false // Builtin, untypeable return type
|
||||
}
|
||||
|
||||
switch fqn.Target.GetName() {
|
||||
case "array":
|
||||
elTy := fqn.TemplateArguments[0].(sem.FullyQualifiedName)
|
||||
elTyName := elTy.Target.GetName()
|
||||
switch {
|
||||
case elTyName == "bool" ||
|
||||
strings.Contains(elTyName, "sampler"),
|
||||
strings.Contains(elTyName, "texture"):
|
||||
return false // Not storable
|
||||
case IsAbstract(elTy):
|
||||
return false // Abstract types are not typeable nor supported by arrays
|
||||
}
|
||||
case "ptr":
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class
|
||||
access := fqn.TemplateArguments[2].(sem.FullyQualifiedName).Target.(*sem.EnumEntry).Name
|
||||
storageClass := fqn.TemplateArguments[0].(sem.FullyQualifiedName).Target.(*sem.EnumEntry).Name
|
||||
switch storageClass {
|
||||
case "function", "private":
|
||||
if access != "read_write" {
|
||||
return false
|
||||
}
|
||||
case "workgroup":
|
||||
uses.Vertex = false
|
||||
uses.Fragment = false
|
||||
if access != "read_write" {
|
||||
return false
|
||||
}
|
||||
case "uniform":
|
||||
if access != "read" {
|
||||
return false
|
||||
}
|
||||
case "storage":
|
||||
if access != "read_write" && access != "read" {
|
||||
return false
|
||||
}
|
||||
case "handle":
|
||||
if access != "read" {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, arg := range fqn.TemplateArguments {
|
||||
if argFQN, ok := arg.(sem.FullyQualifiedName); ok {
|
||||
if !validate(argFQN, uses) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
223
tools/src/tint/intrinsic/lexer/lexer.go
Normal file
223
tools/src/tint/intrinsic/lexer/lexer.go
Normal file
@@ -0,0 +1,223 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package lexer provides a basic lexer for the Tint intrinsic definition
|
||||
// language
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/tok"
|
||||
)
|
||||
|
||||
// Lex produces a list of tokens for the given source code
|
||||
func Lex(src []rune, filepath string) ([]tok.Token, error) {
|
||||
l := lexer{
|
||||
tok.Location{Line: 1, Column: 1, Rune: 0, Filepath: filepath},
|
||||
src,
|
||||
[]tok.Token{},
|
||||
}
|
||||
if err := l.lex(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.tokens, nil
|
||||
}
|
||||
|
||||
type lexer struct {
|
||||
loc tok.Location
|
||||
runes []rune
|
||||
tokens []tok.Token
|
||||
}
|
||||
|
||||
// lex() lexes the source, populating l.tokens
|
||||
func (l *lexer) lex() error {
|
||||
for {
|
||||
switch l.peek(0) {
|
||||
case 0:
|
||||
return nil
|
||||
case ' ', '\t':
|
||||
l.next()
|
||||
case '\n':
|
||||
l.next()
|
||||
case '@':
|
||||
l.tok(1, tok.Attr)
|
||||
case '(':
|
||||
l.tok(1, tok.Lparen)
|
||||
case ')':
|
||||
l.tok(1, tok.Rparen)
|
||||
case '{':
|
||||
l.tok(1, tok.Lbrace)
|
||||
case '}':
|
||||
l.tok(1, tok.Rbrace)
|
||||
case ':':
|
||||
l.tok(1, tok.Colon)
|
||||
case ',':
|
||||
l.tok(1, tok.Comma)
|
||||
case '*':
|
||||
l.tok(1, tok.Star)
|
||||
case '+':
|
||||
l.tok(1, tok.Plus)
|
||||
case '%':
|
||||
l.tok(1, tok.Modulo)
|
||||
case '^':
|
||||
l.tok(1, tok.Xor)
|
||||
case '~':
|
||||
l.tok(1, tok.Complement)
|
||||
case '"':
|
||||
start := l.loc
|
||||
l.next() // Skip opening quote
|
||||
n := l.count(toFirst('\n', '"'))
|
||||
if l.peek(n) != '"' {
|
||||
return fmt.Errorf("%v unterminated string", start)
|
||||
}
|
||||
l.tok(n, tok.String)
|
||||
l.next() // Skip closing quote
|
||||
default:
|
||||
switch {
|
||||
case l.peek(0) == '/' && l.peek(1) == '/':
|
||||
l.skip(l.count(toFirst('\n')))
|
||||
l.next() // Consume newline
|
||||
case l.match("/", tok.Divide):
|
||||
case l.match("->", tok.Arrow):
|
||||
case l.match("-", tok.Minus):
|
||||
case l.match("fn", tok.Function):
|
||||
case l.match("op", tok.Operator):
|
||||
case l.match("enum", tok.Enum):
|
||||
case l.match("type", tok.Type):
|
||||
case l.match("ctor", tok.Constructor):
|
||||
case l.match("conv", tok.Converter):
|
||||
case l.match("match", tok.Match):
|
||||
case unicode.IsLetter(l.peek(0)) || l.peek(0) == '_':
|
||||
l.tok(l.count(alphaNumericOrUnderscore), tok.Identifier)
|
||||
case unicode.IsNumber(l.peek(0)):
|
||||
l.tok(l.count(unicode.IsNumber), tok.Integer)
|
||||
case l.match("&&", tok.AndAnd):
|
||||
case l.match("&", tok.And):
|
||||
case l.match("||", tok.OrOr):
|
||||
case l.match("|", tok.Or):
|
||||
case l.match("!=", tok.NotEqual):
|
||||
case l.match("!", tok.Not):
|
||||
case l.match("==", tok.Equal):
|
||||
case l.match("=", tok.Assign):
|
||||
case l.match("<<", tok.Shl):
|
||||
case l.match("<=", tok.Le):
|
||||
case l.match("<", tok.Lt):
|
||||
case l.match(">=", tok.Ge):
|
||||
case l.match(">>", tok.Shr):
|
||||
case l.match(">", tok.Gt):
|
||||
default:
|
||||
return fmt.Errorf("%v: unexpected '%v'", l.loc, string(l.runes[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// next() consumes and returns the next rune in the source, or 0 if reached EOF
|
||||
func (l *lexer) next() rune {
|
||||
if len(l.runes) > 0 {
|
||||
r := l.runes[0]
|
||||
l.runes = l.runes[1:]
|
||||
l.loc.Rune++
|
||||
if r == '\n' {
|
||||
l.loc.Line++
|
||||
l.loc.Column = 1
|
||||
} else {
|
||||
l.loc.Column++
|
||||
}
|
||||
return r
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// skip() consumes the next `n` runes in the source
|
||||
func (l *lexer) skip(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
l.next()
|
||||
}
|
||||
}
|
||||
|
||||
// peek() returns the rune `i` runes ahead of the current position
|
||||
func (l *lexer) peek(i int) rune {
|
||||
if i >= len(l.runes) {
|
||||
return 0
|
||||
}
|
||||
return l.runes[i]
|
||||
}
|
||||
|
||||
// predicate is a function that can be passed to count()
|
||||
type predicate func(r rune) bool
|
||||
|
||||
// count() returns the number of sequential runes from the current position that
|
||||
// match the predicate `p`
|
||||
func (l *lexer) count(p predicate) int {
|
||||
for i := 0; i < len(l.runes); i++ {
|
||||
if !p(l.peek(i)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(l.runes)
|
||||
}
|
||||
|
||||
// tok() appends a new token of kind `k` using the next `n` runes.
|
||||
// The next `n` runes are consumed by tok().
|
||||
func (l *lexer) tok(n int, k tok.Kind) {
|
||||
start := l.loc
|
||||
runes := l.runes[:n]
|
||||
l.skip(n)
|
||||
end := l.loc
|
||||
|
||||
src := tok.Source{S: start, E: end}
|
||||
l.tokens = append(l.tokens, tok.Token{Kind: k, Source: src, Runes: runes})
|
||||
}
|
||||
|
||||
// match() checks whether the next runes are equal to `s`. If they are, then
|
||||
// these runes are used to append a new token of kind `k`, and match() returns
|
||||
// true. If the next runes are not equal to `s` then false is returned, and no
|
||||
// runes are consumed.
|
||||
func (l *lexer) match(s string, kind tok.Kind) bool {
|
||||
runes := []rune(s)
|
||||
if len(l.runes) < len(runes) {
|
||||
return false
|
||||
}
|
||||
for i, r := range runes {
|
||||
if l.runes[i] != r {
|
||||
return false
|
||||
}
|
||||
}
|
||||
l.tok(len(runes), kind)
|
||||
return true
|
||||
}
|
||||
|
||||
// toFirst() returns a predicate that returns true if the rune is not in `runes`
|
||||
// toFirst() is intended to be used with count(), so `count(toFirst('x'))` will
|
||||
// count up to, but not including the number of consecutive runes that are not
|
||||
// 'x'.
|
||||
func toFirst(runes ...rune) predicate {
|
||||
return func(r rune) bool {
|
||||
for _, t := range runes {
|
||||
if t == r {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// alphaNumericOrUnderscore() returns true if the rune `r` is a number, letter
|
||||
// or underscore.
|
||||
func alphaNumericOrUnderscore(r rune) bool {
|
||||
return r == '_' || unicode.IsLetter(r) || unicode.IsNumber(r)
|
||||
}
|
||||
202
tools/src/tint/intrinsic/lexer/lexer_test.go
Normal file
202
tools/src/tint/intrinsic/lexer/lexer_test.go
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package lexer_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/lexer"
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/tok"
|
||||
)
|
||||
|
||||
func TestLexTokens(t *testing.T) {
|
||||
type test struct {
|
||||
src string
|
||||
expect tok.Token
|
||||
}
|
||||
|
||||
filepath := "test.txt"
|
||||
loc := func(l, c, r int) tok.Location {
|
||||
return tok.Location{Line: l, Column: c, Rune: r, Filepath: filepath}
|
||||
}
|
||||
|
||||
for _, test := range []test{
|
||||
{"ident", tok.Token{Kind: tok.Identifier, Runes: []rune("ident"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 6, 5),
|
||||
}}},
|
||||
{"ident_123", tok.Token{Kind: tok.Identifier, Runes: []rune("ident_123"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 10, 9),
|
||||
}}},
|
||||
{"_ident_", tok.Token{Kind: tok.Identifier, Runes: []rune("_ident_"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 8, 7),
|
||||
}}},
|
||||
{"123456789", tok.Token{Kind: tok.Integer, Runes: []rune("123456789"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 10, 9),
|
||||
}}},
|
||||
{"match", tok.Token{Kind: tok.Match, Runes: []rune("match"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 6, 5),
|
||||
}}},
|
||||
{"fn", tok.Token{Kind: tok.Function, Runes: []rune("fn"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{"op", tok.Token{Kind: tok.Operator, Runes: []rune("op"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{"type", tok.Token{Kind: tok.Type, Runes: []rune("type"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 5, 4),
|
||||
}}},
|
||||
{"ctor", tok.Token{Kind: tok.Constructor, Runes: []rune("ctor"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 5, 4),
|
||||
}}},
|
||||
{"conv", tok.Token{Kind: tok.Converter, Runes: []rune("conv"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 5, 4),
|
||||
}}},
|
||||
{"enum", tok.Token{Kind: tok.Enum, Runes: []rune("enum"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 5, 4),
|
||||
}}},
|
||||
{":", tok.Token{Kind: tok.Colon, Runes: []rune(":"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{",", tok.Token{Kind: tok.Comma, Runes: []rune(","), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"<", tok.Token{Kind: tok.Lt, Runes: []rune("<"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{">", tok.Token{Kind: tok.Gt, Runes: []rune(">"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"{", tok.Token{Kind: tok.Lbrace, Runes: []rune("{"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"}", tok.Token{Kind: tok.Rbrace, Runes: []rune("}"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"&&", tok.Token{Kind: tok.AndAnd, Runes: []rune("&&"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{"&", tok.Token{Kind: tok.And, Runes: []rune("&"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"||", tok.Token{Kind: tok.OrOr, Runes: []rune("||"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{"|", tok.Token{Kind: tok.Or, Runes: []rune("|"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"!", tok.Token{Kind: tok.Not, Runes: []rune("!"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"!=", tok.Token{Kind: tok.NotEqual, Runes: []rune("!="), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{"==", tok.Token{Kind: tok.Equal, Runes: []rune("=="), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{"=", tok.Token{Kind: tok.Assign, Runes: []rune("="), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"<<", tok.Token{Kind: tok.Shl, Runes: []rune("<<"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{"<=", tok.Token{Kind: tok.Le, Runes: []rune("<="), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{"<", tok.Token{Kind: tok.Lt, Runes: []rune("<"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{">=", tok.Token{Kind: tok.Ge, Runes: []rune(">="), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{">>", tok.Token{Kind: tok.Shr, Runes: []rune(">>"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{">", tok.Token{Kind: tok.Gt, Runes: []rune(">"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"@", tok.Token{Kind: tok.Attr, Runes: []rune("@"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"(", tok.Token{Kind: tok.Lparen, Runes: []rune("("), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{")", tok.Token{Kind: tok.Rparen, Runes: []rune(")"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"|", tok.Token{Kind: tok.Or, Runes: []rune("|"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"*", tok.Token{Kind: tok.Star, Runes: []rune("*"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{"->", tok.Token{Kind: tok.Arrow, Runes: []rune("->"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
{"x // y ", tok.Token{Kind: tok.Identifier, Runes: []rune("x"), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 2, 1),
|
||||
}}},
|
||||
{`"abc"`, tok.Token{Kind: tok.String, Runes: []rune("abc"), Source: tok.Source{
|
||||
S: loc(1, 2, 1), E: loc(1, 5, 4),
|
||||
}}},
|
||||
{`
|
||||
//
|
||||
ident
|
||||
|
||||
`, tok.Token{Kind: tok.Identifier, Runes: []rune("ident"), Source: tok.Source{
|
||||
S: loc(3, 4, 10), E: loc(3, 9, 15),
|
||||
}}},
|
||||
} {
|
||||
got, err := lexer.Lex([]rune(test.src), filepath)
|
||||
name := fmt.Sprintf(`Lex("%v")`, test.src)
|
||||
switch {
|
||||
case err != nil:
|
||||
t.Errorf("%v returned error: %v", name, err)
|
||||
case len(got) != 1:
|
||||
t.Errorf("%v returned %d tokens: %v", name, len(got), got)
|
||||
case got[0].Kind != test.expect.Kind:
|
||||
t.Errorf(`%v returned unexpected token kind: got "%+v", expected "%+v"`, name, got[0], test.expect)
|
||||
case string(got[0].Runes) != string(test.expect.Runes):
|
||||
t.Errorf(`%v returned unexpected token runes: got "%+v", expected "%+v"`, name, string(got[0].Runes), string(test.expect.Runes))
|
||||
case got[0].Source != test.expect.Source:
|
||||
t.Errorf(`%v returned unexpected token source: got %+v, expected %+v`, name, got[0].Source, test.expect.Source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrors(t *testing.T) {
|
||||
type test struct {
|
||||
src string
|
||||
expect string
|
||||
}
|
||||
|
||||
for _, test := range []test{
|
||||
{" \"abc", "test.txt:1:2 unterminated string"},
|
||||
{" \"abc\n", "test.txt:1:2 unterminated string"},
|
||||
{"£", "test.txt:1:1: unexpected '£'"},
|
||||
} {
|
||||
got, err := lexer.Lex([]rune(test.src), "test.txt")
|
||||
gotErr := "<nil>"
|
||||
if err != nil {
|
||||
gotErr = err.Error()
|
||||
}
|
||||
if test.expect != gotErr {
|
||||
t.Errorf(`Lex() returned error "%+v", expected error "%+v"`, gotErr, test.expect)
|
||||
}
|
||||
if got != nil {
|
||||
t.Errorf("Lex() returned non-nil for error")
|
||||
}
|
||||
}
|
||||
}
|
||||
376
tools/src/tint/intrinsic/parser/parser.go
Normal file
376
tools/src/tint/intrinsic/parser/parser.go
Normal file
@@ -0,0 +1,376 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package parser provides a basic parser for the Tint builtin definition
|
||||
// language
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/ast"
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/lexer"
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/tok"
|
||||
)
|
||||
|
||||
// Parse produces a list of tokens for the given source code
|
||||
func Parse(source, filepath string) (*ast.AST, error) {
|
||||
runes := []rune(source)
|
||||
tokens, err := lexer.Lex(runes, filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := parser{tokens: tokens}
|
||||
return p.parse()
|
||||
}
|
||||
|
||||
type parser struct {
|
||||
tokens []tok.Token
|
||||
err error
|
||||
}
|
||||
|
||||
func (p *parser) parse() (*ast.AST, error) {
|
||||
out := ast.AST{}
|
||||
var attributes ast.Attributes
|
||||
for p.err == nil {
|
||||
t := p.peek(0)
|
||||
if t == nil {
|
||||
break
|
||||
}
|
||||
switch t.Kind {
|
||||
case tok.Attr:
|
||||
attributes = append(attributes, p.attributes()...)
|
||||
case tok.Enum:
|
||||
if len(attributes) > 0 {
|
||||
p.err = fmt.Errorf("%v unexpected attribute", attributes[0].Source)
|
||||
}
|
||||
out.Enums = append(out.Enums, p.enumDecl())
|
||||
case tok.Match:
|
||||
if len(attributes) > 0 {
|
||||
p.err = fmt.Errorf("%v unexpected attribute", attributes[0].Source)
|
||||
}
|
||||
out.Matchers = append(out.Matchers, p.matcherDecl())
|
||||
case tok.Type:
|
||||
out.Types = append(out.Types, p.typeDecl(attributes))
|
||||
attributes = nil
|
||||
case tok.Function:
|
||||
out.Builtins = append(out.Builtins, p.builtinDecl(attributes))
|
||||
attributes = nil
|
||||
case tok.Operator:
|
||||
out.Operators = append(out.Operators, p.operatorDecl(attributes))
|
||||
attributes = nil
|
||||
case tok.Constructor:
|
||||
out.Constructors = append(out.Constructors, p.constructorDecl(attributes))
|
||||
attributes = nil
|
||||
case tok.Converter:
|
||||
out.Converters = append(out.Converters, p.converterDecl(attributes))
|
||||
attributes = nil
|
||||
default:
|
||||
p.err = fmt.Errorf("%v unexpected token '%v'", t.Source, t.Kind)
|
||||
}
|
||||
if p.err != nil {
|
||||
return nil, p.err
|
||||
}
|
||||
}
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func (p *parser) enumDecl() ast.EnumDecl {
|
||||
p.expect(tok.Enum, "enum declaration")
|
||||
name := p.expect(tok.Identifier, "enum name")
|
||||
e := ast.EnumDecl{Source: name.Source, Name: string(name.Runes)}
|
||||
p.expect(tok.Lbrace, "enum declaration")
|
||||
for p.err == nil && p.match(tok.Rbrace) == nil {
|
||||
e.Entries = append(e.Entries, p.enumEntry())
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (p *parser) enumEntry() ast.EnumEntry {
|
||||
decos := p.attributes()
|
||||
name := p.expect(tok.Identifier, "enum entry")
|
||||
return ast.EnumEntry{Source: name.Source, Attributes: decos, Name: string(name.Runes)}
|
||||
}
|
||||
|
||||
func (p *parser) matcherDecl() ast.MatcherDecl {
|
||||
p.expect(tok.Match, "matcher declaration")
|
||||
name := p.expect(tok.Identifier, "matcher name")
|
||||
m := ast.MatcherDecl{Source: name.Source, Name: string(name.Runes)}
|
||||
p.expect(tok.Colon, "matcher declaration")
|
||||
for p.err == nil {
|
||||
m.Options = append(m.Options, p.templatedName())
|
||||
if p.match(tok.Or) == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (p *parser) typeDecl(decos ast.Attributes) ast.TypeDecl {
|
||||
p.expect(tok.Type, "type declaration")
|
||||
name := p.expect(tok.Identifier, "type name")
|
||||
m := ast.TypeDecl{
|
||||
Source: name.Source,
|
||||
Attributes: decos,
|
||||
Name: string(name.Runes),
|
||||
}
|
||||
if p.peekIs(0, tok.Lt) {
|
||||
m.TemplateParams = p.templateParams()
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (p *parser) attributes() ast.Attributes {
|
||||
var out ast.Attributes
|
||||
for p.match(tok.Attr) != nil && p.err == nil {
|
||||
name := p.expect(tok.Identifier, "attribute name")
|
||||
values := []string{}
|
||||
if p.match(tok.Lparen) != nil {
|
||||
for p.err == nil {
|
||||
values = append(values, string(p.next().Runes))
|
||||
if p.match(tok.Comma) == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
p.expect(tok.Rparen, "attribute values")
|
||||
}
|
||||
out = append(out, ast.Attribute{
|
||||
Source: name.Source,
|
||||
Name: string(name.Runes),
|
||||
Values: values,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (p *parser) builtinDecl(decos ast.Attributes) ast.IntrinsicDecl {
|
||||
p.expect(tok.Function, "function declaration")
|
||||
name := p.expect(tok.Identifier, "function name")
|
||||
f := ast.IntrinsicDecl{
|
||||
Source: name.Source,
|
||||
Kind: ast.Builtin,
|
||||
Attributes: decos,
|
||||
Name: string(name.Runes),
|
||||
}
|
||||
if p.peekIs(0, tok.Lt) {
|
||||
f.TemplateParams = p.templateParams()
|
||||
}
|
||||
f.Parameters = p.parameters()
|
||||
if p.match(tok.Arrow) != nil {
|
||||
ret := p.templatedName()
|
||||
f.ReturnType = &ret
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (p *parser) operatorDecl(decos ast.Attributes) ast.IntrinsicDecl {
|
||||
p.expect(tok.Operator, "operator declaration")
|
||||
name := p.next()
|
||||
f := ast.IntrinsicDecl{
|
||||
Source: name.Source,
|
||||
Kind: ast.Operator,
|
||||
Attributes: decos,
|
||||
Name: string(name.Runes),
|
||||
}
|
||||
if p.peekIs(0, tok.Lt) {
|
||||
f.TemplateParams = p.templateParams()
|
||||
}
|
||||
f.Parameters = p.parameters()
|
||||
if p.match(tok.Arrow) != nil {
|
||||
ret := p.templatedName()
|
||||
f.ReturnType = &ret
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (p *parser) constructorDecl(decos ast.Attributes) ast.IntrinsicDecl {
|
||||
p.expect(tok.Constructor, "constructor declaration")
|
||||
name := p.next()
|
||||
f := ast.IntrinsicDecl{
|
||||
Source: name.Source,
|
||||
Kind: ast.Constructor,
|
||||
Attributes: decos,
|
||||
Name: string(name.Runes),
|
||||
}
|
||||
if p.peekIs(0, tok.Lt) {
|
||||
f.TemplateParams = p.templateParams()
|
||||
}
|
||||
f.Parameters = p.parameters()
|
||||
if p.match(tok.Arrow) != nil {
|
||||
ret := p.templatedName()
|
||||
f.ReturnType = &ret
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (p *parser) converterDecl(decos ast.Attributes) ast.IntrinsicDecl {
|
||||
p.expect(tok.Converter, "converter declaration")
|
||||
name := p.next()
|
||||
f := ast.IntrinsicDecl{
|
||||
Source: name.Source,
|
||||
Kind: ast.Converter,
|
||||
Attributes: decos,
|
||||
Name: string(name.Runes),
|
||||
}
|
||||
if p.peekIs(0, tok.Lt) {
|
||||
f.TemplateParams = p.templateParams()
|
||||
}
|
||||
f.Parameters = p.parameters()
|
||||
if p.match(tok.Arrow) != nil {
|
||||
ret := p.templatedName()
|
||||
f.ReturnType = &ret
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (p *parser) parameters() ast.Parameters {
|
||||
l := ast.Parameters{}
|
||||
p.expect(tok.Lparen, "function parameter list")
|
||||
if p.match(tok.Rparen) == nil {
|
||||
for p.err == nil {
|
||||
l = append(l, p.parameter())
|
||||
if p.match(tok.Comma) == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
p.expect(tok.Rparen, "function parameter list")
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (p *parser) parameter() ast.Parameter {
|
||||
attributes := p.attributes()
|
||||
if p.peekIs(1, tok.Colon) {
|
||||
// name type
|
||||
name := p.expect(tok.Identifier, "parameter name")
|
||||
p.expect(tok.Colon, "parameter type")
|
||||
return ast.Parameter{
|
||||
Source: name.Source,
|
||||
Name: string(name.Runes),
|
||||
Attributes: attributes,
|
||||
Type: p.templatedName(),
|
||||
}
|
||||
}
|
||||
// type
|
||||
ty := p.templatedName()
|
||||
return ast.Parameter{
|
||||
Source: ty.Source,
|
||||
Attributes: attributes,
|
||||
Type: ty,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) string() string {
|
||||
s := p.expect(tok.String, "string")
|
||||
return string(s.Runes)
|
||||
}
|
||||
|
||||
func (p *parser) templatedName() ast.TemplatedName {
|
||||
name := p.expect(tok.Identifier, "type name")
|
||||
m := ast.TemplatedName{Source: name.Source, Name: string(name.Runes)}
|
||||
if p.match(tok.Lt) != nil {
|
||||
for p.err == nil {
|
||||
m.TemplateArgs = append(m.TemplateArgs, p.templatedName())
|
||||
if p.match(tok.Comma) == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
p.expect(tok.Gt, "template argument type list")
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (p *parser) templateParams() ast.TemplateParams {
|
||||
t := ast.TemplateParams{}
|
||||
p.expect(tok.Lt, "template parameter list")
|
||||
for p.err == nil && p.peekIs(0, tok.Identifier) {
|
||||
t = append(t, p.templateParam())
|
||||
}
|
||||
p.expect(tok.Gt, "template parameter list")
|
||||
return t
|
||||
}
|
||||
|
||||
func (p *parser) templateParam() ast.TemplateParam {
|
||||
name := p.match(tok.Identifier)
|
||||
t := ast.TemplateParam{
|
||||
Source: name.Source,
|
||||
Name: string(name.Runes),
|
||||
}
|
||||
if p.match(tok.Colon) != nil {
|
||||
t.Type = p.templatedName()
|
||||
}
|
||||
p.match(tok.Comma)
|
||||
return t
|
||||
}
|
||||
|
||||
func (p *parser) expect(kind tok.Kind, use string) tok.Token {
|
||||
if p.err != nil {
|
||||
return tok.Invalid
|
||||
}
|
||||
t := p.match(kind)
|
||||
if t == nil {
|
||||
if len(p.tokens) > 0 {
|
||||
p.err = fmt.Errorf("%v expected '%v' for %v, got '%v'",
|
||||
p.tokens[0].Source, kind, use, p.tokens[0].Kind)
|
||||
} else {
|
||||
p.err = fmt.Errorf("expected '%v' for %v, but reached end of file", kind, use)
|
||||
}
|
||||
return tok.Invalid
|
||||
}
|
||||
return *t
|
||||
}
|
||||
|
||||
func (p *parser) ident(use string) string {
|
||||
return string(p.expect(tok.Identifier, use).Runes)
|
||||
}
|
||||
|
||||
func (p *parser) match(kind tok.Kind) *tok.Token {
|
||||
if p.err != nil || len(p.tokens) == 0 {
|
||||
return nil
|
||||
}
|
||||
t := p.tokens[0]
|
||||
if t.Kind != kind {
|
||||
return nil
|
||||
}
|
||||
p.tokens = p.tokens[1:]
|
||||
return &t
|
||||
}
|
||||
|
||||
func (p *parser) next() *tok.Token {
|
||||
if p.err != nil {
|
||||
return nil
|
||||
}
|
||||
if len(p.tokens) == 0 {
|
||||
p.err = fmt.Errorf("reached end of file")
|
||||
}
|
||||
t := p.tokens[0]
|
||||
p.tokens = p.tokens[1:]
|
||||
return &t
|
||||
}
|
||||
|
||||
func (p *parser) peekIs(i int, kind tok.Kind) bool {
|
||||
t := p.peek(i)
|
||||
if t == nil {
|
||||
return false
|
||||
}
|
||||
return t.Kind == kind
|
||||
}
|
||||
|
||||
func (p *parser) peek(i int) *tok.Token {
|
||||
if len(p.tokens) <= i {
|
||||
return nil
|
||||
}
|
||||
return &p.tokens[i]
|
||||
}
|
||||
705
tools/src/tint/intrinsic/parser/parser_test.go
Normal file
705
tools/src/tint/intrinsic/parser/parser_test.go
Normal file
@@ -0,0 +1,705 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package parser_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/ast"
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/parser"
|
||||
"dawn.googlesource.com/dawn/tools/src/utils"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
var ignoreSource = cmp.FilterPath(func(p cmp.Path) bool {
|
||||
return p.Last().String() == ".Source"
|
||||
}, cmp.Ignore())
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
type test struct {
|
||||
location string
|
||||
src string
|
||||
expect ast.AST
|
||||
}
|
||||
|
||||
for _, test := range []test{
|
||||
{
|
||||
utils.ThisLine(),
|
||||
"enum E {}",
|
||||
ast.AST{
|
||||
Enums: []ast.EnumDecl{{Name: "E"}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"enum E { A @attr B C }",
|
||||
ast.AST{
|
||||
Enums: []ast.EnumDecl{{
|
||||
Name: "E",
|
||||
Entries: []ast.EnumEntry{
|
||||
{Name: "A"},
|
||||
{
|
||||
Attributes: ast.Attributes{{
|
||||
Name: "attr",
|
||||
Values: []string{},
|
||||
}},
|
||||
Name: "B",
|
||||
},
|
||||
{Name: "C"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"type T",
|
||||
ast.AST{
|
||||
Types: []ast.TypeDecl{{Name: "T"}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"type T<A, B, C>",
|
||||
ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Name: "T",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{Name: "A"},
|
||||
{Name: "B"},
|
||||
{Name: "C"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"@attr type T",
|
||||
ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Attributes: ast.Attributes{
|
||||
{Name: "attr", Values: []string{}},
|
||||
},
|
||||
Name: "T",
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"@attr_a @attr_b type T",
|
||||
ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Attributes: ast.Attributes{
|
||||
{Name: "attr_a", Values: []string{}},
|
||||
{Name: "attr_b", Values: []string{}},
|
||||
},
|
||||
Name: "T",
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
`@attr("a", "b") type T`, ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Attributes: ast.Attributes{
|
||||
{Name: "attr", Values: []string{"a", "b"}},
|
||||
},
|
||||
Name: "T",
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
`@attr(1, "x") type T`, ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Attributes: ast.Attributes{
|
||||
{Name: "attr", Values: []string{"1", "x"}},
|
||||
},
|
||||
Name: "T",
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"match M : A",
|
||||
ast.AST{
|
||||
Matchers: []ast.MatcherDecl{{
|
||||
Name: "M",
|
||||
Options: ast.MatcherOptions{
|
||||
ast.TemplatedName{Name: "A"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"match M : A | B",
|
||||
ast.AST{
|
||||
Matchers: []ast.MatcherDecl{{
|
||||
Name: "M",
|
||||
Options: ast.MatcherOptions{
|
||||
ast.TemplatedName{Name: "A"},
|
||||
ast.TemplatedName{Name: "B"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"fn F()",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"@attr fn F()",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
Attributes: ast.Attributes{
|
||||
{Name: "attr", Values: []string{}},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"fn F(a)",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"fn F(a: T)",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "T"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"fn F(a, b)",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
{Type: ast.TemplatedName{Name: "b"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"fn F<A : B<C> >()",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{
|
||||
Name: "A", Type: ast.TemplatedName{
|
||||
Name: "B",
|
||||
TemplateArgs: ast.TemplatedNames{
|
||||
{Name: "C"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"fn F<T>(a: X, b: Y<T>)",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{Name: "T"},
|
||||
},
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "X"}},
|
||||
{Name: "b", Type: ast.TemplatedName{
|
||||
Name: "Y",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"fn F() -> X",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{Name: "X"},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"fn F() -> X<T>",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{
|
||||
Name: "X",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"op F()",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"@attr op F()",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
Attributes: ast.Attributes{
|
||||
{Name: "attr", Values: []string{}},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"op F(a)",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"op F(@blah a)",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{
|
||||
Attributes: ast.Attributes{{Name: "blah", Values: []string{}}},
|
||||
Type: ast.TemplatedName{Name: "a"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"op F(a: T)",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "T"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"op F(a, b)",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
{Type: ast.TemplatedName{Name: "b"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"op F<A : B<C> >()",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{
|
||||
Name: "A", Type: ast.TemplatedName{
|
||||
Name: "B",
|
||||
TemplateArgs: ast.TemplatedNames{
|
||||
{Name: "C"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"op F<T>(a: X, b: Y<T>)",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{Name: "T"},
|
||||
},
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "X"}},
|
||||
{Name: "b", Type: ast.TemplatedName{
|
||||
Name: "Y",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"op F() -> X",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{Name: "X"},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"op F() -> X<T>",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{
|
||||
Name: "X",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"ctor F()",
|
||||
ast.AST{
|
||||
Constructors: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Constructor,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"@attr ctor F()",
|
||||
ast.AST{
|
||||
Constructors: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Constructor,
|
||||
Name: "F",
|
||||
Attributes: ast.Attributes{
|
||||
{Name: "attr", Values: []string{}},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"ctor F(a)",
|
||||
ast.AST{
|
||||
Constructors: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Constructor,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"ctor F(a: T)",
|
||||
ast.AST{
|
||||
Constructors: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Constructor,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "T"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"ctor F(a, b)",
|
||||
ast.AST{
|
||||
Constructors: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Constructor,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
{Type: ast.TemplatedName{Name: "b"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"ctor F<A : B<C> >()",
|
||||
ast.AST{
|
||||
Constructors: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Constructor,
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{
|
||||
Name: "A", Type: ast.TemplatedName{
|
||||
Name: "B",
|
||||
TemplateArgs: ast.TemplatedNames{
|
||||
{Name: "C"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"ctor F<T>(a: X, b: Y<T>)",
|
||||
ast.AST{
|
||||
Constructors: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Constructor,
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{Name: "T"},
|
||||
},
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "X"}},
|
||||
{Name: "b", Type: ast.TemplatedName{
|
||||
Name: "Y",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"ctor F() -> X",
|
||||
ast.AST{
|
||||
Constructors: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Constructor,
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{Name: "X"},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"ctor F() -> X<T>",
|
||||
ast.AST{
|
||||
Constructors: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Constructor,
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{
|
||||
Name: "X",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"conv F()",
|
||||
ast.AST{
|
||||
Converters: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Converter,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"@attr conv F()",
|
||||
ast.AST{
|
||||
Converters: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Converter,
|
||||
Name: "F",
|
||||
Attributes: ast.Attributes{
|
||||
{Name: "attr", Values: []string{}},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"conv F(a)",
|
||||
ast.AST{
|
||||
Converters: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Converter,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"conv F(a: T)",
|
||||
ast.AST{
|
||||
Converters: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Converter,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "T"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"conv F(a, b)",
|
||||
ast.AST{
|
||||
Converters: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Converter,
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
{Type: ast.TemplatedName{Name: "b"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"conv F<A : B<C> >()",
|
||||
ast.AST{
|
||||
Converters: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Converter,
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{
|
||||
Name: "A", Type: ast.TemplatedName{
|
||||
Name: "B",
|
||||
TemplateArgs: ast.TemplatedNames{
|
||||
{Name: "C"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"conv F<T>(a: X, b: Y<T>)",
|
||||
ast.AST{
|
||||
Converters: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Converter,
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{Name: "T"},
|
||||
},
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "X"}},
|
||||
{Name: "b", Type: ast.TemplatedName{
|
||||
Name: "Y",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"conv F() -> X",
|
||||
ast.AST{
|
||||
Converters: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Converter,
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{Name: "X"},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"conv F() -> X<T>",
|
||||
ast.AST{
|
||||
Converters: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Converter,
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{
|
||||
Name: "X",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
},
|
||||
Parameters: ast.Parameters{},
|
||||
}},
|
||||
}},
|
||||
} {
|
||||
got, err := parser.Parse(test.src, "file.txt")
|
||||
if err != nil {
|
||||
t.Errorf("\n%v\nWhile parsing:\n%s\nParse() returned error: %v",
|
||||
test.location, test.src, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(got, &test.expect, ignoreSource); diff != "" {
|
||||
t.Errorf("\n%v\nWhile parsing:\n%s\n\n%s",
|
||||
test.location, test.src, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrors(t *testing.T) {
|
||||
type test struct {
|
||||
src string
|
||||
expect string
|
||||
}
|
||||
|
||||
for _, test := range []test{
|
||||
{
|
||||
"£",
|
||||
"test.txt:1:1: unexpected '£'",
|
||||
},
|
||||
{
|
||||
"123",
|
||||
"test.txt:1:1 unexpected token 'integer'",
|
||||
},
|
||||
{
|
||||
"@123",
|
||||
"test.txt:1:2 expected 'ident' for attribute name, got 'integer'",
|
||||
},
|
||||
} {
|
||||
got, err := parser.Parse(test.src, "test.txt")
|
||||
gotErr := ""
|
||||
if err != nil {
|
||||
gotErr = err.Error()
|
||||
}
|
||||
if test.expect != gotErr {
|
||||
t.Errorf(`Parse() returned error "%+v", expected error "%+v"`, gotErr, test.expect)
|
||||
}
|
||||
if got != nil {
|
||||
t.Errorf("Lex() returned non-nil for error")
|
||||
}
|
||||
}
|
||||
}
|
||||
738
tools/src/tint/intrinsic/resolver/resolve.go
Normal file
738
tools/src/tint/intrinsic/resolver/resolve.go
Normal file
@@ -0,0 +1,738 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/ast"
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/sem"
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/tok"
|
||||
)
|
||||
|
||||
type resolver struct {
|
||||
a *ast.AST
|
||||
s *sem.Sem
|
||||
|
||||
globals scope
|
||||
builtins map[string]*sem.Intrinsic
|
||||
unaryOperators map[string]*sem.Intrinsic
|
||||
binaryOperators map[string]*sem.Intrinsic
|
||||
constructorsAndConverters map[string]*sem.Intrinsic
|
||||
enumEntryMatchers map[*sem.EnumEntry]*sem.EnumMatcher
|
||||
}
|
||||
|
||||
// Resolve processes the AST
|
||||
func Resolve(a *ast.AST) (*sem.Sem, error) {
|
||||
r := resolver{
|
||||
a: a,
|
||||
s: sem.New(),
|
||||
globals: newScope(nil),
|
||||
builtins: map[string]*sem.Intrinsic{},
|
||||
unaryOperators: map[string]*sem.Intrinsic{},
|
||||
binaryOperators: map[string]*sem.Intrinsic{},
|
||||
constructorsAndConverters: map[string]*sem.Intrinsic{},
|
||||
enumEntryMatchers: map[*sem.EnumEntry]*sem.EnumMatcher{},
|
||||
}
|
||||
// Declare and resolve all the enumerators
|
||||
for _, e := range a.Enums {
|
||||
if err := r.enum(e); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Declare and resolve all the ty types
|
||||
for _, p := range a.Types {
|
||||
if err := r.ty(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Declare and resolve the type matchers
|
||||
for _, m := range a.Matchers {
|
||||
if err := r.matcher(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Declare and resolve the builtins
|
||||
for _, f := range a.Builtins {
|
||||
if err := r.intrinsic(f, r.builtins, &r.s.Builtins); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Declare and resolve the unary and binary operators
|
||||
for _, o := range a.Operators {
|
||||
switch len(o.Parameters) {
|
||||
case 1:
|
||||
if err := r.intrinsic(o, r.unaryOperators, &r.s.UnaryOperators); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case 2:
|
||||
if err := r.intrinsic(o, r.binaryOperators, &r.s.BinaryOperators); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("%v operators must have either 1 or 2 parameters", o.Source)
|
||||
}
|
||||
}
|
||||
|
||||
// Declare and resolve type constructors and converters
|
||||
for _, c := range a.Constructors {
|
||||
if err := r.intrinsic(c, r.constructorsAndConverters, &r.s.ConstructorsAndConverters); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, c := range a.Converters {
|
||||
if len(c.Parameters) != 1 {
|
||||
return nil, fmt.Errorf("%v conversions must have a single parameter", c.Source)
|
||||
}
|
||||
if err := r.intrinsic(c, r.constructorsAndConverters, &r.s.ConstructorsAndConverters); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the unique parameter names
|
||||
r.s.UniqueParameterNames = r.calculateUniqueParameterNames()
|
||||
|
||||
return r.s, nil
|
||||
}
|
||||
|
||||
// enum() resolves an enum declaration.
|
||||
// The resulting sem.Enum is appended to Sem.Enums, and the enum and all its
|
||||
// entries are registered with the global scope.
|
||||
func (r *resolver) enum(e ast.EnumDecl) error {
|
||||
s := &sem.Enum{
|
||||
Decl: e,
|
||||
Name: e.Name,
|
||||
}
|
||||
|
||||
// Register the enum
|
||||
r.s.Enums = append(r.s.Enums, s)
|
||||
if err := r.globals.declare(s, e.Source); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Register each of the enum entries
|
||||
for _, ast := range e.Entries {
|
||||
entry := &sem.EnumEntry{
|
||||
Name: ast.Name,
|
||||
Enum: s,
|
||||
}
|
||||
if internal := ast.Attributes.Take("internal"); internal != nil {
|
||||
entry.IsInternal = true
|
||||
if len(internal.Values) != 0 {
|
||||
return fmt.Errorf("%v unexpected value for internal attribute", ast.Source)
|
||||
}
|
||||
}
|
||||
if len(ast.Attributes) != 0 {
|
||||
return fmt.Errorf("%v unknown attribute", ast.Attributes[0].Source)
|
||||
}
|
||||
if err := r.globals.declare(entry, e.Source); err != nil {
|
||||
return err
|
||||
}
|
||||
s.Entries = append(s.Entries, entry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ty() resolves a type declaration.
|
||||
// The resulting sem.Type is appended to Sem.Types, and the type is registered
|
||||
// with the global scope.
|
||||
func (r *resolver) ty(a ast.TypeDecl) error {
|
||||
t := &sem.Type{
|
||||
Decl: a,
|
||||
Name: a.Name,
|
||||
}
|
||||
|
||||
// Register the type
|
||||
r.s.Types = append(r.s.Types, t)
|
||||
if err := r.globals.declare(t, a.Source); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a new scope for resolving template parameters
|
||||
s := newScope(&r.globals)
|
||||
|
||||
// Resolve the type template parameters
|
||||
templateParams, err := r.templateParams(&s, a.TemplateParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.TemplateParams = templateParams
|
||||
|
||||
// Scan for attributes
|
||||
if d := a.Attributes.Take("display"); d != nil {
|
||||
if len(d.Values) != 1 {
|
||||
return fmt.Errorf("%v expected a single value for 'display' attribute", d.Source)
|
||||
}
|
||||
t.DisplayName = d.Values[0]
|
||||
}
|
||||
if d := a.Attributes.Take("precedence"); d != nil {
|
||||
if len(d.Values) != 1 {
|
||||
return fmt.Errorf("%v expected a single integer value for 'precedence' attribute", d.Source)
|
||||
}
|
||||
n, err := strconv.Atoi(d.Values[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v %v", d.Source, err)
|
||||
}
|
||||
t.Precedence = n
|
||||
}
|
||||
|
||||
if len(a.Attributes) != 0 {
|
||||
return fmt.Errorf("%v unknown attribute", a.Attributes[0].Source)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// matcher() resolves a match declaration to either a sem.TypeMatcher or
|
||||
// sem.EnumMatcher.
|
||||
// The resulting matcher is appended to either Sem.TypeMatchers or
|
||||
// Sem.EnumMatchers, and is registered with the global scope.
|
||||
func (r *resolver) matcher(a ast.MatcherDecl) error {
|
||||
// Determine whether this is a type matcher or enum matcher by resolving the
|
||||
// first option
|
||||
firstOption, err := r.lookupNamed(&r.globals, a.Options[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Resolve to a sem.TypeMatcher or a sem.EnumMatcher
|
||||
switch firstOption := firstOption.(type) {
|
||||
case *sem.Type:
|
||||
options := map[sem.Named]tok.Source{}
|
||||
m := &sem.TypeMatcher{
|
||||
Decl: a,
|
||||
Name: a.Name,
|
||||
}
|
||||
|
||||
// Register the matcher
|
||||
r.s.TypeMatchers = append(r.s.TypeMatchers, m)
|
||||
if err := r.globals.declare(m, a.Source); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Resolve each of the types in the options list
|
||||
for _, ast := range m.Decl.Options {
|
||||
ty, err := r.lookupType(&r.globals, ast)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Types = append(m.Types, ty)
|
||||
if s, dup := options[ty]; dup {
|
||||
return fmt.Errorf("%v duplicate option '%v' in matcher\nFirst declared here: %v", ast.Source, ast.Name, s)
|
||||
}
|
||||
options[ty] = ast.Source
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case *sem.EnumEntry:
|
||||
enum := firstOption.Enum
|
||||
m := &sem.EnumMatcher{
|
||||
Decl: a,
|
||||
Name: a.Name,
|
||||
Enum: enum,
|
||||
}
|
||||
|
||||
// Register the matcher
|
||||
r.s.EnumMatchers = append(r.s.EnumMatchers, m)
|
||||
if err := r.globals.declare(m, a.Source); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Resolve each of the enums in the options list
|
||||
for _, ast := range m.Decl.Options {
|
||||
entry := enum.FindEntry(ast.Name)
|
||||
if entry == nil {
|
||||
return fmt.Errorf("%v enum '%v' does not contain '%v'", ast.Source, enum.Name, ast.Name)
|
||||
}
|
||||
m.Options = append(m.Options, entry)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("'%v' cannot be used for matcher", a.Name)
|
||||
}
|
||||
|
||||
// intrinsic() resolves a intrinsic overload declaration.
|
||||
// The the first overload for the intrinsic creates and appends the sem.Intrinsic
|
||||
// to Sem.Intrinsics. Subsequent overloads append their resolved overload to the
|
||||
// sem.intrinsic.Overloads list.
|
||||
func (r *resolver) intrinsic(
|
||||
a ast.IntrinsicDecl,
|
||||
intrinsicsByName map[string]*sem.Intrinsic,
|
||||
semIntrinsics *[]*sem.Intrinsic) error {
|
||||
// If this is the first overload of the intrinsic, create and register the
|
||||
// semantic intrinsic.
|
||||
intrinsic := intrinsicsByName[a.Name]
|
||||
if intrinsic == nil {
|
||||
intrinsic = &sem.Intrinsic{Name: a.Name}
|
||||
intrinsicsByName[a.Name] = intrinsic
|
||||
*semIntrinsics = append(*semIntrinsics, intrinsic)
|
||||
}
|
||||
|
||||
// Create a new scope for resolving template parameters
|
||||
s := newScope(&r.globals)
|
||||
|
||||
// Resolve the declared template parameters
|
||||
templateParams, err := r.templateParams(&s, a.TemplateParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Construct the semantic overload
|
||||
overload := &sem.Overload{
|
||||
Decl: a,
|
||||
Intrinsic: intrinsic,
|
||||
Parameters: make([]sem.Parameter, len(a.Parameters)),
|
||||
TemplateParams: templateParams,
|
||||
}
|
||||
|
||||
// Process overload attributes
|
||||
if stageDeco := a.Attributes.Take("stage"); stageDeco != nil {
|
||||
for stageDeco != nil {
|
||||
for _, stage := range stageDeco.Values {
|
||||
switch stage {
|
||||
case "vertex":
|
||||
overload.CanBeUsedInStage.Vertex = true
|
||||
case "fragment":
|
||||
overload.CanBeUsedInStage.Fragment = true
|
||||
case "compute":
|
||||
overload.CanBeUsedInStage.Compute = true
|
||||
default:
|
||||
return fmt.Errorf("%v unknown stage '%v'", stageDeco.Source, stage)
|
||||
}
|
||||
}
|
||||
stageDeco = a.Attributes.Take("stage")
|
||||
}
|
||||
} else {
|
||||
overload.CanBeUsedInStage = sem.StageUses{
|
||||
Vertex: true,
|
||||
Fragment: true,
|
||||
Compute: true,
|
||||
}
|
||||
}
|
||||
if constEvalFn := a.Attributes.Take("const"); constEvalFn != nil {
|
||||
switch len(constEvalFn.Values) {
|
||||
case 0:
|
||||
switch overload.Decl.Kind {
|
||||
case ast.Builtin, ast.Operator:
|
||||
overload.ConstEvalFunction = overload.Decl.Name
|
||||
case ast.Constructor:
|
||||
overload.ConstEvalFunction = "Ctor"
|
||||
case ast.Converter:
|
||||
overload.ConstEvalFunction = "Conv"
|
||||
}
|
||||
case 1:
|
||||
overload.ConstEvalFunction = constEvalFn.Values[0]
|
||||
default:
|
||||
return fmt.Errorf("%v too many values for @const attribute", constEvalFn.Source)
|
||||
}
|
||||
}
|
||||
if deprecated := a.Attributes.Take("deprecated"); deprecated != nil {
|
||||
overload.IsDeprecated = true
|
||||
if len(deprecated.Values) != 0 {
|
||||
return fmt.Errorf("%v unexpected value for deprecated attribute", deprecated.Source)
|
||||
}
|
||||
}
|
||||
if len(a.Attributes) != 0 {
|
||||
return fmt.Errorf("%v unknown attribute", a.Attributes[0].Source)
|
||||
}
|
||||
|
||||
// Append the overload to the intrinsic
|
||||
intrinsic.Overloads = append(intrinsic.Overloads, overload)
|
||||
|
||||
// Sort the template parameters by resolved type. Append these to
|
||||
// sem.Overload.TemplateTypes or sem.Overload.TemplateNumbers based on their kind.
|
||||
for _, param := range templateParams {
|
||||
switch param := param.(type) {
|
||||
case *sem.TemplateTypeParam:
|
||||
overload.TemplateTypes = append(overload.TemplateTypes, param)
|
||||
case *sem.TemplateEnumParam, *sem.TemplateNumberParam:
|
||||
overload.TemplateNumbers = append(overload.TemplateNumbers, param)
|
||||
}
|
||||
}
|
||||
|
||||
// Update high-water marks of template types and numbers
|
||||
if r.s.MaxTemplateTypes < len(overload.TemplateTypes) {
|
||||
r.s.MaxTemplateTypes = len(overload.TemplateTypes)
|
||||
}
|
||||
if r.s.MaxTemplateNumbers < len(overload.TemplateNumbers) {
|
||||
r.s.MaxTemplateNumbers = len(overload.TemplateNumbers)
|
||||
}
|
||||
|
||||
// Resolve the parameters
|
||||
for i, p := range a.Parameters {
|
||||
usage, err := r.fullyQualifiedName(&s, p.Type)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isConst := false
|
||||
if attribute := p.Attributes.Take("const"); attribute != nil {
|
||||
isConst = true
|
||||
}
|
||||
if len(p.Attributes) != 0 {
|
||||
return fmt.Errorf("%v unknown attribute", p.Attributes[0].Source)
|
||||
}
|
||||
overload.Parameters[i] = sem.Parameter{
|
||||
Name: p.Name,
|
||||
Type: usage,
|
||||
IsConst: isConst,
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the return type
|
||||
if a.ReturnType != nil {
|
||||
usage, err := r.fullyQualifiedName(&s, *a.ReturnType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch usage.Target.(type) {
|
||||
case *sem.Type, *sem.TemplateTypeParam:
|
||||
overload.ReturnType = &usage
|
||||
default:
|
||||
return fmt.Errorf("%v cannot use '%v' as return type. Must be a type or template type", a.ReturnType.Source, a.ReturnType.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fullyQualifiedName() resolves the ast.TemplatedName to a sem.FullyQualifiedName.
|
||||
func (r *resolver) fullyQualifiedName(s *scope, arg ast.TemplatedName) (sem.FullyQualifiedName, error) {
|
||||
target, err := r.lookupNamed(s, arg)
|
||||
if err != nil {
|
||||
return sem.FullyQualifiedName{}, err
|
||||
}
|
||||
|
||||
if entry, ok := target.(*sem.EnumEntry); ok {
|
||||
// The target resolved to an enum entry.
|
||||
// Automagically transform this into a synthetic matcher with a single
|
||||
// option. i.e.
|
||||
// This:
|
||||
// enum E{ a b c }
|
||||
// fn F(b)
|
||||
// Becomes:
|
||||
// enum E{ a b c }
|
||||
// matcher b
|
||||
// fn F(b)
|
||||
// We don't really care right now that we have a symbol collision
|
||||
// between E.b and b, as the generators return different names for
|
||||
// these.
|
||||
matcher, ok := r.enumEntryMatchers[entry]
|
||||
if !ok {
|
||||
matcher = &sem.EnumMatcher{
|
||||
Name: entry.Name,
|
||||
Enum: entry.Enum,
|
||||
Options: []*sem.EnumEntry{entry},
|
||||
}
|
||||
r.enumEntryMatchers[entry] = matcher
|
||||
r.s.EnumMatchers = append(r.s.EnumMatchers, matcher)
|
||||
}
|
||||
target = matcher
|
||||
}
|
||||
|
||||
fqn := sem.FullyQualifiedName{
|
||||
Target: target,
|
||||
TemplateArguments: make([]interface{}, len(arg.TemplateArgs)),
|
||||
}
|
||||
for i, a := range arg.TemplateArgs {
|
||||
arg, err := r.fullyQualifiedName(s, a)
|
||||
if err != nil {
|
||||
return sem.FullyQualifiedName{}, err
|
||||
}
|
||||
fqn.TemplateArguments[i] = arg
|
||||
}
|
||||
return fqn, nil
|
||||
}
|
||||
|
||||
// templateParams() resolves the ast.TemplateParams into list of sem.TemplateParam.
|
||||
// Each sem.TemplateParam is registered with the scope s.
|
||||
func (r *resolver) templateParams(s *scope, l ast.TemplateParams) ([]sem.TemplateParam, error) {
|
||||
out := []sem.TemplateParam{}
|
||||
for _, ast := range l {
|
||||
param, err := r.templateParam(ast)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.declare(param, ast.Source)
|
||||
out = append(out, param)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// templateParams() resolves the ast.TemplateParam into sem.TemplateParam, which
|
||||
// is either a sem.TemplateEnumParam or a sem.TemplateTypeParam.
|
||||
func (r *resolver) templateParam(a ast.TemplateParam) (sem.TemplateParam, error) {
|
||||
if a.Type.Name == "num" {
|
||||
return &sem.TemplateNumberParam{Name: a.Name}, nil
|
||||
}
|
||||
|
||||
if a.Type.Name != "" {
|
||||
resolved, err := r.lookupNamed(&r.globals, a.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch r := resolved.(type) {
|
||||
case *sem.Enum:
|
||||
return &sem.TemplateEnumParam{Name: a.Name, Enum: r}, nil
|
||||
case *sem.EnumMatcher:
|
||||
return &sem.TemplateEnumParam{Name: a.Name, Enum: r.Enum, Matcher: r}, nil
|
||||
case *sem.TypeMatcher:
|
||||
return &sem.TemplateTypeParam{Name: a.Name, Type: r}, nil
|
||||
case *sem.Type:
|
||||
return &sem.TemplateTypeParam{Name: a.Name, Type: r}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("%v invalid template parameter type '%v'", a.Source, a.Type.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return &sem.TemplateTypeParam{Name: a.Name}, nil
|
||||
}
|
||||
|
||||
// lookupType() searches the scope `s` and its ancestors for the sem.Type with
|
||||
// the given name.
|
||||
func (r *resolver) lookupType(s *scope, a ast.TemplatedName) (*sem.Type, error) {
|
||||
resolved, err := r.lookupNamed(s, a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Something with the given name was found...
|
||||
if ty, ok := resolved.(*sem.Type); ok {
|
||||
return ty, nil
|
||||
}
|
||||
// ... but that something was not a sem.Type
|
||||
return nil, fmt.Errorf("%v '%v' resolves to %v but type is expected", a.Source, a.Name, describe(resolved))
|
||||
}
|
||||
|
||||
// lookupNamed() searches `s` and its ancestors for the sem.Named object with
|
||||
// the given name. If there are template arguments for the name `a`, then
|
||||
// lookupNamed() performs basic validation that those arguments can be passed
|
||||
// to the named object.
|
||||
func (r *resolver) lookupNamed(s *scope, a ast.TemplatedName) (sem.Named, error) {
|
||||
target := s.lookup(a.Name)
|
||||
if target == nil {
|
||||
return nil, fmt.Errorf("%v cannot resolve '%v'", a.Source, a.Name)
|
||||
}
|
||||
|
||||
// Something with the given name was found...
|
||||
var params []sem.TemplateParam
|
||||
var ty sem.ResolvableType
|
||||
switch target := target.object.(type) {
|
||||
case *sem.Type:
|
||||
ty = target
|
||||
params = target.TemplateParams
|
||||
case *sem.TypeMatcher:
|
||||
ty = target
|
||||
params = target.TemplateParams
|
||||
case sem.TemplateParam:
|
||||
if len(a.TemplateArgs) != 0 {
|
||||
return nil, fmt.Errorf("%v '%v' template parameters do not accept template arguments", a.Source, a.Name)
|
||||
}
|
||||
return target.(sem.Named), nil
|
||||
case sem.Named:
|
||||
return target, nil
|
||||
default:
|
||||
panic(fmt.Errorf("Unknown resolved type %T", target))
|
||||
}
|
||||
// ... and that something takes template parameters
|
||||
// Check the number of templated name template arguments match the number of
|
||||
// templated parameters for the target.
|
||||
args := a.TemplateArgs
|
||||
if len(params) != len(args) {
|
||||
return nil, fmt.Errorf("%v '%v' requires %d template arguments, but %d were provided", a.Source, a.Name, len(params), len(args))
|
||||
}
|
||||
|
||||
// Check templated name template argument kinds match the parameter kinds
|
||||
for i, ast := range args {
|
||||
param := params[i]
|
||||
arg, err := r.lookupNamed(s, args[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := checkCompatible(arg, param); err != nil {
|
||||
return nil, fmt.Errorf("%v %w", ast.Source, err)
|
||||
}
|
||||
}
|
||||
return ty, nil
|
||||
}
|
||||
|
||||
// calculateUniqueParameterNames() iterates over all the parameters of all
|
||||
// builtin overloads, calculating the list of unique parameter names
|
||||
func (r *resolver) calculateUniqueParameterNames() []string {
|
||||
set := map[string]struct{}{"": {}}
|
||||
names := []string{}
|
||||
for _, intrinsics := range [][]*sem.Intrinsic{
|
||||
r.s.Builtins,
|
||||
r.s.UnaryOperators,
|
||||
r.s.BinaryOperators,
|
||||
r.s.ConstructorsAndConverters,
|
||||
} {
|
||||
for _, i := range intrinsics {
|
||||
for _, o := range i.Overloads {
|
||||
for _, p := range o.Parameters {
|
||||
if _, dup := set[p.Name]; !dup {
|
||||
set[p.Name] = struct{}{}
|
||||
names = append(names, p.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// describe() returns a string describing a sem.Named
|
||||
func describe(n sem.Named) string {
|
||||
switch n := n.(type) {
|
||||
case *sem.Type:
|
||||
return "type '" + n.Name + "'"
|
||||
case *sem.TypeMatcher:
|
||||
return "type matcher '" + n.Name + "'"
|
||||
case *sem.Enum:
|
||||
return "enum '" + n.Name + "'"
|
||||
case *sem.EnumMatcher:
|
||||
return "enum matcher '" + n.Name + "'"
|
||||
case *sem.TemplateTypeParam:
|
||||
return "template type"
|
||||
case *sem.TemplateEnumParam:
|
||||
return "template enum '" + n.Enum.Name + "'"
|
||||
case *sem.EnumEntry:
|
||||
return "enum entry '" + n.Enum.Name + "." + n.Name + "'"
|
||||
case *sem.TemplateNumberParam:
|
||||
return "template number"
|
||||
default:
|
||||
panic(fmt.Errorf("unhandled type %T", n))
|
||||
}
|
||||
}
|
||||
|
||||
// checkCompatible() returns an error if `arg` cannot be used as an argument for
|
||||
// a parameter of `param`.
|
||||
func checkCompatible(arg, param sem.Named) error {
|
||||
// asEnum() returns the underlying sem.Enum if n is a enum matcher,
|
||||
// templated enum parameter or an enum entry, otherwise nil
|
||||
asEnum := func(n sem.Named) *sem.Enum {
|
||||
switch n := n.(type) {
|
||||
case *sem.EnumMatcher:
|
||||
return n.Enum
|
||||
case *sem.TemplateEnumParam:
|
||||
return n.Enum
|
||||
case *sem.EnumEntry:
|
||||
return n.Enum
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if arg := asEnum(arg); arg != nil {
|
||||
param := asEnum(param)
|
||||
if arg == param {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
anyNumber := "any number"
|
||||
// asNumber() returns anyNumber if n is a TemplateNumberParam.
|
||||
// TODO(bclayton): Once we support number ranges [e.g.: fn F<N: 1..4>()], we
|
||||
// should check number ranges are compatible
|
||||
asNumber := func(n sem.Named) interface{} {
|
||||
switch n.(type) {
|
||||
case *sem.TemplateNumberParam:
|
||||
return anyNumber
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if arg := asNumber(arg); arg != nil {
|
||||
param := asNumber(param)
|
||||
if arg == param {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
anyType := &sem.Type{}
|
||||
// asNumber() returns the sem.Type, sem.TypeMatcher if the named object
|
||||
// resolves to one of these, or anyType if n is a unconstrained template
|
||||
// type parameter.
|
||||
asResolvableType := func(n sem.Named) sem.ResolvableType {
|
||||
switch n := n.(type) {
|
||||
case *sem.TemplateTypeParam:
|
||||
if n.Type != nil {
|
||||
return n.Type
|
||||
}
|
||||
return anyType
|
||||
case *sem.Type:
|
||||
return n
|
||||
case *sem.TypeMatcher:
|
||||
return n
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if arg := asResolvableType(arg); arg != nil {
|
||||
param := asResolvableType(param)
|
||||
if arg == param || param == anyType {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot use %v as %v", describe(arg), describe(param))
|
||||
}
|
||||
|
||||
// scope is a basic hierarchical name to object table
|
||||
type scope struct {
|
||||
objects map[string]objectAndSource
|
||||
parent *scope
|
||||
}
|
||||
|
||||
// objectAndSource is a sem.Named object with a source
|
||||
type objectAndSource struct {
|
||||
object sem.Named
|
||||
source tok.Source
|
||||
}
|
||||
|
||||
// newScope returns a newly initalized scope
|
||||
func newScope(parent *scope) scope {
|
||||
return scope{objects: map[string]objectAndSource{}, parent: parent}
|
||||
}
|
||||
|
||||
// lookup() searches the scope and then its parents for the symbol with the
|
||||
// given name.
|
||||
func (s *scope) lookup(name string) *objectAndSource {
|
||||
if o, found := s.objects[name]; found {
|
||||
return &o
|
||||
}
|
||||
if s.parent == nil {
|
||||
return nil
|
||||
}
|
||||
return s.parent.lookup(name)
|
||||
}
|
||||
|
||||
// declare() declares the symbol with the given name, erroring on symbol
|
||||
// collision.
|
||||
func (s *scope) declare(object sem.Named, source tok.Source) error {
|
||||
name := object.GetName()
|
||||
if existing := s.lookup(name); existing != nil {
|
||||
return fmt.Errorf("%v '%v' already declared\nFirst declared here: %v", source, name, existing.source)
|
||||
}
|
||||
s.objects[name] = objectAndSource{object, source}
|
||||
return nil
|
||||
}
|
||||
547
tools/src/tint/intrinsic/resolver/resolver_test.go
Normal file
547
tools/src/tint/intrinsic/resolver/resolver_test.go
Normal file
@@ -0,0 +1,547 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package resolver_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/parser"
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/resolver"
|
||||
)
|
||||
|
||||
func TestResolver(t *testing.T) {
|
||||
type test struct {
|
||||
src string
|
||||
err string
|
||||
}
|
||||
|
||||
success := ""
|
||||
for _, test := range []test{
|
||||
{
|
||||
`type X`,
|
||||
success,
|
||||
}, {
|
||||
`enum E {}`,
|
||||
success,
|
||||
}, {
|
||||
`enum E {A B C}`,
|
||||
success,
|
||||
}, {
|
||||
`type X`,
|
||||
success,
|
||||
}, {
|
||||
`@display("Y") type X`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type x
|
||||
match y: x`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
enum e {a b c}
|
||||
match y: c | a | b`,
|
||||
success,
|
||||
}, {
|
||||
`fn f()`,
|
||||
success,
|
||||
}, {
|
||||
`fn f<T>()`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
fn f<N: num>()`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
enum e { a b c }
|
||||
fn f<N: e>()`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
fn f<T>(T) -> f32`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
type P<T>
|
||||
match m: f32
|
||||
fn f<T: m>(P<T>) -> T`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
type P<T>
|
||||
match m: f32
|
||||
fn f(P<m>)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
enum e { a }
|
||||
fn f(a)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
enum e { a b }
|
||||
type T<E: e>
|
||||
match m: a
|
||||
fn f<E: m>(T<E>)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
enum e { a b }
|
||||
type T<E: e>
|
||||
match m: a
|
||||
fn f(T<m>)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
enum e { a }
|
||||
type T<E: e>
|
||||
fn f(T<a>)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type T<E: num>
|
||||
fn f<E: num>(T<E>)`,
|
||||
success,
|
||||
}, {
|
||||
`fn f<T>(T)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
enum e { a b }
|
||||
fn f<E: e>()`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
enum e { a b }
|
||||
match m: a | b
|
||||
fn f<E: m>()`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
type T<x>
|
||||
fn f(T< T<f32> >)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
op -(f32)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
type T<x>
|
||||
op +(T<f32>, T<f32>)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
ctor f32(f32)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
type T<x>
|
||||
ctor f32(T<f32>)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
type i32
|
||||
conv f32(i32)`,
|
||||
success,
|
||||
}, {
|
||||
`
|
||||
type f32
|
||||
type T<x>
|
||||
conv f32(T<f32>)`,
|
||||
success,
|
||||
}, {
|
||||
`enum E {A A}`,
|
||||
`
|
||||
file.txt:1:6 'A' already declared
|
||||
First declared here: file.txt:1:6
|
||||
`,
|
||||
},
|
||||
{
|
||||
`type X type X`,
|
||||
`
|
||||
file.txt:1:13 'X' already declared
|
||||
First declared here: file.txt:1:6`,
|
||||
}, {
|
||||
`@meow type X`,
|
||||
`
|
||||
file.txt:1:2 unknown attribute
|
||||
`,
|
||||
}, {
|
||||
`@display("Y", "Z") type X`,
|
||||
`
|
||||
file.txt:1:2 expected a single value for 'display' attribute`,
|
||||
}, {
|
||||
`
|
||||
enum e { a }
|
||||
enum e { b }`,
|
||||
`
|
||||
file.txt:2:6 'e' already declared
|
||||
First declared here: file.txt:1:6`,
|
||||
}, {
|
||||
`
|
||||
type X
|
||||
match X : X`,
|
||||
`
|
||||
file.txt:2:7 'X' already declared
|
||||
First declared here: file.txt:1:6`,
|
||||
}, {
|
||||
`type T<X>
|
||||
match M : T`,
|
||||
`file.txt:2:11 'T' requires 1 template arguments, but 0 were provided`,
|
||||
}, {
|
||||
`
|
||||
match x: y`,
|
||||
`
|
||||
file.txt:1:10 cannot resolve 'y'
|
||||
`,
|
||||
}, {
|
||||
`
|
||||
type a
|
||||
match x: a | b`,
|
||||
`
|
||||
file.txt:2:14 cannot resolve 'b'
|
||||
`,
|
||||
}, {
|
||||
`
|
||||
type a
|
||||
enum e { b }
|
||||
match x: a | b`,
|
||||
`
|
||||
file.txt:3:14 'b' resolves to enum entry 'e.b' but type is expected
|
||||
`,
|
||||
}, {
|
||||
`
|
||||
type a
|
||||
type b
|
||||
match x: a | b | a`,
|
||||
`
|
||||
file.txt:3:18 duplicate option 'a' in matcher
|
||||
First declared here: file.txt:3:10
|
||||
`,
|
||||
}, {
|
||||
`
|
||||
enum e { a c }
|
||||
match x: a | b | c`,
|
||||
`
|
||||
file.txt:2:14 enum 'e' does not contain 'b'
|
||||
`,
|
||||
}, {
|
||||
`
|
||||
enum e { a }
|
||||
match x: a
|
||||
match x: a`,
|
||||
`
|
||||
file.txt:3:7 'x' already declared
|
||||
First declared here: file.txt:2:7
|
||||
`,
|
||||
}, {
|
||||
`
|
||||
type t
|
||||
match x: t
|
||||
match y: x`,
|
||||
`
|
||||
'y' cannot be used for matcher
|
||||
`,
|
||||
}, {
|
||||
`fn f(u)`,
|
||||
`file.txt:1:6 cannot resolve 'u'`,
|
||||
}, {
|
||||
`fn f() -> u`,
|
||||
`file.txt:1:11 cannot resolve 'u'`,
|
||||
}, {
|
||||
`fn f<T: u>()`,
|
||||
`file.txt:1:9 cannot resolve 'u'`,
|
||||
}, {
|
||||
`
|
||||
enum e { a }
|
||||
fn f() -> e`,
|
||||
`file.txt:2:11 cannot use 'e' as return type. Must be a type or template type`,
|
||||
}, {
|
||||
`
|
||||
type T<x>
|
||||
fn f(T<u>)`,
|
||||
`file.txt:2:8 cannot resolve 'u'`,
|
||||
}, {
|
||||
`
|
||||
type x
|
||||
fn f<T>(T<x>)`,
|
||||
`file.txt:2:9 'T' template parameters do not accept template arguments`,
|
||||
}, {
|
||||
`
|
||||
type A<N: num>
|
||||
type B
|
||||
fn f(A<B>)`,
|
||||
`file.txt:3:8 cannot use type 'B' as template number`,
|
||||
}, {
|
||||
`
|
||||
type A<N>
|
||||
enum E { b }
|
||||
fn f(A<b>)`,
|
||||
`file.txt:3:8 cannot use enum entry 'E.b' as template type`,
|
||||
}, {
|
||||
`
|
||||
type T
|
||||
type P<N: num>
|
||||
match m: T
|
||||
fn f(P<m>)`,
|
||||
`file.txt:4:8 cannot use type matcher 'm' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { b }
|
||||
fn f(P<E>)`,
|
||||
`file.txt:3:8 cannot use enum 'E' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { a b }
|
||||
match m: a | b
|
||||
fn f(P<m>)`,
|
||||
`file.txt:4:8 cannot use enum matcher 'm' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { a b }
|
||||
match m: a | b
|
||||
fn f<M: m>(P<M>)`,
|
||||
`file.txt:4:14 cannot use template enum 'E' as template number`,
|
||||
}, {
|
||||
`
|
||||
type i
|
||||
enum e { a }
|
||||
op << (i) -> e`,
|
||||
`file.txt:3:14 cannot use 'e' as return type. Must be a type or template type`,
|
||||
}, {
|
||||
`
|
||||
type T<x>
|
||||
op << (T<u>)`,
|
||||
`file.txt:2:10 cannot resolve 'u'`,
|
||||
}, {
|
||||
`
|
||||
op << ()`,
|
||||
`file.txt:1:4 operators must have either 1 or 2 parameters`,
|
||||
}, {
|
||||
`
|
||||
type i
|
||||
op << (i, i, i)`,
|
||||
`file.txt:2:4 operators must have either 1 or 2 parameters`,
|
||||
}, {
|
||||
`
|
||||
type x
|
||||
op << <T>(T<x>)`,
|
||||
`file.txt:2:11 'T' template parameters do not accept template arguments`,
|
||||
}, {
|
||||
`
|
||||
type A<N: num>
|
||||
type B
|
||||
op << (A<B>)`,
|
||||
`file.txt:3:10 cannot use type 'B' as template number`,
|
||||
}, {
|
||||
`
|
||||
type A<N>
|
||||
enum E { b }
|
||||
op << (A<b>)`,
|
||||
`file.txt:3:10 cannot use enum entry 'E.b' as template type`,
|
||||
}, {
|
||||
`
|
||||
type T
|
||||
type P<N: num>
|
||||
match m: T
|
||||
op << (P<m>)`,
|
||||
`file.txt:4:10 cannot use type matcher 'm' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { b }
|
||||
op << (P<E>)`,
|
||||
`file.txt:3:10 cannot use enum 'E' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { a b }
|
||||
match m: a | b
|
||||
op << (P<m>)`,
|
||||
`file.txt:4:10 cannot use enum matcher 'm' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { a b }
|
||||
match m: a | b
|
||||
op << <M: m>(P<M>)`,
|
||||
`file.txt:4:16 cannot use template enum 'E' as template number`,
|
||||
}, {
|
||||
`
|
||||
type i
|
||||
enum e { a }
|
||||
ctor F(i) -> e`,
|
||||
`file.txt:3:14 cannot use 'e' as return type. Must be a type or template type`,
|
||||
}, {
|
||||
`
|
||||
type T<x>
|
||||
ctor F(T<u>)`,
|
||||
`file.txt:2:10 cannot resolve 'u'`,
|
||||
}, {
|
||||
`
|
||||
type x
|
||||
ctor F<T>(T<x>)`,
|
||||
`file.txt:2:11 'T' template parameters do not accept template arguments`,
|
||||
}, {
|
||||
`
|
||||
type A<N: num>
|
||||
type B
|
||||
ctor F(A<B>)`,
|
||||
`file.txt:3:10 cannot use type 'B' as template number`,
|
||||
}, {
|
||||
`
|
||||
type A<N>
|
||||
enum E { b }
|
||||
ctor F(A<b>)`,
|
||||
`file.txt:3:10 cannot use enum entry 'E.b' as template type`,
|
||||
}, {
|
||||
`
|
||||
type T
|
||||
type P<N: num>
|
||||
match m: T
|
||||
ctor F(P<m>)`,
|
||||
`file.txt:4:10 cannot use type matcher 'm' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { b }
|
||||
ctor F(P<E>)`,
|
||||
`file.txt:3:10 cannot use enum 'E' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { a b }
|
||||
match m: a | b
|
||||
ctor F(P<m>)`,
|
||||
`file.txt:4:10 cannot use enum matcher 'm' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { a b }
|
||||
match m: a | b
|
||||
ctor F<M: m>(P<M>)`,
|
||||
`file.txt:4:16 cannot use template enum 'E' as template number`,
|
||||
}, {
|
||||
`
|
||||
conv F()`,
|
||||
`file.txt:1:6 conversions must have a single parameter`,
|
||||
}, {
|
||||
`
|
||||
type i
|
||||
conv F(i, i, i)`,
|
||||
`file.txt:2:6 conversions must have a single parameter`,
|
||||
}, {
|
||||
`
|
||||
type i
|
||||
enum e { a }
|
||||
conv F(i) -> e`,
|
||||
`file.txt:3:14 cannot use 'e' as return type. Must be a type or template type`,
|
||||
}, {
|
||||
`
|
||||
type T<x>
|
||||
conv F(T<u>)`,
|
||||
`file.txt:2:10 cannot resolve 'u'`,
|
||||
}, {
|
||||
`
|
||||
type x
|
||||
conv F<T>(T<x>)`,
|
||||
`file.txt:2:11 'T' template parameters do not accept template arguments`,
|
||||
}, {
|
||||
`
|
||||
type A<N: num>
|
||||
type B
|
||||
conv F(A<B>)`,
|
||||
`file.txt:3:10 cannot use type 'B' as template number`,
|
||||
}, {
|
||||
`
|
||||
type A<N>
|
||||
enum E { b }
|
||||
conv F(A<b>)`,
|
||||
`file.txt:3:10 cannot use enum entry 'E.b' as template type`,
|
||||
}, {
|
||||
`
|
||||
type T
|
||||
type P<N: num>
|
||||
match m: T
|
||||
conv F(P<m>)`,
|
||||
`file.txt:4:10 cannot use type matcher 'm' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { b }
|
||||
conv F(P<E>)`,
|
||||
`file.txt:3:10 cannot use enum 'E' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { a b }
|
||||
match m: a | b
|
||||
conv F(P<m>)`,
|
||||
`file.txt:4:10 cannot use enum matcher 'm' as template number`,
|
||||
}, {
|
||||
`
|
||||
type P<N: num>
|
||||
enum E { a b }
|
||||
match m: a | b
|
||||
conv F<M: m>(P<M>)`,
|
||||
`file.txt:4:16 cannot use template enum 'E' as template number`,
|
||||
}, {
|
||||
`
|
||||
enum E { a }
|
||||
type T<X: a>`,
|
||||
`file.txt:2:8 invalid template parameter type 'a'`,
|
||||
}, {
|
||||
`
|
||||
enum E { a }
|
||||
fn f<M: a>()`,
|
||||
`file.txt:2:6 invalid template parameter type 'a'`,
|
||||
},
|
||||
} {
|
||||
|
||||
ast, err := parser.Parse(strings.TrimSpace(string(test.src)), "file.txt")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected parser error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
expectErr := strings.TrimSpace(test.err)
|
||||
_, err = resolver.Resolve(ast)
|
||||
if err != nil {
|
||||
gotErr := strings.TrimSpace(fmt.Sprint(err))
|
||||
if gotErr != expectErr {
|
||||
t.Errorf("While parsing:\n%s\nGot error:\n%s\nExpected:\n%s", test.src, gotErr, expectErr)
|
||||
}
|
||||
} else if expectErr != success {
|
||||
t.Errorf("While parsing:\n%s\nGot no error, expected error:\n%s", test.src, expectErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
308
tools/src/tint/intrinsic/sem/sem.go
Normal file
308
tools/src/tint/intrinsic/sem/sem.go
Normal file
@@ -0,0 +1,308 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/tint/intrinsic/ast"
|
||||
)
|
||||
|
||||
// Sem is the root of the semantic tree
|
||||
type Sem struct {
|
||||
Enums []*Enum
|
||||
Types []*Type
|
||||
TypeMatchers []*TypeMatcher
|
||||
EnumMatchers []*EnumMatcher
|
||||
Builtins []*Intrinsic
|
||||
UnaryOperators []*Intrinsic
|
||||
BinaryOperators []*Intrinsic
|
||||
ConstructorsAndConverters []*Intrinsic
|
||||
// Maximum number of template types used across all builtins
|
||||
MaxTemplateTypes int
|
||||
// Maximum number of template numbers used across all builtins
|
||||
MaxTemplateNumbers int
|
||||
// The alphabetically sorted list of unique parameter names
|
||||
UniqueParameterNames []string
|
||||
}
|
||||
|
||||
// New returns a new Sem
|
||||
func New() *Sem {
|
||||
return &Sem{
|
||||
Enums: []*Enum{},
|
||||
Types: []*Type{},
|
||||
TypeMatchers: []*TypeMatcher{},
|
||||
EnumMatchers: []*EnumMatcher{},
|
||||
Builtins: []*Intrinsic{},
|
||||
UnaryOperators: []*Intrinsic{},
|
||||
BinaryOperators: []*Intrinsic{},
|
||||
}
|
||||
}
|
||||
|
||||
// Enum describes an enumerator
|
||||
type Enum struct {
|
||||
Decl ast.EnumDecl
|
||||
Name string
|
||||
Entries []*EnumEntry
|
||||
}
|
||||
|
||||
// FindEntry returns the enum entry with the given name
|
||||
func (e *Enum) FindEntry(name string) *EnumEntry {
|
||||
for _, entry := range e.Entries {
|
||||
if entry.Name == name {
|
||||
return entry
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnumEntry is an entry in an enumerator
|
||||
type EnumEntry struct {
|
||||
Enum *Enum
|
||||
Name string
|
||||
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
|
||||
Decl ast.TypeDecl
|
||||
Name string
|
||||
DisplayName string
|
||||
Precedence int
|
||||
}
|
||||
|
||||
// TypeMatcher declares a type matcher
|
||||
type TypeMatcher struct {
|
||||
TemplateParams []TemplateParam
|
||||
Decl ast.MatcherDecl
|
||||
Name string
|
||||
Types []*Type
|
||||
}
|
||||
|
||||
func (t TypeMatcher) PrecedenceSortedTypes() []*Type {
|
||||
out := make([]*Type, len(t.Types))
|
||||
copy(out, t.Types)
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Precedence > out[j].Precedence })
|
||||
return out
|
||||
}
|
||||
|
||||
// EnumMatcher declares a enum matcher
|
||||
type EnumMatcher struct {
|
||||
TemplateParams []TemplateParam
|
||||
Decl ast.MatcherDecl
|
||||
Name string
|
||||
Enum *Enum
|
||||
Options []*EnumEntry
|
||||
}
|
||||
|
||||
// TemplateEnumParam is a template enum parameter
|
||||
type TemplateEnumParam struct {
|
||||
Name string
|
||||
Enum *Enum
|
||||
Matcher *EnumMatcher // Optional
|
||||
}
|
||||
|
||||
// TemplateTypeParam is a template type parameter
|
||||
type TemplateTypeParam struct {
|
||||
Name string
|
||||
Type ResolvableType
|
||||
}
|
||||
|
||||
// TemplateNumberParam is a template type parameter
|
||||
type TemplateNumberParam struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// Intrinsic describes the overloads of a builtin or operator
|
||||
type Intrinsic struct {
|
||||
Name string
|
||||
Overloads []*Overload
|
||||
}
|
||||
|
||||
// Overload describes a single overload of a builtin or operator
|
||||
type Overload struct {
|
||||
Decl ast.IntrinsicDecl
|
||||
Intrinsic *Intrinsic
|
||||
TemplateParams []TemplateParam
|
||||
TemplateTypes []*TemplateTypeParam
|
||||
TemplateNumbers []TemplateParam
|
||||
ReturnType *FullyQualifiedName
|
||||
Parameters []Parameter
|
||||
CanBeUsedInStage StageUses
|
||||
IsDeprecated bool // True if this overload is deprecated
|
||||
ConstEvalFunction string // Name of the function used to evaluate the intrinsic at shader creation time
|
||||
}
|
||||
|
||||
// StageUses describes the stages an overload can be used in
|
||||
type StageUses struct {
|
||||
Vertex bool
|
||||
Fragment bool
|
||||
Compute bool
|
||||
}
|
||||
|
||||
// List returns the stage uses as a string list
|
||||
func (u StageUses) List() []string {
|
||||
out := []string{}
|
||||
if u.Vertex {
|
||||
out = append(out, "vertex")
|
||||
}
|
||||
if u.Fragment {
|
||||
out = append(out, "fragment")
|
||||
}
|
||||
if u.Compute {
|
||||
out = append(out, "compute")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (o Overload) Format(w fmt.State, verb rune) {
|
||||
switch o.Decl.Kind {
|
||||
case ast.Builtin:
|
||||
fmt.Fprintf(w, "fn ")
|
||||
case ast.Operator:
|
||||
fmt.Fprintf(w, "op ")
|
||||
}
|
||||
fmt.Fprintf(w, "%v", o.Intrinsic.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
|
||||
IsConst bool // Did this parameter have a @const attribute?
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (p Parameter) Format(w fmt.State, verb rune) {
|
||||
if p.IsConst {
|
||||
fmt.Fprint(w, "@const ")
|
||||
}
|
||||
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 []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
|
||||
type TemplateParam interface {
|
||||
Named
|
||||
isTemplateParam()
|
||||
}
|
||||
|
||||
func (*TemplateEnumParam) isTemplateParam() {}
|
||||
func (*TemplateTypeParam) isTemplateParam() {}
|
||||
func (*TemplateNumberParam) isTemplateParam() {}
|
||||
|
||||
// ResolvableType is a Type, TypeMatcher or TemplateTypeParam
|
||||
type ResolvableType interface {
|
||||
Named
|
||||
isResolvableType()
|
||||
}
|
||||
|
||||
func (*Type) isResolvableType() {}
|
||||
func (*TypeMatcher) isResolvableType() {}
|
||||
func (*TemplateTypeParam) isResolvableType() {}
|
||||
|
||||
// Named is something that can be looked up by name
|
||||
type Named interface {
|
||||
isNamed()
|
||||
GetName() string
|
||||
}
|
||||
|
||||
func (*Enum) isNamed() {}
|
||||
func (*EnumEntry) isNamed() {}
|
||||
func (*Type) isNamed() {}
|
||||
func (*TypeMatcher) isNamed() {}
|
||||
func (*EnumMatcher) isNamed() {}
|
||||
func (*TemplateTypeParam) isNamed() {}
|
||||
func (*TemplateEnumParam) isNamed() {}
|
||||
func (*TemplateNumberParam) isNamed() {}
|
||||
|
||||
// GetName returns the name of the Enum
|
||||
func (e *Enum) GetName() string { return e.Name }
|
||||
|
||||
// GetName returns the name of the EnumEntry
|
||||
func (e *EnumEntry) GetName() string { return e.Name }
|
||||
|
||||
// GetName returns the name of the Type
|
||||
func (t *Type) GetName() string { return t.Name }
|
||||
|
||||
// GetName returns the name of the TypeMatcher
|
||||
func (t *TypeMatcher) GetName() string { return t.Name }
|
||||
|
||||
// GetName returns the name of the EnumMatcher
|
||||
func (e *EnumMatcher) GetName() string { return e.Name }
|
||||
|
||||
// GetName returns the name of the TemplateTypeParam
|
||||
func (t *TemplateTypeParam) GetName() string { return t.Name }
|
||||
|
||||
// GetName returns the name of the TemplateEnumParam
|
||||
func (t *TemplateEnumParam) GetName() string { return t.Name }
|
||||
|
||||
// GetName returns the name of the TemplateNumberParam
|
||||
func (t *TemplateNumberParam) GetName() string { return t.Name }
|
||||
139
tools/src/tint/intrinsic/tok/tok.go
Normal file
139
tools/src/tint/intrinsic/tok/tok.go
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2021 The Tint Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package tok defines tokens that are produced by the Tint intrinsic definition
|
||||
// lexer
|
||||
package tok
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Kind is an enumerator of token kinds
|
||||
type Kind string
|
||||
|
||||
// Token enumerator types
|
||||
const (
|
||||
InvalidToken Kind = "<invalid>"
|
||||
Identifier Kind = "ident"
|
||||
Integer Kind = "integer"
|
||||
String Kind = "string"
|
||||
Match Kind = "match"
|
||||
Function Kind = "fn"
|
||||
Operator Kind = "op"
|
||||
Constructor Kind = "ctor"
|
||||
Converter Kind = "conv"
|
||||
Type Kind = "type"
|
||||
Enum Kind = "enum"
|
||||
And Kind = "&"
|
||||
AndAnd Kind = "&&"
|
||||
Arrow Kind = "->"
|
||||
Attr Kind = "@"
|
||||
Assign Kind = "="
|
||||
Colon Kind = ":"
|
||||
Comma Kind = ","
|
||||
Complement Kind = "~"
|
||||
Divide Kind = "/"
|
||||
Equal Kind = "=="
|
||||
Ge Kind = ">="
|
||||
Gt Kind = ">"
|
||||
Lbrace Kind = "{"
|
||||
Le Kind = "<="
|
||||
Lparen Kind = "("
|
||||
Lt Kind = "<"
|
||||
Minus Kind = "-"
|
||||
Modulo Kind = "%"
|
||||
Not Kind = "!"
|
||||
NotEqual Kind = "!="
|
||||
Or Kind = "|"
|
||||
OrOr Kind = "||"
|
||||
Plus Kind = "+"
|
||||
Rbrace Kind = "}"
|
||||
Rparen Kind = ")"
|
||||
Shl Kind = "<<"
|
||||
Shr Kind = ">>"
|
||||
Star Kind = "*"
|
||||
Xor Kind = "^"
|
||||
)
|
||||
|
||||
// Invalid represents an invalid token
|
||||
var Invalid = Token{Kind: InvalidToken}
|
||||
|
||||
// Location describes a rune location in the source code
|
||||
type Location struct {
|
||||
// 1-based line index
|
||||
Line int
|
||||
// 1-based column index
|
||||
Column int
|
||||
// 0-based rune index
|
||||
Rune int
|
||||
// Optional file path
|
||||
Filepath string
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (l Location) Format(w fmt.State, verb rune) {
|
||||
if w.Flag('+') {
|
||||
if l.Filepath != "" {
|
||||
fmt.Fprintf(w, "%v:%v:%v[%v]", l.Filepath, l.Line, l.Column, l.Rune)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%v:%v[%v]", l.Line, l.Column, l.Rune)
|
||||
}
|
||||
} else {
|
||||
if l.Filepath != "" {
|
||||
fmt.Fprintf(w, "%v:%v:%v", l.Filepath, l.Line, l.Column)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%v:%v", l.Line, l.Column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Source describes a start and end range in the source code
|
||||
type Source struct {
|
||||
S, E Location
|
||||
}
|
||||
|
||||
// IsValid returns true if the source is valid
|
||||
func (s Source) IsValid() bool {
|
||||
return s.S.Line != 0 && s.S.Column != 0 && s.E.Line != 0 && s.E.Column != 0
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (s Source) Format(w fmt.State, verb rune) {
|
||||
if w.Flag('+') {
|
||||
fmt.Fprint(w, "[")
|
||||
s.S.Format(w, verb)
|
||||
fmt.Fprint(w, " - ")
|
||||
s.E.Format(w, verb)
|
||||
fmt.Fprint(w, "]")
|
||||
} else {
|
||||
s.S.Format(w, verb)
|
||||
}
|
||||
}
|
||||
|
||||
// Token describes a parsed token
|
||||
type Token struct {
|
||||
Kind Kind
|
||||
Runes []rune
|
||||
Source Source
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (t Token) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprint(w, "[")
|
||||
t.Source.Format(w, verb)
|
||||
fmt.Fprint(w, " ")
|
||||
fmt.Fprint(w, t.Kind)
|
||||
fmt.Fprint(w, " ")
|
||||
fmt.Fprint(w, string(t.Runes))
|
||||
fmt.Fprint(w, "]")
|
||||
}
|
||||
Reference in New Issue
Block a user