github.com/bakjos/protoreflect@v1.9.2/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  }