Protocol Buffers Edition 2023 Sprachspezifikation

Sprachspezifikationsreferenz für die Edition 2023 der Protocol Buffers-Sprache.

Die Syntax wird mithilfe von Extended Backus-Naur Form (EBNF) spezifiziert.

|   alternation
()  grouping
[]  option (zero or one time)
{}  repetition (any number of times)

Lexikalische Elemente

Buchstaben und Ziffern

letter = "A" ... "Z" | "a" ... "z"
capitalLetter =  "A" ... "Z"
decimalDigit = "0" ... "9"
octalDigit   = "0" ... "7"
hexDigit     = "0" ... "9" | "A" ... "F" | "a" ... "f"

Bezeichner

ident = letter { letter | decimalDigit | "_" }
fullIdent = ident { "." ident }
messageName = ident
enumName = ident
fieldName = ident
oneofName = ident
mapName = ident
serviceName = ident
rpcName = ident
streamName = ident
messageType = [ "." ] { ident "." } messageName
enumType = [ "." ] { ident "." } enumName
groupName = capitalLetter { letter | decimalDigit | "_" }

Ganzzahl-Literale

intLit     = decimalLit | octalLit | hexLit
decimalLit = [-] ( "1" ... "9" ) { decimalDigit }
octalLit   = [-] "0" { octalDigit }
hexLit     = [-] "0" ( "x" | "X" ) hexDigit { hexDigit }

Gleitkomma-Literale

floatLit = [-] ( decimals "." [ decimals ] [ exponent ] | decimals exponent | "."decimals [ exponent ] ) | "inf" | "nan"
decimals  = [-] decimalDigit { decimalDigit }
exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals

Boolean

boolLit = "true" | "false"

Zeichenketten-Literale

strLit = strLitSingle { strLitSingle }
strLitSingle = ( "'" { charValue } "'" ) | ( '"' { charValue } '"' )
charValue = hexEscape | octEscape | charEscape | unicodeEscape | unicodeLongEscape | /[^\0\n\\]/
hexEscape = '\' ( "x" | "X" ) hexDigit [ hexDigit ]
octEscape = '\' octalDigit [ octalDigit [ octalDigit ] ]
charEscape = '\' ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | '\' | "'" | '"' )
unicodeEscape = '\' "u" hexDigit hexDigit hexDigit hexDigit
unicodeLongEscape = '\' "U" ( "000" hexDigit hexDigit hexDigit hexDigit hexDigit |
                              "0010" hexDigit hexDigit hexDigit hexDigit

EmptyStatement

emptyStatement = ";"

Konstante

constant = fullIdent | ( [ "-" | "+" ] intLit ) | ( [ "-" | "+" ] floatLit ) |
                strLit | boolLit | MessageValue

MessageValue ist in der Text Format Language Specification definiert.

Edition

Die Edition-Anweisung ersetzt das veraltete Schlüsselwort syntax und wird verwendet, um die Edition zu definieren, die diese Datei verwendet.

edition = "edition" "=" [ ( "'" decimalLit "'" ) | ( '"' decimalLit '"' ) ] ";"

Import-Anweisung

Die Import-Anweisung wird verwendet, um Definitionen aus anderen .proto-Dateien zu importieren.

import = "import" [ "weak" | "public" ] strLit ";"

Beispiel

import public "other.proto";

Paket

Der Paket-Spezifizierer kann verwendet werden, um Namenskonflikte zwischen Protokoll-Nachrichtentypen zu vermeiden.

package = "package" fullIdent ";"

Beispiel

package foo.bar;

Option

Optionen können in Proto-Dateien, Nachrichten, Enums und Diensten verwendet werden. Eine Option kann eine von Protocol Buffers definierte Option oder eine benutzerdefinierte Option sein. Weitere Informationen finden Sie unter Optionen im Sprachleitfaden. Optionen werden auch verwendet, um Feature-Einstellungen zu steuern.

option = "option" optionName  "=" constant ";"
optionName = ( ident | "(" ["."] fullIdent ")" )

Beispiele

option java_package = "com.example.foo";
option features.enum_type = CLOSED;

Felder

Felder sind die grundlegenden Elemente einer Protocol Buffer-Nachricht. Felder können normale Felder, Gruppenfelder, Oneof-Felder oder Map-Felder sein. Ein Feld hat ein Label, einen Typ und eine Feldnummer.

label = [ "repeated" ]
type = "double" | "float" | "int32" | "int64" | "uint32" | "uint64"
      | "sint32" | "sint64" | "fixed32" | "fixed64" | "sfixed32" | "sfixed64"
      | "bool" | "string" | "bytes" | messageType | enumType
fieldNumber = intLit;

Normales Feld

Jedes Feld hat ein Label, einen Typ, einen Namen und eine Feldnummer. Es kann Feldoptionen haben.

field = [label] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
fieldOptions = fieldOption { ","  fieldOption }
fieldOption = optionName "=" constant

Beispiele

foo.bar nested_message = 2;
repeated int32 samples = 4 [packed=true];

Oneof und oneof-Feld

Ein Oneof besteht aus Oneof-Feldern und einem Oneof-Namen. Oneof-Felder haben keine Labels.

oneof = "oneof" oneofName "{" { option | oneofField } "}"
oneofField = type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"

Beispiel

oneof foo {
    string name = 4;
    SubMessage sub_message = 9;
}

Map-Feld

Ein Map-Feld hat einen Schlüsseltyp, einen Werttyp, einen Namen und eine Feldnummer. Der Schlüsseltyp kann jeder ganzzahlige oder Zeichenkettentyp sein. Beachten Sie, dass der Schlüsseltyp kein Enum sein darf.

mapField = "map" "<" keyType "," type ">" mapName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" |
          "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string"

Beispiel

map<string, Project> projects = 3;

Erweiterungen und Reservierungen

Erweiterungen und reservierte Felder sind Nachrichtenelemente, die einen Bereich von Feldnummern oder Feldnamen deklarieren.

Erweiterungen (Extensions)

Erweiterungen deklarieren, dass ein Bereich von Feldnummern in einer Nachricht für Erweiterungen von Drittanbietern verfügbar ist. Andere Personen können neue Felder für Ihren Nachrichtentyp mit diesen numerischen Tags in ihren eigenen .proto-Dateien deklarieren, ohne die Originaldatei bearbeiten zu müssen.

extensions = "extensions" ranges ";"
ranges = range { "," range }
range =  intLit [ "to" ( intLit | "max" ) ]

Beispiele

extensions 100 to 199;
extensions 4, 20 to max;

Reserviert

Reserviert deklariert einen Bereich von Feldnummern oder Namen in einer Nachricht oder einem Enum, der nicht verwendet werden kann.

reserved = "reserved" ( ranges | reservedIdent ) ";"
fieldNames = fieldName { "," fieldName }

Beispiele

reserved 2, 15, 9 to 11;
reserved foo, bar;

Top-Level-Definitionen

Enum-Definition

Die Enum-Definition besteht aus einem Namen und einem Enum-Body. Der Enum-Body kann Optionen, Enum-Felder und reservierte Anweisungen enthalten.

enum = "enum" enumName enumBody
enumBody = "{" { option | enumField | emptyStatement | reserved } "}"
enumField = fieldName "=" [ "-" ] intLit [ "[" enumValueOption { ","  enumValueOption } "]" ]";"
enumValueOption = optionName "=" constant

Beispiel

enum EnumAllowingAlias {
  option allow_alias = true;
  EAA_UNSPECIFIED = 0;
  EAA_STARTED = 1;
  EAA_RUNNING = 2 [(custom_option) = "hello world"];
}

Nachrichten-Definition

Eine Nachricht besteht aus einem Nachrichtennamen und einem Nachrichtenkörper. Der Nachrichtenkörper kann Felder, verschachtelte Enum-Definitionen, verschachtelte Nachrichten-Definitionen, Erweiterungsanweisungen, Erweiterungen, Gruppen, Optionen, Oneofs, Map-Felder und reservierte Anweisungen enthalten. Eine Nachricht kann nicht zwei Felder mit demselben Namen im selben Nachrichtenschema enthalten.

message = "message" messageName messageBody
messageBody = "{" { field | enum | message | extend | extensions | group |
option | oneof | mapField | reserved | emptyStatement } "}"

Beispiel

message Outer {
  option (my_option).a = true;
  message Inner {   // Level 2
    required int64 ival = 1;
  }
  map<int32, string> my_map = 2;
  extensions 20 to 30;
}

Keine der innerhalb einer Nachricht deklarierten Entitäten darf widersprüchliche Namen haben. Alle folgenden sind verboten

message MyMessage {
  string foo = 1;
  message foo {}
}

message MyMessage {
  string foo = 1;
  oneof foo {
    string bar = 2;
  }
}

message MyMessage {
  string foo = 1;
  extend Extendable {
    string foo = 2;
  }
}

message MyMessage {
  string foo = 1;
  enum E {
    foo = 0;
  }
}

Erweitern

Wenn eine Nachricht in derselben oder einer importierten .proto-Datei einen Bereich für Erweiterungen reserviert hat, kann die Nachricht erweitert werden.

extend = "extend" messageType "{" {field | group} "}"

Beispiel

extend Foo {
  int32 bar = 126;
}

Dienstdefinition

service = "service" serviceName "{" { option | rpc | emptyStatement } "}"
rpc = "rpc" rpcName "(" [ "stream" ] messageType ")" "returns" "(" [ "stream" ]
messageType ")" (( "{" { option | emptyStatement } "}" ) | ";" )

Beispiel

service SearchService {
  rpc Search (SearchRequest) returns (SearchResponse);
}

Proto-Datei

proto = [syntax] { import | package | option | topLevelDef | emptyStatement }
topLevelDef = message | enum | extend | service

Ein Beispiel .proto-Datei

edition = "2023";
import public "other.proto";
option java_package = "com.example.foo";
enum EnumAllowingAlias {
  option allow_alias = true;
  EAA_UNSPECIFIED = 0;
  EAA_STARTED = 1;
  EAA_RUNNING = 1;
  EAA_FINISHED = 2 [(custom_option) = "hello world"];
}
message Outer {
  option (my_option).a = true;
  message Inner {   // Level 2
    int64 ival = 1 [features.field_presence = LEGACY_REQUIRED];
  }
  repeated Inner inner_message = 2;
  EnumAllowingAlias enum_field = 3;
  map<int32, string> my_map = 4;
  extensions 20 to 30;
  reserved reserved_field;
}
message Foo {
  message GroupMessage {
    bool a = 1;
  }
  GroupMessage groupmessage = [features.message_encoding = DELIMITED];
}