diff options
Diffstat (limited to 'vendor/github.com/bufbuild/protocompile/ast/options.go')
| -rw-r--r-- | vendor/github.com/bufbuild/protocompile/ast/options.go | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/vendor/github.com/bufbuild/protocompile/ast/options.go b/vendor/github.com/bufbuild/protocompile/ast/options.go new file mode 100644 index 0000000..be31f0b --- /dev/null +++ b/vendor/github.com/bufbuild/protocompile/ast/options.go @@ -0,0 +1,413 @@ +// 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" + +// OptionDeclNode is a placeholder interface for AST nodes that represent +// options. This allows NoSourceNode to be used in place of *OptionNode +// for some usages. +type OptionDeclNode interface { + Node + GetName() Node + GetValue() ValueNode +} + +var _ OptionDeclNode = (*OptionNode)(nil) +var _ OptionDeclNode = (*NoSourceNode)(nil) + +// OptionNode represents the declaration of a single option for an element. +// It is used both for normal option declarations (start with "option" keyword +// and end with semicolon) and for compact options found in fields, enum values, +// and extension ranges. Example: +// +// option (custom.option) = "foo"; +type OptionNode struct { + compositeNode + Keyword *KeywordNode // absent for compact options + Name *OptionNameNode + Equals *RuneNode + Val ValueNode + Semicolon *RuneNode // absent for compact options +} + +func (*OptionNode) fileElement() {} +func (*OptionNode) msgElement() {} +func (*OptionNode) oneofElement() {} +func (*OptionNode) enumElement() {} +func (*OptionNode) serviceElement() {} +func (*OptionNode) methodElement() {} + +// NewOptionNode creates a new *OptionNode for a full option declaration (as +// used in files, messages, oneofs, enums, services, and methods). All arguments +// must be non-nil. (Also see NewCompactOptionNode.) +// - keyword: The token corresponding to the "option" keyword. +// - name: The token corresponding to the name of the option. +// - equals: The token corresponding to the "=" rune after the name. +// - val: The token corresponding to the option value. +// - semicolon: The token corresponding to the ";" rune that ends the declaration. +func NewOptionNode(keyword *KeywordNode, name *OptionNameNode, equals *RuneNode, val ValueNode, semicolon *RuneNode) *OptionNode { + if keyword == nil { + panic("keyword is nil") + } + if name == nil { + panic("name is nil") + } + if equals == nil { + panic("equals is nil") + } + if val == nil { + panic("val is nil") + } + var children []Node + if semicolon == nil { + children = []Node{keyword, name, equals, val} + } else { + children = []Node{keyword, name, equals, val, semicolon} + } + + return &OptionNode{ + compositeNode: compositeNode{ + children: children, + }, + Keyword: keyword, + Name: name, + Equals: equals, + Val: val, + Semicolon: semicolon, + } +} + +// NewCompactOptionNode creates a new *OptionNode for a full compact declaration +// (as used in fields, enum values, and extension ranges). All arguments must be +// non-nil. +// - name: The token corresponding to the name of the option. +// - equals: The token corresponding to the "=" rune after the name. +// - val: The token corresponding to the option value. +func NewCompactOptionNode(name *OptionNameNode, equals *RuneNode, val ValueNode) *OptionNode { + if name == nil { + panic("name is nil") + } + if equals == nil && val != nil { + panic("equals is nil but val is not") + } + if val == nil && equals != nil { + panic("val is nil but equals is not") + } + var children []Node + if equals == nil && val == nil { + children = []Node{name} + } else { + children = []Node{name, equals, val} + } + return &OptionNode{ + compositeNode: compositeNode{ + children: children, + }, + Name: name, + Equals: equals, + Val: val, + } +} + +func (n *OptionNode) GetName() Node { + return n.Name +} + +func (n *OptionNode) GetValue() ValueNode { + return n.Val +} + +// OptionNameNode represents an option name or even a traversal through message +// types to name a nested option field. Example: +// +// (foo.bar).baz.(bob) +type OptionNameNode struct { + compositeNode + Parts []*FieldReferenceNode + // Dots represent the separating '.' characters between name parts. The + // length of this slice must be exactly len(Parts)-1, each item in Parts + // having a corresponding item in this slice *except the last* (since a + // trailing dot is not allowed). + // + // These do *not* include dots that are inside of an extension name. For + // example: (foo.bar).baz.(bob) has three parts: + // 1. (foo.bar) - an extension name + // 2. baz - a regular field in foo.bar + // 3. (bob) - an extension field in baz + // Note that the dot in foo.bar will thus not be present in Dots but is + // instead in Parts[0]. + Dots []*RuneNode +} + +// NewOptionNameNode creates a new *OptionNameNode. The dots arg must have a +// length that is one less than the length of parts. The parts arg must not be +// empty. +func NewOptionNameNode(parts []*FieldReferenceNode, dots []*RuneNode) *OptionNameNode { + if len(parts) == 0 { + panic("must have at least one part") + } + if len(dots) != len(parts)-1 && len(dots) != len(parts) { + panic(fmt.Sprintf("%d parts requires %d dots, not %d", len(parts), len(parts)-1, len(dots))) + } + children := make([]Node, 0, len(parts)+len(dots)) + for i, part := range parts { + if part == nil { + panic(fmt.Sprintf("parts[%d] is nil", i)) + } + if i > 0 { + if dots[i-1] == nil { + panic(fmt.Sprintf("dots[%d] is nil", i-1)) + } + children = append(children, dots[i-1]) + } + children = append(children, part) + } + if len(dots) == len(parts) { // Add the erroneous, but tolerated trailing dot. + if dots[len(dots)-1] == nil { + panic(fmt.Sprintf("dots[%d] is nil", len(dots)-1)) + } + children = append(children, dots[len(dots)-1]) + } + return &OptionNameNode{ + compositeNode: compositeNode{ + children: children, + }, + Parts: parts, + Dots: dots, + } +} + +// FieldReferenceNode is a reference to a field name. It can indicate a regular +// field (simple unqualified name), an extension field (possibly-qualified name +// that is enclosed either in brackets or parentheses), or an "any" type +// reference (a type URL in the form "server.host/fully.qualified.Name" that is +// enclosed in brackets). +// +// Extension names are used in options to refer to custom options (which are +// actually extensions), in which case the name is enclosed in parentheses "(" +// and ")". They can also be used to refer to extension fields of options. +// +// Extension names are also used in message literals to set extension fields, +// in which case the name is enclosed in square brackets "[" and "]". +// +// "Any" type references can only be used in message literals, and are not +// allowed in option names. They are always enclosed in square brackets. An +// "any" type reference is distinguished from an extension name by the presence +// of a slash, which must be present in an "any" type reference and must be +// absent in an extension name. +// +// Examples: +// +// foobar +// (foo.bar) +// [foo.bar] +// [type.googleapis.com/foo.bar] +type FieldReferenceNode struct { + compositeNode + Open *RuneNode // only present for extension names and "any" type references + + // only present for "any" type references + URLPrefix IdentValueNode + Slash *RuneNode + + Name IdentValueNode + + Close *RuneNode // only present for extension names and "any" type references +} + +// NewFieldReferenceNode creates a new *FieldReferenceNode for a regular field. +// The name arg must not be nil. +func NewFieldReferenceNode(name *IdentNode) *FieldReferenceNode { + if name == nil { + panic("name is nil") + } + children := []Node{name} + return &FieldReferenceNode{ + compositeNode: compositeNode{ + children: children, + }, + Name: name, + } +} + +// NewExtensionFieldReferenceNode creates a new *FieldReferenceNode for an +// extension field. All args must be non-nil. The openSym and closeSym runes +// should be "(" and ")" or "[" and "]". +func NewExtensionFieldReferenceNode(openSym *RuneNode, name IdentValueNode, closeSym *RuneNode) *FieldReferenceNode { + if name == nil { + panic("name is nil") + } + if openSym == nil { + panic("openSym is nil") + } + if closeSym == nil { + panic("closeSym is nil") + } + children := []Node{openSym, name, closeSym} + return &FieldReferenceNode{ + compositeNode: compositeNode{ + children: children, + }, + Open: openSym, + Name: name, + Close: closeSym, + } +} + +// NewAnyTypeReferenceNode creates a new *FieldReferenceNode for an "any" +// type reference. All args must be non-nil. The openSym and closeSym runes +// should be "[" and "]". The slashSym run should be "/". +func NewAnyTypeReferenceNode(openSym *RuneNode, urlPrefix IdentValueNode, slashSym *RuneNode, name IdentValueNode, closeSym *RuneNode) *FieldReferenceNode { + if name == nil { + panic("name is nil") + } + if openSym == nil { + panic("openSym is nil") + } + if closeSym == nil { + panic("closeSym is nil") + } + if urlPrefix == nil { + panic("urlPrefix is nil") + } + if slashSym == nil { + panic("slashSym is nil") + } + children := []Node{openSym, urlPrefix, slashSym, name, closeSym} + return &FieldReferenceNode{ + compositeNode: compositeNode{ + children: children, + }, + Open: openSym, + URLPrefix: urlPrefix, + Slash: slashSym, + Name: name, + Close: closeSym, + } +} + +// IsExtension reports if this is an extension name or not (e.g. enclosed in +// punctuation, such as parentheses or brackets). +func (a *FieldReferenceNode) IsExtension() bool { + return a.Open != nil && a.Slash == nil +} + +// IsAnyTypeReference reports if this is an Any type reference. +func (a *FieldReferenceNode) IsAnyTypeReference() bool { + return a.Slash != nil +} + +func (a *FieldReferenceNode) Value() string { + if a.Open != nil { + if a.Slash != nil { + return string(a.Open.Rune) + string(a.URLPrefix.AsIdentifier()) + string(a.Slash.Rune) + string(a.Name.AsIdentifier()) + string(a.Close.Rune) + } + return string(a.Open.Rune) + string(a.Name.AsIdentifier()) + string(a.Close.Rune) + } + return string(a.Name.AsIdentifier()) +} + +// CompactOptionsNode represents a compact options declaration, as used with +// fields, enum values, and extension ranges. Example: +// +// [deprecated = true, json_name = "foo_bar"] +type CompactOptionsNode struct { + compositeNode + OpenBracket *RuneNode + Options []*OptionNode + // Commas represent the separating ',' characters between options. The + // length of this slice must be exactly len(Options)-1, with each item + // in Options having a corresponding item in this slice *except the last* + // (since a trailing comma is not allowed). + Commas []*RuneNode + CloseBracket *RuneNode +} + +// NewCompactOptionsNode creates a *CompactOptionsNode. All args must be +// non-nil. The commas arg must have a length that is one less than the +// length of opts. The opts arg must not be empty. +func NewCompactOptionsNode(openBracket *RuneNode, opts []*OptionNode, commas []*RuneNode, closeBracket *RuneNode) *CompactOptionsNode { + if openBracket == nil { + panic("openBracket is nil") + } + if closeBracket == nil { + panic("closeBracket is nil") + } + if len(opts) == 0 && len(commas) != 0 { + panic("opts is empty but commas is not") + } + if len(opts) != len(commas) && len(opts) != len(commas)+1 { + panic(fmt.Sprintf("%d opts requires %d commas, not %d", len(opts), len(opts)-1, len(commas))) + } + children := make([]Node, 0, len(opts)+len(commas)+2) + children = append(children, openBracket) + if len(opts) > 0 { + for i, opt := range opts { + if i > 0 { + if commas[i-1] == nil { + panic(fmt.Sprintf("commas[%d] is nil", i-1)) + } + children = append(children, commas[i-1]) + } + if opt == nil { + panic(fmt.Sprintf("opts[%d] is nil", i)) + } + children = append(children, opt) + } + if len(opts) == len(commas) { // Add the erroneous, but tolerated trailing comma. + if commas[len(commas)-1] == nil { + panic(fmt.Sprintf("commas[%d] is nil", len(commas)-1)) + } + children = append(children, commas[len(commas)-1]) + } + } + children = append(children, closeBracket) + + return &CompactOptionsNode{ + compositeNode: compositeNode{ + children: children, + }, + OpenBracket: openBracket, + Options: opts, + Commas: commas, + CloseBracket: closeBracket, + } +} + +func (e *CompactOptionsNode) GetElements() []*OptionNode { + if e == nil { + return nil + } + return e.Options +} + +// NodeWithOptions represents a node in the AST that contains +// option statements. +type NodeWithOptions interface { + Node + RangeOptions(func(*OptionNode) bool) +} + +var _ NodeWithOptions = FileDeclNode(nil) +var _ NodeWithOptions = MessageDeclNode(nil) +var _ NodeWithOptions = OneofDeclNode(nil) +var _ NodeWithOptions = (*EnumNode)(nil) +var _ NodeWithOptions = (*ServiceNode)(nil) +var _ NodeWithOptions = RPCDeclNode(nil) +var _ NodeWithOptions = FieldDeclNode(nil) +var _ NodeWithOptions = EnumValueDeclNode(nil) +var _ NodeWithOptions = (*ExtensionRangeNode)(nil) +var _ NodeWithOptions = (*NoSourceNode)(nil) |
