Add cmd/intrinsic-gen parser and ast
Ground work for the new intrinsic definition parser. Bug: tint:832 Change-Id: I0aff7f8cf5db4b6aa3a56939b944a37abc360cc7 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/52501 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
parent
c95e05784d
commit
6cf2fd42c5
|
@ -0,0 +1,290 @@
|
|||
// 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/tint/tools/src/cmd/intrinsic-gen/tok"
|
||||
)
|
||||
|
||||
// AST is the parsed syntax tree of the intrinsic definition file
|
||||
type AST struct {
|
||||
Enums []EnumDecl
|
||||
Types []TypeDecl
|
||||
Matchers []MatcherDecl
|
||||
Functions []FunctionDecl
|
||||
}
|
||||
|
||||
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 _, f := range a.Functions {
|
||||
fmt.Fprintf(&sb, "%v", f)
|
||||
fmt.Fprintln(&sb)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// EnumDecl describes an enumerator
|
||||
type EnumDecl struct {
|
||||
Source tok.Source
|
||||
Name string
|
||||
Entries []string
|
||||
}
|
||||
|
||||
// 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, " %s\n", e)
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// FunctionDecl describes a function declaration
|
||||
type FunctionDecl struct {
|
||||
Source tok.Source
|
||||
Name string
|
||||
TemplateParams TemplateParams
|
||||
Parameters Parameters
|
||||
ReturnType *TemplatedName
|
||||
}
|
||||
|
||||
// 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 {
|
||||
fmt.Fprintf(w, " -> ")
|
||||
f.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.Format(w, verb)
|
||||
}
|
||||
fmt.Fprintf(w, ")")
|
||||
}
|
||||
|
||||
// Parameter describes a single parameter of a function
|
||||
type Parameter struct {
|
||||
Source tok.Source
|
||||
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
|
||||
Decorations Decorations
|
||||
Name string
|
||||
TemplateParams TemplateParams
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (p TypeDecl) Format(w fmt.State, verb rune) {
|
||||
if len(p.Decorations) > 0 {
|
||||
p.Decorations.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)
|
||||
}
|
||||
}
|
||||
|
||||
// Decorations is a list of Decoration
|
||||
// Example:
|
||||
// [[a(x), b(y)]]
|
||||
type Decorations []Decoration
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (l Decorations) Format(w fmt.State, verb rune) {
|
||||
fmt.Fprint(w, "[[")
|
||||
for i, d := range l {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(w, ", ")
|
||||
}
|
||||
d.Format(w, verb)
|
||||
}
|
||||
fmt.Fprint(w, "]]")
|
||||
}
|
||||
|
||||
// Take looks up the decoration with the given name. If the decoration is found
|
||||
// it is removed from the Decorations list and returned, otherwise nil is
|
||||
// returned and the Decorations are not altered.
|
||||
func (l *Decorations) Take(name string) *Decoration {
|
||||
for i, d := range *l {
|
||||
if d.Name == name {
|
||||
*l = append((*l)[:i], (*l)[i+1:]...)
|
||||
return &d
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decoration describes a single decoration
|
||||
// Example:
|
||||
// a(x)
|
||||
type Decoration struct {
|
||||
Source tok.Source
|
||||
Name string
|
||||
Values []string
|
||||
}
|
||||
|
||||
// Format implements the fmt.Formatter interface
|
||||
func (d Decoration) 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, ")")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
// 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 intrinsic definition
|
||||
// language
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"dawn.googlesource.com/tint/tools/src/cmd/intrinsic-gen/ast"
|
||||
"dawn.googlesource.com/tint/tools/src/cmd/intrinsic-gen/lexer"
|
||||
"dawn.googlesource.com/tint/tools/src/cmd/intrinsic-gen/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{}
|
||||
for p.err == nil {
|
||||
t := p.peek(0)
|
||||
if t == nil {
|
||||
break
|
||||
}
|
||||
switch t.Kind {
|
||||
case tok.Enum:
|
||||
out.Enums = append(out.Enums, p.enumDecl())
|
||||
case tok.Match:
|
||||
out.Matchers = append(out.Matchers, p.matcherDecl())
|
||||
case tok.Type, tok.Ldeco:
|
||||
out.Types = append(out.Types, p.typeDecl())
|
||||
case tok.Function:
|
||||
out.Functions = append(out.Functions, p.functionDecl())
|
||||
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.ident("enumerator value"))
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
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() ast.TypeDecl {
|
||||
decos := p.decorations()
|
||||
p.expect(tok.Type, "type declaration")
|
||||
name := p.expect(tok.Identifier, "type name")
|
||||
m := ast.TypeDecl{
|
||||
Source: name.Source,
|
||||
Decorations: decos,
|
||||
Name: string(name.Runes),
|
||||
}
|
||||
if p.peekIs(0, tok.Lt) {
|
||||
m.TemplateParams = p.templateParams()
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (p *parser) decorations() ast.Decorations {
|
||||
if p.match(tok.Ldeco) == nil {
|
||||
return nil
|
||||
}
|
||||
out := ast.Decorations{}
|
||||
for p.err == nil {
|
||||
name := p.expect(tok.Identifier, "decoration name")
|
||||
values := []string{}
|
||||
if p.match(tok.Lparen) != nil {
|
||||
for p.err == nil {
|
||||
values = append(values, p.string())
|
||||
if p.match(tok.Comma) == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
p.expect(tok.Rparen, "decoration values")
|
||||
}
|
||||
out = append(out, ast.Decoration{
|
||||
Source: name.Source,
|
||||
Name: string(name.Runes),
|
||||
Values: values,
|
||||
})
|
||||
if !p.peekIs(0, tok.Comma) {
|
||||
break
|
||||
}
|
||||
}
|
||||
p.expect(tok.Rdeco, "decoration list")
|
||||
return out
|
||||
}
|
||||
|
||||
func (p *parser) functionDecl() ast.FunctionDecl {
|
||||
p.expect(tok.Function, "function declaration")
|
||||
name := p.expect(tok.Identifier, "function name")
|
||||
f := ast.FunctionDecl{Source: name.Source, 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 {
|
||||
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),
|
||||
Type: p.templatedName(),
|
||||
}
|
||||
}
|
||||
// type
|
||||
ty := p.templatedName()
|
||||
return ast.Parameter{
|
||||
Source: ty.Source,
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
t := p.tokens[0]
|
||||
if t.Kind != kind {
|
||||
return nil
|
||||
}
|
||||
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]
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
// 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/tint/tools/src/cmd/intrinsic-gen/ast"
|
||||
"dawn.googlesource.com/tint/tools/src/cmd/intrinsic-gen/parser"
|
||||
)
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
type test struct {
|
||||
src string
|
||||
expect ast.AST
|
||||
}
|
||||
|
||||
for _, test := range []test{
|
||||
{"enum E {}", ast.AST{
|
||||
Enums: []ast.EnumDecl{{Name: "E"}},
|
||||
}},
|
||||
{"enum E { A B C }", ast.AST{
|
||||
Enums: []ast.EnumDecl{{
|
||||
Name: "E",
|
||||
Entries: []string{"A", "B", "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",
|
||||
}},
|
||||
}},
|
||||
{"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{
|
||||
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",
|
||||
TemplateArgs: []ast.TemplatedName{{Name: "T"}},
|
||||
}},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
{"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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: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 {
|
||||
t.Errorf(`Parse() returned error "%+v", expected error "%+v"`, gotErr, test.expect)
|
||||
}
|
||||
if got != nil {
|
||||
t.Errorf("Lex() returned non-nil for error")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue