github.com/jhump/protocompile@v0.0.0-20221021153901-4f6f732835e8/ast/node.go (about) 1 package ast 2 3 // Node is the interface implemented by all nodes in the AST. It 4 // provides information about the span of this AST node in terms 5 // of location in the source file. It also provides information 6 // about all prior comments (attached as leading comments) and 7 // optional subsequent comments (attached as trailing comments). 8 type Node interface { 9 Start() Token 10 End() Token 11 } 12 13 // TerminalNode represents a leaf in the AST. These represent 14 // the tokens/lexemes in the protobuf language. Comments and 15 // whitespace are accumulated by the lexer and associated with 16 // the following lexed token. 17 type TerminalNode interface { 18 Node 19 Token() Token 20 } 21 22 var _ TerminalNode = (*StringLiteralNode)(nil) 23 var _ TerminalNode = (*UintLiteralNode)(nil) 24 var _ TerminalNode = (*FloatLiteralNode)(nil) 25 var _ TerminalNode = (*IdentNode)(nil) 26 var _ TerminalNode = (*BoolLiteralNode)(nil) 27 var _ TerminalNode = (*SpecialFloatLiteralNode)(nil) 28 var _ TerminalNode = (*KeywordNode)(nil) 29 var _ TerminalNode = (*RuneNode)(nil) 30 31 // CompositeNode represents any non-terminal node in the tree. These 32 // are interior or root nodes and have child nodes. 33 type CompositeNode interface { 34 Node 35 // All AST nodes that are immediate children of this one. 36 Children() []Node 37 } 38 39 // terminalNode contains book-keeping shared by all TerminalNode 40 // implementations. It is embedded in all such node types in this 41 // package. It provides the implementation of the TerminalNode 42 // interface. 43 type terminalNode Token 44 45 func (n terminalNode) Start() Token { 46 return Token(n) 47 } 48 49 func (n terminalNode) End() Token { 50 return Token(n) 51 } 52 53 func (n terminalNode) Token() Token { 54 return Token(n) 55 } 56 57 // compositeNode contains book-keeping shared by all CompositeNode 58 // implementations. It is embedded in all such node types in this 59 // package. It provides the implementation of the CompositeNode 60 // interface. 61 type compositeNode struct { 62 children []Node 63 } 64 65 func (n *compositeNode) Children() []Node { 66 return n.children 67 } 68 69 func (n *compositeNode) Start() Token { 70 return n.children[0].Start() 71 } 72 73 func (n *compositeNode) End() Token { 74 return n.children[len(n.children)-1].End() 75 } 76 77 // RuneNode represents a single rune in protobuf source. Runes 78 // are typically collected into tokens, but some runes stand on 79 // their own, such as punctuation/symbols like commas, semicolons, 80 // equals signs, open and close symbols (braces, brackets, angles, 81 // and parentheses), and periods/dots. 82 // TODO: make this more compact; if runes don't have attributed comments 83 // then we don't need a Token to represent them and only need an offset 84 // into the file's contents 85 type RuneNode struct { 86 terminalNode 87 Rune rune 88 } 89 90 // NewRuneNode creates a new *RuneNode with the given properties. 91 func NewRuneNode(r rune, tok Token) *RuneNode { 92 return &RuneNode{ 93 terminalNode: tok.asTerminalNode(), 94 Rune: r, 95 } 96 } 97 98 // EmptyDeclNode represents an empty declaration in protobuf source. 99 // These amount to extra semicolons, with no actual content preceding 100 // the semicolon. 101 type EmptyDeclNode struct { 102 compositeNode 103 Semicolon *RuneNode 104 } 105 106 // NewEmptyDeclNode creates a new *EmptyDeclNode. The one argument must 107 // be non-nil. 108 func NewEmptyDeclNode(semicolon *RuneNode) *EmptyDeclNode { 109 if semicolon == nil { 110 panic("semicolon is nil") 111 } 112 return &EmptyDeclNode{ 113 compositeNode: compositeNode{ 114 children: []Node{semicolon}, 115 }, 116 Semicolon: semicolon, 117 } 118 } 119 120 func (e *EmptyDeclNode) fileElement() {} 121 func (e *EmptyDeclNode) msgElement() {} 122 func (e *EmptyDeclNode) extendElement() {} 123 func (e *EmptyDeclNode) oneOfElement() {} 124 func (e *EmptyDeclNode) enumElement() {} 125 func (e *EmptyDeclNode) serviceElement() {} 126 func (e *EmptyDeclNode) methodElement() {}