%{ package protoparse //lint:file-ignore SA4006 generated parser has unused values import ( "math" "github.com/jhump/protoreflect/desc/protoparse/ast" ) %} // fields inside this union end up as the fields in a structure known // as ${PREFIX}SymType, of which a reference is passed to the lexer. %union{ file *ast.FileNode syn *ast.SyntaxNode fileDecl ast.FileElement fileDecls []ast.FileElement pkg *ast.PackageNode imprt *ast.ImportNode msg *ast.MessageNode msgDecl ast.MessageElement msgDecls []ast.MessageElement fld *ast.FieldNode mapFld *ast.MapFieldNode mapType *ast.MapTypeNode grp *ast.GroupNode oo *ast.OneOfNode ooDecl ast.OneOfElement ooDecls []ast.OneOfElement ext *ast.ExtensionRangeNode resvd *ast.ReservedNode en *ast.EnumNode enDecl ast.EnumElement enDecls []ast.EnumElement env *ast.EnumValueNode extend *ast.ExtendNode extDecl ast.ExtendElement extDecls []ast.ExtendElement svc *ast.ServiceNode svcDecl ast.ServiceElement svcDecls []ast.ServiceElement mtd *ast.RPCNode rpcType *ast.RPCTypeNode rpcDecl ast.RPCElement rpcDecls []ast.RPCElement opt *ast.OptionNode opts *compactOptionList ref *ast.FieldReferenceNode optNms *fieldRefList cmpctOpts *ast.CompactOptionsNode rng *ast.RangeNode rngs *rangeList names *nameList cid *identList tid ast.IdentValueNode sl *valueList msgField *ast.MessageFieldNode msgEntry *messageFieldEntry msgLit *messageFieldList v ast.ValueNode il ast.IntValueNode str *stringList s *ast.StringLiteralNode i *ast.UintLiteralNode f *ast.FloatLiteralNode id *ast.IdentNode b *ast.RuneNode err error } // any non-terminal which returns a value needs a type, which is // really a field name in the above union struct %type file %type syntax %type fileDecl %type fileDecls %type import %type package %type option compactOption %type compactOptionDecls %type rpcDecl %type rpcDecls %type optionNameComponent aggName %type optionName %type compactOptions %type constant scalarConstant aggregate numLit %type intLit %type name keyType %type ident %type typeIdent %type constantList %type aggFieldEntry %type aggField %type aggFields %type field oneofField %type oneof %type group oneofGroup %type mapField %type mapType %type message %type messageDecl %type messageDecls %type ooDecl %type ooDecls %type fieldNames %type msgReserved enumReserved reservedNames %type tagRange enumRange %type tagRanges enumRanges %type extensions %type enum %type enumDecl %type enumDecls %type enumValue %type extend %type extendDecl %type extendDecls %type stringLit %type service %type serviceDecl %type serviceDecls %type rpc %type rpcType // same for terminals %token _STRING_LIT %token _INT_LIT %token _FLOAT_LIT %token _NAME %token _SYNTAX _IMPORT _WEAK _PUBLIC _PACKAGE _OPTION _TRUE _FALSE _INF _NAN _REPEATED _OPTIONAL _REQUIRED %token _DOUBLE _FLOAT _INT32 _INT64 _UINT32 _UINT64 _SINT32 _SINT64 _FIXED32 _FIXED64 _SFIXED32 _SFIXED64 %token _BOOL _STRING _BYTES _GROUP _ONEOF _MAP _EXTENSIONS _TO _MAX _RESERVED _ENUM _MESSAGE _EXTEND %token _SERVICE _RPC _STREAM _RETURNS %token _ERROR // we define all of these, even ones that aren't used, to improve error messages // so it shows the unexpected symbol instead of showing "$unk" %token '=' ';' ':' '{' '}' '\\' '/' '?' '.' ',' '>' '<' '+' '-' '(' ')' '[' ']' '*' '&' '^' '%' '$' '#' '@' '!' '~' '`' %% file : syntax { $$ = ast.NewFileNode($1, nil) protolex.(*protoLex).res = $$ } | fileDecls { $$ = ast.NewFileNode(nil, $1) protolex.(*protoLex).res = $$ } | syntax fileDecls { $$ = ast.NewFileNode($1, $2) protolex.(*protoLex).res = $$ } | { } fileDecls : fileDecls fileDecl { if $2 != nil { $$ = append($1, $2) } else { $$ = $1 } } | fileDecl { if $1 != nil { $$ = []ast.FileElement{$1} } else { $$ = nil } } fileDecl : import { $$ = $1 } | package { $$ = $1 } | option { $$ = $1 } | message { $$ = $1 } | enum { $$ = $1 } | extend { $$ = $1 } | service { $$ = $1 } | ';' { $$ = ast.NewEmptyDeclNode($1) } | error ';' { $$ = nil } | error { $$ = nil } syntax : _SYNTAX '=' stringLit ';' { $$ = ast.NewSyntaxNode($1.ToKeyword(), $2, $3.toStringValueNode(), $4) } import : _IMPORT stringLit ';' { $$ = ast.NewImportNode($1.ToKeyword(), nil, nil, $2.toStringValueNode(), $3) } | _IMPORT _WEAK stringLit ';' { $$ = ast.NewImportNode($1.ToKeyword(), nil, $2.ToKeyword(), $3.toStringValueNode(), $4) } | _IMPORT _PUBLIC stringLit ';' { $$ = ast.NewImportNode($1.ToKeyword(), $2.ToKeyword(), nil, $3.toStringValueNode(), $4) } package : _PACKAGE ident ';' { $$ = ast.NewPackageNode($1.ToKeyword(), $2.toIdentValueNode(nil), $3) } ident : name { $$ = &identList{$1, nil, nil} } | name '.' ident { $$ = &identList{$1, $2, $3} } option : _OPTION optionName '=' constant ';' { refs, dots := $2.toNodes() optName := ast.NewOptionNameNode(refs, dots) $$ = ast.NewOptionNode($1.ToKeyword(), optName, $3, $4, $5) } optionName : optionNameComponent { $$ = &fieldRefList{$1, nil, nil} } | optionNameComponent '.' optionName { $$ = &fieldRefList{$1, $2, $3} } optionNameComponent : name { $$ = ast.NewFieldReferenceNode($1) } | '(' typeIdent ')' { $$ = ast.NewExtensionFieldReferenceNode($1, $2, $3) } constant : scalarConstant | aggregate scalarConstant : stringLit { $$ = $1.toStringValueNode() } | numLit | name { if $1.Val == "true" || $1.Val == "false" { $$ = ast.NewBoolLiteralNode($1.ToKeyword()) } else if $1.Val == "inf" || $1.Val == "nan" { $$ = ast.NewSpecialFloatLiteralNode($1.ToKeyword()) } else { $$ = $1 } } numLit : _FLOAT_LIT { $$ = $1 } | '-' _FLOAT_LIT { $$ = ast.NewSignedFloatLiteralNode($1, $2) } | '+' _FLOAT_LIT { $$ = ast.NewSignedFloatLiteralNode($1, $2) } | '+' _INF { f := ast.NewSpecialFloatLiteralNode($2.ToKeyword()) $$ = ast.NewSignedFloatLiteralNode($1, f) } | '-' _INF { f := ast.NewSpecialFloatLiteralNode($2.ToKeyword()) $$ = ast.NewSignedFloatLiteralNode($1, f) } | _INT_LIT { $$ = $1 } | '+' _INT_LIT { $$ = ast.NewPositiveUintLiteralNode($1, $2) } | '-' _INT_LIT { if $2.Val > math.MaxInt64 + 1 { // can't represent as int so treat as float literal $$ = ast.NewSignedFloatLiteralNode($1, $2) } else { $$ = ast.NewNegativeIntLiteralNode($1, $2) } } stringLit : _STRING_LIT { $$ = &stringList{$1, nil} } | _STRING_LIT stringLit { $$ = &stringList{$1, $2} } aggregate : '{' aggFields '}' { fields, delims := $2.toNodes() $$ = ast.NewMessageLiteralNode($1, fields, delims, $3) } aggFields : aggField { if $1 != nil { $$ = &messageFieldList{$1, nil} } else { $$ = nil } } | aggField aggFields { if $1 != nil { $$ = &messageFieldList{$1, $2} } else { $$ = $2 } } | { $$ = nil } aggField : aggFieldEntry { if $1 != nil { $$ = &messageFieldEntry{$1, nil} } else { $$ = nil } } | aggFieldEntry ',' { if $1 != nil { $$ = &messageFieldEntry{$1, $2} } else { $$ = nil } } | aggFieldEntry ';' { if $1 != nil { $$ = &messageFieldEntry{$1, $2} } else { $$ = nil } } | error ',' { $$ = nil } | error ';' { $$ = nil } | error { $$ = nil } aggFieldEntry : aggName ':' scalarConstant { if $1 != nil { $$ = ast.NewMessageFieldNode($1, $2, $3) } else { $$ = nil } } | aggName ':' '[' ']' { if $1 != nil { val := ast.NewArrayLiteralNode($3, nil, nil, $4) $$ = ast.NewMessageFieldNode($1, $2, val) } else { $$ = nil } } | aggName ':' '[' constantList ']' { if $1 != nil { vals, commas := $4.toNodes() val := ast.NewArrayLiteralNode($3, vals, commas, $5) $$ = ast.NewMessageFieldNode($1, $2, val) } else { $$ = nil } } | aggName ':' '[' error ']' { $$ = nil } | aggName ':' aggregate { if $1 != nil { $$ = ast.NewMessageFieldNode($1, $2, $3) } else { $$ = nil } } | aggName aggregate { if $1 != nil { $$ = ast.NewMessageFieldNode($1, nil, $2) } else { $$ = nil } } | aggName ':' '<' aggFields '>' { if $1 != nil { fields, delims := $4.toNodes() msg := ast.NewMessageLiteralNode($3, fields, delims, $5) $$ = ast.NewMessageFieldNode($1, $2, msg) } else { $$ = nil } } | aggName '<' aggFields '>' { if $1 != nil { fields, delims := $3.toNodes() msg := ast.NewMessageLiteralNode($2, fields, delims, $4) $$ = ast.NewMessageFieldNode($1, nil, msg) } else { $$ = nil } } | aggName ':' '<' error '>' { $$ = nil } | aggName '<' error '>' { $$ = nil } aggName : name { $$ = ast.NewFieldReferenceNode($1) } | '[' typeIdent ']' { $$ = ast.NewExtensionFieldReferenceNode($1, $2, $3) } | '[' error ']' { $$ = nil } constantList : constant { $$ = &valueList{$1, nil, nil} } | constant ',' constantList { $$ = &valueList{$1, $2, $3} } | '<' aggFields '>' { fields, delims := $2.toNodes() msg := ast.NewMessageLiteralNode($1, fields, delims, $3) $$ = &valueList{msg, nil, nil} } | '<' aggFields '>' ',' constantList { fields, delims := $2.toNodes() msg := ast.NewMessageLiteralNode($1, fields, delims, $3) $$ = &valueList{msg, $4, $5} } | '<' error '>' { $$ = nil } | '<' error '>' ',' constantList { $$ = $5 } typeIdent : ident { $$ = $1.toIdentValueNode(nil) } | '.' ident { $$ = $2.toIdentValueNode($1) } field : _REQUIRED typeIdent name '=' _INT_LIT ';' { $$ = ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, nil, $6) } | _OPTIONAL typeIdent name '=' _INT_LIT ';' { $$ = ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, nil, $6) } | _REPEATED typeIdent name '=' _INT_LIT ';' { $$ = ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, nil, $6) } | typeIdent name '=' _INT_LIT ';' { $$ = ast.NewFieldNode(nil, $1, $2, $3, $4, nil, $5) } | _REQUIRED typeIdent name '=' _INT_LIT compactOptions ';' { $$ = ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, $6, $7) } | _OPTIONAL typeIdent name '=' _INT_LIT compactOptions ';' { $$ = ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, $6, $7) } | _REPEATED typeIdent name '=' _INT_LIT compactOptions ';' { $$ = ast.NewFieldNode($1.ToKeyword(), $2, $3, $4, $5, $6, $7) } | typeIdent name '=' _INT_LIT compactOptions ';' { $$ = ast.NewFieldNode(nil, $1, $2, $3, $4, $5, $6) } compactOptions: '[' compactOptionDecls ']' { opts, commas := $2.toNodes() $$ = ast.NewCompactOptionsNode($1, opts, commas, $3) } compactOptionDecls : compactOption { $$ = &compactOptionList{$1, nil, nil} } | compactOption ',' compactOptionDecls { $$ = &compactOptionList{$1, $2, $3} } compactOption: optionName '=' constant { refs, dots := $1.toNodes() optName := ast.NewOptionNameNode(refs, dots) $$ = ast.NewCompactOptionNode(optName, $2, $3) } group : _REQUIRED _GROUP name '=' _INT_LIT '{' messageDecls '}' { $$ = ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, nil, $6, $7, $8) } | _OPTIONAL _GROUP name '=' _INT_LIT '{' messageDecls '}' { $$ = ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, nil, $6, $7, $8) } | _REPEATED _GROUP name '=' _INT_LIT '{' messageDecls '}' { $$ = ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, nil, $6, $7, $8) } | _REQUIRED _GROUP name '=' _INT_LIT compactOptions '{' messageDecls '}' { $$ = ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, $6, $7, $8, $9) } | _OPTIONAL _GROUP name '=' _INT_LIT compactOptions '{' messageDecls '}' { $$ = ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, $6, $7, $8, $9) } | _REPEATED _GROUP name '=' _INT_LIT compactOptions '{' messageDecls '}' { $$ = ast.NewGroupNode($1.ToKeyword(), $2.ToKeyword(), $3, $4, $5, $6, $7, $8, $9) } oneof : _ONEOF name '{' ooDecls '}' { $$ = ast.NewOneOfNode($1.ToKeyword(), $2, $3, $4, $5) } ooDecls : ooDecls ooDecl { if $2 != nil { $$ = append($1, $2) } else { $$ = $1 } } | ooDecl { if $1 != nil { $$ = []ast.OneOfElement{$1} } else { $$ = nil } } | { $$ = nil } ooDecl : option { $$ = $1 } | oneofField { $$ = $1 } | oneofGroup { $$ = $1 } | ';' { $$ = ast.NewEmptyDeclNode($1) } | error ';' { $$ = nil } | error { $$ = nil } oneofField : typeIdent name '=' _INT_LIT ';' { $$ = ast.NewFieldNode(nil, $1, $2, $3, $4, nil, $5) } | typeIdent name '=' _INT_LIT compactOptions ';' { $$ = ast.NewFieldNode(nil, $1, $2, $3, $4, $5, $6) } oneofGroup : _GROUP name '=' _INT_LIT '{' messageDecls '}' { $$ = ast.NewGroupNode(nil, $1.ToKeyword(), $2, $3, $4, nil, $5, $6, $7) } | _GROUP name '=' _INT_LIT compactOptions '{' messageDecls '}' { $$ = ast.NewGroupNode(nil, $1.ToKeyword(), $2, $3, $4, $5, $6, $7, $8) } mapField : mapType name '=' _INT_LIT ';' { $$ = ast.NewMapFieldNode($1, $2, $3, $4, nil, $5) } | mapType name '=' _INT_LIT compactOptions ';' { $$ = ast.NewMapFieldNode($1, $2, $3, $4, $5, $6) } mapType : _MAP '<' keyType ',' typeIdent '>' { $$ = ast.NewMapTypeNode($1.ToKeyword(), $2, $3, $4, $5, $6) } keyType : _INT32 | _INT64 | _UINT32 | _UINT64 | _SINT32 | _SINT64 | _FIXED32 | _FIXED64 | _SFIXED32 | _SFIXED64 | _BOOL | _STRING extensions : _EXTENSIONS tagRanges ';' { ranges, commas := $2.toNodes() $$ = ast.NewExtensionRangeNode($1.ToKeyword(), ranges, commas, nil, $3) } | _EXTENSIONS tagRanges compactOptions ';' { ranges, commas := $2.toNodes() $$ = ast.NewExtensionRangeNode($1.ToKeyword(), ranges, commas, $3, $4) } tagRanges : tagRange { $$ = &rangeList{$1, nil, nil} } | tagRange ',' tagRanges { $$ = &rangeList{$1, $2, $3} } tagRange : _INT_LIT { $$ = ast.NewRangeNode($1, nil, nil, nil) } | _INT_LIT _TO _INT_LIT { $$ = ast.NewRangeNode($1, $2.ToKeyword(), $3, nil) } | _INT_LIT _TO _MAX { $$ = ast.NewRangeNode($1, $2.ToKeyword(), nil, $3.ToKeyword()) } enumRanges : enumRange { $$ = &rangeList{$1, nil, nil} } | enumRange ',' enumRanges { $$ = &rangeList{$1, $2, $3} } enumRange : intLit { $$ = ast.NewRangeNode($1, nil, nil, nil) } | intLit _TO intLit { $$ = ast.NewRangeNode($1, $2.ToKeyword(), $3, nil) } | intLit _TO _MAX { $$ = ast.NewRangeNode($1, $2.ToKeyword(), nil, $3.ToKeyword()) } intLit : _INT_LIT { $$ = $1 } | '-' _INT_LIT { $$ = ast.NewNegativeIntLiteralNode($1, $2) } msgReserved : _RESERVED tagRanges ';' { ranges, commas := $2.toNodes() $$ = ast.NewReservedRangesNode($1.ToKeyword(), ranges, commas, $3) } | reservedNames enumReserved : _RESERVED enumRanges ';' { ranges, commas := $2.toNodes() $$ = ast.NewReservedRangesNode($1.ToKeyword(), ranges, commas, $3) } | reservedNames reservedNames : _RESERVED fieldNames ';' { names, commas := $2.toNodes() $$ = ast.NewReservedNamesNode($1.ToKeyword(), names, commas, $3) } fieldNames : stringLit { $$ = &nameList{$1.toStringValueNode(), nil, nil} } | stringLit ',' fieldNames { $$ = &nameList{$1.toStringValueNode(), $2, $3} } enum : _ENUM name '{' enumDecls '}' { $$ = ast.NewEnumNode($1.ToKeyword(), $2, $3, $4, $5) } enumDecls : enumDecls enumDecl { if $2 != nil { $$ = append($1, $2) } else { $$ = $1 } } | enumDecl { if $1 != nil { $$ = []ast.EnumElement{$1} } else { $$ = nil } } | { $$ = nil } enumDecl : option { $$ = $1 } | enumValue { $$ = $1 } | enumReserved { $$ = $1 } | ';' { $$ = ast.NewEmptyDeclNode($1) } | error ';' { $$ = nil } | error { $$ = nil } enumValue : name '=' intLit ';' { $$ = ast.NewEnumValueNode($1, $2, $3, nil, $4) } | name '=' intLit compactOptions ';' { $$ = ast.NewEnumValueNode($1, $2, $3, $4, $5) } message : _MESSAGE name '{' messageDecls '}' { $$ = ast.NewMessageNode($1.ToKeyword(), $2, $3, $4, $5) } messageDecls : messageDecls messageDecl { if $2 != nil { $$ = append($1, $2) } else { $$ = $1 } } | messageDecl { if $1 != nil { $$ = []ast.MessageElement{$1} } else { $$ = nil } } | { $$ = nil } messageDecl : field { $$ = $1 } | enum { $$ = $1 } | message { $$ = $1 } | extend { $$ = $1 } | extensions { $$ = $1 } | group { $$ = $1 } | option { $$ = $1 } | oneof { $$ = $1 } | mapField { $$ = $1 } | msgReserved { $$ = $1 } | ';' { $$ = ast.NewEmptyDeclNode($1) } | error ';' { $$ = nil } | error { $$ = nil } extend : _EXTEND typeIdent '{' extendDecls '}' { $$ = ast.NewExtendNode($1.ToKeyword(), $2, $3, $4, $5) } extendDecls : extendDecls extendDecl { if $2 != nil { $$ = append($1, $2) } else { $$ = $1 } } | extendDecl { if $1 != nil { $$ = []ast.ExtendElement{$1} } else { $$ = nil } } | { $$ = nil } extendDecl : field { $$ = $1 } | group { $$ = $1 } | ';' { $$ = ast.NewEmptyDeclNode($1) } | error ';' { $$ = nil } | error { $$ = nil } service : _SERVICE name '{' serviceDecls '}' { $$ = ast.NewServiceNode($1.ToKeyword(), $2, $3, $4, $5) } serviceDecls : serviceDecls serviceDecl { if $2 != nil { $$ = append($1, $2) } else { $$ = $1 } } | serviceDecl { if $1 != nil { $$ = []ast.ServiceElement{$1} } else { $$ = nil } } | { $$ = nil } // NB: doc suggests support for "stream" declaration, separate from "rpc", but // it does not appear to be supported in protoc (doc is likely from grammar for // Google-internal version of protoc, with support for streaming stubby) serviceDecl : option { $$ = $1 } | rpc { $$ = $1 } | ';' { $$ = ast.NewEmptyDeclNode($1) } | error ';' { $$ = nil } | error { $$ = nil } rpc : _RPC name rpcType _RETURNS rpcType ';' { $$ = ast.NewRPCNode($1.ToKeyword(), $2, $3, $4.ToKeyword(), $5, $6) } | _RPC name rpcType _RETURNS rpcType '{' rpcDecls '}' { $$ = ast.NewRPCNodeWithBody($1.ToKeyword(), $2, $3, $4.ToKeyword(), $5, $6, $7, $8) } rpcType : '(' _STREAM typeIdent ')' { $$ = ast.NewRPCTypeNode($1, $2.ToKeyword(), $3, $4) } | '(' typeIdent ')' { $$ = ast.NewRPCTypeNode($1, nil, $2, $3) } rpcDecls : rpcDecls rpcDecl { if $2 != nil { $$ = append($1, $2) } else { $$ = $1 } } | rpcDecl { if $1 != nil { $$ = []ast.RPCElement{$1} } else { $$ = nil } } | { $$ = nil } rpcDecl : option { $$ = $1 } | ';' { $$ = ast.NewEmptyDeclNode($1) } | error ';' { $$ = nil } | error { $$ = nil } name : _NAME | _SYNTAX | _IMPORT | _WEAK | _PUBLIC | _PACKAGE | _OPTION | _TRUE | _FALSE | _INF | _NAN | _REPEATED | _OPTIONAL | _REQUIRED | _DOUBLE | _FLOAT | _INT32 | _INT64 | _UINT32 | _UINT64 | _SINT32 | _SINT64 | _FIXED32 | _FIXED64 | _SFIXED32 | _SFIXED64 | _BOOL | _STRING | _BYTES | _GROUP | _ONEOF | _MAP | _EXTENSIONS | _TO | _MAX | _RESERVED | _ENUM | _MESSAGE | _EXTEND | _SERVICE | _RPC | _STREAM | _RETURNS %%