OLD | NEW |
1 // Protocol Buffers - Google's data interchange format | 1 // Protocol Buffers - Google's data interchange format |
2 // Copyright 2008 Google Inc. All rights reserved. | 2 // Copyright 2008 Google Inc. All rights reserved. |
3 // https://developers.google.com/protocol-buffers/ | 3 // https://developers.google.com/protocol-buffers/ |
4 // | 4 // |
5 // Redistribution and use in source and binary forms, with or without | 5 // Redistribution and use in source and binary forms, with or without |
6 // modification, are permitted provided that the following conditions are | 6 // modification, are permitted provided that the following conditions are |
7 // met: | 7 // met: |
8 // | 8 // |
9 // * Redistributions of source code must retain the above copyright | 9 // * Redistributions of source code must retain the above copyright |
10 // notice, this list of conditions and the following disclaimer. | 10 // notice, this list of conditions and the following disclaimer. |
(...skipping 30 matching lines...) Expand all Loading... |
41 | 41 |
42 using google::protobuf::internal::scoped_ptr; | 42 using google::protobuf::internal::scoped_ptr; |
43 | 43 |
44 namespace google { | 44 namespace google { |
45 namespace protobuf { | 45 namespace protobuf { |
46 namespace compiler { | 46 namespace compiler { |
47 namespace ruby { | 47 namespace ruby { |
48 | 48 |
49 // Forward decls. | 49 // Forward decls. |
50 std::string IntToString(int32 value); | 50 std::string IntToString(int32 value); |
51 std::string GetRequireName(const std::string& proto_file); | 51 std::string StripDotProto(const std::string& proto_file); |
52 std::string LabelForField(google::protobuf::FieldDescriptor* field); | 52 std::string LabelForField(google::protobuf::FieldDescriptor* field); |
53 std::string TypeName(google::protobuf::FieldDescriptor* field); | 53 std::string TypeName(google::protobuf::FieldDescriptor* field); |
54 void GenerateMessage(const google::protobuf::Descriptor* message, | 54 void GenerateMessage(const google::protobuf::Descriptor* message, |
55 google::protobuf::io::Printer* printer); | 55 google::protobuf::io::Printer* printer); |
56 void GenerateEnum(const google::protobuf::EnumDescriptor* en, | 56 void GenerateEnum(const google::protobuf::EnumDescriptor* en, |
57 google::protobuf::io::Printer* printer); | 57 google::protobuf::io::Printer* printer); |
58 void GenerateMessageAssignment( | 58 void GenerateMessageAssignment( |
59 const std::string& prefix, | 59 const std::string& prefix, |
60 const google::protobuf::Descriptor* message, | 60 const google::protobuf::Descriptor* message, |
61 google::protobuf::io::Printer* printer); | 61 google::protobuf::io::Printer* printer); |
62 void GenerateEnumAssignment( | 62 void GenerateEnumAssignment( |
63 const std::string& prefix, | 63 const std::string& prefix, |
64 const google::protobuf::EnumDescriptor* en, | 64 const google::protobuf::EnumDescriptor* en, |
65 google::protobuf::io::Printer* printer); | 65 google::protobuf::io::Printer* printer); |
66 | 66 |
67 std::string IntToString(int32 value) { | 67 std::string IntToString(int32 value) { |
68 std::ostringstream os; | 68 std::ostringstream os; |
69 os << value; | 69 os << value; |
70 return os.str(); | 70 return os.str(); |
71 } | 71 } |
72 | 72 |
73 std::string GetRequireName(const std::string& proto_file) { | 73 std::string StripDotProto(const std::string& proto_file) { |
74 int lastindex = proto_file.find_last_of("."); | 74 int lastindex = proto_file.find_last_of("."); |
75 return proto_file.substr(0, lastindex) + "_pb"; | 75 return proto_file.substr(0, lastindex); |
76 } | 76 } |
77 | 77 |
78 std::string GetOutputFilename(const std::string& proto_file) { | 78 std::string GetOutputFilename(const std::string& proto_file) { |
79 return GetRequireName(proto_file) + ".rb"; | 79 return StripDotProto(proto_file) + ".rb"; |
80 } | 80 } |
81 | 81 |
82 std::string LabelForField(const google::protobuf::FieldDescriptor* field) { | 82 std::string LabelForField(const google::protobuf::FieldDescriptor* field) { |
83 switch (field->label()) { | 83 switch (field->label()) { |
84 case FieldDescriptor::LABEL_OPTIONAL: return "optional"; | 84 case FieldDescriptor::LABEL_OPTIONAL: return "optional"; |
85 case FieldDescriptor::LABEL_REQUIRED: return "required"; | 85 case FieldDescriptor::LABEL_REQUIRED: return "required"; |
86 case FieldDescriptor::LABEL_REPEATED: return "repeated"; | 86 case FieldDescriptor::LABEL_REPEATED: return "repeated"; |
87 default: assert(false); return ""; | 87 default: assert(false); return ""; |
88 } | 88 } |
89 } | 89 } |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
230 "value :$name$, $number$\n", | 230 "value :$name$, $number$\n", |
231 "name", value->name(), | 231 "name", value->name(), |
232 "number", IntToString(value->number())); | 232 "number", IntToString(value->number())); |
233 } | 233 } |
234 | 234 |
235 printer->Outdent(); | 235 printer->Outdent(); |
236 printer->Print( | 236 printer->Print( |
237 "end\n"); | 237 "end\n"); |
238 } | 238 } |
239 | 239 |
240 // Locale-agnostic utility functions. | 240 // Module names, class names, and enum value names need to be Ruby constants, |
241 bool IsLower(char ch) { return ch >= 'a' && ch <= 'z'; } | 241 // which must start with a capital letter. |
242 | |
243 bool IsUpper(char ch) { return ch >= 'A' && ch <= 'Z'; } | |
244 | |
245 bool IsAlpha(char ch) { return IsLower(ch) || IsUpper(ch); } | |
246 | |
247 char ToUpper(char ch) { return IsLower(ch) ? (ch - 'a' + 'A') : ch; } | |
248 | |
249 | |
250 // Package names in protobuf are snake_case by convention, but Ruby module | |
251 // names must be PascalCased. | |
252 // | |
253 // foo_bar_baz -> FooBarBaz | |
254 std::string PackageToModule(const std::string& name) { | |
255 bool next_upper = true; | |
256 std::string result; | |
257 result.reserve(name.size()); | |
258 | |
259 for (int i = 0; i < name.size(); i++) { | |
260 if (name[i] == '_') { | |
261 next_upper = true; | |
262 } else { | |
263 if (next_upper) { | |
264 result.push_back(ToUpper(name[i])); | |
265 } else { | |
266 result.push_back(name[i]); | |
267 } | |
268 next_upper = false; | |
269 } | |
270 } | |
271 | |
272 return result; | |
273 } | |
274 | |
275 // Class and enum names in protobuf should be PascalCased by convention, but | |
276 // since there is nothing enforcing this we need to ensure that they are valid | |
277 // Ruby constants. That mainly means making sure that the first character is | |
278 // an upper-case letter. | |
279 std::string RubifyConstant(const std::string& name) { | 242 std::string RubifyConstant(const std::string& name) { |
280 std::string ret = name; | 243 std::string ret = name; |
281 if (!ret.empty()) { | 244 if (!ret.empty()) { |
282 if (IsLower(ret[0])) { | 245 if (ret[0] >= 'a' && ret[0] <= 'z') { |
283 // If it starts with a lowercase letter, capitalize it. | 246 // If it starts with a lowercase letter, capitalize it. |
284 ret[0] = ToUpper(ret[0]); | 247 ret[0] = ret[0] - 'a' + 'A'; |
285 } else if (!IsAlpha(ret[0])) { | 248 } else if (ret[0] < 'A' || ret[0] > 'Z') { |
286 // Otherwise (e.g. if it begins with an underscore), we need to come up | 249 // Otherwise (e.g. if it begins with an underscore), we need to come up |
287 // with some prefix that starts with a capital letter. We could be smarter | 250 // with some prefix that starts with a capital letter. We could be smarter |
288 // here, e.g. try to strip leading underscores, but this may cause other | 251 // here, e.g. try to strip leading underscores, but this may cause other |
289 // problems if the user really intended the name. So let's just prepend a | 252 // problems if the user really intended the name. So let's just prepend a |
290 // well-known suffix. | 253 // well-known suffix. |
291 ret = "PB_" + ret; | 254 ret = "PB_" + ret; |
292 } | 255 } |
293 } | 256 } |
294 | |
295 return ret; | 257 return ret; |
296 } | 258 } |
297 | 259 |
298 void GenerateMessageAssignment( | 260 void GenerateMessageAssignment( |
299 const std::string& prefix, | 261 const std::string& prefix, |
300 const google::protobuf::Descriptor* message, | 262 const google::protobuf::Descriptor* message, |
301 google::protobuf::io::Printer* printer) { | 263 google::protobuf::io::Printer* printer) { |
302 | 264 |
303 // Don't generate MapEntry messages -- we use the Ruby extension's native | 265 // Don't generate MapEntry messages -- we use the Ruby extension's native |
304 // support for map fields instead. | 266 // support for map fields instead. |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
345 while (!package_name.empty()) { | 307 while (!package_name.empty()) { |
346 size_t dot_index = package_name.find("."); | 308 size_t dot_index = package_name.find("."); |
347 string component; | 309 string component; |
348 if (dot_index == string::npos) { | 310 if (dot_index == string::npos) { |
349 component = package_name; | 311 component = package_name; |
350 package_name = ""; | 312 package_name = ""; |
351 } else { | 313 } else { |
352 component = package_name.substr(0, dot_index); | 314 component = package_name.substr(0, dot_index); |
353 package_name = package_name.substr(dot_index + 1); | 315 package_name = package_name.substr(dot_index + 1); |
354 } | 316 } |
355 component = PackageToModule(component); | 317 component = RubifyConstant(component); |
356 printer->Print( | 318 printer->Print( |
357 "module $name$\n", | 319 "module $name$\n", |
358 "name", component); | 320 "name", component); |
359 printer->Indent(); | 321 printer->Indent(); |
360 levels++; | 322 levels++; |
361 } | 323 } |
362 return levels; | 324 return levels; |
363 } | 325 } |
364 | 326 |
365 void EndPackageModules( | 327 void EndPackageModules( |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
422 | 384 |
423 // Ok to omit this proto2 dependency -- so we won't print anything. | 385 // Ok to omit this proto2 dependency -- so we won't print anything. |
424 GOOGLE_LOG(WARNING) << "Omitting proto2 dependency '" << import->name() | 386 GOOGLE_LOG(WARNING) << "Omitting proto2 dependency '" << import->name() |
425 << "' from proto3 output file '" | 387 << "' from proto3 output file '" |
426 << GetOutputFilename(from->name()) | 388 << GetOutputFilename(from->name()) |
427 << "' because we don't support proto2 and no proto2 " | 389 << "' because we don't support proto2 and no proto2 " |
428 "types from that file are being used."; | 390 "types from that file are being used."; |
429 return true; | 391 return true; |
430 } else { | 392 } else { |
431 printer->Print( | 393 printer->Print( |
432 "require '$name$'\n", "name", GetRequireName(import->name())); | 394 "require '$name$'\n", "name", StripDotProto(import->name())); |
433 return true; | 395 return true; |
434 } | 396 } |
435 } | 397 } |
436 | 398 |
437 bool GenerateFile(const FileDescriptor* file, io::Printer* printer, | 399 bool GenerateFile(const FileDescriptor* file, io::Printer* printer, |
438 string* error) { | 400 string* error) { |
439 printer->Print( | 401 printer->Print( |
440 "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" | 402 "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" |
441 "# source: $filename$\n" | 403 "# source: $filename$\n" |
442 "\n", | 404 "\n", |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
492 generator_context->Open(GetOutputFilename(file->name()))); | 454 generator_context->Open(GetOutputFilename(file->name()))); |
493 io::Printer printer(output.get(), '$'); | 455 io::Printer printer(output.get(), '$'); |
494 | 456 |
495 return GenerateFile(file, &printer, error); | 457 return GenerateFile(file, &printer, error); |
496 } | 458 } |
497 | 459 |
498 } // namespace ruby | 460 } // namespace ruby |
499 } // namespace compiler | 461 } // namespace compiler |
500 } // namespace protobuf | 462 } // namespace protobuf |
501 } // namespace google | 463 } // namespace google |
OLD | NEW |