github.com/jhump/protocompile@v0.0.0-20221021153901-4f6f732835e8/parser/parser.go (about) 1 package parser 2 3 import ( 4 "fmt" 5 "io" 6 7 "google.golang.org/protobuf/proto" 8 "google.golang.org/protobuf/types/descriptorpb" 9 10 "github.com/jhump/protocompile/ast" 11 "github.com/jhump/protocompile/reporter" 12 ) 13 14 //go:generate goyacc -o proto.y.go -p proto proto.y 15 16 func init() { 17 protoErrorVerbose = true 18 19 // fix up the generated "token name" array so that error messages are nicer 20 setTokenName(_STRING_LIT, "string literal") 21 setTokenName(_INT_LIT, "int literal") 22 setTokenName(_FLOAT_LIT, "float literal") 23 setTokenName(_NAME, "identifier") 24 setTokenName(_ERROR, "error") 25 // for keywords, just show the keyword itself wrapped in quotes 26 for str, i := range keywords { 27 setTokenName(i, fmt.Sprintf(`"%s"`, str)) 28 } 29 } 30 31 func setTokenName(token int, text string) { 32 // NB: this is based on logic in generated parse code that translates the 33 // int returned from the lexer into an internal token number. 34 var intern int 35 if token < len(protoTok1) { 36 intern = protoTok1[token] 37 } else { 38 if token >= protoPrivate { 39 if token < protoPrivate+len(protoTok2) { 40 intern = protoTok2[token-protoPrivate] 41 } 42 } 43 if intern == 0 { 44 for i := 0; i+1 < len(protoTok3); i += 2 { 45 if protoTok3[i] == token { 46 intern = protoTok3[i+1] 47 break 48 } 49 } 50 } 51 } 52 53 if intern >= 1 && intern-1 < len(protoToknames) { 54 protoToknames[intern-1] = text 55 return 56 } 57 58 panic(fmt.Sprintf("Unknown token value: %d", token)) 59 } 60 61 // Parse parses the given source code info and returns an AST. The given filename 62 // is used to construct error messages and position information. The given reader 63 // supplies the source code. The given handler is used to report errors and 64 // warnings encountered while parsing. If any errors are reported, this function 65 // returns a non-nil error. 66 func Parse(filename string, r io.Reader, handler *reporter.Handler) (*ast.FileNode, error) { 67 lx, err := newLexer(r, filename, handler) 68 if err != nil { 69 return nil, err 70 } 71 protoParse(lx) 72 if lx.res == nil || len(lx.res.Children()) == 0 { 73 // nil AST means there was an error that prevented any parsing 74 // or the file was empty; synthesize empty non-nil AST 75 lx.res = ast.NewEmptyFileNode(filename) 76 } 77 return lx.res, handler.Error() 78 } 79 80 // Result is the result of constructing a descriptor proto from a parsed AST. 81 // From this result, the AST and the file descriptor proto can be had. This 82 // also contains numerous lookup functions, for looking up AST nodes that 83 // correspond to various elements of the descriptor hierarchy. 84 // 85 // Results can be created without AST information, using the ResultWithoutAST() 86 // function. All functions other than AST() will still return non-nil values, 87 // allowing compile operations to work with files that have only intermediate 88 // descriptor protos and no source code. For such results, the function that 89 // return AST nodes will return placeholder nodes. The position information for 90 // placeholder nodes contains only the filename. 91 type Result interface { 92 // AST returns the parsed abstract syntax tree. This returns nil if the 93 // Result was created without an AST. 94 AST() *ast.FileNode 95 // Proto returns the file descriptor proto. 96 Proto() *descriptorpb.FileDescriptorProto 97 98 // FileNode returns the root of the AST. If this result has no AST then a 99 // placeholder node is returned. 100 FileNode() ast.FileDeclNode 101 // Node returns the AST node from which the given message was created. This 102 // can return nil, such as if the given message is not part of the 103 // FileDescriptorProto hierarchy. If this result has no AST, this returns a 104 // placeholder node. 105 Node(proto.Message) ast.Node 106 // OptionNode returns the AST node corresponding to the given uninterpreted 107 // option. This can return nil, such as if the given option is not part of 108 // the FileDescriptorProto hierarchy. If this result has no AST, this 109 // returns a placeholder node. 110 OptionNode(*descriptorpb.UninterpretedOption) ast.OptionDeclNode 111 // OptionNamePartNode returns the AST node corresponding to the given name 112 // part for an uninterpreted option. This can return nil, such as if the 113 // given name part is not part of the FileDescriptorProto hierarchy. If this 114 // result has no AST, this returns a placeholder node. 115 OptionNamePartNode(*descriptorpb.UninterpretedOption_NamePart) ast.Node 116 // MessageNode returns the AST node corresponding to the given message. This 117 // can return nil, such as if the given message is not part of the 118 // FileDescriptorProto hierarchy. If this result has no AST, this returns a 119 // placeholder node. 120 MessageNode(*descriptorpb.DescriptorProto) ast.MessageDeclNode 121 // FieldNode returns the AST node corresponding to the given field. This can 122 // return nil, such as if the given field is not part of the 123 // FileDescriptorProto hierarchy. If this result has no AST, this returns a 124 // placeholder node. 125 FieldNode(*descriptorpb.FieldDescriptorProto) ast.FieldDeclNode 126 // OneOfNode returns the AST node corresponding to the given oneof. This can 127 // return nil, such as if the given oneof is not part of the 128 // FileDescriptorProto hierarchy. If this result has no AST, this returns a 129 // placeholder node. 130 OneOfNode(*descriptorpb.OneofDescriptorProto) ast.Node 131 // ExtensionRangeNode returns the AST node corresponding to the given 132 // extension range. This can return nil, such as if the given range is not 133 // part of the FileDescriptorProto hierarchy. If this result has no AST, 134 // this returns a placeholder node. 135 ExtensionRangeNode(*descriptorpb.DescriptorProto_ExtensionRange) ast.RangeDeclNode 136 // MessageReservedRangeNode returns the AST node corresponding to the given 137 // reserved range. This can return nil, such as if the given range is not 138 // part of the FileDescriptorProto hierarchy. If this result has no AST, 139 // this returns a placeholder node. 140 MessageReservedRangeNode(*descriptorpb.DescriptorProto_ReservedRange) ast.RangeDeclNode 141 // EnumNode returns the AST node corresponding to the given enum. This can 142 // return nil, such as if the given enum is not part of the 143 // FileDescriptorProto hierarchy. If this result has no AST, this returns a 144 // placeholder node. 145 EnumNode(*descriptorpb.EnumDescriptorProto) ast.Node 146 // EnumValueNode returns the AST node corresponding to the given enum. This 147 // can return nil, such as if the given enum value is not part of the 148 // FileDescriptorProto hierarchy. If this result has no AST, this returns a 149 // placeholder node. 150 EnumValueNode(*descriptorpb.EnumValueDescriptorProto) ast.EnumValueDeclNode 151 // EnumReservedRangeNode returns the AST node corresponding to the given 152 // reserved range. This can return nil, such as if the given range is not 153 // part of the FileDescriptorProto hierarchy. If this result has no AST, 154 // this returns a placeholder node. 155 EnumReservedRangeNode(*descriptorpb.EnumDescriptorProto_EnumReservedRange) ast.RangeDeclNode 156 // ServiceNode returns the AST node corresponding to the given service. This 157 // can return nil, such as if the given service is not part of the 158 // FileDescriptorProto hierarchy. If this result has no AST, this returns a 159 // placeholder node. 160 ServiceNode(*descriptorpb.ServiceDescriptorProto) ast.Node 161 // MethodNode returns the AST node corresponding to the given method. This 162 // can return nil, such as if the given method is not part of the 163 // FileDescriptorProto hierarchy. If this result has no AST, this returns a 164 // placeholder node. 165 MethodNode(*descriptorpb.MethodDescriptorProto) ast.RPCDeclNode 166 }