github.com/jhump/protoreflect@v1.16.0/desc/protoparse/ast/identifiers.go (about) 1 package ast 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 // Identifier is a possibly-qualified name. This is used to distinguish 9 // ValueNode values that are references/identifiers vs. those that are 10 // string literals. 11 type Identifier string 12 13 // IdentValueNode is an AST node that represents an identifier. 14 type IdentValueNode interface { 15 ValueNode 16 AsIdentifier() Identifier 17 } 18 19 var _ IdentValueNode = (*IdentNode)(nil) 20 var _ IdentValueNode = (*CompoundIdentNode)(nil) 21 22 // IdentNode represents a simple, unqualified identifier. These are used to name 23 // elements declared in a protobuf file or to refer to elements. Example: 24 // 25 // foobar 26 type IdentNode struct { 27 terminalNode 28 Val string 29 } 30 31 // NewIdentNode creates a new *IdentNode. The given val is the identifier text. 32 func NewIdentNode(val string, info TokenInfo) *IdentNode { 33 return &IdentNode{ 34 terminalNode: info.asTerminalNode(), 35 Val: val, 36 } 37 } 38 39 func (n *IdentNode) Value() interface{} { 40 return n.AsIdentifier() 41 } 42 43 func (n *IdentNode) AsIdentifier() Identifier { 44 return Identifier(n.Val) 45 } 46 47 // ToKeyword is used to convert identifiers to keywords. Since keywords are not 48 // reserved in the protobuf language, they are initially lexed as identifiers 49 // and then converted to keywords based on context. 50 func (n *IdentNode) ToKeyword() *KeywordNode { 51 return (*KeywordNode)(n) 52 } 53 54 // CompoundIdentNode represents a qualified identifier. A qualified identifier 55 // has at least one dot and possibly multiple identifier names (all separated by 56 // dots). If the identifier has a leading dot, then it is a *fully* qualified 57 // identifier. Example: 58 // 59 // .com.foobar.Baz 60 type CompoundIdentNode struct { 61 compositeNode 62 // Optional leading dot, indicating that the identifier is fully qualified. 63 LeadingDot *RuneNode 64 Components []*IdentNode 65 // Dots[0] is the dot after Components[0]. The length of Dots is always 66 // one less than the length of Components. 67 Dots []*RuneNode 68 // The text value of the identifier, with all components and dots 69 // concatenated. 70 Val string 71 } 72 73 // NewCompoundIdentNode creates a *CompoundIdentNode. The leadingDot may be nil. 74 // The dots arg must have a length that is one less than the length of 75 // components. The components arg must not be empty. 76 func NewCompoundIdentNode(leadingDot *RuneNode, components []*IdentNode, dots []*RuneNode) *CompoundIdentNode { 77 if len(components) == 0 { 78 panic("must have at least one component") 79 } 80 if len(dots) != len(components)-1 { 81 panic(fmt.Sprintf("%d components requires %d dots, not %d", len(components), len(components)-1, len(dots))) 82 } 83 numChildren := len(components)*2 - 1 84 if leadingDot != nil { 85 numChildren++ 86 } 87 children := make([]Node, 0, numChildren) 88 var b strings.Builder 89 if leadingDot != nil { 90 children = append(children, leadingDot) 91 b.WriteRune(leadingDot.Rune) 92 } 93 for i, comp := range components { 94 if i > 0 { 95 dot := dots[i-1] 96 children = append(children, dot) 97 b.WriteRune(dot.Rune) 98 } 99 children = append(children, comp) 100 b.WriteString(comp.Val) 101 } 102 return &CompoundIdentNode{ 103 compositeNode: compositeNode{ 104 children: children, 105 }, 106 LeadingDot: leadingDot, 107 Components: components, 108 Dots: dots, 109 Val: b.String(), 110 } 111 } 112 113 func (n *CompoundIdentNode) Value() interface{} { 114 return n.AsIdentifier() 115 } 116 117 func (n *CompoundIdentNode) AsIdentifier() Identifier { 118 return Identifier(n.Val) 119 } 120 121 // KeywordNode is an AST node that represents a keyword. Keywords are 122 // like identifiers, but they have special meaning in particular contexts. 123 // Example: 124 // 125 // message 126 type KeywordNode IdentNode 127 128 // NewKeywordNode creates a new *KeywordNode. The given val is the keyword. 129 func NewKeywordNode(val string, info TokenInfo) *KeywordNode { 130 return &KeywordNode{ 131 terminalNode: info.asTerminalNode(), 132 Val: val, 133 } 134 }