diff options
Diffstat (limited to 'vendor/github.com/bufbuild/protocompile/ast/file.go')
| -rw-r--r-- | vendor/github.com/bufbuild/protocompile/ast/file.go | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/vendor/github.com/bufbuild/protocompile/ast/file.go b/vendor/github.com/bufbuild/protocompile/ast/file.go new file mode 100644 index 0000000..50d4ca9 --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/file.go @@ -0,0 +1,358 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// 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 + +import "fmt" + +// FileDeclNode is a placeholder interface for AST nodes that represent files. +// This allows NoSourceNode to be used in place of *FileNode for some usages. +type FileDeclNode interface { + NodeWithOptions + Name() string + NodeInfo(n Node) NodeInfo +} + +var _ FileDeclNode = (*FileNode)(nil) +var _ FileDeclNode = (*NoSourceNode)(nil) + +// FileNode is the root of the AST hierarchy. It represents an entire +// protobuf source file. +type FileNode struct { + compositeNode + fileInfo *FileInfo + + // A file has either a Syntax or Edition node, never both. + // If both are nil, neither declaration is present and the + // file is assumed to use "proto2" syntax. + Syntax *SyntaxNode + Edition *EditionNode + + Decls []FileElement + + // This synthetic node allows access to final comments and whitespace + EOF *RuneNode +} + +// NewFileNode creates a new *FileNode. The syntax parameter is optional. If it +// is absent, it means the file had no syntax declaration. +// +// This function panics if the concrete type of any element of decls is not +// from this package. +func NewFileNode(info *FileInfo, syntax *SyntaxNode, decls []FileElement, eof Token) *FileNode { + return newFileNode(info, syntax, nil, decls, eof) +} + +// NewFileNodeWithEdition creates a new *FileNode. The edition parameter is required. If a file +// has no edition declaration, use NewFileNode instead. +// +// This function panics if the concrete type of any element of decls is not +// from this package. +func NewFileNodeWithEdition(info *FileInfo, edition *EditionNode, decls []FileElement, eof Token) *FileNode { + if edition == nil { + panic("edition is nil") + } + return newFileNode(info, nil, edition, decls, eof) +} + +func newFileNode(info *FileInfo, syntax *SyntaxNode, edition *EditionNode, decls []FileElement, eof Token) *FileNode { + numChildren := len(decls) + 1 + if syntax != nil || edition != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + if syntax != nil { + children = append(children, syntax) + } else if edition != nil { + children = append(children, edition) + } + for _, decl := range decls { + switch decl := decl.(type) { + case *PackageNode, *ImportNode, *OptionNode, *MessageNode, + *EnumNode, *ExtendNode, *ServiceNode, *EmptyDeclNode: + default: + panic(fmt.Sprintf("invalid FileElement type: %T", decl)) + } + children = append(children, decl) + } + + eofNode := NewRuneNode(0, eof) + children = append(children, eofNode) + + return &FileNode{ + compositeNode: compositeNode{ + children: children, + }, + fileInfo: info, + Syntax: syntax, + Edition: edition, + Decls: decls, + EOF: eofNode, + } +} + +// NewEmptyFileNode returns an empty AST for a file with the given name. +func NewEmptyFileNode(filename string) *FileNode { + fileInfo := NewFileInfo(filename, []byte{}) + return NewFileNode(fileInfo, nil, nil, fileInfo.AddToken(0, 0)) +} + +func (f *FileNode) Name() string { + return f.fileInfo.Name() +} + +func (f *FileNode) NodeInfo(n Node) NodeInfo { + return f.fileInfo.NodeInfo(n) +} + +func (f *FileNode) TokenInfo(t Token) NodeInfo { + return f.fileInfo.TokenInfo(t) +} + +func (f *FileNode) ItemInfo(i Item) ItemInfo { + return f.fileInfo.ItemInfo(i) +} + +func (f *FileNode) GetItem(i Item) (Token, Comment) { + return f.fileInfo.GetItem(i) +} + +func (f *FileNode) Items() Sequence[Item] { + return f.fileInfo.Items() +} + +func (f *FileNode) Tokens() Sequence[Token] { + return f.fileInfo.Tokens() +} + +func (f *FileNode) RangeOptions(fn func(*OptionNode) bool) { + for _, decl := range f.Decls { + if opt, ok := decl.(*OptionNode); ok { + if !fn(opt) { + return + } + } + } +} + +// FileElement is an interface implemented by all AST nodes that are +// allowed as top-level declarations in the file. +type FileElement interface { + Node + fileElement() +} + +var _ FileElement = (*ImportNode)(nil) +var _ FileElement = (*PackageNode)(nil) +var _ FileElement = (*OptionNode)(nil) +var _ FileElement = (*MessageNode)(nil) +var _ FileElement = (*EnumNode)(nil) +var _ FileElement = (*ExtendNode)(nil) +var _ FileElement = (*ServiceNode)(nil) +var _ FileElement = (*EmptyDeclNode)(nil) + +// SyntaxNode represents a syntax declaration, which if present must be +// the first non-comment content. Example: +// +// syntax = "proto2"; +// +// Files that don't have a syntax node are assumed to use proto2 syntax. +type SyntaxNode struct { + compositeNode + Keyword *KeywordNode + Equals *RuneNode + Syntax StringValueNode + Semicolon *RuneNode +} + +// NewSyntaxNode creates a new *SyntaxNode. All four arguments must be non-nil: +// - keyword: The token corresponding to the "syntax" keyword. +// - equals: The token corresponding to the "=" rune. +// - syntax: The actual syntax value, e.g. "proto2" or "proto3". +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewSyntaxNode(keyword *KeywordNode, equals *RuneNode, syntax StringValueNode, semicolon *RuneNode) *SyntaxNode { + if keyword == nil { + panic("keyword is nil") + } + if equals == nil { + panic("equals is nil") + } + if syntax == nil { + panic("syntax is nil") + } + var children []Node + if semicolon == nil { + children = []Node{keyword, equals, syntax} + } else { + children = []Node{keyword, equals, syntax, semicolon} + } + return &SyntaxNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Equals: equals, + Syntax: syntax, + Semicolon: semicolon, + } +} + +// EditionNode represents an edition declaration, which if present must be +// the first non-comment content. Example: +// +// edition = "2023"; +// +// Files may include either an edition node or a syntax node, but not both. +// If neither are present, the file is assumed to use proto2 syntax. +type EditionNode struct { + compositeNode + Keyword *KeywordNode + Equals *RuneNode + Edition StringValueNode + Semicolon *RuneNode +} + +// NewEditionNode creates a new *EditionNode. All four arguments must be non-nil: +// - keyword: The token corresponding to the "edition" keyword. +// - equals: The token corresponding to the "=" rune. +// - edition: The actual edition value, e.g. "2023". +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewEditionNode(keyword *KeywordNode, equals *RuneNode, edition StringValueNode, semicolon *RuneNode) *EditionNode { + if keyword == nil { + panic("keyword is nil") + } + if equals == nil { + panic("equals is nil") + } + if edition == nil { + panic("edition is nil") + } + if semicolon == nil { + panic("semicolon is nil") + } + children := []Node{keyword, equals, edition, semicolon} + return &EditionNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Equals: equals, + Edition: edition, + Semicolon: semicolon, + } +} + +// ImportNode represents an import statement. Example: +// +// import "google/protobuf/empty.proto"; +type ImportNode struct { + compositeNode + Keyword *KeywordNode + // Optional; if present indicates this is a public import + Public *KeywordNode + // Optional; if present indicates this is a weak import + Weak *KeywordNode + Name StringValueNode + Semicolon *RuneNode +} + +// NewImportNode creates a new *ImportNode. The public and weak arguments are optional +// and only one or the other (or neither) may be specified, not both. When public is +// non-nil, it indicates the "public" keyword in the import statement and means this is +// a public import. When weak is non-nil, it indicates the "weak" keyword in the import +// statement and means this is a weak import. When both are nil, this is a normal import. +// The other arguments must be non-nil: +// - keyword: The token corresponding to the "import" keyword. +// - public: The token corresponding to the optional "public" keyword. +// - weak: The token corresponding to the optional "weak" keyword. +// - name: The actual imported file name. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewImportNode(keyword *KeywordNode, public *KeywordNode, weak *KeywordNode, name StringValueNode, semicolon *RuneNode) *ImportNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + numChildren := 2 + if semicolon == nil { + numChildren++ + } + if public != nil || weak != nil { + numChildren++ + } + children := make([]Node, 0, numChildren) + children = append(children, keyword) + if public != nil { + children = append(children, public) + } else if weak != nil { + children = append(children, weak) + } + children = append(children, name) + if semicolon != nil { + children = append(children, semicolon) + } + + return &ImportNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Public: public, + Weak: weak, + Name: name, + Semicolon: semicolon, + } +} + +func (*ImportNode) fileElement() {} + +// PackageNode represents a package declaration. Example: +// +// package foobar.com; +type PackageNode struct { + compositeNode + Keyword *KeywordNode + Name IdentValueNode + Semicolon *RuneNode +} + +func (*PackageNode) fileElement() {} + +// NewPackageNode creates a new *PackageNode. All three arguments must be non-nil: +// - keyword: The token corresponding to the "package" keyword. +// - name: The package name declared for the file. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewPackageNode(keyword *KeywordNode, name IdentValueNode, semicolon *RuneNode) *PackageNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + var children []Node + if semicolon == nil { + children = []Node{keyword, name} + } else { + children = []Node{keyword, name, semicolon} + } + return &PackageNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + Semicolon: semicolon, + } +} |
