ruby_protobuf 0.3.2 → 0.3.3

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