ruby_protobuf 0.3.2 → 0.3.3

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 (39) hide show
  1. data/History.txt +7 -2
  2. data/Manifest.txt +74 -0
  3. data/README.txt +1 -1
  4. data/Rakefile +5 -5
  5. data/bin/rprotoc +25 -10
  6. data/{test/proto → examples}/addressbook.pb.rb +1 -14
  7. data/examples/addressbook.proto +24 -0
  8. data/examples/reading_a_message.rb +32 -0
  9. data/examples/writing_a_message.rb +46 -0
  10. data/lib/protobuf/compiler/compiler.rb +9 -11
  11. data/lib/protobuf/compiler/nodes.rb +15 -7
  12. data/lib/protobuf/compiler/proto.y +56 -36
  13. data/lib/protobuf/compiler/proto_parser.rb +373 -236
  14. data/lib/protobuf/compiler/visitors.rb +5 -1
  15. data/lib/protobuf/descriptor/enum_descriptor.rb +1 -1
  16. data/lib/protobuf/message/decoder.rb +12 -19
  17. data/lib/protobuf/message/encoder.rb +5 -2
  18. data/lib/protobuf/message/enum.rb +1 -1
  19. data/lib/protobuf/message/field.rb +302 -298
  20. data/lib/protobuf/message/message.rb +63 -35
  21. data/lib/ruby_protobuf.rb +1 -1
  22. data/{bin → script}/mk_parser +0 -0
  23. data/test/merge.rb +1 -1
  24. data/test/proto/types.proto +20 -0
  25. data/test/test_addressbook.rb +1 -0
  26. data/test/test_compiler.rb +21 -7
  27. data/test/test_message.rb +19 -0
  28. data/test/test_optional_field.rb +80 -0
  29. data/test/test_repeated_types.rb +106 -0
  30. data/test/test_serialize.rb +34 -0
  31. data/test/test_standard_message.rb +10 -0
  32. data/test/test_types.rb +49 -4
  33. data/test/types.rb +23 -2
  34. metadata +22 -20
  35. data/lib/protobuf/compiler/compiler_old.rb +0 -123
  36. data/test/proto/addressbook.rb +0 -69
  37. data/test/proto/bool_default.pb.rb +0 -16
  38. data/test/proto/bool_default.proto +0 -3
  39. data/test/proto/float_default.proto +0 -2
@@ -1,6 +1,11 @@
1
- === 0.3.2 / 2008-10-01
1
+ === 0.3.3 / 2009-07-10
2
2
 
3
- * 0.3.2 fixed a lot of bugs. Thanks code.monkey.steve. (rev.177)
3
+ * 0.3.3 (rev.218)
4
+ * Support C-like comment.
5
+ * Optimize for speed.
6
+ * Bug fixes.
7
+ * 0.3.2 (rev.185)
8
+ * Bug fixes.
4
9
  * 0.3.1 fixed bugs concerning RPC (rev.146)
5
10
  * 0.3.0 RPC is available (rev.124)
6
11
  * RPC is available
@@ -0,0 +1,74 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ script/mk_parser
6
+ bin/rprotoc
7
+ examples/addressbook.proto
8
+ examples/addressbook.pb.rb
9
+ examples/reading_a_message.rb
10
+ examples/writing_a_message.rb
11
+ lib/protobuf/common/wire_type.rb
12
+ lib/protobuf/compiler/compiler.rb
13
+ lib/protobuf/compiler/nodes.rb
14
+ lib/protobuf/compiler/proto.y
15
+ lib/protobuf/compiler/proto2.ebnf
16
+ lib/protobuf/compiler/proto_parser.rb
17
+ lib/protobuf/compiler/template/rpc_bin.erb
18
+ lib/protobuf/compiler/template/rpc_client.erb
19
+ lib/protobuf/compiler/template/rpc_service.erb
20
+ lib/protobuf/compiler/visitors.rb
21
+ lib/protobuf/descriptor/descriptor.proto
22
+ lib/protobuf/descriptor/descriptor.rb
23
+ lib/protobuf/descriptor/descriptor_builder.rb
24
+ lib/protobuf/descriptor/descriptor_proto.rb
25
+ lib/protobuf/descriptor/enum_descriptor.rb
26
+ lib/protobuf/descriptor/field_descriptor.rb
27
+ lib/protobuf/descriptor/file_descriptor.rb
28
+ lib/protobuf/message/decoder.rb
29
+ lib/protobuf/message/encoder.rb
30
+ lib/protobuf/message/enum.rb
31
+ lib/protobuf/message/extend.rb
32
+ lib/protobuf/message/field.rb
33
+ lib/protobuf/message/message.rb
34
+ lib/protobuf/message/protoable.rb
35
+ lib/protobuf/message/service.rb
36
+ lib/protobuf/rpc/client.rb
37
+ lib/protobuf/rpc/handler.rb
38
+ lib/protobuf/rpc/server.rb
39
+ lib/ruby_protobuf.rb
40
+ test/addressbook.rb
41
+ test/addressbook_base.rb
42
+ test/addressbook_ext.rb
43
+ test/check_unbuild.rb
44
+ test/collision.rb
45
+ test/data/data.bin
46
+ test/data/data_source.py
47
+ test/data/types.bin
48
+ test/data/types_source.py
49
+ test/data/unk.png
50
+ test/ext_collision.rb
51
+ test/ext_range.rb
52
+ test/merge.rb
53
+ test/nested.rb
54
+ test/proto/addressbook.proto
55
+ test/proto/addressbook_base.proto
56
+ test/proto/addressbook_ext.proto
57
+ test/proto/collision.proto
58
+ test/proto/ext_collision.proto
59
+ test/proto/ext_range.proto
60
+ test/proto/merge.proto
61
+ test/proto/nested.proto
62
+ test/proto/rpc.proto
63
+ test/proto/types.proto
64
+ test/test_addressbook.rb
65
+ test/test_compiler.rb
66
+ test/test_descriptor.rb
67
+ test/test_extension.rb
68
+ test/test_message.rb
69
+ test/test_parse.rb
70
+ test/test_ruby_protobuf.rb
71
+ test/test_serialize.rb
72
+ test/test_standard_message.rb
73
+ test/test_types.rb
74
+ test/types.rb
data/README.txt CHANGED
@@ -14,7 +14,7 @@ Protocol Buffers for Ruby.
14
14
 
15
15
  == SYNOPSIS:
16
16
 
17
- rprotoc test/addressbook.proto
17
+ rprotoc examples/addressbook.proto
18
18
 
19
19
  == REQUIREMENTS:
20
20
 
data/Rakefile CHANGED
@@ -5,14 +5,14 @@ require 'rubygems'
5
5
  require 'hoe'
6
6
  require 'ruby_protobuf'
7
7
 
8
- Hoe.new('ruby_protobuf', RubyProtobuf::VERSION) do |p|
8
+ Hoe.spec('ruby_protobuf') do |p|
9
+ p.version = RubyProtobuf::VERSION
9
10
  p.rubyforge_name = 'ruby-protobuf'
10
- p.author = 'ANDO Yasushi'
11
- p.email = 'andyjpn@gmail.com'
11
+ p.developer('MATSUYAMA Kengo', 'macksx@gmail.com')
12
12
  p.summary = 'Protocol Buffers for Ruby'
13
13
  p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
14
- p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
14
+ p.url = 'http://code.google.com/p/ruby-protobuf'
15
15
  p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
16
16
  end
17
17
 
18
- # vim: syntax=Ruby
18
+ # vim: syntax=ruby
@@ -1,21 +1,36 @@
1
1
  #!/usr/bin/env ruby
2
- $: << "#{File.dirname(__FILE__)}/../lib"
3
2
  require 'optparse'
3
+ if File.exist? "#{File.dirname(__FILE__)}/../lib"
4
+ $: << "#{File.dirname(__FILE__)}/../lib"
5
+ else
6
+ require 'rubygems'
7
+ gem 'ruby_protobuf'
8
+ end
4
9
  require 'ruby_protobuf'
5
- #require 'protobuf/compiler/compiler_old'
6
10
  require 'protobuf/compiler/compiler'
7
11
 
8
12
 
9
- COMMAND_LINE = "#{$0} #{ARGV.join(' ')} PROTO_FILE"
10
- OPT = {}
11
- opts = OptionParser.new
12
- opts.on('-p', '--proto_path <PATH>', 'Specify the directory in which to search for imports. The current directory is default.'){|v| OPT[:proto_path] = v}
13
- opts.on('-o', '--out <OUT_DIR>', 'Specify the directory in which Ruby source file is generated. The current directory is default.'){|v| OPT[:out] = v}
13
+ options = {}
14
+ opts = OptionParser.new("#{$0} [OPTIONS] PROTO_FILE")
15
+ opts.on('-p', '--proto_path <PATH>', 'Specify the directory in which to search for imports. The current directory is default.'){|v| options[:proto_path] = v}
16
+ opts.on('-o', '--out <OUT_DIR>', 'Specify the directory in which Ruby source file is generated. The current directory is default.'){|v| options[:out] = v}
14
17
  opts.on_tail('-v', '--version', 'Show version.'){puts(opts.ver); exit}
15
18
  opts.on_tail('-h', '--help', 'Show this message.'){puts(opts.help); exit}
16
19
 
17
20
  ::Version = RubyProtobuf::VERSION
18
- opts.order! ARGV
19
- PROTO_FILE = ARGV.shift
20
21
 
21
- Protobuf::Compiler.compile(PROTO_FILE, (OPT[:proto_path] or '.'), (OPT[:out] or '.'))
22
+ begin
23
+ opts.order! ARGV
24
+ rescue OptionParser::ParseError
25
+ $stderr.puts $!.to_s
26
+ exit 1
27
+ end
28
+
29
+ proto_file = ARGV.shift
30
+
31
+ unless proto_file
32
+ puts opts
33
+ exit
34
+ end
35
+
36
+ Protobuf::Compiler.compile(proto_file, (options[:proto_path] or '.'), (options[:out] or '.'))
@@ -1,5 +1,5 @@
1
1
  ### Generated by rprotoc. DO NOT EDIT!
2
- ### <proto file: test/proto/addressbook.proto>
2
+ ### <proto file: examples/addressbook.proto>
3
3
  # package tutorial;
4
4
  #
5
5
  # message Person {
@@ -19,13 +19,6 @@
19
19
  # }
20
20
  #
21
21
  # repeated PhoneNumber phone = 4;
22
- # optional uint32 age = 5 [default = 20];
23
- #
24
- # extensions 100 to 200;
25
- # }
26
- #
27
- # extend Person {
28
- # optional int32 age = 100;
29
22
  # }
30
23
  #
31
24
  # message AddressBook {
@@ -55,12 +48,6 @@ module Tutorial
55
48
  optional :PhoneType, :type, 2, :default => :HOME
56
49
  end
57
50
  repeated :PhoneNumber, :phone, 4
58
- optional :uint32, :age, 5, :default => 20
59
- extensions 100..200
60
- end
61
- class Person < ::Protobuf::Message
62
- defined_in __FILE__
63
- optional :int32, :age, 100, :extension => true
64
51
  end
65
52
  class AddressBook < ::Protobuf::Message
66
53
  defined_in __FILE__
@@ -0,0 +1,24 @@
1
+ package tutorial;
2
+
3
+ message Person {
4
+ required string name = 1;
5
+ required int32 id = 2;
6
+ optional string email = 3;
7
+
8
+ enum PhoneType {
9
+ MOBILE = 0;
10
+ HOME = 1;
11
+ WORK = 2;
12
+ }
13
+
14
+ message PhoneNumber {
15
+ required string number = 1;
16
+ optional PhoneType type = 2 [default = HOME];
17
+ }
18
+
19
+ repeated PhoneNumber phone = 4;
20
+ }
21
+
22
+ message AddressBook {
23
+ repeated Person person = 1;
24
+ }
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'addressbook.pb'
4
+
5
+ def list_people(address_book)
6
+ address_book.person.each do |person|
7
+ puts "Person ID: #{person.id}"
8
+ puts " Name: #{person.name}"
9
+ puts " E-mail: #{person.email}" unless person.email.empty?
10
+ person.phone.each do |phone_number|
11
+ print(case phone_number.type
12
+ when Tutorial::Person::PhoneType::MOBILE
13
+ ' Mobile phone #: '
14
+ when Tutorial::Person::PhoneType::HOME
15
+ ' Home phone #: '
16
+ when Tutorial::Person::PhoneType::WORK
17
+ ' Work phone #: '
18
+ end)
19
+ puts phone_number.number
20
+ end
21
+ end
22
+ end
23
+
24
+ unless ARGV.size == 1
25
+ puts "Usage: #{$0} ADDRESS_BOOK_FILE"
26
+ exit
27
+ end
28
+
29
+ address_book = Tutorial::AddressBook.new
30
+ address_book.parse_from_file ARGV[0]
31
+
32
+ list_people address_book
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'addressbook.pb'
4
+
5
+ def prompt_for_address(person)
6
+ print 'Enter person ID number: '
7
+ person.id = STDIN.gets.strip.to_i
8
+ print 'Enter name: '
9
+ person.name = STDIN.gets.strip
10
+ print 'Enter email address (blank for none): '
11
+ email = STDIN.gets.strip
12
+ person.email = email unless email.empty?
13
+
14
+ loop do
15
+ print 'Enter a phone number (or leave blank to finish): '
16
+ break if (number = STDIN.gets.strip).empty?
17
+
18
+ person.phone << Tutorial::Person::PhoneNumber.new
19
+ person.phone.last.number = number
20
+
21
+ print 'Is this a mobile, home, or work phone? '
22
+ person.phone.last.type =
23
+ case type = STDIN.gets.strip
24
+ when 'mobile'
25
+ Tutorial::Person::PhoneType::MOBILE
26
+ when 'home'
27
+ Tutorial::Person::PhoneType::HOME
28
+ when 'work'
29
+ Tutorial::Person::PhoneType::WORK
30
+ else
31
+ puts 'Unknown phone type; leaving as default value.'
32
+ nil
33
+ end
34
+ end
35
+ end
36
+
37
+ unless ARGV.size == 1
38
+ puts "Usage: #{$0} ADDRESS_BOOK_FILE"
39
+ exit
40
+ end
41
+
42
+ address_book = Tutorial::AddressBook.new
43
+ address_book.parse_from_file ARGV[0] if File.exist? ARGV[0]
44
+ address_book.person << Tutorial::Person.new
45
+ prompt_for_address address_book.person.last
46
+ address_book.serialize_to_file ARGV[0]
@@ -15,12 +15,7 @@ module Protobuf
15
15
  end
16
16
 
17
17
  def create_message(proto_file, proto_dir='.', out_dir='.', file_create=true)
18
- out_dir.sub! %r{/$}, ''
19
- proto_dir.sub! %r{/$}, ''
20
- rb_file =
21
- if proto_file =~ %r{^/}
22
- then "#{out_dir}/#{proto_file.split('/').last.sub(/\.proto$/, '.pb.rb')}"
23
- else "#{out_dir}/#{proto_file.sub(/\.proto$/, '.pb.rb')}" end
18
+ rb_file = File.join(out_dir, File.basename(proto_file).sub(/\.[^\0]*\z/, '') + '.pb.rb')
24
19
  proto_path = validate_existence proto_file, proto_dir
25
20
 
26
21
  message_visitor = Protobuf::Visitor::CreateMessageVisitor.new proto_file, proto_dir, out_dir
@@ -31,8 +26,7 @@ module Protobuf
31
26
  end
32
27
 
33
28
  def create_rpc(proto_file, proto_dir='.', out_dir='.', file_create=true)
34
- message_file = "#{out_dir}/#{proto_file.sub(/\.proto$/, '.pb.rb')}"
35
- out_dir = "#{out_dir}/#{File.dirname proto_file}"
29
+ message_file = File.join(out_dir, File.basename(proto_file).sub(/\.[^\0]*\z/, '') + '.pb.rb')
36
30
  proto_path = validate_existence proto_file, proto_dir
37
31
 
38
32
  rpc_visitor = Protobuf::Visitor::CreateRpcVisitor.new
@@ -44,11 +38,15 @@ module Protobuf
44
38
 
45
39
  def validate_existence(path, base_dir)
46
40
  if File.exist? path
47
- elsif File.exist?(path = "#{base_dir or '.'}/#{path}")
41
+ path
48
42
  else
49
- raise ArgumentError.new("File does not exist: #{path}")
43
+ newpath = File.join(base_dir, path)
44
+ if File.exist? newpath
45
+ newpath
46
+ else
47
+ raise ArgumentError.new("File does not exist: #{path}")
48
+ end
50
49
  end
51
- path
52
50
  end
53
51
  end
54
52
  end
@@ -106,10 +106,14 @@ require 'protobuf/message/extend'
106
106
  end
107
107
 
108
108
  def accept_message_visitor(visitor)
109
- visitor.write "class #{@name} < ::Protobuf::Message"
109
+ class_name = @name.to_s
110
+ class_name.gsub!(/\A[a-z]/) {|c| c.upcase}
111
+ visitor.write "class #{class_name} < ::Protobuf::Message"
110
112
  visitor.in_context self.class do
111
113
  define_in_the_file visitor
112
- @children.each {|child| child.accept_message_visitor visitor}
114
+ #@children.each {|child| child.accept_message_visitor visitor}
115
+ @children.each {|child| next if child == ';'; child.accept_message_visitor visitor}
116
+ # TODO: `next if child == ';';' is a monky patching. There must be a parser error.
113
117
  end
114
118
  visitor.write "end"
115
119
  end
@@ -129,7 +133,8 @@ require 'protobuf/message/extend'
129
133
  end
130
134
 
131
135
  def accept_message_visitor(visitor)
132
- visitor.write "class #{@name} < ::Protobuf::Message"
136
+ name = @name.is_a?(Array) ? @name.join : name.to_s
137
+ visitor.write "class #{name} < ::Protobuf::Message"
133
138
  visitor.in_context self.class do
134
139
  define_in_the_file visitor
135
140
  @children.each {|child| child.accept_message_visitor visitor}
@@ -246,7 +251,10 @@ require 'protobuf/message/extend'
246
251
  if visitor.context.first == Protobuf::Node::ExtendNode
247
252
  opts += ', :extension => true'
248
253
  end
249
- type = (Array === @type and 1 < @type.size) ? "'#{@type.join '::'}'" : @type
254
+ type = if @type.is_a?(Array)
255
+ then (@type.size > 1) ? "'#{@type.join '::'}'" : @type[0]
256
+ else @type
257
+ end
250
258
  visitor.write "#{@label} :#{type}, :#{@name}, #{@value}#{opts}"
251
259
  end
252
260
 
@@ -254,7 +262,7 @@ require 'protobuf/message/extend'
254
262
  descriptor = Google::Protobuf::FieldDescriptorProto.new :name => @name.to_s, :number => @value
255
263
  descriptor.label = Google::Protobuf::FieldDescriptorProto::Label.const_get "LABEL_#{@label.to_s.upcase}"
256
264
  descriptor.type = Google::Protobuf::FieldDescriptorProto::Type.const_get "TYPE_#{@type.to_s.upcase}" if predefined_type?
257
- descriptor.type_name = @type.to_s
265
+ descriptor.type_name = @type.is_a?(Array) ? @type.join : @type.to_s
258
266
  @opts.each do |key, val|
259
267
  case key.to_sym
260
268
  when :default
@@ -275,7 +283,7 @@ require 'protobuf/message/extend'
275
283
  end
276
284
 
277
285
  def accept_message_visitor(visitor)
278
- visitor.write "extensions #{@range.to_s}"
286
+ visitor.write "extensions #{@range.first.to_s}"
279
287
  end
280
288
 
281
289
  def accept_descriptor_visitor(visitor)
@@ -303,7 +311,7 @@ require 'protobuf/message/extend'
303
311
  if @high.nil?
304
312
  @low.to_s
305
313
  elsif @high == :max
306
- "#{@low}..Protobuf::Extend::MAX"
314
+ "#{@low}..::Protobuf::Extend::MAX"
307
315
  else
308
316
  "#{@low}..#{@high}"
309
317
  end
@@ -1,5 +1,4 @@
1
1
  class Protobuf::ProtoParser
2
- #options no_result_var
3
2
  rule
4
3
  proto : proto_item
5
4
  { result = Protobuf::Node::ProtoNode.new val }
@@ -105,11 +104,13 @@ rule
105
104
  group : label 'group' CAMEL_IDENT '=' integer_literal message_body
106
105
  { result = Protobuf::Node::GroupNode.new val[0], val[2], val[4], val[5] }
107
106
 
108
- field : label type IDENT '=' integer_literal ';'
107
+ field : label type field_name '=' integer_literal ';'
109
108
  { result = Protobuf::Node::FieldNode.new val[0], val[1], val[2], val[4] }
110
- | label type IDENT '=' integer_literal '[' field_option_list ']' ';'
109
+ | label type field_name '=' integer_literal '[' field_option_list ']' ';'
111
110
  { result = Protobuf::Node::FieldNode.new val[0], val[1], val[2], val[4], val[6] }
112
111
 
112
+ 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"
113
+
113
114
  field_option_list : field_option
114
115
  { result = val }
115
116
  | field_option_list ',' field_option
@@ -160,42 +161,61 @@ end
160
161
 
161
162
  ---- inner
162
163
 
164
+ require 'strscan'
165
+
163
166
  def parse(f)
164
- @q = []
165
- f.each do |line|
166
- until line.empty? do
167
- case line
168
- when /\A\s+/, /\A\/\/.*/
169
- ;
170
- 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/
171
- @q.push [$&, $&.to_sym]
172
- when /\A[1-9]\d*(?!\.)/, /\A0(?![.xX0-9])/
173
- @q.push [:DEC_INTEGER, $&.to_i]
174
- when /\A0[xX]([A-Fa-f0-9])+/
175
- @q.push [:HEX_INTEGER, $&.to_i(0)]
176
- when /\A0[0-7]+/
177
- @q.push [:OCT_INTEGER, $&.to_i(0)]
178
- when /\A\d+(\.\d+)?([Ee][\+-]?\d+)?/
179
- @q.push [:FLOAT_LITERAL, $&.to_f]
180
- when /\A(true|false)/
181
- @q.push [:BOOLEAN_LITERAL, $& == 'true']
182
- when /\A"(?:[^"\\]+|\\.)*"/, /\A'(?:[^'\\]+|\\.)*'/
183
- @q.push [:STRING_LITERAL, eval($&)]
184
- when /\A[a-zA-Z_][\w_]*/
185
- @q.push [:IDENT, $&.to_sym]
186
- when /\A[A-Z][\w_]*/
187
- @q.push [:CAMEL_IDENT, $&.to_sym]
188
- when /\A./
189
- @q.push [$&, $&]
190
- else
191
- raise ArgumentError.new(line)
192
- end
193
- line = $'
167
+ @scanner = StringScanner.new(f.read)
168
+ yyparse(self, :scan)
169
+ end
170
+
171
+ def scan_debug
172
+ scan do |token, value|
173
+ p [token, value]
174
+ yield [token, value]
175
+ end
176
+ end
177
+
178
+ def scan
179
+ until @scanner.eos?
180
+ case
181
+ when match(/\s+/, /\/\/.*/)
182
+ # skip
183
+ when match(/\/\*/)
184
+ # C-like comment
185
+ raise 'EOF inside block comment' until @scanner.scan_until(/\*\//)
186
+ when match(/(?: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/)
187
+ yield [@token, @token.to_sym]
188
+ when match(/[+-]?\d*\.\d+([Ee][\+-]?\d+)?/)
189
+ yield [:FLOAT_LITERAL, @token.to_f]
190
+ when match(/[+-]?[1-9]\d*(?!\.)/, /0(?![.xX0-9])/)
191
+ yield [:DEC_INTEGER, @token.to_i]
192
+ when match(/0[xX]([A-Fa-f0-9])+/)
193
+ yield [:HEX_INTEGER, @token.to_i(0)]
194
+ when match(/0[0-7]+/)
195
+ yield [:OCT_INTEGER, @token.to_i(0)]
196
+ when match(/(true|false)\b/)
197
+ yield [:BOOLEAN_LITERAL, @token == 'true']
198
+ when match(/"(?:[^"\\]+|\\.)*"/, /'(?:[^'\\]+|\\.)*'/)
199
+ yield [:STRING_LITERAL, eval(@token)]
200
+ when match(/[a-zA-Z_][\w_]*/)
201
+ yield [:IDENT, @token.to_sym]
202
+ when match(/[A-Z][\w_]*/)
203
+ yield [:CAMEL_IDENT, @token.to_sym]
204
+ when match(/./)
205
+ yield [@token, @token]
206
+ else
207
+ raise "parse error around #{@scanner.string[@scanner.pos, 32].inspect}"
194
208
  end
195
209
  end
196
- do_parse
210
+ yield [false, nil]
197
211
  end
198
212
 
199
- def next_token
200
- @q.shift
213
+ def match(*regular_expressions)
214
+ regular_expressions.each do |re|
215
+ if @scanner.scan(re)
216
+ @token = @scanner[0]
217
+ return true
218
+ end
219
+ end
220
+ false
201
221
  end