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  }