mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-09 13:38:00 +00:00
tint: Add operator support to intrinsic-gen
Adapt the builtin parsing and resolving to also support operators. Will be used to generate intrinsic table entries for operators. This will simplify maintenance of the operators, and will greatly simplify the [AbstractInt -> i32|u32] [AbstractFloat -> f32|f16] logic. Bug: tint:1504 Change-Id: Id75735ea24e501877418812185796f3fba88a521 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/89026 Commit-Queue: Ben Clayton <bclayton@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
committed by
Dawn LUCI CQ
parent
d84daed72c
commit
e6e96def66
@@ -28,7 +28,8 @@ type AST struct {
|
||||
Enums []EnumDecl
|
||||
Types []TypeDecl
|
||||
Matchers []MatcherDecl
|
||||
Functions []FunctionDecl
|
||||
Builtins []IntrinsicDecl
|
||||
Operators []IntrinsicDecl
|
||||
}
|
||||
|
||||
func (a AST) String() string {
|
||||
@@ -45,8 +46,12 @@ func (a AST) String() string {
|
||||
fmt.Fprintf(&sb, "%v", m)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
for _, f := range a.Functions {
|
||||
fmt.Fprintf(&sb, "%v", f)
|
||||
for _, b := range a.Builtins {
|
||||
fmt.Fprintf(&sb, "%v", b)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
for _, o := range a.Operators {
|
||||
fmt.Fprintf(&sb, "%v", o)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
return sb.String()
|
||||
@@ -98,9 +103,18 @@ func (m MatcherDecl) Format(w fmt.State, verb rune) {
|
||||
m.Options.Format(w, verb)
|
||||
}
|
||||
|
||||
// FunctionDecl describes a function declaration
|
||||
type FunctionDecl struct {
|
||||
// IntrinsicKind is either a Builtin or Operator
|
||||
type IntrinsicKind string
|
||||
|
||||
const (
|
||||
Builtin IntrinsicKind = "builtin"
|
||||
Operator IntrinsicKind = "operator"
|
||||
)
|
||||
|
||||
// IntrinsicDecl describes a builtin or operator declaration
|
||||
type IntrinsicDecl struct {
|
||||
Source tok.Source
|
||||
Kind IntrinsicKind
|
||||
Name string
|
||||
Decorations Decorations
|
||||
TemplateParams TemplateParams
|
||||
@@ -109,13 +123,19 @@ type FunctionDecl struct {
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (f FunctionDecl) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprintf(w, "fn %v", f.Name)
|
||||
f.TemplateParams.Format(w, verb)
|
||||
f.Parameters.Format(w, verb)
|
||||
if f.ReturnType != nil {
|
||||
func (i IntrinsicDecl) Format(w fmt.State, verb rune) {
|
||||
switch i.Kind {
|
||||
case Builtin:
|
||||
fmt.Fprintf(w, "fn ")
|
||||
case Operator:
|
||||
fmt.Fprintf(w, "op ")
|
||||
}
|
||||
fmt.Fprintf(w, "%v", i.Name)
|
||||
i.TemplateParams.Format(w, verb)
|
||||
i.Parameters.Format(w, verb)
|
||||
if i.ReturnType != nil {
|
||||
fmt.Fprintf(w, " -> ")
|
||||
f.ReturnType.Format(w, verb)
|
||||
i.ReturnType.Format(w, verb)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
"dawn.googlesource.com/dawn/tools/src/lut"
|
||||
)
|
||||
|
||||
// BuiltinTable holds data specific to the intrinsic_table.inl.tmpl template
|
||||
type BuiltinTable struct {
|
||||
// IntrinsicTable holds data specific to the intrinsic_table.inl.tmpl template
|
||||
type IntrinsicTable struct {
|
||||
// The semantic info
|
||||
Sem *sem.Sem
|
||||
|
||||
@@ -42,7 +42,8 @@ type BuiltinTable struct {
|
||||
OpenNumbers []OpenNumber // kOpenNumbers table content
|
||||
Parameters []Parameter // kParameters table content
|
||||
Overloads []Overload // kOverloads table content
|
||||
Functions []Function // kBuiltins table content
|
||||
Builtins []Intrinsic // kBuiltins table content
|
||||
Operators []Intrinsic // kOperators table content
|
||||
}
|
||||
|
||||
// OpenType is used to create the C++ OpenTypeInfo structure
|
||||
@@ -68,9 +69,9 @@ type Parameter struct {
|
||||
// The parameter usage (parameter name)
|
||||
Usage string
|
||||
|
||||
// Index into BuiltinTable.MatcherIndices, beginning the list of matchers
|
||||
// Index into IntrinsicTable.MatcherIndices, beginning the list of matchers
|
||||
// required to match the parameter type. The matcher indices index
|
||||
// into BuiltinTable::TMatchers and / or BuiltinTable::NMatchers.
|
||||
// into IntrinsicTable::TMatchers and / or IntrinsicTable::NMatchers.
|
||||
// These indices are consumed by the matchers themselves.
|
||||
// The first index is always a TypeMatcher.
|
||||
MatcherIndicesOffset *int
|
||||
@@ -84,15 +85,15 @@ type Overload struct {
|
||||
NumOpenTypes int
|
||||
// Total number of open numbers for the overload
|
||||
NumOpenNumbers int
|
||||
// Index to the first open type in BuiltinTable.OpenTypes
|
||||
// Index to the first open type in IntrinsicTable.OpenTypes
|
||||
OpenTypesOffset *int
|
||||
// Index to the first open number in BuiltinTable.OpenNumbers
|
||||
// Index to the first open number in IntrinsicTable.OpenNumbers
|
||||
OpenNumbersOffset *int
|
||||
// Index to the first parameter in BuiltinTable.Parameters
|
||||
// Index to the first parameter in IntrinsicTable.Parameters
|
||||
ParametersOffset *int
|
||||
// Index into BuiltinTable.MatcherIndices, beginning the list of matchers
|
||||
// Index into IntrinsicTable.MatcherIndices, beginning the list of matchers
|
||||
// required to match the return type. The matcher indices index
|
||||
// into BuiltinTable::TMatchers and / or BuiltinTable::NMatchers.
|
||||
// into IntrinsicTable::TMatchers and / or IntrinsicTable::NMatchers.
|
||||
// These indices are consumed by the matchers themselves.
|
||||
// The first index is always a TypeMatcher.
|
||||
ReturnMatcherIndicesOffset *int
|
||||
@@ -102,17 +103,18 @@ type Overload struct {
|
||||
IsDeprecated bool
|
||||
}
|
||||
|
||||
// Function is used to create the C++ IntrinsicInfo structure
|
||||
type Function struct {
|
||||
// Intrinsic is used to create the C++ IntrinsicInfo structure
|
||||
type Intrinsic struct {
|
||||
Name string
|
||||
OverloadDescriptions []string
|
||||
NumOverloads int
|
||||
OverloadsOffset *int
|
||||
}
|
||||
|
||||
// Helper for building the BuiltinTable
|
||||
type BuiltinTableBuilder struct {
|
||||
// Helper for building the IntrinsicTable
|
||||
type IntrinsicTableBuilder struct {
|
||||
// The output of the builder
|
||||
BuiltinTable
|
||||
IntrinsicTable
|
||||
|
||||
// Lookup tables.
|
||||
// These are packed (compressed) once all the entries have been added.
|
||||
@@ -127,7 +129,7 @@ type BuiltinTableBuilder struct {
|
||||
|
||||
// Helper for building a single overload
|
||||
type overloadBuilder struct {
|
||||
*BuiltinTableBuilder
|
||||
*IntrinsicTableBuilder
|
||||
// Maps TemplateParam to index in openTypes
|
||||
openTypeIndex map[sem.TemplateParam]int
|
||||
// Maps TemplateParam to index in openNumbers
|
||||
@@ -138,9 +140,9 @@ type overloadBuilder struct {
|
||||
openNumbers []OpenNumber
|
||||
// All parameters declared by the overload
|
||||
parameters []Parameter
|
||||
// Index into BuiltinTable.MatcherIndices, beginning the list of matchers
|
||||
// Index into IntrinsicTable.MatcherIndices, beginning the list of matchers
|
||||
// required to match the return type. The matcher indices index
|
||||
// into BuiltinTable::TMatchers and / or BuiltinTable::NMatchers.
|
||||
// into IntrinsicTable::TMatchers and / or IntrinsicTable::NMatchers.
|
||||
// These indices are consumed by the matchers themselves.
|
||||
// The first index is always a TypeMatcher.
|
||||
returnTypeMatcherIndicesOffset *int
|
||||
@@ -148,7 +150,7 @@ type overloadBuilder struct {
|
||||
|
||||
// layoutMatchers assigns each of the TMatchers and NMatchers a unique index
|
||||
// in the C++ Matchers::type and Matchers::number arrays, respectively.
|
||||
func (b *BuiltinTableBuilder) layoutMatchers(s *sem.Sem) {
|
||||
func (b *IntrinsicTableBuilder) layoutMatchers(s *sem.Sem) {
|
||||
// First MaxOpenTypes of TMatchers are open types
|
||||
b.TMatchers = make([]sem.Named, s.MaxOpenTypes)
|
||||
for _, m := range s.Types {
|
||||
@@ -169,11 +171,11 @@ func (b *BuiltinTableBuilder) layoutMatchers(s *sem.Sem) {
|
||||
}
|
||||
|
||||
// buildOverload constructs an Overload for a sem.Overload
|
||||
func (b *BuiltinTableBuilder) buildOverload(o *sem.Overload) (Overload, error) {
|
||||
func (b *IntrinsicTableBuilder) buildOverload(o *sem.Overload) (Overload, error) {
|
||||
ob := overloadBuilder{
|
||||
BuiltinTableBuilder: b,
|
||||
openTypeIndex: map[sem.TemplateParam]int{},
|
||||
openNumberIndex: map[sem.TemplateParam]int{},
|
||||
IntrinsicTableBuilder: b,
|
||||
openTypeIndex: map[sem.TemplateParam]int{},
|
||||
openNumberIndex: map[sem.TemplateParam]int{},
|
||||
}
|
||||
|
||||
if err := ob.buildOpenTypes(o); err != nil {
|
||||
@@ -279,7 +281,7 @@ func (b *overloadBuilder) buildReturnType(o *sem.Overload) error {
|
||||
}
|
||||
|
||||
// matcherIndex returns the index of TMatcher or NMatcher in
|
||||
// BuiltinTable.TMatcher or BuiltinTable.NMatcher, respectively.
|
||||
// IntrinsicTable.TMatcher or IntrinsicTable.NMatcher, respectively.
|
||||
func (b *overloadBuilder) matcherIndex(n sem.Named) (int, error) {
|
||||
switch n := n.(type) {
|
||||
case *sem.Type, *sem.TypeMatcher:
|
||||
@@ -342,10 +344,10 @@ func (b *overloadBuilder) collectMatcherIndices(fqn sem.FullyQualifiedName) ([]i
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// buildBuiltinTable builds the BuiltinTable from the semantic info
|
||||
func buildBuiltinTable(s *sem.Sem) (*BuiltinTable, error) {
|
||||
b := BuiltinTableBuilder{
|
||||
BuiltinTable: BuiltinTable{
|
||||
// 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{},
|
||||
@@ -359,22 +361,34 @@ func buildBuiltinTable(s *sem.Sem) (*BuiltinTable, error) {
|
||||
|
||||
b.layoutMatchers(s)
|
||||
|
||||
for _, f := range s.Functions {
|
||||
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
|
||||
buildIntrinsics := func(in []*sem.Intrinsic) ([]Intrinsic, error) {
|
||||
out := make([]Intrinsic, len(in))
|
||||
for i, f := range 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),
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
b.Functions = append(b.Functions, Function{
|
||||
OverloadDescriptions: overloadDescriptions,
|
||||
NumOverloads: len(overloads),
|
||||
OverloadsOffset: b.lut.overloads.Add(overloads),
|
||||
})
|
||||
var err error
|
||||
if b.Builtins, err = buildIntrinsics(s.Builtins); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if b.Operators, err = buildIntrinsics(s.Operators); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.lut.matcherIndices.Compact()
|
||||
@@ -383,5 +397,5 @@ func buildBuiltinTable(s *sem.Sem) (*BuiltinTable, error) {
|
||||
b.lut.parameters.Compact()
|
||||
b.lut.overloads.Compact()
|
||||
|
||||
return &b.BuiltinTable, nil
|
||||
return &b.IntrinsicTable, nil
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ type generator struct {
|
||||
s *sem.Sem
|
||||
t *template.Template
|
||||
cached struct {
|
||||
builtinTable *BuiltinTable // lazily built by builtinTable()
|
||||
permuter *Permuter // lazily built by permute()
|
||||
intrinsicTable *IntrinsicTable // lazily built by intrinsicTable()
|
||||
permuter *Permuter // lazily built by permute()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (g *generator) generate(tmpl string, w io.Writer, writeFile WriteFile) erro
|
||||
"IsDeclarable": isDeclarable,
|
||||
"IsFirstIn": isFirstIn,
|
||||
"IsLastIn": isLastIn,
|
||||
"BuiltinTable": g.builtinTable,
|
||||
"IntrinsicTable": g.intrinsicTable,
|
||||
"Permute": g.permute,
|
||||
"Eval": g.eval,
|
||||
"WriteFile": func(relpath, content string) (string, error) { return "", writeFile(relpath, content) },
|
||||
@@ -121,17 +121,17 @@ func (g *generator) eval(template string, args ...interface{}) (string, error) {
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
// builtinTable lazily calls and returns the result of buildBuiltinTable(),
|
||||
// intrinsicTable lazily calls and returns the result of buildIntrinsicTable(),
|
||||
// caching the result for repeated calls.
|
||||
func (g *generator) builtinTable() (*BuiltinTable, error) {
|
||||
if g.cached.builtinTable == nil {
|
||||
func (g *generator) intrinsicTable() (*IntrinsicTable, error) {
|
||||
if g.cached.intrinsicTable == nil {
|
||||
var err error
|
||||
g.cached.builtinTable, err = buildBuiltinTable(g.s)
|
||||
g.cached.intrinsicTable, err = buildIntrinsicTable(g.s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return g.cached.builtinTable, nil
|
||||
return g.cached.intrinsicTable, nil
|
||||
}
|
||||
|
||||
// permute lazily calls buildPermuter(), caching the result for repeated
|
||||
|
||||
@@ -74,7 +74,7 @@ func (p *Permuter) Permute(overload *sem.Overload) ([]Permutation, error) {
|
||||
permutate := func() error {
|
||||
o := sem.Overload{
|
||||
Decl: overload.Decl,
|
||||
Function: overload.Function,
|
||||
Intrinsic: overload.Intrinsic,
|
||||
CanBeUsedInStage: overload.CanBeUsedInStage,
|
||||
}
|
||||
for i, p := range overload.Parameters {
|
||||
|
||||
@@ -52,10 +52,6 @@ func (l *lexer) lex() error {
|
||||
l.next()
|
||||
case '\n':
|
||||
l.next()
|
||||
case '<':
|
||||
l.tok(1, tok.Lt)
|
||||
case '>':
|
||||
l.tok(1, tok.Gt)
|
||||
case '(':
|
||||
l.tok(1, tok.Lparen)
|
||||
case ')':
|
||||
@@ -68,8 +64,14 @@ func (l *lexer) lex() error {
|
||||
l.tok(1, tok.Colon)
|
||||
case ',':
|
||||
l.tok(1, tok.Comma)
|
||||
case '|':
|
||||
l.tok(1, tok.Or)
|
||||
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 '"':
|
||||
start := l.loc
|
||||
l.next() // Skip opening quote
|
||||
@@ -81,13 +83,16 @@ func (l *lexer) lex() error {
|
||||
l.next() // Skip closing quote
|
||||
default:
|
||||
switch {
|
||||
case l.peek(1) == '/':
|
||||
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.Ldeco):
|
||||
case l.match("]]", tok.Rdeco):
|
||||
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("match", tok.Match):
|
||||
@@ -95,6 +100,19 @@ func (l *lexer) lex() error {
|
||||
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.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]))
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ func TestLexTokens(t *testing.T) {
|
||||
{"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),
|
||||
}}},
|
||||
@@ -76,6 +79,45 @@ func TestLexTokens(t *testing.T) {
|
||||
{"}", 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.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.Ldeco, Runes: []rune("[["), Source: tok.Source{
|
||||
S: loc(1, 1, 0), E: loc(1, 3, 2),
|
||||
}}},
|
||||
@@ -91,6 +133,9 @@ func TestLexTokens(t *testing.T) {
|
||||
{"|", 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),
|
||||
}}},
|
||||
@@ -134,10 +179,14 @@ func TestErrors(t *testing.T) {
|
||||
for _, test := range []test{
|
||||
{" \"abc", "test.txt:1:2 unterminated string"},
|
||||
{" \"abc\n", "test.txt:1:2 unterminated string"},
|
||||
{"*", "test.txt:1:1: unexpected '*'"},
|
||||
{"£", "test.txt:1:1: unexpected '£'"},
|
||||
} {
|
||||
got, err := lexer.Lex([]rune(test.src), "test.txt")
|
||||
if gotErr := err.Error(); test.expect != gotErr {
|
||||
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 {
|
||||
|
||||
@@ -66,7 +66,10 @@ func (p *parser) parse() (*ast.AST, error) {
|
||||
out.Types = append(out.Types, p.typeDecl(decorations))
|
||||
decorations = nil
|
||||
case tok.Function:
|
||||
out.Functions = append(out.Functions, p.functionDecl(decorations))
|
||||
out.Builtins = append(out.Builtins, p.builtinDecl(decorations))
|
||||
decorations = nil
|
||||
case tok.Operator:
|
||||
out.Operators = append(out.Operators, p.operatorDecl(decorations))
|
||||
decorations = nil
|
||||
default:
|
||||
p.err = fmt.Errorf("%v unexpected token '%v'", t.Source, t.Kind)
|
||||
@@ -153,11 +156,12 @@ func (p *parser) decorations() ast.Decorations {
|
||||
return out
|
||||
}
|
||||
|
||||
func (p *parser) functionDecl(decos ast.Decorations) ast.FunctionDecl {
|
||||
func (p *parser) builtinDecl(decos ast.Decorations) ast.IntrinsicDecl {
|
||||
p.expect(tok.Function, "function declaration")
|
||||
name := p.expect(tok.Identifier, "function name")
|
||||
f := ast.FunctionDecl{
|
||||
f := ast.IntrinsicDecl{
|
||||
Source: name.Source,
|
||||
Kind: ast.Builtin,
|
||||
Decorations: decos,
|
||||
Name: string(name.Runes),
|
||||
}
|
||||
@@ -172,6 +176,25 @@ func (p *parser) functionDecl(decos ast.Decorations) ast.FunctionDecl {
|
||||
return f
|
||||
}
|
||||
|
||||
func (p *parser) operatorDecl(decos ast.Decorations) ast.IntrinsicDecl {
|
||||
p.expect(tok.Operator, "operator declaration")
|
||||
name := p.next()
|
||||
f := ast.IntrinsicDecl{
|
||||
Source: name.Source,
|
||||
Kind: ast.Operator,
|
||||
Decorations: 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")
|
||||
@@ -270,20 +293,6 @@ func (p *parser) ident(use string) string {
|
||||
return string(p.expect(tok.Identifier, use).Runes)
|
||||
}
|
||||
|
||||
// TODO(bclayton): Currently unused, but will be needed for integer bounds
|
||||
// func (p *parser) integer(use string) int {
|
||||
// t := p.expect(tok.Integer, use)
|
||||
// if t.Kind != tok.Integer {
|
||||
// return 0
|
||||
// }
|
||||
// i, err := strconv.Atoi(string(t.Runes))
|
||||
// if err != nil {
|
||||
// p.err = err
|
||||
// return 0
|
||||
// }
|
||||
// return i
|
||||
// }
|
||||
|
||||
func (p *parser) match(kind tok.Kind) *tok.Token {
|
||||
if p.err != nil || len(p.tokens) == 0 {
|
||||
return nil
|
||||
@@ -296,6 +305,18 @@ func (p *parser) match(kind tok.Kind) *tok.Token {
|
||||
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 {
|
||||
|
||||
@@ -19,170 +19,369 @@ import (
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/cmd/intrinsic-gen/ast"
|
||||
"dawn.googlesource.com/dawn/tools/src/cmd/intrinsic-gen/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 {
|
||||
src string
|
||||
expect ast.AST
|
||||
location string
|
||||
src string
|
||||
expect ast.AST
|
||||
}
|
||||
|
||||
for _, test := range []test{
|
||||
{"enum E {}", ast.AST{
|
||||
Enums: []ast.EnumDecl{{Name: "E"}},
|
||||
}},
|
||||
{"enum E { A [[deco]] B C }", ast.AST{
|
||||
Enums: []ast.EnumDecl{{
|
||||
Name: "E",
|
||||
Entries: []ast.EnumEntry{
|
||||
{Name: "A"},
|
||||
{
|
||||
Decorations: ast.Decorations{{Name: "deco"}},
|
||||
Name: "B",
|
||||
},
|
||||
{Name: "C"},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"type T", ast.AST{
|
||||
Types: []ast.TypeDecl{{Name: "T"}},
|
||||
}},
|
||||
{"type T<A, B, C>", ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Name: "T",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{Name: "A"},
|
||||
{Name: "B"},
|
||||
{Name: "C"},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"[[deco]] type T", ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Decorations: ast.Decorations{
|
||||
{Name: "deco"},
|
||||
},
|
||||
Name: "T",
|
||||
}},
|
||||
}},
|
||||
{`[[deco("a", "b")]] type T`, ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Decorations: ast.Decorations{
|
||||
{Name: "deco", Values: []string{"a", "b"}},
|
||||
},
|
||||
Name: "T",
|
||||
}},
|
||||
}},
|
||||
{"match M : A", ast.AST{
|
||||
Matchers: []ast.MatcherDecl{{
|
||||
Name: "M",
|
||||
Options: ast.MatcherOptions{
|
||||
ast.TemplatedName{Name: "A"},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"match M : A | B", ast.AST{
|
||||
Matchers: []ast.MatcherDecl{{
|
||||
Name: "M",
|
||||
Options: ast.MatcherOptions{
|
||||
ast.TemplatedName{Name: "A"},
|
||||
ast.TemplatedName{Name: "B"},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"fn F()", ast.AST{
|
||||
Functions: []ast.FunctionDecl{{
|
||||
Name: "F",
|
||||
}},
|
||||
}},
|
||||
{"[[deco]] fn F()", ast.AST{
|
||||
Functions: []ast.FunctionDecl{{
|
||||
Name: "F",
|
||||
Decorations: ast.Decorations{
|
||||
{Name: "deco"},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"fn F(a)", ast.AST{
|
||||
Functions: []ast.FunctionDecl{{
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"fn F(a: T)", ast.AST{
|
||||
Functions: []ast.FunctionDecl{{
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "T"}},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"fn F(a, b)", ast.AST{
|
||||
Functions: []ast.FunctionDecl{{
|
||||
Name: "F",
|
||||
Parameters: ast.Parameters{
|
||||
{Type: ast.TemplatedName{Name: "a"}},
|
||||
{Type: ast.TemplatedName{Name: "b"}},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"fn F<A : B<C>>()", ast.AST{
|
||||
Functions: []ast.FunctionDecl{{
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{
|
||||
Name: "A", Type: ast.TemplatedName{
|
||||
{
|
||||
utils.ThisLine(),
|
||||
"enum E {}",
|
||||
ast.AST{
|
||||
Enums: []ast.EnumDecl{{Name: "E"}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
"enum E { A [[deco]] B C }",
|
||||
ast.AST{
|
||||
Enums: []ast.EnumDecl{{
|
||||
Name: "E",
|
||||
Entries: []ast.EnumEntry{
|
||||
{Name: "A"},
|
||||
{
|
||||
Decorations: ast.Decorations{{
|
||||
Name: "deco",
|
||||
Values: []string{},
|
||||
}},
|
||||
Name: "B",
|
||||
TemplateArgs: ast.TemplatedNames{
|
||||
{Name: "C"},
|
||||
},
|
||||
{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(),
|
||||
"[[deco]] type T",
|
||||
ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Decorations: ast.Decorations{
|
||||
{Name: "deco", Values: []string{}},
|
||||
},
|
||||
Name: "T",
|
||||
}},
|
||||
},
|
||||
}, { ///////////////////////////////////////////////////////////////////
|
||||
utils.ThisLine(),
|
||||
`[[deco("a", "b")]] type T`, ast.AST{
|
||||
Types: []ast.TypeDecl{{
|
||||
Decorations: ast.Decorations{
|
||||
{Name: "deco", Values: []string{"a", "b"}},
|
||||
},
|
||||
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(),
|
||||
"[[deco]] fn F()",
|
||||
ast.AST{
|
||||
Builtins: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Builtin,
|
||||
Name: "F",
|
||||
Decorations: ast.Decorations{
|
||||
{Name: "deco", 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"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"fn F<T>(a: X, b: Y<T>)", ast.AST{
|
||||
Functions: []ast.FunctionDecl{{
|
||||
Name: "F",
|
||||
TemplateParams: ast.TemplateParams{
|
||||
{Name: "T"},
|
||||
},
|
||||
Parameters: ast.Parameters{
|
||||
{Name: "a", Type: ast.TemplatedName{Name: "X"}},
|
||||
{Name: "b", Type: ast.TemplatedName{
|
||||
Name: "Y",
|
||||
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(),
|
||||
"[[deco]] op F()",
|
||||
ast.AST{
|
||||
Operators: []ast.IntrinsicDecl{{
|
||||
Kind: ast.Operator,
|
||||
Name: "F",
|
||||
Decorations: ast.Decorations{
|
||||
{Name: "deco", 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(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{},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
{"fn F() -> X", ast.AST{
|
||||
Functions: []ast.FunctionDecl{{
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{Name: "X"},
|
||||
}},
|
||||
}},
|
||||
{"fn F() -> X<T>", ast.AST{
|
||||
Functions: []ast.FunctionDecl{{
|
||||
Name: "F",
|
||||
ReturnType: &ast.TemplatedName{
|
||||
Name: "X",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
} {
|
||||
got, err := parser.Parse(test.src, "file.txt")
|
||||
if err != nil {
|
||||
t.Errorf("While parsing:\n%s\nParse() returned error: %v", test.src, err)
|
||||
t.Errorf("\n%v\nWhile parsing:\n%s\nParse() returned error: %v",
|
||||
test.location, test.src, err)
|
||||
continue
|
||||
}
|
||||
|
||||
gotStr, expectStr := got.String(), test.expect.String()
|
||||
if gotStr != expectStr {
|
||||
t.Errorf("While parsing:\n%s\nGot:\n%s\nExpected:\n%s", test.src, gotStr, expectStr)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,10 +393,22 @@ func TestErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range []test{
|
||||
{"+", "test.txt:1:1: unexpected '+'"},
|
||||
{"123", "test.txt:1:1 unexpected token 'integer'"},
|
||||
{"[[123]]", "test.txt:1:3 expected 'ident' for decoration name, got 'integer'"},
|
||||
{"[[abc", "expected ']]' for decoration list, but reached end of file"},
|
||||
{
|
||||
"£",
|
||||
"test.txt:1:1: unexpected '£'",
|
||||
},
|
||||
{
|
||||
"123",
|
||||
"test.txt:1:1 unexpected token 'integer'",
|
||||
},
|
||||
{
|
||||
"[[123]]",
|
||||
"test.txt:1:3 expected 'ident' for decoration name, got 'integer'",
|
||||
},
|
||||
{
|
||||
"[[abc",
|
||||
"expected ']]' for decoration list, but reached end of file",
|
||||
},
|
||||
} {
|
||||
got, err := parser.Parse(test.src, "test.txt")
|
||||
if gotErr := err.Error(); test.expect != gotErr {
|
||||
|
||||
@@ -28,7 +28,8 @@ type resolver struct {
|
||||
s *sem.Sem
|
||||
|
||||
globals scope
|
||||
functions map[string]*sem.Function
|
||||
builtins map[string]*sem.Intrinsic
|
||||
operators map[string]*sem.Intrinsic
|
||||
enumEntryMatchers map[*sem.EnumEntry]*sem.EnumMatcher
|
||||
}
|
||||
|
||||
@@ -38,7 +39,8 @@ func Resolve(a *ast.AST) (*sem.Sem, error) {
|
||||
a: a,
|
||||
s: sem.New(),
|
||||
globals: newScope(nil),
|
||||
functions: map[string]*sem.Function{},
|
||||
builtins: map[string]*sem.Intrinsic{},
|
||||
operators: map[string]*sem.Intrinsic{},
|
||||
enumEntryMatchers: map[*sem.EnumEntry]*sem.EnumMatcher{},
|
||||
}
|
||||
// Declare and resolve all the enumerators
|
||||
@@ -59,9 +61,15 @@ func Resolve(a *ast.AST) (*sem.Sem, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Declare and resolve the functions
|
||||
for _, f := range a.Functions {
|
||||
if err := r.function(f); err != nil {
|
||||
// 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 operators
|
||||
for _, o := range a.Operators {
|
||||
if err := r.intrinsic(o, r.operators, &r.s.Operators); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -220,18 +228,21 @@ func (r *resolver) matcher(a ast.MatcherDecl) error {
|
||||
return fmt.Errorf("'%v' cannot be used for matcher", a.Name)
|
||||
}
|
||||
|
||||
// function() resolves a function overload declaration.
|
||||
// The the first overload for the function creates and appends the sem.Function
|
||||
// to Sem.Functions. Subsequent overloads append their resolved overload to the
|
||||
// sem.Function.Overloads list.
|
||||
func (r *resolver) function(a ast.FunctionDecl) error {
|
||||
// If this is the first overload of the function, create and register the
|
||||
// semantic function.
|
||||
f := r.functions[a.Name]
|
||||
if f == nil {
|
||||
f = &sem.Function{Name: a.Name}
|
||||
r.functions[a.Name] = f
|
||||
r.s.Functions = append(r.s.Functions, f)
|
||||
// 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
|
||||
@@ -246,7 +257,7 @@ func (r *resolver) function(a ast.FunctionDecl) error {
|
||||
// Construct the semantic overload
|
||||
overload := &sem.Overload{
|
||||
Decl: a,
|
||||
Function: f,
|
||||
Intrinsic: intrinsic,
|
||||
Parameters: make([]sem.Parameter, len(a.Parameters)),
|
||||
TemplateParams: templateParams,
|
||||
}
|
||||
@@ -285,8 +296,8 @@ func (r *resolver) function(a ast.FunctionDecl) error {
|
||||
return fmt.Errorf("%v unknown decoration", a.Decorations[0].Source)
|
||||
}
|
||||
|
||||
// Append the overload to the function
|
||||
f.Overloads = append(f.Overloads, overload)
|
||||
// Append the overload to the intrinsic
|
||||
intrinsic.Overloads = append(intrinsic.Overloads, overload)
|
||||
|
||||
// Sort the template parameters by resolved type. Append these to
|
||||
// sem.Overload.OpenTypes or sem.Overload.OpenNumbers based on their kind.
|
||||
@@ -307,6 +318,10 @@ func (r *resolver) function(a ast.FunctionDecl) error {
|
||||
r.s.MaxOpenNumbers = len(overload.OpenNumbers)
|
||||
}
|
||||
|
||||
if a.Kind == ast.Operator && (len(a.Parameters) < 1 || len(a.Parameters) > 2) {
|
||||
return fmt.Errorf("%v operators must have either 1 or 2 parameters", a.Source)
|
||||
}
|
||||
|
||||
// Resolve the parameters
|
||||
for i, p := range a.Parameters {
|
||||
usage, err := r.fullyQualifiedName(&s, p.Type)
|
||||
@@ -495,11 +510,11 @@ func (r *resolver) lookupNamed(s *scope, a ast.TemplatedName) (sem.Named, error)
|
||||
}
|
||||
|
||||
// calculateUniqueParameterNames() iterates over all the parameters of all
|
||||
// overloads, calculating the list of unique parameter names
|
||||
// builtin overloads, calculating the list of unique parameter names
|
||||
func (r *resolver) calculateUniqueParameterNames() []string {
|
||||
set := map[string]struct{}{"": {}}
|
||||
names := []string{}
|
||||
for _, f := range r.s.Functions {
|
||||
for _, f := range r.s.Builtins {
|
||||
for _, o := range f.Overloads {
|
||||
for _, p := range o.Parameters {
|
||||
if _, dup := set[p.Name]; !dup {
|
||||
|
||||
@@ -139,7 +139,7 @@ fn f<E: m>()`,
|
||||
`
|
||||
type f32
|
||||
type T<x>
|
||||
fn f(T<T<f32>>)`,
|
||||
fn f(T< T<f32> >)`,
|
||||
success,
|
||||
}, {
|
||||
`enum E {A A}`,
|
||||
@@ -299,6 +299,70 @@ 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`,
|
||||
}, {
|
||||
`
|
||||
enum E { a }
|
||||
type T<X: a>`,
|
||||
`file.txt:2:8 invalid template parameter type 'a'`,
|
||||
|
||||
@@ -26,7 +26,8 @@ type Sem struct {
|
||||
Types []*Type
|
||||
TypeMatchers []*TypeMatcher
|
||||
EnumMatchers []*EnumMatcher
|
||||
Functions []*Function
|
||||
Builtins []*Intrinsic
|
||||
Operators []*Intrinsic
|
||||
// Maximum number of open-types used across all builtins
|
||||
MaxOpenTypes int
|
||||
// Maximum number of open-numbers used across all builtins
|
||||
@@ -42,7 +43,8 @@ func New() *Sem {
|
||||
Types: []*Type{},
|
||||
TypeMatchers: []*TypeMatcher{},
|
||||
EnumMatchers: []*EnumMatcher{},
|
||||
Functions: []*Function{},
|
||||
Builtins: []*Intrinsic{},
|
||||
Operators: []*Intrinsic{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,16 +123,16 @@ type TemplateNumberParam struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// Function describes the overloads of a builtin function
|
||||
type Function struct {
|
||||
// Intrinsic describes the overloads of a builtin or operator
|
||||
type Intrinsic struct {
|
||||
Name string
|
||||
Overloads []*Overload
|
||||
}
|
||||
|
||||
// Overload describes a single overload of a function
|
||||
// Overload describes a single overload of a builtin or operator
|
||||
type Overload struct {
|
||||
Decl ast.FunctionDecl
|
||||
Function *Function
|
||||
Decl ast.IntrinsicDecl
|
||||
Intrinsic *Intrinsic
|
||||
TemplateParams []TemplateParam
|
||||
OpenTypes []*TemplateTypeParam
|
||||
OpenNumbers []TemplateParam
|
||||
@@ -164,7 +166,13 @@ func (u StageUses) List() []string {
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (o Overload) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprintf(w, "fn %v", o.Function.Name)
|
||||
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 {
|
||||
|
||||
@@ -29,12 +29,17 @@ const (
|
||||
String Kind = "string"
|
||||
Match Kind = "match"
|
||||
Function Kind = "fn"
|
||||
Operator Kind = "op"
|
||||
Type Kind = "type"
|
||||
Enum Kind = "enum"
|
||||
Colon Kind = ":"
|
||||
Comma Kind = ","
|
||||
Shl Kind = "<<"
|
||||
Shr Kind = ">>"
|
||||
Lt Kind = "<"
|
||||
Le Kind = "<="
|
||||
Gt Kind = ">"
|
||||
Ge Kind = ">="
|
||||
Lbrace Kind = "{"
|
||||
Rbrace Kind = "}"
|
||||
Ldeco Kind = "[["
|
||||
@@ -43,6 +48,18 @@ const (
|
||||
Rparen Kind = ")"
|
||||
Or Kind = "|"
|
||||
Arrow Kind = "->"
|
||||
Star Kind = "*"
|
||||
Divide Kind = "/"
|
||||
Modulo Kind = "%"
|
||||
Xor Kind = "^"
|
||||
Plus Kind = "+"
|
||||
Minus Kind = "-"
|
||||
And Kind = "&"
|
||||
AndAnd Kind = "&&"
|
||||
OrOr Kind = "||"
|
||||
NotEqual Kind = "!="
|
||||
Equal Kind = "=="
|
||||
Assign Kind = "="
|
||||
)
|
||||
|
||||
// Invalid represents an invalid token
|
||||
|
||||
Reference in New Issue
Block a user