github.com/bakjos/protoreflect@v1.9.2/desc/protoparse/ast/file.go (about)

     1  package ast
     2  
     3  import "fmt"
     4  
     5  // FileDeclNode is a placeholder interface for AST nodes that represent files.
     6  // This allows NoSourceNode to be used in place of *FileNode for some usages.
     7  type FileDeclNode interface {
     8  	Node
     9  	GetSyntax() Node
    10  }
    11  
    12  var _ FileDeclNode = (*FileNode)(nil)
    13  var _ FileDeclNode = NoSourceNode{}
    14  
    15  // FileNode is the root of the AST hierarchy. It represents an entire
    16  // protobuf source file.
    17  type FileNode struct {
    18  	compositeNode
    19  	Syntax *SyntaxNode // nil if file has no syntax declaration
    20  	Decls  []FileElement
    21  
    22  	// Any comments that follow the last token in the file.
    23  	FinalComments []Comment
    24  	// Any whitespace at the end of the file (after the last token or
    25  	// last comment in the file).
    26  	FinalWhitespace string
    27  }
    28  
    29  // NewFileElement creates a new *FileNode. The syntax parameter is optional. If it
    30  // is absent, it means the file had no syntax declaration.
    31  //
    32  // This function panics if the concrete type of any element of decls is not
    33  // from this package.
    34  func NewFileNode(syntax *SyntaxNode, decls []FileElement) *FileNode {
    35  	numChildren := len(decls)
    36  	if syntax != nil {
    37  		numChildren++
    38  	}
    39  	children := make([]Node, 0, numChildren)
    40  	if syntax != nil {
    41  		children = append(children, syntax)
    42  	}
    43  	for _, decl := range decls {
    44  		children = append(children, decl)
    45  	}
    46  
    47  	for _, decl := range decls {
    48  		switch decl := decl.(type) {
    49  		case *PackageNode, *ImportNode, *OptionNode, *MessageNode,
    50  			*EnumNode, *ExtendNode, *ServiceNode, *EmptyDeclNode:
    51  		default:
    52  			panic(fmt.Sprintf("invalid FileElement type: %T", decl))
    53  		}
    54  	}
    55  
    56  	return &FileNode{
    57  		compositeNode: compositeNode{
    58  			children: children,
    59  		},
    60  		Syntax: syntax,
    61  		Decls:  decls,
    62  	}
    63  }
    64  
    65  func NewEmptyFileNode(filename string) *FileNode {
    66  	return &FileNode{
    67  		compositeNode: compositeNode{
    68  			children: []Node{NewNoSourceNode(filename)},
    69  		},
    70  	}
    71  }
    72  
    73  func (f *FileNode) GetSyntax() Node {
    74  	return f.Syntax
    75  }
    76  
    77  // FileElement is an interface implemented by all AST nodes that are
    78  // allowed as top-level declarations in the file.
    79  type FileElement interface {
    80  	Node
    81  	fileElement()
    82  }
    83  
    84  var _ FileElement = (*ImportNode)(nil)
    85  var _ FileElement = (*PackageNode)(nil)
    86  var _ FileElement = (*OptionNode)(nil)
    87  var _ FileElement = (*MessageNode)(nil)
    88  var _ FileElement = (*EnumNode)(nil)
    89  var _ FileElement = (*ExtendNode)(nil)
    90  var _ FileElement = (*ServiceNode)(nil)
    91  var _ FileElement = (*EmptyDeclNode)(nil)
    92  
    93  // SyntaxNode represents a syntax declaration, which if present must be
    94  // the first non-comment content. Example:
    95  //
    96  //  syntax = "proto2";
    97  //
    98  // Files that don't have a syntax node are assumed to use proto2 syntax.
    99  type SyntaxNode struct {
   100  	compositeNode
   101  	Keyword   *KeywordNode
   102  	Equals    *RuneNode
   103  	Syntax    StringValueNode
   104  	Semicolon *RuneNode
   105  }
   106  
   107  // NewSyntaxNode creates a new *SyntaxNode. All four arguments must be non-nil:
   108  //  - keyword: The token corresponding to the "syntax" keyword.
   109  //  - equals: The token corresponding to the "=" rune.
   110  //  - syntax: The actual syntax value, e.g. "proto2" or "proto3".
   111  //  - semicolon: The token corresponding to the ";" rune that ends the declaration.
   112  func NewSyntaxNode(keyword *KeywordNode, equals *RuneNode, syntax StringValueNode, semicolon *RuneNode) *SyntaxNode {
   113  	if keyword == nil {
   114  		panic("keyword is nil")
   115  	}
   116  	if equals == nil {
   117  		panic("equals is nil")
   118  	}
   119  	if syntax == nil {
   120  		panic("syntax is nil")
   121  	}
   122  	if semicolon == nil {
   123  		panic("semicolon is nil")
   124  	}
   125  	children := []Node{keyword, equals, syntax, semicolon}
   126  	return &SyntaxNode{
   127  		compositeNode: compositeNode{
   128  			children: children,
   129  		},
   130  		Keyword:   keyword,
   131  		Equals:    equals,
   132  		Syntax:    syntax,
   133  		Semicolon: semicolon,
   134  	}
   135  }
   136  
   137  // ImportNode represents an import statement. Example:
   138  //
   139  //  import "google/protobuf/empty.proto";
   140  type ImportNode struct {
   141  	compositeNode
   142  	Keyword *KeywordNode
   143  	// Optional; if present indicates this is a public import
   144  	Public *KeywordNode
   145  	// Optional; if present indicates this is a weak import
   146  	Weak      *KeywordNode
   147  	Name      StringValueNode
   148  	Semicolon *RuneNode
   149  }
   150  
   151  // NewImportNode creates a new *ImportNode. The public and weak arguments are optional
   152  // and only one or the other (or neither) may be specified, not both. When public is
   153  // non-nil, it indicates the "public" keyword in the import statement and means this is
   154  // a public import. When weak is non-nil, it indicates the "weak" keyword in the import
   155  // statement and means this is a weak import. When both are nil, this is a normal import.
   156  // The other arguments must be non-nil:
   157  //  - keyword: The token corresponding to the "import" keyword.
   158  //  - public: The token corresponding to the optional "public" keyword.
   159  //  - weak: The token corresponding to the optional "weak" keyword.
   160  //  - name: The actual imported file name.
   161  //  - semicolon: The token corresponding to the ";" rune that ends the declaration.
   162  func NewImportNode(keyword *KeywordNode, public *KeywordNode, weak *KeywordNode, name StringValueNode, semicolon *RuneNode) *ImportNode {
   163  	if keyword == nil {
   164  		panic("keyword is nil")
   165  	}
   166  	if name == nil {
   167  		panic("name is nil")
   168  	}
   169  	if semicolon == nil {
   170  		panic("semicolon is nil")
   171  	}
   172  	numChildren := 3
   173  	if public != nil || weak != nil {
   174  		numChildren++
   175  	}
   176  	children := make([]Node, 0, numChildren)
   177  	children = append(children, keyword)
   178  	if public != nil {
   179  		children = append(children, public)
   180  	} else if weak != nil {
   181  		children = append(children, weak)
   182  	}
   183  	children = append(children, name, semicolon)
   184  
   185  	return &ImportNode{
   186  		compositeNode: compositeNode{
   187  			children: children,
   188  		},
   189  		Keyword:   keyword,
   190  		Public:    public,
   191  		Weak:      weak,
   192  		Name:      name,
   193  		Semicolon: semicolon,
   194  	}
   195  }
   196  
   197  func (*ImportNode) fileElement() {}
   198  
   199  // PackageNode represents a package declaration. Example:
   200  //
   201  //  package foobar.com;
   202  type PackageNode struct {
   203  	compositeNode
   204  	Keyword   *KeywordNode
   205  	Name      IdentValueNode
   206  	Semicolon *RuneNode
   207  }
   208  
   209  func (*PackageNode) fileElement() {}
   210  
   211  // NewPackageNode creates a new *PackageNode. All three arguments must be non-nil:
   212  //  - keyword: The token corresponding to the "package" keyword.
   213  //  - name: The package name declared for the file.
   214  //  - semicolon: The token corresponding to the ";" rune that ends the declaration.
   215  func NewPackageNode(keyword *KeywordNode, name IdentValueNode, semicolon *RuneNode) *PackageNode {
   216  	if keyword == nil {
   217  		panic("keyword is nil")
   218  	}
   219  	if name == nil {
   220  		panic("name is nil")
   221  	}
   222  	if semicolon == nil {
   223  		panic("semicolon is nil")
   224  	}
   225  	children := []Node{keyword, name, semicolon}
   226  	return &PackageNode{
   227  		compositeNode: compositeNode{
   228  			children: children,
   229  		},
   230  		Keyword:   keyword,
   231  		Name:      name,
   232  		Semicolon: semicolon,
   233  	}
   234  }