summaryrefslogtreecommitdiff
path: root/vendor/github.com/bufbuild/protocompile/ast/file.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/bufbuild/protocompile/ast/file.go')
-rw-r--r--vendor/github.com/bufbuild/protocompile/ast/file.go358
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,
+ }
+}