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.
- data/History.txt +7 -2
- data/Manifest.txt +74 -0
- data/README.txt +1 -1
- data/Rakefile +5 -5
- data/bin/rprotoc +25 -10
- data/{test/proto → examples}/addressbook.pb.rb +1 -14
- data/examples/addressbook.proto +24 -0
- data/examples/reading_a_message.rb +32 -0
- data/examples/writing_a_message.rb +46 -0
- data/lib/protobuf/compiler/compiler.rb +9 -11
- data/lib/protobuf/compiler/nodes.rb +15 -7
- data/lib/protobuf/compiler/proto.y +56 -36
- data/lib/protobuf/compiler/proto_parser.rb +373 -236
- data/lib/protobuf/compiler/visitors.rb +5 -1
- data/lib/protobuf/descriptor/enum_descriptor.rb +1 -1
- data/lib/protobuf/message/decoder.rb +12 -19
- data/lib/protobuf/message/encoder.rb +5 -2
- data/lib/protobuf/message/enum.rb +1 -1
- data/lib/protobuf/message/field.rb +302 -298
- data/lib/protobuf/message/message.rb +63 -35
- data/lib/ruby_protobuf.rb +1 -1
- data/{bin → script}/mk_parser +0 -0
- data/test/merge.rb +1 -1
- data/test/proto/types.proto +20 -0
- data/test/test_addressbook.rb +1 -0
- data/test/test_compiler.rb +21 -7
- data/test/test_message.rb +19 -0
- data/test/test_optional_field.rb +80 -0
- data/test/test_repeated_types.rb +106 -0
- data/test/test_serialize.rb +34 -0
- data/test/test_standard_message.rb +10 -0
- data/test/test_types.rb +49 -4
- data/test/types.rb +23 -2
- metadata +22 -20
- data/lib/protobuf/compiler/compiler_old.rb +0 -123
- data/test/proto/addressbook.rb +0 -69
- data/test/proto/bool_default.pb.rb +0 -16
- data/test/proto/bool_default.proto +0 -3
- data/test/proto/float_default.proto +0 -2
data/History.txt
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
-
=== 0.3.
|
1
|
+
=== 0.3.3 / 2009-07-10
|
2
2
|
|
3
|
-
* 0.3.
|
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
|
data/Manifest.txt
ADDED
@@ -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
data/Rakefile
CHANGED
@@ -5,14 +5,14 @@ require 'rubygems'
|
|
5
5
|
require 'hoe'
|
6
6
|
require 'ruby_protobuf'
|
7
7
|
|
8
|
-
Hoe.
|
8
|
+
Hoe.spec('ruby_protobuf') do |p|
|
9
|
+
p.version = RubyProtobuf::VERSION
|
9
10
|
p.rubyforge_name = 'ruby-protobuf'
|
10
|
-
p.
|
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 =
|
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=
|
18
|
+
# vim: syntax=ruby
|
data/bin/rprotoc
CHANGED
@@ -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
|
-
|
10
|
-
|
11
|
-
opts =
|
12
|
-
opts.on('-
|
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
|
-
|
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:
|
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
|
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 =
|
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
|
-
|
41
|
+
path
|
48
42
|
else
|
49
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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}
|
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
|
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
|
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
|
-
@
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
210
|
+
yield [false, nil]
|
197
211
|
end
|
198
212
|
|
199
|
-
def
|
200
|
-
|
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
|