mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-05 22:23:29 +00:00
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>
334 lines
7.9 KiB
Go
334 lines
7.9 KiB
Go
// 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/cmd/intrinsic-gen/ast"
|
|
"dawn.googlesource.com/dawn/tools/src/cmd/intrinsic-gen/lexer"
|
|
"dawn.googlesource.com/dawn/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{}
|
|
var decorations ast.Decorations
|
|
for p.err == nil {
|
|
t := p.peek(0)
|
|
if t == nil {
|
|
break
|
|
}
|
|
switch t.Kind {
|
|
case tok.Ldeco:
|
|
decorations = append(decorations, p.decorations()...)
|
|
case tok.Enum:
|
|
if len(decorations) > 0 {
|
|
p.err = fmt.Errorf("%v unexpected decoration", decorations[0].Source)
|
|
}
|
|
out.Enums = append(out.Enums, p.enumDecl())
|
|
case tok.Match:
|
|
if len(decorations) > 0 {
|
|
p.err = fmt.Errorf("%v unexpected decoration", decorations[0].Source)
|
|
}
|
|
out.Matchers = append(out.Matchers, p.matcherDecl())
|
|
case tok.Type:
|
|
out.Types = append(out.Types, p.typeDecl(decorations))
|
|
decorations = nil
|
|
case tok.Function:
|
|
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)
|
|
}
|
|
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.decorations()
|
|
name := p.expect(tok.Identifier, "enum entry")
|
|
return ast.EnumEntry{Source: name.Source, Decorations: 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.Decorations) ast.TypeDecl {
|
|
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) builtinDecl(decos ast.Decorations) ast.IntrinsicDecl {
|
|
p.expect(tok.Function, "function declaration")
|
|
name := p.expect(tok.Identifier, "function name")
|
|
f := ast.IntrinsicDecl{
|
|
Source: name.Source,
|
|
Kind: ast.Builtin,
|
|
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) 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")
|
|
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)
|
|
}
|
|
|
|
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]
|
|
}
|