macks-ruby_protobuf 0.3.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
+