Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit after forking from macks/ruby-protobuf

  • Loading branch information...
commit 4c5e581f486c332f7c0e350fb008dfb275563671 0 parents
@localshred localshred authored
Showing with 9,342 additions and 0 deletions.
  1. +5 −0 .gitignore
  2. +3 −0  Gemfile
  3. +28 −0 Gemfile.lock
  4. +216 −0 README.md
  5. +1 −0  Rakefile
  6. +117 −0 bin/rpc_server
  7. +46 −0 bin/rprotoc
  8. +55 −0 examples/addressbook.pb.rb
  9. +24 −0 examples/addressbook.proto
  10. +32 −0 examples/reading_a_message.rb
  11. +46 −0 examples/writing_a_message.rb
  12. +6 −0 lib/protobuf.rb
  13. +11 −0 lib/protobuf/common/exceptions.rb
  14. +64 −0 lib/protobuf/common/logger.rb
  15. +59 −0 lib/protobuf/common/util.rb
  16. +10 −0 lib/protobuf/common/wire_type.rb
  17. +52 −0 lib/protobuf/compiler/compiler.rb
  18. +323 −0 lib/protobuf/compiler/nodes.rb
  19. +216 −0 lib/protobuf/compiler/proto.y
  20. +79 −0 lib/protobuf/compiler/proto2.ebnf
  21. +1,425 −0 lib/protobuf/compiler/proto_parser.rb
  22. +4 −0 lib/protobuf/compiler/template/rpc_bin.erb
  23. +18 −0 lib/protobuf/compiler/template/rpc_client.erb
  24. +25 −0 lib/protobuf/compiler/template/rpc_service.erb
  25. +42 −0 lib/protobuf/compiler/template/rpc_service_implementation.erb
  26. +302 −0 lib/protobuf/compiler/visitors.rb
  27. +286 −0 lib/protobuf/descriptor/descriptor.proto
  28. +55 −0 lib/protobuf/descriptor/descriptor.rb
  29. +143 −0 lib/protobuf/descriptor/descriptor_builder.rb
  30. +138 −0 lib/protobuf/descriptor/descriptor_proto.rb
  31. +33 −0 lib/protobuf/descriptor/enum_descriptor.rb
  32. +49 −0 lib/protobuf/descriptor/field_descriptor.rb
  33. +37 −0 lib/protobuf/descriptor/file_descriptor.rb
  34. +83 −0 lib/protobuf/message/decoder.rb
  35. +46 −0 lib/protobuf/message/encoder.rb
  36. +62 −0 lib/protobuf/message/enum.rb
  37. +8 −0 lib/protobuf/message/extend.rb
  38. +701 −0 lib/protobuf/message/field.rb
  39. +402 −0 lib/protobuf/message/message.rb
  40. +38 −0 lib/protobuf/message/protoable.rb
  41. +74 −0 lib/protobuf/rpc/buffer.rb
  42. +268 −0 lib/protobuf/rpc/client.rb
  43. +225 −0 lib/protobuf/rpc/client_connection.rb
  44. +34 −0 lib/protobuf/rpc/error.rb
  45. +31 −0 lib/protobuf/rpc/error/client_error.rb
  46. +43 −0 lib/protobuf/rpc/error/server_error.rb
  47. +107 −0 lib/protobuf/rpc/rpc.pb.rb
  48. +183 −0 lib/protobuf/rpc/server.rb
  49. +244 −0 lib/protobuf/rpc/service.rb
  50. +70 −0 lib/protobuf/rpc/stat.rb
  51. +3 −0  lib/protobuf/version.rb
  52. +73 −0 proto/rpc.proto
  53. +25 −0 protobuf.gemspec
  54. +2 −0  script/mk_parser
  55. +7 −0 spec/functional/embedded_service_spec.rb
  56. +31 −0 spec/proto/test.pb.rb
  57. +31 −0 spec/proto/test.proto
  58. +30 −0 spec/proto/test_service.rb
  59. +17 −0 spec/proto/test_service_impl.rb
  60. +26 −0 spec/spec_helper.rb
  61. +128 −0 spec/unit/client_spec.rb
  62. +121 −0 spec/unit/common/logger_spec.rb
  63. +13 −0 spec/unit/enum_spec.rb
  64. +67 −0 spec/unit/message_spec.rb
  65. +27 −0 spec/unit/server_spec.rb
  66. +75 −0 spec/unit/service_spec.rb
  67. +30 −0 test/check_unbuild.rb
  68. +3 −0  test/data/data.bin
  69. +14 −0 test/data/data_source.py
  70. BIN  test/data/types.bin
  71. +22 −0 test/data/types_source.py
  72. BIN  test/data/unk.png
  73. +66 −0 test/proto/addressbook.pb.rb
  74. +33 −0 test/proto/addressbook.proto
  75. +58 −0 test/proto/addressbook_base.pb.rb
  76. +26 −0 test/proto/addressbook_base.proto
  77. +20 −0 test/proto/addressbook_ext.pb.rb
  78. +6 −0 test/proto/addressbook_ext.proto
  79. +17 −0 test/proto/collision.pb.rb
  80. +5 −0 test/proto/collision.proto
  81. +24 −0 test/proto/ext_collision.pb.rb
  82. +8 −0 test/proto/ext_collision.proto
  83. +22 −0 test/proto/ext_range.pb.rb
  84. +7 −0 test/proto/ext_range.proto
  85. +10 −0 test/proto/float_default.proto
  86. +30 −0 test/proto/lowercase.pb.rb
  87. +9 −0 test/proto/lowercase.proto
  88. +39 −0 test/proto/merge.pb.rb
  89. +15 −0 test/proto/merge.proto
  90. +30 −0 test/proto/nested.pb.rb
  91. +9 −0 test/proto/nested.proto
  92. +35 −0 test/proto/optional_field.pb.rb
  93. +12 −0 test/proto/optional_field.proto
  94. +22 −0 test/proto/packed.pb.rb
  95. +6 −0 test/proto/packed.proto
  96. +6 −0 test/proto/rpc.proto
  97. +84 −0 test/proto/types.pb.rb
  98. +37 −0 test/proto/types.proto
  99. +56 −0 test/test_addressbook.rb
  100. +325 −0 test/test_compiler.rb
  101. +122 −0 test/test_descriptor.rb
  102. +41 −0 test/test_enum_value.rb
  103. +36 −0 test/test_extension.rb
  104. +11 −0 test/test_lowercase.rb
  105. +128 −0 test/test_message.rb
  106. +103 −0 test/test_optional_field.rb
  107. +40 −0 test/test_packed_field.rb
  108. +15 −0 test/test_parse.rb
  109. +132 −0 test/test_repeated_types.rb
  110. +61 −0 test/test_serialize.rb
  111. +96 −0 test/test_standard_message.rb
  112. +226 −0 test/test_types.rb
5 .gitignore
@@ -0,0 +1,5 @@
+*.gem
+pkg/*
+.bundle
+.rvmrc
+*.log
3  Gemfile
@@ -0,0 +1,3 @@
+source :rubygems
+
+gemspec
28 Gemfile.lock
@@ -0,0 +1,28 @@
+PATH
+ remote: .
+ specs:
+ protobuf (1.0.0)
+ eventmachine (~> 0.12.10)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ diff-lcs (1.1.2)
+ eventmachine (0.12.10)
+ rake (0.8.7)
+ rspec (2.7.0)
+ rspec-core (~> 2.7.0)
+ rspec-expectations (~> 2.7.0)
+ rspec-mocks (~> 2.7.0)
+ rspec-core (2.7.1)
+ rspec-expectations (2.7.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.7.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ protobuf!
+ rake (~> 0.8.7)
+ rspec (~> 2.7.0)
216 README.md
@@ -0,0 +1,216 @@
+protobuf
+========
+
+Protobuf is an implementation of [Google's protocol buffers][google-pb] in ruby. It's a gem for managing 3 things:
+
+1. Compiling `.proto` definitions to ruby
+2. Provide a Socket-RPC mechanism for calling services
+3. Provide RPC interop between ruby and other protobuf-rpc aware implementations for different languages (e.g. [protobuf-socket-rpc][])
+
+So let's dive in and see how to work with all three.
+
+1. Compile `.proto` definitions to ruby
+=======================================
+
+Protocol Buffers are great because they allow you to clearly define data storage or data transfer packets. Google officially supports Java, C++, and Python for compilation and usage. Let's make it ruby aware!
+
+Let's say you have a `defs.proto` file that defines a User message.
+
+ package mycompany;
+ message User {
+ required string first_name = 1;
+ required string last_name = 1;
+ }
+
+Now let's compile that definition to ruby:
+
+ $ rprotoc defs.proto -o ./lib
+
+The previous line will take whatever is defined in defs.proto and output ruby classes to the `./lib` directory, obeying the package directive. Assuming that's all defs.proto had defined, `./lib` should now look like this:
+
+ - lib
+ |- mycompany
+ |- defs.pb.rb
+
+And `defs.pb.rb` should look like this:
+
+ module Mycompany
+ class User
+ optional :string, :first_name, 1
+ optional :string, :last_name, 2
+ end
+ end
+
+You can then use that class just like normal:
+
+ require 'lib/mycompany/user.pb'
+
+ # dot notation reading/writing fields
+ user = Mycompany::User.new
+ user.first_name = "Lloyd"
+ user.last_name = "Christmas"
+ user.first_name # => "Lloyd"
+
+ # or pass in the fields as a hash to the initializer
+ user = Mycompany::User.new :first_name => "Lloyd", :last_name => "Christmas"
+ user.first_name # => Lloyd
+ user.last_name # => Christmas
+
+------------------
+
+2. RPC
+======
+
+RPC is one of many technologies that tries to solve the problem of getting smaller pieces of data from one place to another. Many will argue for or against RPC and its usefulness, but I'm not going to do that here. Google's Protocol Buffers relies on RPC and that's why you're here.
+
+Any discussion about RPC leads to a discussion about clients and servers and the remote procedures themselves. For our purposes, we'll talk about a `Client` (process that is calling the server/service), a `Service` (the remote procedure), and a `Server` (the process that manages one or more services). We'll start with the Service first.
+
+**Services**
+
+Services are simply classes that have endpoint methods defined. Here's what one looks like in protobuf:
+
+ message UserRequest {
+ optional string email = 1;
+ }
+ message UserList {
+ repeated User users = 1;
+ }
+ service UserService {
+ rpc Find (UserRequest) returns (UserList);
+ }
+
+And the equivalent ruby stub for the service (generated with `rprotoc`):
+
+ # lib/mycompany/user_service.rb
+ module Mycompany
+ class UserService < Protobuf::Rpc::Service
+ rpc :find, UserRequest, UserList
+ end
+ end
+
+Recognize that the extra messages would actually have gone into the `defs.pb.rb` file while the service stub would receive it's own file at `user_service.rb`.
+
+**Important Note: The *stubbed* class here is a *stub*. You should not alter it directly in any way as it will break your definition. Read on to learn how to use this stub.**
+
+Did you read the note above? Go read it. I'll wait.
+
+Ok, now that you have a compiled service stub, you'll want to require it from `lib` and implement the methods. You'll notice when you compile the stub there is a large comment at the top of the file. You can use this code comment to start your real implementation. Go ahead and copy it to your services directory (probably `app/services` if we're in rails).
+
+ # app/services/user_service.rb
+ require 'lib/mycompany/user_service'
+ module Mycompany
+ class UserService
+
+ # request -> Mycompany::UserRequest
+ # response -> Mycompany::UserResponse
+ def find
+ # request.email will be the unpacked string that was sent by the client request
+ User.find_by_email(request.email).each do |user|
+ # must only use a proto instance of Mycompany::User when appending to the `users` field
+ response.users << user.to_proto
+ end
+ end
+
+ end
+ end
+
+Simply implement the instance method for the defined rpc. No other methods will be allowed in the class (even helpers or private methods). An implicit `request` and `response` object are provided for you, pre-instantiated, and in the case of the request, already are populated with the data that was sent by the client.
+
+If you need to create your own response object (a valid case), be sure to assign it back to the instance by using `self.response = your_response_obj`. The object you assign **MUST** be of the defined return type, in this case `Mycompany::UserList`. Any other type will result in an error.
+
+Triggering an error from the service is simple:
+
+ #...
+ def find
+ if request.email.blank?
+ rpc_failed 'Unable to find user without an email'
+ else
+ # query/populate response
+ end
+ end
+
+This means that the client's `on_failure` callback will be invoked instead of the `on_success` callback. Read more below on client callbacks.
+
+I find it very convenient to use a CRUD-style interface when defining certain data services, though this is certainly not always the case.
+
+**Servers**
+
+A service is nothing without being hooked up to a socket. It's the nerdy kid waiting by the telephone for someone to call without knowing that the phone company disconnected their house. Sad and pathetic. So hook the phone lines!
+
+ $ rpc_server -o myserver.com -p 9939 -e production -l ./log/protobuf.log config/environment.rb
+
+The previous call will start an EventMachine server running on the given host and port which will load your application into memory. You certainly don't have to run rails or any other framework, just make sure you have some kind of file that will load your services all into memory. The server doesn't know where you put your code, so tell it.
+
+Be aware that server needs to be able to translate the socket stream of bytes into an actual protobuf request object. If the definition for that request object aren't known to the server, you're going to have a long day getting this going. It's necessary to store all your definitions and their generated classes in a shared repository (read: gem) that both client and server have access to in their respective load paths.
+
+Once the server starts, you should see it as a running process with `ps`. Sending a KILL, QUIT, or TERM signal to the pid will result in shutting the server down gracefully.
+
+ $ ps aux | grep rpc_server
+ 1234 ... rpc_server myservice.com:9939
+
+ $ kill -QUIT 1234
+ rpc_server shutdown
+
+**Clients**
+
+A lot of work has gone into making the client calls simple and easy to use yet still powerful. Clients have a DSL that feels very ajax-ish, mostly because of the nature of EventMachine, but I also think it works quite well.
+
+ # require the defs from the shared gem/repo
+ require 'sharedgem/mycompany/user.pb'
+ require 'sharedgem/mycompany/user_service'
+
+ # Create a request object for the method we are invoking
+ req = Mycompany::UserRequest.new(:email => 'jeff@gmail.com')
+
+ # Use the UserService class to generate a client, invoke the rpc method
+ # while passing the request object
+ Mycompany::UserService.client.find(req) do |c|
+ # This block will be executed (registering the callbacks)
+ # before the request actualy occurs.
+ # the `c` param in this block is the `.client` object
+ # that is generated from the call above
+
+ # Register a block for execution when the response
+ # is deemed successful from the service. Accepts
+ # the unpacked response as its only parameter
+ c.on_success do |response|
+ response.users.each do |u|
+ puts u.inspect
+ end
+ end
+
+ # Register a block for execution when the response
+ # is deemed a failure. This can be either a client-side
+ # or server-side failure. The object passed the to the
+ # block has a `message` and a `code` attribute
+ # to aid in logging/diagnosing the failure.
+ c.on_failure do |err|
+ puts 'It failed: ' + err.message
+ end
+ end
+
+Many different options can be passed to the `.client` call above (such as `:async => true` or `:timeout => 600`). See the `lib/protobuf/rpc/client.rb` and `lib/protobuf/rpc/service.rb` files for more documentation. It hsould be noted that the default behavior of `UserService.client` is to return a blocking client. The nature of using Client calls within an framework like Rails demands a blocking call if the response of a web request is dependent on data returned from the service.
+
+---
+
+3. RPC Interop
+==============
+
+The main reason I wrote this gem was to provide a ruby implementation to google's protobuf that worked on the RPC layer with a Java Service layer that was already running [protobuf-socket-rpc][], the supported socket rpc library for protobuf from Google. The [old gem][] did not provide a very robust RPC implementation and it most certainly did not work with the Java stack.
+
+---
+
+Accreditation & Caveats
+=======================
+
+It must be noted a large amount of the code in this library was taken from the [ruby-protobuf][old gem] gem. Its authors and I were unable to reach a communication point to be able to merge all of my RPC updates in with their master. Unfortunately I just simply couldn't use their RPC code and so I've decided to diverge from their codeset. I take no credit whatsoever for the (de)serialization and `rprotoc` code generation original work, though I have modified it slightly to be more compliant with my understanding of the pb spec. I want to say thanks to the original devs for the good work they did to get me most of the way there. The code was initially diverged at their 0.4.0 version.
+
+It should also be noted that there are many more features I haven't really shown here, so please let me know if you have any questions on usage or support for various features. Happy protobufing.
+
+-- BJ Neilsen, [@localshred][], [rand9.com][]
+
+ [google-pb]: http://code.google.com/p/protobuf "Google Protocol Buffers"
+ [protobuf-socket-rpc]: http://code.google.com/p/protobuf-socket-rpc/ "Google's official Socket-RPC library for protobuf"
+ [old gem]: https://github.com/macks/ruby-protobuf "Macks ruby-protobuf on github"
+ [@localshred]: http://twitter.com/localshred "Follow on twitter @localshred"
+ [rand9.com]: http://rand9.com "Blog"
1  Rakefile
@@ -0,0 +1 @@
+require "bundler/gem_tasks"
117 bin/rpc_server
@@ -0,0 +1,117 @@
+#!/usr/bin/env ruby
+
+require 'optparse'
+require 'ostruct'
+require 'logger'
+require 'protobuf'
+require 'protobuf/rpc/server'
+
+[:INT, :QUIT, :TERM].each do |sig|
+ trap(sig) do
+ EventMachine.stop_event_loop if EventMachine.reactor_running?
+ Protobuf::Logger.info 'Shutdown complete'
+ $stdout.puts 'Shutdown complete'
+ end
+end
+
+# Default options
+server = OpenStruct.new({
+ :app => nil,
+ :env => ENV['RPC_SERVER_ENV'] || 'development',
+ :host => '127.0.0.1',
+ :port => 9595,
+ :log => File.expand_path('./protobuf.log'),
+ :level => ::Logger::INFO,
+ :debug => false
+})
+
+parser = OptionParser.new do |opts|
+ opts.banner = "Usage: rpc_server [options] app_file.rb"
+
+ opts.on("-e ENVIRONMENT", "--env=ENVIRONMENT", "Environment to run the server") do |v|
+ server.env = ENV['RACK_ENV'] = ENV['RAILS_ENV'] = ENV['APP_ENV'] = v
+ end
+
+ opts.on("-o HOST", "--host=HOST", "Server host") do |v|
+ server.host = v
+ end
+
+ opts.on("-p PORT", "--port=PORT", Integer, "Server port") do |v|
+ server.port = v
+ end
+
+ opts.on("-l LOG_FILE", "--log=LOG_FILE", "Log file or device") do |v|
+ server.log = v
+ end
+
+ opts.on("-v N", "--level=N", Integer, "Log level to use, 0-5 (see http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/)") do |v|
+ server.level = v.to_i
+ end
+
+ opts.on("-d", "--[no-]debug", "Debug Mode. Override log level to DEBUG.") do |v|
+ server.debug = v
+ server.level = ::Logger::DEBUG if v === true
+ end
+
+ opts.separator ""
+ opts.separator "Common options:"
+
+ opts.on_tail("-h", "--help", "Show this message") do
+ puts opts
+ exit
+ end
+
+ opts.on_tail("--version", "Show version") do
+ puts Protobuf::VERSION
+ exit
+ end
+end
+
+begin
+ parser.parse!
+
+ if ARGV.empty?
+ raise 'You must specify an app file to use.'
+ else
+ server.app = ARGV.pop
+ raise 'Invalid app file specified (%s).' % server.app unless File.exists?(server.app)
+ end
+
+ # Configure the Protobuf::Logger
+ Protobuf::Logger.configure :file => server.log, :level => server.debug ? ::Logger::DEBUG : server.level
+
+ # Output the server opts
+ Protobuf::Logger.debug 'Debugging options:'
+ Protobuf::Logger.debug server.inspect
+
+ # Ensure errors thrown within EM are caught and logged appropriately
+ EventMachine.error_handler do |error|
+ if error.message == 'no acceptor'
+ raise 'Failed binding to %s:%d (%s)' % [server.host, server.port, error.message]
+ else
+ Protobuf::Logger.error error.message
+ Protobuf::Logger.error error.backtrace.join("\n")
+ end
+ end
+
+ # Set the name of the process
+ $0 = 'rpc_server %s:%d' % [server.host, server.port]
+
+ # Require the given application file
+ require server.app
+
+ # Startup and run the rpc server
+ EM.schedule do
+ EventMachine.start_server(server.host, server.port, Protobuf::Rpc::Server) && \
+ Protobuf::Logger.info('RPC Server listening at %s:%d in %s' % [server.host, server.port, server.env])
+ end
+
+ # Join or start the reactor
+ EM.reactor_running? ? EM.reactor_thread.join : EM.run
+rescue
+ msg = 'ERROR: RPC Server failed to start. %s' % $!.message
+ $stderr.puts msg, *($!.backtrace)
+ Protobuf::Logger.error msg
+ Protobuf::Logger.error $!.backtrace.join("\n")
+ exit 1
+end
46 bin/rprotoc
@@ -0,0 +1,46 @@
+#!/usr/bin/env ruby
+
+require 'optparse'
+
+if File.directory?("#{File.dirname(__FILE__)}/../lib")
+ $: << "#{File.dirname(__FILE__)}/../lib"
+else
+ require 'rubygems'
+ gem 'protobuf'
+end
+require 'protobuf'
+require 'protobuf/compiler/compiler'
+
+
+options = {
+ :proto_path => '.',
+ :out => '.',
+}
+opts = OptionParser.new("#{$0} [OPTIONS] PROTO_FILE")
+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}
+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}
+opts.on_tail('-v', '--version', 'Show version.'){ puts(opts.ver); exit }
+opts.on_tail('-h', '--help', 'Show this message.'){ puts(opts.help); exit }
+
+::Version = Protobuf::VERSION
+
+begin
+ opts.order!
+rescue OptionParser::ParseError
+ $stderr.puts $!.to_s
+ exit 1
+end
+
+unless ARGV.size > 0
+ puts opts
+ exit
+end
+
+begin
+ ARGV.each do |proto_file|
+ Protobuf::Compiler.compile(proto_file, options[:proto_path], options[:out])
+ end
+rescue
+ $stderr.puts $!.message
+ exit 1
+end
55 examples/addressbook.pb.rb
@@ -0,0 +1,55 @@
+### Generated by rprotoc. DO NOT EDIT!
+### <proto file: examples/addressbook.proto>
+# package tutorial;
+#
+# message Person {
+# required string name = 1;
+# required int32 id = 2;
+# optional string email = 3;
+#
+# enum PhoneType {
+# MOBILE = 0;
+# HOME = 1;
+# WORK = 2;
+# }
+#
+# message PhoneNumber {
+# required string number = 1;
+# optional PhoneType type = 2 [default = HOME];
+# }
+#
+# repeated PhoneNumber phone = 4;
+# }
+#
+# message AddressBook {
+# repeated Person person = 1;
+# }
+
+require 'protobuf/message/message'
+require 'protobuf/message/enum'
+require 'protobuf/message/extend'
+
+module Tutorial
+ class Person < ::Protobuf::Message
+ defined_in __FILE__
+ required :string, :name, 1
+ required :int32, :id, 2
+ optional :string, :email, 3
+ class PhoneType < ::Protobuf::Enum
+ defined_in __FILE__
+ MOBILE = 0
+ HOME = 1
+ WORK = 2
+ end
+ class PhoneNumber < ::Protobuf::Message
+ defined_in __FILE__
+ required :string, :number, 1
+ optional :PhoneType, :type, 2, :default => :HOME
+ end
+ repeated :PhoneNumber, :phone, 4
+ end
+ class AddressBook < ::Protobuf::Message
+ defined_in __FILE__
+ repeated :Person, :person, 1
+ end
+end
24 examples/addressbook.proto
@@ -0,0 +1,24 @@
+package tutorial;
+
+message Person {
+ required string name = 1;
+ required int32 id = 2;
+ optional string email = 3;
+
+ enum PhoneType {
+ MOBILE = 0;
+ HOME = 1;
+ WORK = 2;
+ }
+
+ message PhoneNumber {
+ required string number = 1;
+ optional PhoneType type = 2 [default = HOME];
+ }
+
+ repeated PhoneNumber phone = 4;
+}
+
+message AddressBook {
+ repeated Person person = 1;
+}
32 examples/reading_a_message.rb
@@ -0,0 +1,32 @@
+#!/usr/bin/env ruby
+
+require 'addressbook.pb'
+
+def list_people(address_book)
+ address_book.person.each do |person|
+ puts "Person ID: #{person.id}"
+ puts " Name: #{person.name}"
+ puts " E-mail: #{person.email}" unless person.email.empty?
+ person.phone.each do |phone_number|
+ print(case phone_number.type
+ when Tutorial::Person::PhoneType::MOBILE
+ ' Mobile phone #: '
+ when Tutorial::Person::PhoneType::HOME
+ ' Home phone #: '
+ when Tutorial::Person::PhoneType::WORK
+ ' Work phone #: '
+ end)
+ puts phone_number.number
+ end
+ end
+end
+
+unless ARGV.size == 1
+ puts "Usage: #{$0} ADDRESS_BOOK_FILE"
+ exit
+end
+
+address_book = Tutorial::AddressBook.new
+address_book.parse_from_file ARGV[0]
+
+list_people address_book
46 examples/writing_a_message.rb
@@ -0,0 +1,46 @@
+#!/usr/bin/env ruby
+
+require 'addressbook.pb'
+
+def prompt_for_address(person)
+ print 'Enter person ID number: '
+ person.id = STDIN.gets.strip.to_i
+ print 'Enter name: '
+ person.name = STDIN.gets.strip
+ print 'Enter email address (blank for none): '
+ email = STDIN.gets.strip
+ person.email = email unless email.empty?
+
+ loop do
+ print 'Enter a phone number (or leave blank to finish): '
+ break if (number = STDIN.gets.strip).empty?
+
+ person.phone << Tutorial::Person::PhoneNumber.new
+ person.phone.last.number = number
+
+ print 'Is this a mobile, home, or work phone? '
+ person.phone.last.type =
+ case type = STDIN.gets.strip
+ when 'mobile'
+ Tutorial::Person::PhoneType::MOBILE
+ when 'home'
+ Tutorial::Person::PhoneType::HOME
+ when 'work'
+ Tutorial::Person::PhoneType::WORK
+ else
+ puts 'Unknown phone type; leaving as default value.'
+ nil
+ end
+ end
+end
+
+unless ARGV.size == 1
+ puts "Usage: #{$0} ADDRESS_BOOK_FILE"
+ exit
+end
+
+address_book = Tutorial::AddressBook.new
+address_book.parse_from_file ARGV[0] if File.exist? ARGV[0]
+address_book.person << Tutorial::Person.new
+prompt_for_address address_book.person.last
+address_book.serialize_to_file ARGV[0]
6 lib/protobuf.rb
@@ -0,0 +1,6 @@
+module Protobuf
+end
+
+require 'protobuf/rpc/client'
+require 'protobuf/rpc/server'
+require 'protobuf/rpc/service'
11 lib/protobuf/common/exceptions.rb
@@ -0,0 +1,11 @@
+module Protobuf
+
+ class Error < StandardError; end
+
+ class InvalidWireType < Error; end
+
+ class NotInitializedError < Error; end
+
+ class TagCollisionError < Error; end
+
+end
64 lib/protobuf/common/logger.rb
@@ -0,0 +1,64 @@
+require 'logger'
+
+module Protobuf
+ class Logger < ::Logger
+
+ class << self
+ attr_accessor :file, :level
+
+ # One-line file/level configuration
+ def configure options
+ self.file = options[:file] if options[:file]
+ self.level = options[:level] if options[:level]
+ end
+
+ def configured?
+ ! instance.nil?
+ end
+
+ # Use to reset the instance
+ def reset_device!
+ self.file = self.level = @__instance = nil
+ end
+
+ # Singleton instance
+ def instance
+ @__instance ||= begin
+ log = nil
+ if @file and @level
+ log = new(self.file)
+ log.level = self.level
+ end
+ log
+ end
+ end
+
+ # Stub out the log methods for Protobuf::Logger as singleton methods
+ [:debug, :info, :warn, :error, :fatal, :any, :add, :log].each do |m|
+ define_method(m) do |*params, &block|
+ instance && instance.__send__(m, *params, &block)
+ end
+ end
+ end
+
+ #
+ # LogMethods module for log method including, e.g.:
+ #
+ # class MyClass
+ # include Protobuf::Logger::LogMethods
+ # ...
+ # end
+ #
+ # Produce a module to allow "include" in other classes to avoid
+ # cluttering the namespace of the including class with the other methods defined above
+ #
+ module LogMethods
+ [:debug, :info, :warn, :error, :fatal, :any, :add, :log].each do |m|
+ define_method("log_#{m}") do |*params, &block|
+ Protobuf::Logger.__send__(m, *params, &block)
+ end
+ end
+ end
+
+ end
+end
59 lib/protobuf/common/util.rb
@@ -0,0 +1,59 @@
+module Protobuf
+ module Util
+ module_function
+
+ # Takes a string or symbol and camelizes it:
+ # Expects: some_long_name
+ # Returns: SomeLongName
+ def camelize(str)
+ if (str.is_a? Array)
+ str.map{|p| camelize(p.to_s) }.join('::')
+ else
+ str.to_s.gsub(/(?:\A|_)(\w)/) { $1.upcase }
+ end
+ end
+
+ # Expects: SomeLongName
+ # Returns: some_long_name
+ def underscore(str)
+ str.to_s.gsub(/\B[A-Z]/, '_\&').downcase
+ end
+
+ # Expects: SomeModule::Path
+ # Returns: some_module/path
+ def module_to_path(str)
+ pkg = str.to_s.split('::')
+ pkg.map{|e| underscore(e) }.join('/')
+ end
+
+ # Expects: PackageA.PackageB
+ # Returns: package_a/package_b
+ def package_to_path(str)
+ str.to_s.split('.').map{|e| underscore(e) }.join('/')
+ end
+
+ # Takes a class constant and converts it to a string resembling a java package path
+ # Expects: ModA::ModB::MyService
+ # Returns: mod_a.mod_b.MyService
+ def packagize(klass)
+ klass = klass.to_s.split('::') unless klass.is_a? Array
+ klass_name = klass.pop
+ klass.map{|e| underscore(e) }.join('.') + ".#{klass_name}"
+ end
+
+ # The reverse of packagize. Takes a string resembling a java package path
+ # and converts it into a module constant
+ # Expects: mod_a.mod_b.MyService
+ # Returns: ModA::ModB::MyService
+ def moduleize(str)
+ str = str.join('.') if str.is_a? Array
+ str.split('.').map{|e| camelize(e) }.join('::')
+ end
+
+ def constantize(klass)
+ constants = moduleize(klass).split('::')
+ constants.inject(Module.const_get(constants.shift)) {|const, obj| const.const_get(obj) }
+ end
+
+ end
+end
10 lib/protobuf/common/wire_type.rb
@@ -0,0 +1,10 @@
+module Protobuf
+ module WireType
+ VARINT = 0
+ FIXED64 = 1
+ LENGTH_DELIMITED = 2
+ START_GROUP = 3
+ END_GROUP = 4
+ FIXED32 = 5
+ end
+end
52 lib/protobuf/compiler/compiler.rb
@@ -0,0 +1,52 @@
+require 'fileutils'
+require 'protobuf/compiler/proto_parser'
+require 'protobuf/compiler/nodes'
+require 'protobuf/compiler/visitors'
+
+module Protobuf
+ class Compiler
+ def self.compile(proto_file, proto_dir='.', out_dir='.', file_create=true)
+ self.new.compile(proto_file, proto_dir, out_dir, file_create)
+ end
+
+ def compile(proto_file, proto_dir='.', out_dir='.', file_create=true)
+ create_message(proto_file, proto_dir, out_dir, file_create)
+ create_rpc(proto_file, proto_dir, out_dir, file_create)
+ end
+
+ def create_message(proto_file, proto_dir='.', out_dir='.', file_create=true)
+ rb_file = File.join(out_dir, File.basename(proto_file).sub(/\.[^\0]*\z/, '') + '.pb.rb')
+ proto_path = validate_existence(proto_file, proto_dir)
+
+ message_visitor = Visitor::CreateMessageVisitor.new(proto_file, proto_dir, out_dir)
+ File.open(proto_path) do |file|
+ message_visitor.visit(ProtoParser.new.parse(file))
+ end
+ message_visitor.create_files(rb_file, out_dir, file_create)
+ end
+
+ def create_rpc(proto_file, proto_dir='.', out_dir='.', file_create=true)
+ message_file = File.join(out_dir, File.basename(proto_file).sub(/\.[^\0]*\z/, '') + '.pb.rb')
+ proto_path = validate_existence(proto_file, proto_dir)
+
+ rpc_visitor = Visitor::CreateRpcVisitor.new
+ File.open(proto_path) do |file|
+ rpc_visitor.visit(ProtoParser.new.parse(file))
+ end
+ rpc_visitor.create_files(message_file, out_dir, file_create)
+ end
+
+ def validate_existence(path, base_dir)
+ if File.exist?(path)
+ path
+ else
+ newpath = File.join(base_dir, path)
+ if File.exist?(newpath)
+ newpath
+ else
+ raise ArgumentError, "File does not exist: #{path}"
+ end
+ end
+ end
+ end
+end
323 lib/protobuf/compiler/nodes.rb
@@ -0,0 +1,323 @@
+require 'protobuf/common/util'
+require 'protobuf/descriptor/descriptor_proto'
+
+module Protobuf
+ module Node
+ class Base
+ def define_in_the_file(visitor)
+ visitor.write("defined_in __FILE__") if visitor.attach_proto?
+ end
+
+ def accept_message_visitor(visitor)
+ end
+
+ def accept_rpc_visitor(vistor)
+ end
+
+ def accept_descriptor_visitor(visitor)
+ end
+ end
+
+ class ProtoNode < Base
+ attr_reader :children
+
+ def initialize(children)
+ @children = children || []
+ end
+
+ def accept_message_visitor(visitor)
+ visitor.write('### Generated by rprotoc. DO NOT EDIT!')
+ visitor.write("### <proto file: #{visitor.proto_file}>") if visitor.attach_proto?
+ visitor.write(visitor.commented_proto_contents) if visitor.attach_proto?
+ visitor.write(<<-EOS)
+require 'protobuf/message/message'
+require 'protobuf/message/enum'
+require 'protobuf/message/extend'
+ EOS
+ @children.each {|child| child.accept_message_visitor(visitor) }
+ visitor.close_ruby
+ end
+
+ def accept_rpc_visitor(visitor)
+ @children.each {|child| child.accept_rpc_visitor(visitor) }
+ end
+
+ def accept_descriptor_visitor(visitor)
+ descriptor = Google::Protobuf::FileDescriptorProto.new(:name => visitor.filename)
+ visitor.file_descriptor = descriptor
+ visitor.in_context(descriptor) do
+ @children.each {|child| child.accept_descriptor_visitor(visitor) }
+ end
+ end
+ end
+
+ class ImportNode < Base
+ def initialize(path)
+ @path = path
+ end
+
+ def accept_message_visitor(visitor)
+ visitor.write("require '#{visitor.required_message_from_proto(@path)}'")
+ end
+
+ def accept_descriptor_visitor(visitor)
+ visitor.current_descriptor.dependency << @path
+ end
+ end
+
+ class PackageNode < Base
+ def initialize(path_list)
+ @path_list = path_list
+ end
+
+ def accept_message_visitor(visitor)
+ visitor.package = @path_list.dup
+ @path_list.each do |path|
+ visitor.write("module #{Util.camelize(path)}")
+ visitor.increment
+ end
+ end
+
+ def accept_rpc_visitor(visitor)
+ visitor.package = @path_list.dup
+ end
+
+ def accept_descriptor_visitor(visitor)
+ visitor.current_descriptor.package = @path_list.join('.')
+ end
+ end
+
+ class OptionNode < Base
+ def initialize(name_list, value)
+ @name_list, @value = name_list, value
+ end
+
+ def accept_message_visitor(visitor)
+ visitor.write("::Protobuf::OPTIONS[:#{@name_list.join('.').inspect}] = #{@value.inspect}")
+ end
+
+ def accept_descriptor_visitor(visitor)
+ visitor.add_option(@name_list.join('.'), @value)
+ end
+ end
+
+ class MessageNode < Base
+ def initialize(name, children)
+ @name, @children = name, children
+ end
+
+ def accept_message_visitor(visitor)
+ class_name = @name.to_s
+ class_name.gsub!(/\A[a-z]/) {|c| c.upcase}
+ visitor.write("class #{class_name} < ::Protobuf::Message")
+ visitor.in_context(self.class) do
+ define_in_the_file(visitor)
+ @children.each {|child| child.accept_message_visitor(visitor) }
+ end
+ visitor.write('end')
+ end
+
+ def accept_descriptor_visitor(visitor)
+ descriptor = Google::Protobuf::DescriptorProto.new(:name => @name.to_s)
+ visitor.descriptor = descriptor
+ visitor.in_context(descriptor) do
+ @children.each {|child| child.accept_descriptor_visitor(visitor) }
+ end
+ end
+ end
+
+ class ExtendNode < Base
+ def initialize(name, children)
+ @name, @children = name, children
+ end
+
+ def accept_message_visitor(visitor)
+ name = @name.is_a?(Array) ? @name.join : name.to_s
+ visitor.write("class #{name} < ::Protobuf::Message")
+ visitor.in_context(self.class) do
+ define_in_the_file(visitor)
+ @children.each {|child| child.accept_message_visitor(visitor) }
+ end
+ visitor.write('end')
+ end
+
+ def accept_descriptor_visitor(visitor)
+ # TODO: how should i handle this?
+ end
+ end
+
+ class EnumNode < Base
+ def initialize(name, children)
+ @name, @children = name, children
+ end
+
+ def accept_message_visitor(visitor)
+ visitor.write("class #{@name} < ::Protobuf::Enum")
+ visitor.in_context(self.class) do
+ define_in_the_file(visitor)
+ @children.each {|child| child.accept_message_visitor(visitor) }
+ end
+ visitor.write('end')
+ end
+
+ def accept_descriptor_visitor(visitor)
+ descriptor = Google::Protobuf::EnumDescriptorProto.new(:name => @name.to_s)
+ visitor.enum_descriptor = descriptor
+ visitor.in_context(descriptor) do
+ @children.each {|child| child.accept_descriptor_visitor(visitor) }
+ end
+ end
+ end
+
+ class EnumFieldNode < Base
+ def initialize(name, value)
+ @name, @value = name, value
+ end
+
+ def accept_message_visitor(visitor)
+ visitor.write("define :#{@name}, #{@value}")
+ end
+
+ def accept_descriptor_visitor(visitor)
+ descriptor = Google::Protobuf::EnumValueDescriptorProto.new(:name => @name.to_s, :number => @value)
+ visitor.enum_value_descriptor = descriptor
+ end
+ end
+
+ class ServiceNode < Base
+ def initialize(name, children)
+ @name, @children = name, children
+ end
+
+ def accept_message_visitor(visitor)
+ # do nothing
+ end
+
+ def accept_rpc_visitor(visitor)
+ visitor.current_service = @name
+ @children.each {|child| child.accept_rpc_visitor(visitor) }
+ end
+
+ def accept_descriptor_visitor(visitor)
+ descriptor = Google::Protobuf::ServiceDescriptorProto.new(:name => @name.to_s)
+ visitor.service_descriptor = descriptor
+ visitor.in_context(descriptor) do
+ @children.each {|child| child.accept_descriptor_visitor(visitor) }
+ end
+ end
+ end
+
+ class RpcNode < Base
+ def initialize(name, request, response)
+ @name, @request, @response = name, request, response
+ end
+
+ def accept_message_visitor(visitor)
+ # do nothing
+ end
+
+ def accept_rpc_visitor(visitor)
+ visitor.add_rpc(@name, @request, @response)
+ end
+
+ def accept_descriptor_visitor(visitor)
+ descriptor = Google::Protobuf::MethodDescriptorProto.new(:name => @name.to_s, :input_type => @request.to_s, :output_type => @response.to_s)
+ visitor.method_descriptor = descriptor
+ end
+ end
+
+ class GroupNode < Base
+ def initialize(label, name, value, children)
+ @label, @name, @value, @children = label, name, value, children
+ end
+
+ def accept_message_visitor(visitor)
+ raise NotImplementedError
+ end
+
+ def accept_descriptor_visitor(visitor)
+ raise NotImplementedError
+ end
+ end
+
+ class FieldNode < Base
+ def initialize(label, type, name, value, opts={})
+ @label, @type, @name, @value, @opts = label, type, name, value, opts
+ end
+
+ def accept_message_visitor(visitor)
+ opts = @opts.empty? ? '' : ", #{@opts.map{|k, v| ":#{k} => #{v.inspect}" }.join(', ')}"
+ if visitor.context.first == ExtendNode
+ opts << ', :extension => true'
+ end
+ type = if @type.is_a?(Array)
+ then (@type.size > 1) ? "'#{@type.map{|e| Util.camelize(e) }.join('::')}'" : @type[0]
+ else @type
+ end
+ visitor.write("#{@label} :#{type}, :#{@name}, #{@value}#{opts}")
+ end
+
+ def accept_descriptor_visitor(visitor)
+ descriptor = Google::Protobuf::FieldDescriptorProto.new(:name => @name.to_s, :number => @value)
+ descriptor.label = Google::Protobuf::FieldDescriptorProto::Label.const_get("LABEL_#{@label.to_s.upcase}")
+ descriptor.type = Google::Protobuf::FieldDescriptorProto::Type.const_get("TYPE_#{@type.to_s.upcase}") if predefined_type?
+ descriptor.type_name = @type.is_a?(Array) ? @type.join : @type.to_s
+ @opts.each do |key, val|
+ case key.to_sym
+ when :default
+ descriptor.default_value = val.to_s
+ end
+ end
+ visitor.field_descriptor = descriptor
+ end
+
+ private
+
+ def predefined_type?
+ # TODO: constantize
+ %w{double float int64 uint64 int32 fixed64 fixed32 bool string group message bytes uint32 enum sfixed32 sfixed64 sint32 sint64}.include?(@type.to_s)
+ end
+ end
+
+ class ExtensionsNode < Base
+ def initialize(range)
+ @range = range
+ end
+
+ def accept_message_visitor(visitor)
+ visitor.write("extensions #{@range.first.to_s}")
+ end
+
+ def accept_descriptor_visitor(visitor)
+ descriptor = Google::Protobuf::DescriptorProto::ExtensionRange.new(:start => @range.first.low)
+ case @range.first.high
+ when NilClass then # ignore
+ when :max then descriptor.end = 1
+ else descriptor.end = @range.first.high
+ end
+ visitor.extension_range_descriptor = descriptor
+ end
+ end
+
+ class ExtensionRangeNode < Base
+ attr_reader :low, :high
+
+ def initialize(low, high=nil)
+ @low, @high = low, high
+ end
+
+ #def accept_message_visitor(visitor)
+ #end
+
+ def to_s
+ if @high.nil?
+ @low.to_s
+ elsif @high == :max
+ "#{@low}..::Protobuf::Extend::MAX"
+ else
+ "#{@low}..#{@high}"
+ end
+ end
+ end
+ end
+end
216 lib/protobuf/compiler/proto.y
@@ -0,0 +1,216 @@
+class Protobuf::ProtoParser
+rule
+ proto : proto_item
+ { result = Protobuf::Node::ProtoNode.new(val) }
+ | proto proto_item
+ { result.children << val[1] if val[1]}
+
+ proto_item : message
+ | extend
+ | enum
+ | import
+ | package
+ | option
+ | service
+ | ';' { result = nil }
+
+ import : 'import' STRING_LITERAL ';'
+ { result = Protobuf::Node::ImportNode.new(val[1]) }
+
+ package : 'package' IDENT dot_ident_list ';'
+ { result = Protobuf::Node::PackageNode.new(val[2].unshift(val[1])) }
+
+ dot_ident_list :
+ { result = [] }
+ | dot_ident_list '.' IDENT
+ { result << val[2] }
+
+ option : 'option' option_body ';'
+ { result = Protobuf::Node::OptionNode.new(*val[1]) }
+
+ option_body : IDENT dot_ident_list '=' constant
+ { result = [val[1].unshift(val[0]), val[3]] }
+
+ message : 'message' IDENT message_body
+ { result = Protobuf::Node::MessageNode.new(val[1], val[2]) }
+
+ extend : 'extend' user_type '{' extend_body_list '}'
+ { result = Protobuf::Node::ExtendNode.new(val[1], val[3]) }
+
+ extend_body_list :
+ { result = [] }
+ | extend_body_list extend_body
+ { result << val[1] if val[1] }
+
+ extend_body : field
+ | group
+ | ';' { result = nil }
+
+ enum : 'enum' IDENT '{' enum_body_list '}'
+ { result = Protobuf::Node::EnumNode.new(val[1], val[3]) }
+
+ enum_body_list :
+ { result = [] }
+ | enum_body_list enum_body
+ { result << val[1] if val[1] }
+
+ enum_body : option
+ | enum_field
+ | ';' { result = nil }
+
+ enum_field : IDENT '=' integer_literal ';'
+ { result = Protobuf::Node::EnumFieldNode.new(val[0], val[2]) }
+
+ service : 'service' IDENT '{' service_body_list '}'
+ { result = Protobuf::Node::ServiceNode.new(val[1], val[3]) }
+
+ service_body_list :
+ { result = [] }
+ | service_body_list service_body
+ { result << val[1] if val[1] }
+
+ service_body : option
+ | rpc
+ | ';' { result = nil}
+
+ rpc : 'rpc' IDENT '(' rpc_arg ')' 'returns' '(' rpc_arg ')' ';'
+ { result = Protobuf::Node::RpcNode.new(val[1], val[3], val[7]) }
+
+ rpc_arg :
+ | user_type
+
+ message_body : '{' message_body_body_list '}'
+ { result = val[1] }
+
+ message_body_body_list :
+ { result = [] }
+ | message_body_body_list message_body_body
+ { result << val[1] if val[1] }
+
+ message_body_body : field
+ | enum
+ | message
+ | extend
+ | extensions
+ | group
+ | option
+ | ';' { result = nil }
+
+ group : label 'group' CAMEL_IDENT '=' integer_literal message_body
+ { result = Protobuf::Node::GroupNode.new(val[0], val[2], val[4], val[5]) }
+
+ field : label type field_name '=' integer_literal ';'
+ { result = Protobuf::Node::FieldNode.new(val[0], val[1], val[2], val[4]) }
+ | label type field_name '=' integer_literal '[' field_option_list ']' ';'
+ { result = Protobuf::Node::FieldNode.new(val[0], val[1], val[2], val[4], val[6]) }
+
+ 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"
+
+ field_option_list : field_option
+ { result = val }
+ | field_option_list ',' field_option
+ { result << val[2] }
+
+ field_option : option_body
+ | 'default' '=' constant
+ { result = [:default, val[2]] }
+
+ extensions : 'extensions' extension comma_extension_list ';'
+ { result = Protobuf::Node::ExtensionsNode.new(val[2].unshift(val[1])) }
+
+ comma_extension_list :
+ { result = [] }
+ | ',' extension
+ { result << val[1] }
+
+ extension : integer_literal
+ { result = Protobuf::Node::ExtensionRangeNode.new(val[0]) }
+ | integer_literal 'to' integer_literal
+ { result = Protobuf::Node::ExtensionRangeNode.new(val[0], val[2]) }
+ | integer_literal 'to' 'max'
+ { result = Protobuf::Node::ExtensionRangeNode.new(val[0], :max) }
+
+ label : 'required'
+ | 'optional'
+ | 'repeated'
+
+ type : 'double' | 'float' | 'int32' | 'int64' | 'uint32' | 'uint64'
+ | 'sint32' | 'sint64' | 'fixed32' | 'fixed64' | 'sfixed32' | 'sfixed64'
+ | 'bool' | 'string' | 'bytes' | user_type
+
+ user_type : IDENT dot_ident_list
+ { result = val[1].unshift(val[0]) }
+ | '.' IDENT dot_ident_list
+ { result = val[1].unshift(val[0]) }
+
+ constant : IDENT
+ | integer_literal
+ | FLOAT_LITERAL
+ | STRING_LITERAL
+ | BOOLEAN_LITERAL
+
+ integer_literal : DEC_INTEGER
+ | HEX_INTEGER
+ | OCT_INTEGER
+end
+
+---- inner
+
+ require 'strscan'
+
+ def parse(f)
+ @scanner = StringScanner.new(f.read)
+ yyparse(self, :scan)
+ end
+
+ def scan_debug
+ scan do |token, value|
+ p [token, value]
+ yield [token, value]
+ end
+ end
+
+ def scan
+ until @scanner.eos?
+ case
+ when match(/\s+/, /\/\/.*/)
+ # skip
+ when match(/\/\*/)
+ # C-like comment
+ raise 'EOF inside block comment' until @scanner.scan_until(/\*\//)
+ 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/)
+ yield [@token, @token.to_sym]
+ when match(/[+-]?\d*\.\d+([Ee][\+-]?\d+)?/)
+ yield [:FLOAT_LITERAL, @token.to_f]
+ when match(/[+-]?[1-9]\d*(?!\.)/, /0(?![.xX0-9])/)
+ yield [:DEC_INTEGER, @token.to_i]
+ when match(/0[xX]([A-Fa-f0-9])+/)
+ yield [:HEX_INTEGER, @token.to_i(0)]
+ when match(/0[0-7]+/)
+ yield [:OCT_INTEGER, @token.to_i(0)]
+ when match(/(true|false)\b/)
+ yield [:BOOLEAN_LITERAL, @token == 'true']
+ when match(/"(?:[^"\\]+|\\.)*"/, /'(?:[^'\\]+|\\.)*'/)
+ yield [:STRING_LITERAL, eval(@token)]
+ when match(/[a-zA-Z_]\w*/)
+ yield [:IDENT, @token.to_sym]
+ when match(/[A-Z]\w*/)
+ yield [:CAMEL_IDENT, @token.to_sym]
+ when match(/./)
+ yield [@token, @token]
+ else
+ raise "parse error around #{@scanner.string[@scanner.pos, 32].inspect}"
+ end
+ end
+ yield [false, nil]
+ end
+
+ def match(*regular_expressions)
+ regular_expressions.each do |re|
+ if @scanner.scan(re)
+ @token = @scanner[0]
+ return true
+ end
+ end
+ false
+ end
79 lib/protobuf/compiler/proto2.ebnf
@@ -0,0 +1,79 @@
+# See: http://groups.google.com/group/protobuf/browse_thread/thread/1cccfc624cd612da
+
+proto ::= ( message | extend | enum | import | package | option | ";" )*
+
+import ::= "import" strLit ";"
+
+package ::= "package" ident ( "." ident )* ";"
+
+option ::= "option" optionBody ";"
+
+optionBody ::= ident ( "." ident )* "=" constant
+
+message ::= "message" ident messageBody
+
+extend ::= "extend" userType "{" ( field | group | ";" )* "}"
+
+enum ::= "enum" ident "{" ( option | enumField | ";" )* "}"
+
+enumField ::= ident "=" intLit ";"
+
+service ::= "service" ident "{" ( option | rpc | ";" )* "}"
+
+rpc ::= "rpc" ident "(" userType ")" "returns" "(" userType ")" ";"
+
+messageBody ::= "{" ( field | enum | message | extend | extensions | group | option | ":" )* "}"
+
+group ::= label "group" camelIdent "=" intLit messageBody
+
+field ::= label type ident "=" intLit ( "[" fieldOption ( "," fieldOption )* "]" )? ";"
+# tag number must be 2^29-1 or lower, not 0, and not 19000-19999 (reserved)
+
+fieldOption ::= optionBody | "default" "=" constant
+
+# extension numbers must not overlap with field or other extension numbers
+extensions ::= "extensions" extension ( "," extension )* ";"
+
+extension ::= intLit ( "to" ( intLit | "max" ) )?
+
+label ::= "required" | "optional" | "repeated"
+
+type ::= "double" | "float" | "int32" | "int64" | "uint32" | "uint64"
+ | "sint32" | "sint64" | "fixed32" | "fixed64" | "sfixed32" | "sfixed64"
+ | "bool" | "string" | "bytes" | userType
+
+# leading dot for identifiers means they're fully qualified
+userType ::= "."? ident ( "." ident )*
+
+constant ::= ident | intLit | floatLit | strLit | boolLit
+
+ident ::= /[A-Za-z_][\w_]*/
+
+# according to parser.cc, group names must start with a capital letter as a
+# hack for backwards-compatibility
+camelIdent ::= /[A-Z][\w_]*/
+
+intLit ::= decInt | hexInt | octInt
+
+decInt ::= /[1-9]\d*/
+
+hexInt ::= /0[xX]([A-Fa-f0-9])+/
+
+octInt ::= /0[0-7]+/
+
+# allow_f_after_float_ is disabled by default in tokenizer.cc
+floatLit ::= /\d+(\.\d+)?([Ee][\+-]?\d+)?/
+
+boolLit ::= "true" | "false"
+
+# contents must not contain unescaped quote character
+strLit ::= quote ( hexEscape | octEscape | charEscape | /[^\0\n]/ )* quote
+
+quote ::= /["']/
+
+hexEscape ::= /\\[Xx][A-Fa-f0-9]{1,2}/
+
+octEscape ::= /\\0?[0-7]{1,3}/
+
+charEscape ::= /\\[abfnrtv\\\?'"]/
+
1,425 lib/protobuf/compiler/proto_parser.rb
<
@@ -0,0 +1,1425 @@
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by racc 1.4.5
+# from racc grammer file "lib/protobuf/compiler/proto.y".
+#
+#
+# lib/protobuf/compiler/proto_parser.rb: generated by racc (runtime embedded)
+#
+###### racc/parser.rb begin
+unless $".index 'racc/parser.rb'
+$".push 'racc/parser.rb'
+
+self.class.module_eval <<'..end racc/parser.rb modeval..id24fd9e97a6', 'racc/parser.rb', 1
+#
+# $Id: parser.rb,v 1.7 2005/11/20 17:31:32 aamine Exp $
+#
+# Copyright (c) 1999-2005 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+#
+# As a special exception, when this code is copied by Racc
+# into a Racc output file, you may use that output file
+# without restriction.
+#
+
+unless defined?(NotImplementedError)
+ NotImplementedError = NotImplementError
+end
+
+module Racc
+ class ParseError < StandardError; end
+end
+unless defined?(::ParseError)
+ ParseError = Racc::ParseError
+end
+
+module Racc
+
+ unless defined?(Racc_No_Extentions)
+ Racc_No_Extentions = false
+ end
+
+ class Parser
+
+ Racc_Runtime_Version = '1.4.5'
+ Racc_Runtime_Revision = '$Revision: 1.7 $'.split[1]
+
+ Racc_Runtime_Core_Version_R = '1.4.5'
+ Racc_Runtime_Core_Revision_R = '$Revision: 1.7 $'.split[1]
+ begin
+ require 'racc/cparse'
+ # Racc_Runtime_Core_Version_C = (defined in extention)
+ Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2]
+ unless new.respond_to?(:_racc_do_parse_c, true)
+ raise LoadError, 'old cparse.so'
+ end
+ if Racc_No_Extentions
+ raise LoadError, 'selecting ruby version of racc runtime core'
+ end
+
+ Racc_Main_Parsing_Routine = :_racc_do_parse_c
+ Racc_YY_Parse_Method = :_racc_yyparse_c
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C
+ Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_C
+ Racc_Runtime_Type = 'c'
+ rescue LoadError
+ $stderr.puts $!
+ Racc_Main_Parsing_Routine = :_racc_do_parse_rb
+ Racc_YY_Parse_Method = :_racc_yyparse_rb
+ Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R
+ Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_R
+ Racc_Runtime_Type = 'ruby'
+ end
+
+ def Parser.racc_runtime_type
+ Racc_Runtime_Type
+ end
+
+ private
+
+ def _racc_setup
+ @yydebug = false unless self.class::Racc_debug_parser
+ @yydebug = false unless defined?(@yydebug)
+ if @yydebug
+ @racc_debug_out = $stderr unless defined?(@racc_debug_out)
+ @racc_debug_out ||= $stderr
+ end
+ arg = self.class::Racc_arg
+ arg[13] = true if arg.size < 14
+ arg
+ end
+
+ def _racc_init_sysvars
+ @racc_state = [0]
+ @racc_tstack = []
+ @racc_vstack = []
+
+ @racc_t = nil
+ @racc_val = nil
+
+ @racc_read_next = true
+
+ @racc_user_yyerror = false
+ @racc_error_status = 0
+ end
+
+ ###
+ ### do_parse
+ ###
+
+ def do_parse
+ __send__(Racc_Main_Parsing_Routine, _racc_setup(), false)
+ end
+
+ def next_token
+ raise NotImplementedError, "#{self.class}\#next_token is not defined"
+ end
+
+ def _racc_do_parse_rb(arg, in_debug)
+ action_table, action_check, action_default, action_pointer,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, token_table, shift_n,
+ reduce_n, use_result, * = arg
+
+ _racc_init_sysvars
+ tok = act = i = nil
+ nerr = 0
+
+ catch(:racc_end_parse) {
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ if @racc_read_next
+ if @racc_t != 0 # not EOF
+ tok, @racc_val = next_token()
+ unless tok # EOF
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ racc_read_token(@racc_t, tok, @racc_val) if @yydebug
+ @racc_read_next = false
+ end
+ end
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ else
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ }
+ end
+
+ ###
+ ### yyparse
+ ###
+
+ def yyparse(recv, mid)
+ __send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), true)
+ end
+
+ def _racc_yyparse_rb(recv, mid, arg, c_debug)
+ action_table, action_check, action_default, action_pointer,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, token_table, shift_n,
+ reduce_n, use_result, * = arg
+
+ _racc_init_sysvars
+ tok = nil
+ act = nil
+ i = nil
+ nerr = 0
+
+ catch(:racc_end_parse) {
+ until i = action_pointer[@racc_state[-1]]
+ while act = _racc_evalact(action_default[@racc_state[-1]], arg)
+ ;
+ end
+ end
+ recv.__send__(mid) do |tok, val|
+ unless tok
+ @racc_t = 0
+ else
+ @racc_t = (token_table[tok] or 1) # error token
+ end
+ @racc_val = val
+ @racc_read_next = false
+
+ i += @racc_t
+ unless i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+
+ while not (i = action_pointer[@racc_state[-1]]) or
+ not @racc_read_next or
+ @racc_t == 0 # $
+ unless i and i += @racc_t and
+ i >= 0 and
+ act = action_table[i] and
+ action_check[i] == @racc_state[-1]
+ act = action_default[@racc_state[-1]]
+ end
+ while act = _racc_evalact(act, arg)
+ ;
+ end
+ end
+ end
+ }
+ end
+
+ ###
+ ### common
+ ###
+
+ def _racc_evalact(act, arg)
+ action_table, action_check, action_default, action_pointer,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, token_table, shift_n,
+ reduce_n, use_result, * = arg
+ nerr = 0 # tmp
+
+ if act > 0 and act < shift_n
+ #
+ # shift
+ #
+ if @racc_error_status > 0
+ @racc_error_status -= 1 unless @racc_t == 1 # error token
+ end
+ @racc_vstack.push @racc_val
+ @racc_state.push act
+ @racc_read_next = true
+ if @yydebug
+ @racc_tstack.push @racc_t
+ racc_shift @racc_t, @racc_tstack, @racc_vstack
+ end
+
+ elsif act < 0 and act > -reduce_n
+ #
+ # reduce
+ #
+ code = catch(:racc_jump) {
+ @racc_state.push _racc_do_reduce(arg, act)
+ false
+ }
+ if code
+ case code
+ when 1 # yyerror
+ @racc_user_yyerror = true # user_yyerror
+ return -reduce_n
+ when 2 # yyaccept
+ return shift_n
+ else
+ raise '[Racc Bug] unknown jump code'
+ end
+ end
+
+ elsif act == shift_n
+ #
+ # accept
+ #
+ racc_accept if @yydebug
+ throw :racc_end_parse, @racc_vstack[0]
+
+ elsif act == -reduce_n
+ #
+ # error
+ #
+ case @racc_error_status
+ when 0
+ unless arg[21] # user_yyerror
+ nerr += 1
+ on_error @racc_t, @racc_val, @racc_vstack
+ end
+ when 3
+ if @racc_t == 0 # is $
+ throw :racc_end_parse, nil
+ end
+ @racc_read_next = true
+ end
+ @racc_user_yyerror = false
+ @racc_error_status = 3
+ while true
+ if i = action_pointer[@racc_state[-1]]
+ i += 1 # error token
+ if i >= 0 and
+ (act = action_table[i]) and
+ action_check[i] == @racc_state[-1]
+ break
+ end
+ end
+ throw :racc_end_parse, nil if @racc_state.size <= 1
+ @racc_state.pop
+ @racc_vstack.pop
+ if @yydebug
+ @racc_tstack.pop
+ racc_e_pop @racc_state, @racc_tstack, @racc_vstack
+ end
+ end
+ return act
+
+ else
+ raise "[Racc Bug] unknown action #{act.inspect}"
+ end
+
+ racc_next_state(@racc_state[-1], @racc_state) if @yydebug
+
+ nil
+ end
+
+ def _racc_do_reduce(arg, act)
+ action_table, action_check, action_default, action_pointer,
+ goto_table, goto_check, goto_default, goto_pointer,
+ nt_base, reduce_table, token_table, shift_n,
+ reduce_n, use_result, * = arg
+ state = @racc_state
+ vstack = @racc_vstack
+ tstack = @racc_tstack
+
+ i = act * -3
+ len = reduce_table[i]
+ reduce_to = reduce_table[i+1]
+ method_id = reduce_table[i+2]
+ void_array = []
+
+ tmp_t = tstack[-len, len] if @yydebug
+ tmp_v = vstack[-len, len]
+ tstack[-len, len] = void_array if @yydebug
+ vstack[-len, len] = void_array
+ state[-len, len] = void_array
+
+ # tstack must be updated AFTER method call
+ if use_result
+ vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0])
+ else
+ vstack.push __send__(method_id, tmp_v, vstack)
+ end
+ tstack.push reduce_to
+
+ racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug
+
+ k1 = reduce_to - nt_base
+ if i = goto_pointer[k1]
+ i += state[-1]
+ if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1
+ return curstate
+ end
+ end
+ goto_default[k1]
+ end
+
+ def on_error(t, val, vstack)
+ raise ParseError, sprintf("\nparse error on value %s (%s)",
+ val.inspect, token_to_str(t) || '?')
+ end
+
+ def yyerror
+ throw :racc_jump, 1
+ end
+
+ def yyaccept
+ throw :racc_jump, 2
+ end
+
+ def yyerrok
+ @racc_error_status = 0
+ end
+
+ #
+ # for debugging output
+ #
+
+ def racc_read_token(t, tok, val)
+ @racc_debug_out.print 'read '
+ @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') '
+ @racc_debug_out.puts val.inspect
+ @racc_debug_out.puts
+ end
+
+ def racc_shift(tok, tstack, vstack)
+ @racc_debug_out.puts "shift #{racc_token2str tok}"
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_reduce(toks, sim, tstack, vstack)
+ out = @racc_debug_out
+ out.print 'reduce '
+ if toks.empty?
+ out.print ' <none>'
+ else
+ toks.each {|t| out.print ' ', racc_token2str(t) }
+ end
+ out.puts " --> #{racc_token2str(sim)}"
+
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_accept
+ @racc_debug_out.puts 'accept'
+ @racc_debug_out.puts
+ end
+
+ def racc_e_pop(state, tstack, vstack)
+ @racc_debug_out.puts 'error recovering mode: pop token'
+ racc_print_states state
+ racc_print_stacks tstack, vstack
+ @racc_debug_out.puts
+ end
+
+ def racc_next_state(curstate, state)
+ @racc_debug_out.puts "goto #{curstate}"
+ racc_print_states state
+ @racc_debug_out.puts
+ end
+
+ def racc_print_stacks(t, v)
+ out = @racc_debug_out
+ out.print ' ['
+ t.each_index do |i|
+ out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')'
+ end
+ out.puts ' ]'
+ end
+
+ def racc_print_states(s)
+ out = @racc_debug_out
+ out.print ' ['
+ s.each {|st| out.print ' ', st }
+ out.puts ' ]'
+ end
+
+ def racc_token2str(tok)
+ self.class::Racc_token_to_s_table[tok] or
+ raise "[Racc Bug] can't convert token #{tok} to string"
+ end
+
+ def token_to_str(t)
+ self.class::Racc_token_to_s_table[t]
+ end
+
+ end
+
+end
+..end racc/parser.rb modeval..id24fd9e97a6
+end
+###### racc/parser.rb end
+
+
+module Protobuf
+
+ class ProtoParser < Racc::Parser
+
+module_eval <<'..end lib/protobuf/compiler/proto.y modeval..id110d2bf917', 'lib/protobuf/compiler/proto.y', 158
+
+ require 'strscan'
+
+ def parse(f)
+ @scanner = StringScanner.new(f.read)
+ yyparse(self, :scan)
+ end
+
+ def scan_debug
+ scan do |token, value|
+ p [token, value]
+ yield [token, value]
+ end
+ end
+
+ def scan
+ until @scanner.eos?
+ case
+ when match(/\s+/, /\/\/.*/)
+ # skip
+ when match(/\/\*/)
+ # C-like comment
+ raise 'EOF inside block comment' until @scanner.scan_until(/\*\//)
+ 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/)
+ yield [@token, @token.to_sym]
+ when match(/[+-]?\d*\.\d+([Ee][\+-]?\d+)?/)
+ yield [:FLOAT_LITERAL, @token.to_f]
+ when match(/[+-]?[1-9]\d*(?!\.)/, /0(?![.xX0-9])/)
+ yield [:DEC_INTEGER, @token.to_i]
+ when match(/0[xX]([A-Fa-f0-9])+/)
+ yield [:HEX_INTEGER, @token.to_i(0)]
+ when match(/0[0-7]+/)
+ yield [:OCT_INTEGER, @token.to_i(0)]
+ when match(/(true|false)\b/)
+ yield [:BOOLEAN_LITERAL, @token == 'true']
+ when match(/"(?:[^"\\]+|\\.)*"/, /'(?:[^'\\]+|\\.)*'/)
+ yield [:STRING_LITERAL, eval(@token)]
+ when match(/[a-zA-Z_]\w*/)
+ yield [:IDENT, @token.to_sym]
+ when match(/[A-Z]\w*/)
+ yield [:CAMEL_IDENT, @token.to_sym]
+ when match(/./)
+ yield [@token, @token]
+ else
+ raise "parse error around #{@scanner.string[@scanner.pos, 32].inspect}"
+ end
+ end
+ yield [false, nil]
+ end
+
+ def match(*regular_expressions)
+ regular_expressions.each do |re|
+ if @scanner.scan(re)
+ @token = @scanner[0]
+ return true
+ end
+ end
+ false
+ end
+..end lib/protobuf/compiler/proto.y modeval..id110d2bf917
+
+##### racc 1.4.5 generates ###
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 1, 53, :_reduce_1,
+ 2, 53, :_reduce_2,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_none,
+ 1, 54, :_reduce_10,
+ 3, 58, :_reduce_11,
+ 4, 59, :_reduce_12,
+ 0, 62, :_reduce_13,
+ 3, 62, :_reduce_14,
+ 3, 60, :_reduce_15,
+ 4, 63, :_reduce_16,
+ 3, 55, :_reduce_17,
+ 5, 56, :_reduce_18,
+ 0, 67, :_reduce_19,
+ 2, 67, :_reduce_20,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_none,
+ 1, 68, :_reduce_23,
+ 5, 57, :_reduce_24,
+ 0, 71, :_reduce_25,
+ 2, 71, :_reduce_26,
+ 1, 72, :_reduce_none,
+ 1, 72, :_reduce_none,
+ 1, 72, :_reduce_29,
+ 4, 73, :_reduce_30,
+ 5, 61, :_reduce_31,
+ 0, 75, :_reduce_32,
+ 2, 75, :_reduce_33,
+ 1, 76, :_reduce_none,
+ 1, 76, :_reduce_none,
+ 1, 76, :_reduce_36,
+ 10, 77, :_reduce_37,
+ 0, 78, :_reduce_none,
+ 1, 78, :_reduce_none,
+ 3, 65, :_reduce_40,
+ 0, 79, :_reduce_41,
+ 2, 79, :_reduce_42,
+ 1, 80, :_reduce_none,
+ 1, 80, :_reduce_none,
+ 1, 80, :_reduce_none,
+ 1, 80, :_reduce_none,
+ 1, 80, :_reduce_none,
+ 1, 80, :_reduce_none,
+ 1, 80, :_reduce_none,
+ 1, 80, :_reduce_50,
+ 6, 70, :_reduce_51,
+ 6, 69, :_reduce_52,
+ 9, 69, :_reduce_53,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 84, :_reduce_none,
+ 1, 85, :_reduce_87,
+ 3, 85, :_reduce_88,
+ 1, 86, :_reduce_none,
+ 3, 86, :_reduce_90,
+ 4, 81, :_reduce_91,
+ 0, 88, :_reduce_92,
+ 2, 88, :_reduce_93,
+ 1, 87, :_reduce_94,
+ 3, 87, :_reduce_95,
+ 3, 87, :_reduce_96,
+ 1, 82, :_reduce_none,
+ 1, 82, :_reduce_none,
+ 1, 82, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 1, 83, :_reduce_none,
+ 2, 66, :_reduce_116,
+ 3, 66, :_reduce_117,
+ 1, 64, :_reduce_none,
+ 1, 64, :_reduce_none,
+ 1, 64, :_reduce_none,
+ 1, 64, :_reduce_none,
+ 1, 64, :_reduce_none,
+ 1, 74, :_reduce_none,
+ 1, 74, :_reduce_none,
+ 1, 74, :_reduce_none ]
+
+racc_reduce_n = 126
+
+racc_shift_n = 184
+
+racc_action_table = [
+ 74, 51, 77, 19, 20, 74, 25, 77, 67, 60,
+ 32, 47, 53, 63, 14, 14, 43, 107, 39, 68,
+ 61, 38, 69, 50, 54, 56, 110, 170, 106, 109,
+ 94, 96, 97, 98, 99, 100, 101, 103, 104, 105,
+ 108, 93, 95, 72, 73, 75, 76, 78, 72, 73,
+ 75, 76, 78, 123, 111, 131, 134, 43, 141, 48,
+ 147, 116, 19, 20, 122, 126, 130, 19, 20, 140,
+ 144, 75, 76, 78, 120, 124, 127, 129, 133, 136,
+ 139, 143, 146, 115, 118, 119, 121, 125, 128, 132,
+ 135, 138, 142, 145, 114, 117, 83, 167, 176, 75,
+ 76, 78, 14, 25, 16, 1, 159, 85, 6, 75,
+ 76, 78, 75, 76, 78, 19, 20, 166, 50, 54,
+ 56, 177, 91, 35, 170, 75, 76, 78, 27, 34,
+ 4, 7, 148, 11, 33, 150, 14, 151, 16, 1,
+ 153, 154, 6, 9, 4, 7, 155, 11, 156, 43,
+ 14, 40, 16, 1, 161, 30, 6, 9, 75, 76,
+ 78, 29, 25, 165, 24, 40, 169, 23, 174, 175,
+ 22, 43, 21, 180, 59, 182, 183 ]
+
+racc_action_check = [
+ 48, 42, 48, 58, 58, 175, 177, 175, 46, 45,
+ 20, 36, 42, 45, 46, 45, 36, 58, 27, 46,
+ 45, 26, 46, 42, 42, 42, 63, 177, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 48, 48, 48, 48, 48, 175, 175,
+ 175, 175, 175, 102, 69, 102, 102, 37, 102, 37,
+ 102, 102, 150, 150, 102, 102, 102, 1, 1, 102,
+ 102, 91, 91, 91, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 102, 49, 163, 172, 110,
+ 110, 110, 49, 166, 49, 49, 151, 49, 49, 154,
+ 154, 154, 153, 153, 153, 174, 174, 163, 49, 49,
+ 49, 172, 49, 23, 166, 151, 151, 151, 15, 22,
+ 15, 15, 107, 15, 21, 111, 15, 112, 15, 15,
+ 113, 137, 15, 15, 0, 0, 148, 0, 149, 44,
+ 0, 29, 0, 0, 152, 18, 0, 0, 155, 155,
+ 155, 16, 14, 158, 11, 164, 165, 9, 169, 170,
+ 7, 31, 6, 176, 43, 178, 182 ]
+
+racc_action_pointer = [
+ 142, 61, nil, nil, nil, nil, 166, 166, nil, 161,
+ nil, 158, nil, nil, 156, 128, 155, nil, 143, nil,
+ 4, 122, 127, 111, nil, nil, 19, 18, nil, 139,
+ nil, 164, nil, nil, nil, nil, 9, 50, nil, nil,
+ nil, nil, -1, 168, 142, 7, 6, nil, -4, 94,
+ nil, nil, nil, nil, nil, nil, nil, nil, -3, nil,
+ nil, nil, nil, 17, nil, nil, nil, nil, nil, 48,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 22, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, 50, nil, nil, nil, nil, 111, nil, nil,
+ 50, 118, 108, 94, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, 132, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 137, 146,
+ 56, 76, 152, 63, 60, 109, nil, nil, 145, nil,
+ nil, nil, nil, 95, 153, 147, 97, nil, nil, 151,
+ 160, nil, 75, nil, 109, 1, 171, 0, 157, nil,
+ nil, nil, 174, nil ]
+
+racc_action_default = [
+ -126, -126, -3, -4, -10, -5, -126, -126, -6, -126,
+ -7, -126, -8, -9, -126, -126, -126, -1, -126, -13,
+ -126, -126, -126, -126, -13, -13, -126, -126, -2, -126,
+ -19, -116, -13, -25, -11, -32, -126, -126, -15, 184,
+ -41, -17, -126, -126, -117, -126, -126, -12, -126, -126,
+ -97, -23, -20, -18, -98, -21, -99, -22, -126, -14,
+ -29, -24, -27, -126, -26, -28, -35, -36, -31, -126,
+ -34, -33, -120, -122, -121, -123, -124, -118, -125, -119,
+ -16, -45, -46, -50, -44, -40, -43, -42, -48, -47,
+ -49, -126, -115, -113, -102, -114, -103, -104, -105, -106,
+ -107, -108, -126, -109, -110, -111, -100, -126, -112, -101,
+ -126, -126, -94, -92, -85, -74, -62, -86, -75, -76,
+ -55, -77, -63, -58, -56, -78, -64, -57, -79, -68,
+ -65, -59, -80, -69, -54, -81, -70, -126, -82, -71,
+ -66, -60, -83, -72, -67, -84, -73, -61, -126, -126,
+ -38, -126, -126, -126, -126, -126, -30, -39, -126, -96,
+ -95, -91, -93, -126, -126, -126, -126, -52, -51, -126,
+ -126, -89, -126, -87, -38, -126, -126, -126, -126, -90,
+ -53, -88, -126, -37 ]
+
+racc_goto_table = [
+ 41, 80, 112, 18, 158, 113, 31, 17, 57, 173,
+ 55, 36, 37, 64, 42, 88, 26, 86, 45, 44,
+ 181, 149, 28, 62, 70, 52, 65, 90, 178, 84,
+ 46, 71, 66, 82, 49, 87, 89, 102, 137, 172,
+ 81, 15, 152, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ 92, nil, 160, nil, 112, 163, 164, 162, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,