macks-ruby_protobuf 0.3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. data/History.txt +14 -0
  2. data/Manifest.txt +74 -0
  3. data/README.txt +58 -0
  4. data/Rakefile +18 -0
  5. data/bin/mk_parser +2 -0
  6. data/bin/rprotoc +36 -0
  7. data/examples/addressbook.pb.rb +56 -0
  8. data/examples/addressbook.proto +24 -0
  9. data/examples/reading_a_message.rb +32 -0
  10. data/examples/writing_a_message.rb +46 -0
  11. data/lib/protobuf/common/wire_type.rb +10 -0
  12. data/lib/protobuf/compiler/compiler.rb +54 -0
  13. data/lib/protobuf/compiler/nodes.rb +319 -0
  14. data/lib/protobuf/compiler/proto.y +203 -0
  15. data/lib/protobuf/compiler/proto2.ebnf +79 -0
  16. data/lib/protobuf/compiler/proto_parser.rb +1394 -0
  17. data/lib/protobuf/compiler/template/rpc_bin.erb +4 -0
  18. data/lib/protobuf/compiler/template/rpc_client.erb +18 -0
  19. data/lib/protobuf/compiler/template/rpc_service.erb +25 -0
  20. data/lib/protobuf/compiler/visitors.rb +288 -0
  21. data/lib/protobuf/descriptor/descriptor.proto +286 -0
  22. data/lib/protobuf/descriptor/descriptor.rb +54 -0
  23. data/lib/protobuf/descriptor/descriptor_builder.rb +144 -0
  24. data/lib/protobuf/descriptor/descriptor_proto.rb +119 -0
  25. data/lib/protobuf/descriptor/enum_descriptor.rb +33 -0
  26. data/lib/protobuf/descriptor/field_descriptor.rb +50 -0
  27. data/lib/protobuf/descriptor/file_descriptor.rb +38 -0
  28. data/lib/protobuf/message/decoder.rb +93 -0
  29. data/lib/protobuf/message/encoder.rb +37 -0
  30. data/lib/protobuf/message/enum.rb +28 -0
  31. data/lib/protobuf/message/extend.rb +8 -0
  32. data/lib/protobuf/message/field.rb +654 -0
  33. data/lib/protobuf/message/message.rb +308 -0
  34. data/lib/protobuf/message/protoable.rb +37 -0
  35. data/lib/protobuf/message/service.rb +9 -0
  36. data/lib/protobuf/rpc/client.rb +19 -0
  37. data/lib/protobuf/rpc/handler.rb +17 -0
  38. data/lib/protobuf/rpc/server.rb +39 -0
  39. data/lib/ruby_protobuf.rb +3 -0
  40. data/test/addressbook.rb +98 -0
  41. data/test/addressbook_base.rb +62 -0
  42. data/test/addressbook_ext.rb +12 -0
  43. data/test/check_unbuild.rb +30 -0
  44. data/test/collision.rb +18 -0
  45. data/test/data/data.bin +3 -0
  46. data/test/data/data_source.py +14 -0
  47. data/test/data/types.bin +0 -0
  48. data/test/data/types_source.py +22 -0
  49. data/test/data/unk.png +0 -0
  50. data/test/ext_collision.rb +25 -0
  51. data/test/ext_range.rb +23 -0
  52. data/test/merge.rb +40 -0
  53. data/test/nested.rb +25 -0
  54. data/test/proto/addressbook.proto +31 -0
  55. data/test/proto/addressbook_base.proto +26 -0
  56. data/test/proto/addressbook_ext.proto +6 -0
  57. data/test/proto/collision.proto +5 -0
  58. data/test/proto/ext_collision.proto +8 -0
  59. data/test/proto/ext_range.proto +7 -0
  60. data/test/proto/merge.proto +15 -0
  61. data/test/proto/nested.proto +7 -0
  62. data/test/proto/rpc.proto +6 -0
  63. data/test/proto/types.proto +17 -0
  64. data/test/test_addressbook.rb +43 -0
  65. data/test/test_compiler.rb +313 -0
  66. data/test/test_descriptor.rb +122 -0
  67. data/test/test_extension.rb +40 -0
  68. data/test/test_message.rb +106 -0
  69. data/test/test_optional_field.rb +68 -0
  70. data/test/test_parse.rb +15 -0
  71. data/test/test_ruby_protobuf.rb +1 -0
  72. data/test/test_serialize.rb +42 -0
  73. data/test/test_standard_message.rb +96 -0
  74. data/test/test_types.rb +181 -0
  75. data/test/types.rb +22 -0
  76. metadata +150 -0
@@ -0,0 +1,319 @@
1
+ require 'protobuf/descriptor/descriptor_proto'
2
+
3
+ module Protobuf
4
+ module Node
5
+ class Base
6
+ def define_in_the_file(visitor)
7
+ visitor.write "defined_in __FILE__" if visitor.attach_proto?
8
+ end
9
+
10
+ def accept_message_visitor(visitor)
11
+ end
12
+
13
+ def accept_rpc_visitor(vistor)
14
+ end
15
+
16
+ def accept_descriptor_visitor(visitor)
17
+ end
18
+ end
19
+
20
+ class ProtoNode < Base
21
+ attr_reader :children
22
+
23
+ def initialize(children)
24
+ @children = children
25
+ end
26
+
27
+ def accept_message_visitor(visitor)
28
+ visitor.write '### Generated by rprotoc. DO NOT EDIT!'
29
+ visitor.write "### <proto file: #{visitor.proto_file}>" if visitor.attach_proto?
30
+ visitor.write visitor.commented_proto_contents if visitor.attach_proto?
31
+ visitor.write <<-eos
32
+ require 'protobuf/message/message'
33
+ require 'protobuf/message/enum'
34
+ require 'protobuf/message/service'
35
+ require 'protobuf/message/extend'
36
+ eos
37
+ @children.map{|child| child.accept_message_visitor visitor}
38
+ visitor.close_ruby
39
+ end
40
+
41
+ def accept_rpc_visitor(visitor)
42
+ @children.map{|child| child.accept_rpc_visitor visitor}
43
+ end
44
+
45
+ def accept_descriptor_visitor(visitor)
46
+ descriptor = Google::Protobuf::FileDescriptorProto.new :name => visitor.filename
47
+ visitor.file_descriptor = descriptor
48
+ visitor.in_context descriptor do
49
+ @children.map{|child| child.accept_descriptor_visitor visitor}
50
+ end
51
+ end
52
+ end
53
+
54
+ class ImportNode < Base
55
+ def initialize(path)
56
+ @path = path
57
+ end
58
+
59
+ def accept_message_visitor(visitor)
60
+ visitor.write "require '#{visitor.required_message_from_proto @path}'"
61
+ end
62
+
63
+ def accept_descriptor_visitor(visitor)
64
+ visitor.current_descriptor.dependency << @path
65
+ end
66
+ end
67
+
68
+ class PackageNode < Base
69
+ def initialize(path_list)
70
+ @path_list = path_list
71
+ end
72
+
73
+ def accept_message_visitor(visitor)
74
+ @path_list.each do |path|
75
+ visitor.write "module #{path.to_s.capitalize}"
76
+ visitor.increment
77
+ end
78
+ end
79
+
80
+ def accept_rpc_visitor(visitor)
81
+ visitor.package = @path_list.dup
82
+ end
83
+
84
+ def accept_descriptor_visitor(visitor)
85
+ visitor.current_descriptor.package = @path_list.join '.'
86
+ end
87
+ end
88
+
89
+ class OptionNode < Base
90
+ def initialize(name_list, value)
91
+ @name_list, @value = name_list, value
92
+ end
93
+
94
+ def accept_message_visitor(visitor)
95
+ visitor.write "::Protobuf::OPTIONS[:#{@name_list.join('.').inspect}] = #{@value.inspect}"
96
+ end
97
+
98
+ def accept_descriptor_visitor(visitor)
99
+ visitor.add_option @name_list.join('.'), @value
100
+ end
101
+ end
102
+
103
+ class MessageNode < Base
104
+ def initialize(name, children)
105
+ @name, @children = name, children
106
+ end
107
+
108
+ def accept_message_visitor(visitor)
109
+ visitor.write "class #{@name} < ::Protobuf::Message"
110
+ visitor.in_context self.class do
111
+ define_in_the_file visitor
112
+ #@children.each {|child| child.accept_message_visitor visitor}
113
+ @children.each {|child| next if child == ';'; child.accept_message_visitor visitor}
114
+ # TODO: `next if child == ';';' is a monky patching. There must be a parser error.
115
+ end
116
+ visitor.write "end"
117
+ end
118
+
119
+ def accept_descriptor_visitor(visitor)
120
+ descriptor = Google::Protobuf::DescriptorProto.new :name => @name.to_s
121
+ visitor.descriptor = descriptor
122
+ visitor.in_context descriptor do
123
+ @children.each {|child| child.accept_descriptor_visitor visitor}
124
+ end
125
+ end
126
+ end
127
+
128
+ class ExtendNode < Base
129
+ def initialize(name, children)
130
+ @name, @children = name, children
131
+ end
132
+
133
+ def accept_message_visitor(visitor)
134
+ name = @name.is_a?(Array) ? @name.join : name.to_s
135
+ visitor.write "class #{name} < ::Protobuf::Message"
136
+ visitor.in_context self.class do
137
+ define_in_the_file visitor
138
+ @children.each {|child| child.accept_message_visitor visitor}
139
+ end
140
+ visitor.write "end"
141
+ end
142
+
143
+ def accept_descriptor_visitor(visitor)
144
+ # TODO: how should i handle this?
145
+ end
146
+ end
147
+
148
+ class EnumNode < Base
149
+ def initialize(name, children)
150
+ @name, @children = name, children
151
+ end
152
+
153
+ def accept_message_visitor(visitor)
154
+ visitor.write "class #{@name} < ::Protobuf::Enum"
155
+ visitor.in_context self.class do
156
+ define_in_the_file visitor
157
+ @children.each {|child| child.accept_message_visitor visitor}
158
+ end
159
+ visitor.write "end"
160
+ end
161
+
162
+ def accept_descriptor_visitor(visitor)
163
+ descriptor = Google::Protobuf::EnumDescriptorProto.new :name => @name.to_s
164
+ visitor.enum_descriptor = descriptor
165
+ visitor.in_context descriptor do
166
+ @children.each {|child| child.accept_descriptor_visitor visitor}
167
+ end
168
+ end
169
+ end
170
+
171
+ class EnumFieldNode < Base
172
+ def initialize(name, value)
173
+ @name, @value = name, value
174
+ end
175
+
176
+ def accept_message_visitor(visitor)
177
+ visitor.write "#{@name} = #{@value}"
178
+ end
179
+
180
+ def accept_descriptor_visitor(visitor)
181
+ descriptor = Google::Protobuf::EnumValueDescriptorProto.new :name => @name.to_s, :number => @value
182
+ visitor.enum_value_descriptor = descriptor
183
+ end
184
+ end
185
+
186
+ class ServiceNode < Base
187
+ def initialize(name, children)
188
+ @name, @children = name, children
189
+ end
190
+
191
+ def accept_message_visitor(visitor)
192
+ # do nothing
193
+ end
194
+
195
+ def accept_rpc_visitor(visitor)
196
+ visitor.current_service = @name
197
+ @children.each {|child| child.accept_rpc_visitor visitor}
198
+ end
199
+
200
+ def accept_descriptor_visitor(visitor)
201
+ descriptor = Google::Protobuf::ServiceDescriptorProto.new :name => @name.to_s
202
+ visitor.service_descriptor = descriptor
203
+ visitor.in_context descriptor do
204
+ @children.each {|child| child.accept_descriptor_visitor visitor}
205
+ end
206
+ end
207
+ end
208
+
209
+ class RpcNode < Base
210
+ def initialize(name, request, response)
211
+ @name, @request, @response = name, request, response
212
+ end
213
+
214
+ def accept_message_visitor(visitor)
215
+ # do nothing
216
+ end
217
+
218
+ def accept_rpc_visitor(visitor)
219
+ visitor.add_rpc @name, @request, @response
220
+ end
221
+
222
+ def accept_descriptor_visitor(visitor)
223
+ descriptor = Google::Protobuf::MethodDescriptorProto.new :name => @name.to_s, :input_type => @request.to_s, :output_type => @response.to_s
224
+ visitor.method_descriptor = descriptor
225
+ end
226
+ end
227
+
228
+ class GroupNode < Base
229
+ def initialize(label, name, value, children)
230
+ @label, @name, @value, @children = label, name, value, children
231
+ end
232
+
233
+ def accept_message_visitor(visitor)
234
+ raise NotImplementedError.new
235
+ end
236
+
237
+ def accept_descriptor_visitor(visitor)
238
+ raise NotImplementedError.new
239
+ end
240
+ end
241
+
242
+ class FieldNode < Base
243
+ def initialize(label, type, name, value, opts={})
244
+ @label, @type, @name, @value, @opts = label, type, name, value, opts
245
+ end
246
+
247
+ def accept_message_visitor(visitor)
248
+ opts = @opts.empty? ? '' : ", #{@opts.map{|k, v| ":#{k} => #{v.inspect}" }.join(', ')}"
249
+ if visitor.context.first == Protobuf::Node::ExtendNode
250
+ opts += ', :extension => true'
251
+ end
252
+ type = if @type.is_a?(Array)
253
+ then (@type.size > 1) ? "'#{@type.join '::'}'" : @type[0]
254
+ else @type
255
+ end
256
+ visitor.write "#{@label} :#{type}, :#{@name}, #{@value}#{opts}"
257
+ end
258
+
259
+ def accept_descriptor_visitor(visitor)
260
+ descriptor = Google::Protobuf::FieldDescriptorProto.new :name => @name.to_s, :number => @value
261
+ descriptor.label = Google::Protobuf::FieldDescriptorProto::Label.const_get "LABEL_#{@label.to_s.upcase}"
262
+ descriptor.type = Google::Protobuf::FieldDescriptorProto::Type.const_get "TYPE_#{@type.to_s.upcase}" if predefined_type?
263
+ descriptor.type_name = @type.is_a?(Array) ? @type.join : @type.to_s
264
+ @opts.each do |key, val|
265
+ case key.to_sym
266
+ when :default
267
+ descriptor.default_value = val.to_s
268
+ end
269
+ end
270
+ visitor.field_descriptor = descriptor
271
+ end
272
+
273
+ def predefined_type?
274
+ %w{double float int64 uint64 int32 fixed64 fixed32 bool string group message bytes uint32 enum sfixed32 sfixed64 sint32 sint64}.include? @type.to_s
275
+ end
276
+ end
277
+
278
+ class ExtensionsNode < Base
279
+ def initialize(range)
280
+ @range = range
281
+ end
282
+
283
+ def accept_message_visitor(visitor)
284
+ visitor.write "extensions #{@range.first.to_s}"
285
+ end
286
+
287
+ def accept_descriptor_visitor(visitor)
288
+ descriptor = Google::Protobuf::DescriptorProto::ExtensionRange.new :start => @range.first.low
289
+ case @range.first.high
290
+ when NilClass; # ignore
291
+ when :max; descriptor.end = 1
292
+ else; descriptor.end = @range.first.high
293
+ end
294
+ visitor.extension_range_descriptor = descriptor
295
+ end
296
+ end
297
+
298
+ class ExtensionRangeNode < Base
299
+ attr_reader :low, :high
300
+
301
+ def initialize(low, high=nil)
302
+ @low, @high = low, high
303
+ end
304
+
305
+ #def accept_message_visitor(visitor)
306
+ #end
307
+
308
+ def to_s
309
+ if @high.nil?
310
+ @low.to_s
311
+ elsif @high == :max
312
+ "#{@low}..::Protobuf::Extend::MAX"
313
+ else
314
+ "#{@low}..#{@high}"
315
+ end
316
+ end
317
+ end
318
+ end
319
+ end
@@ -0,0 +1,203 @@
1
+ class Protobuf::ProtoParser
2
+ #options no_result_var
3
+ rule
4
+ proto : proto_item
5
+ { result = Protobuf::Node::ProtoNode.new val }
6
+ | proto proto_item
7
+ { result.children << val[1] }
8
+
9
+ proto_item : message
10
+ | extend
11
+ | enum
12
+ | import
13
+ | package
14
+ | option
15
+ | service
16
+ | ';'
17
+ { }
18
+
19
+ import : 'import' STRING_LITERAL ';'
20
+ { result = Protobuf::Node::ImportNode.new val[1] }
21
+
22
+ package : 'package' IDENT dot_ident_list ';'
23
+ { result = Protobuf::Node::PackageNode.new val[2].unshift(val[1]) }
24
+
25
+ dot_ident_list :
26
+ { result = [] }
27
+ | dot_ident_list '.' IDENT
28
+ { result << val[2] }
29
+
30
+ option : 'option' option_body ';'
31
+ { result = Protobuf::Node::OptionNode.new(*val[1]) }
32
+
33
+ option_body : IDENT dot_ident_list '=' constant
34
+ { result = [val[1].unshift(val[0]), val[3]] }
35
+
36
+ message : 'message' IDENT message_body
37
+ { result = Protobuf::Node::MessageNode.new val[1], val[2] }
38
+
39
+ extend : 'extend' user_type '{' extend_body_list '}'
40
+ { result = Protobuf::Node::ExtendNode.new val[1], val[3] }
41
+
42
+ extend_body_list :
43
+ { result = [] }
44
+ | extend_body_list extend_body
45
+ { result << val[1] }
46
+
47
+ extend_body : field
48
+ | group
49
+ | ';'
50
+ { }
51
+
52
+ enum : 'enum' IDENT '{' enum_body_list '}'
53
+ { result = Protobuf::Node::EnumNode.new val[1], val[3] }
54
+
55
+ enum_body_list :
56
+ { result = [] }
57
+ | enum_body_list enum_body
58
+ { result << val[1] }
59
+
60
+ enum_body : option
61
+ | enum_field
62
+ | ';'
63
+ { }
64
+
65
+ enum_field : IDENT '=' integer_literal ';'
66
+ { result = Protobuf::Node::EnumFieldNode.new val[0], val[2] }
67
+
68
+ service : 'service' IDENT '{' service_body_list '}'
69
+ { result = Protobuf::Node::ServiceNode.new val[1], val[3] }
70
+
71
+ service_body_list :
72
+ { result = [] }
73
+ | service_body_list service_body
74
+ { result << val[1] }
75
+
76
+ service_body : option
77
+ | rpc
78
+ | ';'
79
+ { }
80
+
81
+ rpc : 'rpc' IDENT '(' rpc_arg ')' 'returns' '(' rpc_arg ')' ';'
82
+ { result = Protobuf::Node::RpcNode.new val[1], val[3], val[7] }
83
+
84
+ rpc_arg :
85
+ | user_type
86
+
87
+ message_body : '{' message_body_body_list '}'
88
+ { result = val[1] }
89
+
90
+ message_body_body_list :
91
+ { result = [] }
92
+ | message_body_body_list message_body_body
93
+ { result << val[1] }
94
+
95
+ message_body_body : field
96
+ | enum
97
+ | message
98
+ | extend
99
+ | extensions
100
+ | group
101
+ | option
102
+ | ';'
103
+ { }
104
+
105
+ group : label 'group' CAMEL_IDENT '=' integer_literal message_body
106
+ { result = Protobuf::Node::GroupNode.new val[0], val[2], val[4], val[5] }
107
+
108
+ field : label type field_name '=' integer_literal ';'
109
+ { result = Protobuf::Node::FieldNode.new val[0], val[1], val[2], val[4] }
110
+ | label type field_name '=' integer_literal '[' field_option_list ']' ';'
111
+ { result = Protobuf::Node::FieldNode.new val[0], val[1], val[2], val[4], val[6] }
112
+
113
+ field_name : IDENT | "required" | "optional" | "repeated" | "import" | "package" | "option" | "message" | "extend" | "enum" | "service" | "rpc" | "returns" | "group" | "default" | "extensions" | "to" | "max" | "double" | "float" | "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" | "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string" | "bytes"
114
+
115
+ field_option_list : field_option
116
+ { result = val }
117
+ | field_option_list ',' field_option
118
+ { result << val[2] }
119
+
120
+ field_option : option_body
121
+ | 'default' '=' constant
122
+ { result = [:default, val[2]] }
123
+
124
+ extensions : 'extensions' extension comma_extension_list ';'
125
+ { result = Protobuf::Node::ExtensionsNode.new val[2].unshift(val[1]) }
126
+
127
+ comma_extension_list :
128
+ { result = [] }
129
+ | ',' extension
130
+ { result << val[1] }
131
+
132
+ extension : integer_literal
133
+ { result = Protobuf::Node::ExtensionRangeNode.new val[0] }
134
+ | integer_literal 'to' integer_literal
135
+ { result = Protobuf::Node::ExtensionRangeNode.new val[0], val[2] }
136
+ | integer_literal 'to' 'max'
137
+ { result = Protobuf::Node::ExtensionRangeNode.new val[0], :max }
138
+
139
+ label : 'required'
140
+ | 'optional'
141
+ | 'repeated'
142
+
143
+ type : 'double' | 'float' | 'int32' | 'int64' | 'uint32' | 'uint64'
144
+ | 'sint32' | 'sint64' | 'fixed32' | 'fixed64' | 'sfixed32' | 'sfixed64'
145
+ | 'bool' | 'string' | 'bytes' | user_type
146
+
147
+ user_type : IDENT dot_ident_list
148
+ { result = val[1].unshift(val[0]) }
149
+ | '.' IDENT dot_ident_list
150
+ { result = val[1].unshift(val[0]) }
151
+
152
+ constant : IDENT
153
+ | integer_literal
154
+ | FLOAT_LITERAL
155
+ | STRING_LITERAL
156
+ | BOOLEAN_LITERAL
157
+
158
+ integer_literal : DEC_INTEGER
159
+ | HEX_INTEGER
160
+ | OCT_INTEGER
161
+ end
162
+
163
+ ---- inner
164
+
165
+ def parse(f)
166
+ @q = []
167
+ f.each do |line|
168
+ until line.empty? do
169
+ case line
170
+ when /\A\s+/, /\A\/\/.*/
171
+ ;
172
+ when /\A(required|optional|repeated|import|package|option|message|extend|enum|service|rpc|returns|group|default|extensions|to|max|double|float|int32|int64|uint32|uint64|sint32|sint64|fixed32|fixed64|sfixed32|sfixed64|bool|string|bytes)\b/
173
+ @q.push [$&, $&.to_sym]
174
+ when /\A[1-9]\d*(?!\.)/, /\A0(?![.xX0-9])/
175
+ @q.push [:DEC_INTEGER, $&.to_i]
176
+ when /\A0[xX]([A-Fa-f0-9])+/
177
+ @q.push [:HEX_INTEGER, $&.to_i(0)]
178
+ when /\A0[0-7]+/
179
+ @q.push [:OCT_INTEGER, $&.to_i(0)]
180
+ when /\A\d+(\.\d+)?([Ee][\+-]?\d+)?/
181
+ @q.push [:FLOAT_LITERAL, $&.to_f]
182
+ when /\A(true|false)/
183
+ @q.push [:BOOLEAN_LITERAL, $& == 'true']
184
+ when /\A"(?:[^"\\]+|\\.)*"/, /\A'(?:[^'\\]+|\\.)*'/
185
+ @q.push [:STRING_LITERAL, eval($&)]
186
+ when /\A[a-zA-Z_][\w_]*/
187
+ @q.push [:IDENT, $&.to_sym]
188
+ when /\A[A-Z][\w_]*/
189
+ @q.push [:CAMEL_IDENT, $&.to_sym]
190
+ when /\A./
191
+ @q.push [$&, $&]
192
+ else
193
+ raise ArgumentError.new(line)
194
+ end
195
+ line = $'
196
+ end
197
+ end
198
+ do_parse
199
+ end
200
+
201
+ def next_token
202
+ @q.shift
203
+ end
@@ -0,0 +1,79 @@
1
+ # See: http://groups.google.com/group/protobuf/browse_thread/thread/1cccfc624cd612da
2
+
3
+ proto ::= ( message | extend | enum | import | package | option | ";" )*
4
+
5
+ import ::= "import" strLit ";"
6
+
7
+ package ::= "package" ident ( "." ident )* ";"
8
+
9
+ option ::= "option" optionBody ";"
10
+
11
+ optionBody ::= ident ( "." ident )* "=" constant
12
+
13
+ message ::= "message" ident messageBody
14
+
15
+ extend ::= "extend" userType "{" ( field | group | ";" )* "}"
16
+
17
+ enum ::= "enum" ident "{" ( option | enumField | ";" )* "}"
18
+
19
+ enumField ::= ident "=" intLit ";"
20
+
21
+ service ::= "service" ident "{" ( option | rpc | ";" )* "}"
22
+
23
+ rpc ::= "rpc" ident "(" userType ")" "returns" "(" userType ")" ";"
24
+
25
+ messageBody ::= "{" ( field | enum | message | extend | extensions | group | option | ":" )* "}"
26
+
27
+ group ::= label "group" camelIdent "=" intLit messageBody
28
+
29
+ field ::= label type ident "=" intLit ( "[" fieldOption ( "," fieldOption )* "]" )? ";"
30
+ # tag number must be 2^29-1 or lower, not 0, and not 19000-19999 (reserved)
31
+
32
+ fieldOption ::= optionBody | "default" "=" constant
33
+
34
+ # extension numbers must not overlap with field or other extension numbers
35
+ extensions ::= "extensions" extension ( "," extension )* ";"
36
+
37
+ extension ::= intLit ( "to" ( intLit | "max" ) )?
38
+
39
+ label ::= "required" | "optional" | "repeated"
40
+
41
+ type ::= "double" | "float" | "int32" | "int64" | "uint32" | "uint64"
42
+ | "sint32" | "sint64" | "fixed32" | "fixed64" | "sfixed32" | "sfixed64"
43
+ | "bool" | "string" | "bytes" | userType
44
+
45
+ # leading dot for identifiers means they're fully qualified
46
+ userType ::= "."? ident ( "." ident )*
47
+
48
+ constant ::= ident | intLit | floatLit | strLit | boolLit
49
+
50
+ ident ::= /[A-Za-z_][\w_]*/
51
+
52
+ # according to parser.cc, group names must start with a capital letter as a
53
+ # hack for backwards-compatibility
54
+ camelIdent ::= /[A-Z][\w_]*/
55
+
56
+ intLit ::= decInt | hexInt | octInt
57
+
58
+ decInt ::= /[1-9]\d*/
59
+
60
+ hexInt ::= /0[xX]([A-Fa-f0-9])+/
61
+
62
+ octInt ::= /0[0-7]+/
63
+
64
+ # allow_f_after_float_ is disabled by default in tokenizer.cc
65
+ floatLit ::= /\d+(\.\d+)?([Ee][\+-]?\d+)?/
66
+
67
+ boolLit ::= "true" | "false"
68
+
69
+ # contents must not contain unescaped quote character
70
+ strLit ::= quote ( hexEscape | octEscape | charEscape | /[^\0\n]/ )* quote
71
+
72
+ quote ::= /["']/
73
+
74
+ hexEscape ::= /\\[Xx][A-Fa-f0-9]{1,2}/
75
+
76
+ octEscape ::= /\\0?[0-7]{1,3}/
77
+
78
+ charEscape ::= /\\[abfnrtv\\\?'"]/
79
+