protostar 0.1.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +60 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +647 -0
  5. data/THIRD_PARTY_NOTICES.md +61 -0
  6. data/exe/protoc-gen-protostar +7 -0
  7. data/exe/protostar +7 -0
  8. data/lib/protostar/builder/generator.rb +71 -0
  9. data/lib/protostar/builder/sink.rb +35 -0
  10. data/lib/protostar/builder.rb +965 -0
  11. data/lib/protostar/cli.rb +267 -0
  12. data/lib/protostar/codec/decode_generator.rb +199 -0
  13. data/lib/protostar/codec/decoder.rb +296 -0
  14. data/lib/protostar/codec/encode_generator.rb +224 -0
  15. data/lib/protostar/codec/encoder.rb +121 -0
  16. data/lib/protostar/codec/sizer.rb +91 -0
  17. data/lib/protostar/codec.rb +15 -0
  18. data/lib/protostar/compiler/ast.rb +193 -0
  19. data/lib/protostar/compiler/emitters/descriptor_set.rb +57 -0
  20. data/lib/protostar/compiler/emitters/google_ruby.rb +103 -0
  21. data/lib/protostar/compiler/emitters/grpc.rb +175 -0
  22. data/lib/protostar/compiler/emitters/protostar_rbs.rb +252 -0
  23. data/lib/protostar/compiler/emitters/rbs.rb +394 -0
  24. data/lib/protostar/compiler/emitters/ruby.rb +503 -0
  25. data/lib/protostar/compiler/emitters.rb +268 -0
  26. data/lib/protostar/compiler/include/LICENSE +32 -0
  27. data/lib/protostar/compiler/include/google/protobuf/any.proto +106 -0
  28. data/lib/protostar/compiler/include/google/protobuf/api.proto +229 -0
  29. data/lib/protostar/compiler/include/google/protobuf/compiler/plugin.proto +180 -0
  30. data/lib/protostar/compiler/include/google/protobuf/cpp_features.proto +87 -0
  31. data/lib/protostar/compiler/include/google/protobuf/descriptor.proto +1532 -0
  32. data/lib/protostar/compiler/include/google/protobuf/duration.proto +115 -0
  33. data/lib/protostar/compiler/include/google/protobuf/empty.proto +51 -0
  34. data/lib/protostar/compiler/include/google/protobuf/field_mask.proto +243 -0
  35. data/lib/protostar/compiler/include/google/protobuf/java_features.proto +132 -0
  36. data/lib/protostar/compiler/include/google/protobuf/source_context.proto +48 -0
  37. data/lib/protostar/compiler/include/google/protobuf/struct.proto +111 -0
  38. data/lib/protostar/compiler/include/google/protobuf/timestamp.proto +145 -0
  39. data/lib/protostar/compiler/include/google/protobuf/type.proto +217 -0
  40. data/lib/protostar/compiler/include/google/protobuf/wrappers.proto +157 -0
  41. data/lib/protostar/compiler/lexer.rb +659 -0
  42. data/lib/protostar/compiler/linker.rb +894 -0
  43. data/lib/protostar/compiler/option_interpreter.rb +363 -0
  44. data/lib/protostar/compiler/parser.rb +1696 -0
  45. data/lib/protostar/compiler/retention.rb +104 -0
  46. data/lib/protostar/compiler/run.rb +184 -0
  47. data/lib/protostar/compiler/source_info.rb +161 -0
  48. data/lib/protostar/compiler/symbol_table.rb +223 -0
  49. data/lib/protostar/compiler/text_format.rb +340 -0
  50. data/lib/protostar/compiler/validator.rb +944 -0
  51. data/lib/protostar/compiler.rb +304 -0
  52. data/lib/protostar/diagnostics/codes.rb +1307 -0
  53. data/lib/protostar/diagnostics/formats.rb +64 -0
  54. data/lib/protostar/diagnostics/renderer.rb +240 -0
  55. data/lib/protostar/diagnostics/source.rb +153 -0
  56. data/lib/protostar/diagnostics.rb +117 -0
  57. data/lib/protostar/dsl.rb +367 -0
  58. data/lib/protostar/enum.rb +70 -0
  59. data/lib/protostar/errors.rb +23 -0
  60. data/lib/protostar/google/protobuf/any_pb.rb +90 -0
  61. data/lib/protostar/google/protobuf/api_pb.rb +199 -0
  62. data/lib/protostar/google/protobuf/compiler/plugin_pb.rb +168 -0
  63. data/lib/protostar/google/protobuf/descriptor_pb.rb +1239 -0
  64. data/lib/protostar/google/protobuf/duration_pb.rb +99 -0
  65. data/lib/protostar/google/protobuf/empty_pb.rb +36 -0
  66. data/lib/protostar/google/protobuf/field_mask_pb.rb +228 -0
  67. data/lib/protostar/google/protobuf/source_context_pb.rb +33 -0
  68. data/lib/protostar/google/protobuf/struct_pb.rb +91 -0
  69. data/lib/protostar/google/protobuf/timestamp_pb.rb +129 -0
  70. data/lib/protostar/google/protobuf/type_pb.rb +202 -0
  71. data/lib/protostar/google/protobuf/wrappers_pb.rb +125 -0
  72. data/lib/protostar/google_protobuf_shim.rb +24 -0
  73. data/lib/protostar/json/encoder.rb +340 -0
  74. data/lib/protostar/json/errors.rb +34 -0
  75. data/lib/protostar/json/parser.rb +437 -0
  76. data/lib/protostar/json/path.rb +39 -0
  77. data/lib/protostar/json.rb +95 -0
  78. data/lib/protostar/lint/command.rb +197 -0
  79. data/lib/protostar/lint/config.rb +98 -0
  80. data/lib/protostar/lint/context.rb +126 -0
  81. data/lib/protostar/lint/directives.rb +56 -0
  82. data/lib/protostar/lint/fixer.rb +165 -0
  83. data/lib/protostar/lint/naming.rb +54 -0
  84. data/lib/protostar/lint/rule.rb +120 -0
  85. data/lib/protostar/lint/rules/comment_enum.rb +31 -0
  86. data/lib/protostar/lint/rules/comment_enum_value.rb +32 -0
  87. data/lib/protostar/lint/rules/comment_field.rb +33 -0
  88. data/lib/protostar/lint/rules/comment_message.rb +31 -0
  89. data/lib/protostar/lint/rules/comment_rpc.rb +31 -0
  90. data/lib/protostar/lint/rules/comment_service.rb +31 -0
  91. data/lib/protostar/lint/rules/edition_recommended.rb +31 -0
  92. data/lib/protostar/lint/rules/enum_first_value_zero.rb +34 -0
  93. data/lib/protostar/lint/rules/enum_pascal_case.rb +25 -0
  94. data/lib/protostar/lint/rules/enum_value_prefix.rb +38 -0
  95. data/lib/protostar/lint/rules/enum_value_upper_snake_case.rb +28 -0
  96. data/lib/protostar/lint/rules/enum_zero_value_suffix.rb +41 -0
  97. data/lib/protostar/lint/rules/field_lower_snake_case.rb +34 -0
  98. data/lib/protostar/lint/rules/field_not_required.rb +39 -0
  99. data/lib/protostar/lint/rules/field_number_reuse_risk.rb +45 -0
  100. data/lib/protostar/lint/rules/field_numbers_hot_range.rb +44 -0
  101. data/lib/protostar/lint/rules/file_lower_snake_case.rb +27 -0
  102. data/lib/protostar/lint/rules/import_no_public.rb +29 -0
  103. data/lib/protostar/lint/rules/import_no_weak.rb +28 -0
  104. data/lib/protostar/lint/rules/import_used.rb +33 -0
  105. data/lib/protostar/lint/rules/message_pascal_case.rb +27 -0
  106. data/lib/protostar/lint/rules/no_deprecated_features.rb +50 -0
  107. data/lib/protostar/lint/rules/no_groups.rb +34 -0
  108. data/lib/protostar/lint/rules/oneof_lower_snake_case.rb +27 -0
  109. data/lib/protostar/lint/rules/package_defined.rb +28 -0
  110. data/lib/protostar/lint/rules/package_directory_match.rb +33 -0
  111. data/lib/protostar/lint/rules/package_lower_snake_case.rb +28 -0
  112. data/lib/protostar/lint/rules/package_same_directory.rb +39 -0
  113. data/lib/protostar/lint/rules/package_version_suffix.rb +31 -0
  114. data/lib/protostar/lint/rules/reserved_names_with_numbers.rb +44 -0
  115. data/lib/protostar/lint/rules/rpc_pascal_case.rb +25 -0
  116. data/lib/protostar/lint/rules/rpc_request_response_unique.rb +40 -0
  117. data/lib/protostar/lint/rules/rpc_request_standard_name.rb +32 -0
  118. data/lib/protostar/lint/rules/rpc_response_standard_name.rb +31 -0
  119. data/lib/protostar/lint/rules/service_pascal_case.rb +25 -0
  120. data/lib/protostar/lint/rules/service_suffix.rb +28 -0
  121. data/lib/protostar/lint/rules/syntax_specified.rb +35 -0
  122. data/lib/protostar/lint/runner.rb +135 -0
  123. data/lib/protostar/lint.rb +122 -0
  124. data/lib/protostar/message/accessors.rb +86 -0
  125. data/lib/protostar/message/coercion.rb +171 -0
  126. data/lib/protostar/message/field_info.rb +145 -0
  127. data/lib/protostar/message/reflection.rb +149 -0
  128. data/lib/protostar/message.rb +572 -0
  129. data/lib/protostar/plugin.rb +94 -0
  130. data/lib/protostar/schema/default_value.rb +113 -0
  131. data/lib/protostar/schema/descriptor.rb +41 -0
  132. data/lib/protostar/schema/draft/copy.rb +88 -0
  133. data/lib/protostar/schema/draft/resolution.rb +177 -0
  134. data/lib/protostar/schema/draft.rb +779 -0
  135. data/lib/protostar/schema/enum.rb +106 -0
  136. data/lib/protostar/schema/extension_range.rb +37 -0
  137. data/lib/protostar/schema/features.rb +153 -0
  138. data/lib/protostar/schema/field.rb +201 -0
  139. data/lib/protostar/schema/file.rb +217 -0
  140. data/lib/protostar/schema/freezer.rb +61 -0
  141. data/lib/protostar/schema/message.rb +104 -0
  142. data/lib/protostar/schema/oneof.rb +38 -0
  143. data/lib/protostar/schema/options.rb +367 -0
  144. data/lib/protostar/schema/pool.rb +179 -0
  145. data/lib/protostar/schema/proto.rb +576 -0
  146. data/lib/protostar/schema/resolver.rb +118 -0
  147. data/lib/protostar/schema/service.rb +89 -0
  148. data/lib/protostar/schema/source_info.rb +53 -0
  149. data/lib/protostar/schema.rb +65 -0
  150. data/lib/protostar/short_names/any.rb +7 -0
  151. data/lib/protostar/short_names/duration.rb +7 -0
  152. data/lib/protostar/short_names/empty.rb +7 -0
  153. data/lib/protostar/short_names/field_mask.rb +7 -0
  154. data/lib/protostar/short_names/struct.rb +9 -0
  155. data/lib/protostar/short_names/timestamp.rb +7 -0
  156. data/lib/protostar/short_names/wrappers.rb +15 -0
  157. data/lib/protostar/text/extensions.rb +114 -0
  158. data/lib/protostar/text/parser.rb +439 -0
  159. data/lib/protostar/text/printer.rb +304 -0
  160. data/lib/protostar/text/tokenizer.rb +116 -0
  161. data/lib/protostar/text.rb +67 -0
  162. data/lib/protostar/types.rb +152 -0
  163. data/lib/protostar/unknown_field.rb +58 -0
  164. data/lib/protostar/version.rb +5 -0
  165. data/lib/protostar/view/generator.rb +298 -0
  166. data/lib/protostar/view/map.rb +194 -0
  167. data/lib/protostar/view/repeated.rb +143 -0
  168. data/lib/protostar/view/scan.rb +183 -0
  169. data/lib/protostar/view.rb +283 -0
  170. data/lib/protostar/well_known/any.rb +52 -0
  171. data/lib/protostar/well_known/duration.rb +53 -0
  172. data/lib/protostar/well_known/list_value.rb +63 -0
  173. data/lib/protostar/well_known/struct.rb +55 -0
  174. data/lib/protostar/well_known/timestamp.rb +64 -0
  175. data/lib/protostar/well_known/value.rb +60 -0
  176. data/lib/protostar/well_known.rb +38 -0
  177. data/lib/protostar/wire/io_source.rb +40 -0
  178. data/lib/protostar/wire/reader.rb +254 -0
  179. data/lib/protostar/wire/writer.rb +201 -0
  180. data/lib/protostar/wire.rb +96 -0
  181. data/lib/protostar.rb +95 -0
  182. data/shims/google-protobuf/lib/google/protobuf/any_pb.rb +17 -0
  183. data/shims/google-protobuf/lib/google/protobuf/api_pb.rb +22 -0
  184. data/shims/google-protobuf/lib/google/protobuf/compiler/plugin_pb.rb +25 -0
  185. data/shims/google-protobuf/lib/google/protobuf/descriptor_pb.rb +72 -0
  186. data/shims/google-protobuf/lib/google/protobuf/descriptor_pool.rb +164 -0
  187. data/shims/google-protobuf/lib/google/protobuf/descriptors.rb +553 -0
  188. data/shims/google-protobuf/lib/google/protobuf/duration_pb.rb +17 -0
  189. data/shims/google-protobuf/lib/google/protobuf/empty_pb.rb +17 -0
  190. data/shims/google-protobuf/lib/google/protobuf/field_mask_pb.rb +17 -0
  191. data/shims/google-protobuf/lib/google/protobuf/internal/convert.rb +180 -0
  192. data/shims/google-protobuf/lib/google/protobuf/internal/extensions.rb +154 -0
  193. data/shims/google-protobuf/lib/google/protobuf/internal/json.rb +104 -0
  194. data/shims/google-protobuf/lib/google/protobuf/internal/well_known.rb +140 -0
  195. data/shims/google-protobuf/lib/google/protobuf/map.rb +216 -0
  196. data/shims/google-protobuf/lib/google/protobuf/message.rb +544 -0
  197. data/shims/google-protobuf/lib/google/protobuf/message_exts.rb +41 -0
  198. data/shims/google-protobuf/lib/google/protobuf/plugin_pb.rb +4 -0
  199. data/shims/google-protobuf/lib/google/protobuf/repeated_field.rb +366 -0
  200. data/shims/google-protobuf/lib/google/protobuf/source_context_pb.rb +17 -0
  201. data/shims/google-protobuf/lib/google/protobuf/struct_pb.rb +20 -0
  202. data/shims/google-protobuf/lib/google/protobuf/timestamp_pb.rb +17 -0
  203. data/shims/google-protobuf/lib/google/protobuf/type_pb.rb +27 -0
  204. data/shims/google-protobuf/lib/google/protobuf/well_known_types.rb +37 -0
  205. data/shims/google-protobuf/lib/google/protobuf/wrappers_pb.rb +25 -0
  206. data/shims/google-protobuf/lib/google/protobuf.rb +107 -0
  207. data/shims/google-protobuf/lib/google/protobuf_ffi.rb +5 -0
  208. data/shims/google-protobuf/lib/google/protobuf_native.rb +5 -0
  209. data/sig/protostar/google/protobuf/any_pb.rbs +77 -0
  210. data/sig/protostar/google/protobuf/api_pb.rbs +242 -0
  211. data/sig/protostar/google/protobuf/compiler/plugin_pb.rbs +264 -0
  212. data/sig/protostar/google/protobuf/descriptor_pb.rbs +2220 -0
  213. data/sig/protostar/google/protobuf/duration_pb.rbs +82 -0
  214. data/sig/protostar/google/protobuf/empty_pb.rbs +18 -0
  215. data/sig/protostar/google/protobuf/field_mask_pb.rbs +170 -0
  216. data/sig/protostar/google/protobuf/source_context_pb.rbs +24 -0
  217. data/sig/protostar/google/protobuf/struct_pb.rbs +135 -0
  218. data/sig/protostar/google/protobuf/timestamp_pb.rbs +102 -0
  219. data/sig/protostar/google/protobuf/type_pb.rbs +304 -0
  220. data/sig/protostar/google/protobuf/wrappers_pb.rbs +169 -0
  221. data/sig/protostar/lint.rbs +216 -0
  222. data/sig/protostar.rbs +326 -0
  223. metadata +366 -0
@@ -0,0 +1,1696 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protostar
4
+ class Compiler
5
+ # protoc's `.proto` parser (`compiler/parser.cc`). Produces an {AST::File}
6
+ # shaped like the parser's FileDescriptorProto, the source locations protoc
7
+ # records, and protoc's parse errors with its recovery (skip to the end of
8
+ # the statement or block and carry on).
9
+ #
10
+ # @api private
11
+ class Parser
12
+ TYPES = {
13
+ "double" => :TYPE_DOUBLE, "float" => :TYPE_FLOAT, "uint64" => :TYPE_UINT64, "fixed64" => :TYPE_FIXED64,
14
+ "fixed32" => :TYPE_FIXED32, "bool" => :TYPE_BOOL, "string" => :TYPE_STRING, "group" => :TYPE_GROUP,
15
+ "bytes" => :TYPE_BYTES, "uint32" => :TYPE_UINT32, "sfixed32" => :TYPE_SFIXED32,
16
+ "sfixed64" => :TYPE_SFIXED64, "int32" => :TYPE_INT32, "int64" => :TYPE_INT64,
17
+ "sint32" => :TYPE_SINT32, "sint64" => :TYPE_SINT64
18
+ }.freeze
19
+
20
+ # EDITION_UNSTABLE spells integer types by their wire format
21
+ UNSTABLE_TYPES = {
22
+ "double" => :TYPE_DOUBLE, "float" => :TYPE_FLOAT, "uint64" => :TYPE_FIXED64, "bool" => :TYPE_BOOL,
23
+ "string" => :TYPE_STRING, "group" => :TYPE_GROUP, "bytes" => :TYPE_BYTES, "uint32" => :TYPE_FIXED32,
24
+ "int32" => :TYPE_SFIXED32, "int64" => :TYPE_SFIXED64, "varint32" => :TYPE_INT32,
25
+ "varint64" => :TYPE_INT64, "uvarint32" => :TYPE_UINT32, "uvarint64" => :TYPE_UINT64,
26
+ "zigzag32" => :TYPE_SINT32, "zigzag64" => :TYPE_SINT64
27
+ }.freeze
28
+
29
+ UNSTABLE_REPLACEMENTS = {
30
+ "fixed32" => "uint32", "fixed64" => "uint64", "sfixed32" => "int32", "sfixed64" => "int64",
31
+ "sint32" => "zigzag32", "sint64" => "zigzag64"
32
+ }.freeze
33
+
34
+ INT32_MAX = 2**31 - 1
35
+ INT64_MAX = 2**63 - 1
36
+ UINT32_MAX = 2**32 - 1
37
+ UINT64_MAX = 2**64 - 1
38
+ MAX_FIELD_NUMBER = 536_870_911
39
+ MAX_NESTING = 32
40
+ # end of a `to max` range until the message's maximum is known
41
+ MAX_RANGE_SENTINEL = -1
42
+
43
+ # descriptor.proto field numbers used in source paths
44
+ FILE_PACKAGE = 2
45
+ FILE_DEPENDENCY = 3
46
+ FILE_MESSAGE_TYPE = 4
47
+ FILE_ENUM_TYPE = 5
48
+ FILE_SERVICE = 6
49
+ FILE_EXTENSION = 7
50
+ FILE_OPTIONS = 8
51
+ FILE_PUBLIC_DEPENDENCY = 10
52
+ FILE_WEAK_DEPENDENCY = 11
53
+ FILE_SYNTAX = 12
54
+ FILE_OPTION_DEPENDENCY = 15
55
+ MESSAGE_NAME = 1
56
+ MESSAGE_FIELD = 2
57
+ MESSAGE_NESTED_TYPE = 3
58
+ MESSAGE_ENUM_TYPE = 4
59
+ MESSAGE_EXTENSION_RANGE = 5
60
+ MESSAGE_EXTENSION = 6
61
+ MESSAGE_OPTIONS = 7
62
+ MESSAGE_ONEOF_DECL = 8
63
+ MESSAGE_RESERVED_RANGE = 9
64
+ MESSAGE_RESERVED_NAME = 10
65
+ RANGE_START = 1
66
+ RANGE_END = 2
67
+ RANGE_OPTIONS = 3
68
+ FIELD_NAME = 1
69
+ FIELD_EXTENDEE = 2
70
+ FIELD_NUMBER = 3
71
+ FIELD_LABEL = 4
72
+ FIELD_TYPE = 5
73
+ FIELD_TYPE_NAME = 6
74
+ FIELD_DEFAULT_VALUE = 7
75
+ FIELD_OPTIONS = 8
76
+ FIELD_JSON_NAME = 10
77
+ ONEOF_NAME = 1
78
+ ONEOF_OPTIONS = 2
79
+ ENUM_NAME = 1
80
+ ENUM_VALUE = 2
81
+ ENUM_OPTIONS = 3
82
+ ENUM_RESERVED_RANGE = 4
83
+ ENUM_RESERVED_NAME = 5
84
+ ENUM_VALUE_NAME = 1
85
+ ENUM_VALUE_NUMBER = 2
86
+ ENUM_VALUE_OPTIONS = 3
87
+ SERVICE_NAME = 1
88
+ SERVICE_METHOD = 2
89
+ SERVICE_OPTIONS = 3
90
+ METHOD_NAME = 1
91
+ METHOD_INPUT_TYPE = 2
92
+ METHOD_OUTPUT_TYPE = 3
93
+ METHOD_OPTIONS = 4
94
+ METHOD_CLIENT_STREAMING = 5
95
+ METHOD_SERVER_STREAMING = 6
96
+ UNINTERPRETED_OPTION = 999
97
+ OPTION_NAME = 2
98
+ OPTION_IDENTIFIER_VALUE = 3
99
+ OPTION_POSITIVE_INT_VALUE = 4
100
+ OPTION_NEGATIVE_INT_VALUE = 5
101
+ OPTION_DOUBLE_VALUE = 6
102
+ OPTION_STRING_VALUE = 7
103
+ OPTION_AGGREGATE_VALUE = 8
104
+ NAME_PART = 1
105
+
106
+ # @return [Array<Array(Integer, Integer, String)>] 0-based errors, lexer's included
107
+ attr_reader :errors
108
+ # @return [Array<Array(Integer, Integer, String)>]
109
+ attr_reader :warnings
110
+ # @return [String] "proto2", "proto3" or "editions" once parsed
111
+ attr_reader :syntax_identifier
112
+
113
+ # @param source [String]
114
+ # @param require_syntax [Boolean] reject files without `syntax` or `edition`
115
+ def initialize(source, require_syntax: false)
116
+ @errors = []
117
+ @warnings = []
118
+ @lexer = Lexer.new(source, ->(line, column, message) { @errors << [line, column, message] })
119
+ @require_syntax = require_syntax
120
+ @syntax_identifier = ""
121
+ @edition = :EDITION_UNKNOWN
122
+ @upcoming_doc_comments = +""
123
+ @upcoming_detached_comments = []
124
+ @recursion_depth = MAX_NESTING
125
+ end
126
+
127
+ # @param name [String, nil] the file name to record
128
+ # @return [AST::File] the tree, complete as far as parsing got
129
+ def parse(name = nil)
130
+ file = AST::File.new
131
+ file.name = name
132
+ @file = file
133
+ @locations = file.locations
134
+ if looking_at_type?(:start)
135
+ @lexer.next_with_comments(nil, @upcoming_detached_comments, @upcoming_doc_comments)
136
+ end
137
+
138
+ root = SourceInfo::Recorder.new(@lexer, [], @locations)
139
+ begin
140
+ root.record_legacy(file, :other)
141
+ if @require_syntax || looking_at?("syntax") || looking_at?("edition")
142
+ return file unless parse_syntax_identifier(file, root)
143
+ file.syntax = @syntax_identifier
144
+ file.edition = @edition if @syntax_identifier == "editions"
145
+ else
146
+ @syntax_identifier = "proto2"
147
+ end
148
+
149
+ until at_end?
150
+ next if parse_top_level_statement(file, root)
151
+ skip_statement
152
+ if looking_at?("}")
153
+ record_error("Unmatched \"}\".")
154
+ @lexer.next_with_comments(nil, @upcoming_detached_comments, @upcoming_doc_comments)
155
+ end
156
+ end
157
+ ensure
158
+ root.finish
159
+ end
160
+ file
161
+ end
162
+
163
+ # @return [Boolean] whether parsing went without errors
164
+ def success?
165
+ @errors.empty?
166
+ end
167
+
168
+ # @return [Lexer::Token] the token parsing stopped at
169
+ def current_token
170
+ @lexer.current
171
+ end
172
+
173
+ private
174
+
175
+ def looking_at?(text)
176
+ @lexer.current.text == text
177
+ end
178
+
179
+ def looking_at_type?(type)
180
+ @lexer.current.type == type
181
+ end
182
+
183
+ def at_end?
184
+ looking_at_type?(:end)
185
+ end
186
+
187
+ def try_consume(text)
188
+ return false unless looking_at?(text)
189
+ @lexer.next
190
+ true
191
+ end
192
+
193
+ def consume(text, error = nil)
194
+ return true if try_consume(text)
195
+ record_error(error || "Expected \"#{text}\".")
196
+ false
197
+ end
198
+
199
+ # @return [String, nil]
200
+ def consume_identifier(error)
201
+ if looking_at_type?(:identifier)
202
+ text = @lexer.current.text.dup.force_encoding(Encoding::UTF_8)
203
+ @lexer.next
204
+ return text
205
+ end
206
+ record_error(error)
207
+ nil
208
+ end
209
+
210
+ # Parser::ConsumeInteger: an unsigned int32
211
+ # @return [Integer, nil]
212
+ def consume_integer(error)
213
+ if looking_at_type?(:integer)
214
+ value = Lexer.parse_integer(@lexer.current.text, INT32_MAX)
215
+ unless value
216
+ record_error("Integer out of range.")
217
+ value = 0
218
+ end
219
+ @lexer.next
220
+ return value
221
+ end
222
+ record_error(error)
223
+ nil
224
+ end
225
+
226
+ # @return [Integer, nil] an int32, sign included
227
+ def consume_signed_integer(error)
228
+ negative = false
229
+ max = INT32_MAX
230
+ if try_consume("-")
231
+ negative = true
232
+ max += 1
233
+ end
234
+ value = consume_integer64(max, error)
235
+ return nil if value.nil?
236
+ negative ? -value : value
237
+ end
238
+
239
+ def consume_integer64(max, error)
240
+ if looking_at_type?(:integer)
241
+ value = Lexer.parse_integer(@lexer.current.text, max)
242
+ unless value
243
+ record_error("Integer out of range.")
244
+ value = 0
245
+ end
246
+ @lexer.next
247
+ return value
248
+ end
249
+ record_error(error)
250
+ nil
251
+ end
252
+
253
+ def try_consume_integer64(max)
254
+ return nil unless looking_at_type?(:integer)
255
+ value = Lexer.parse_integer(@lexer.current.text, max)
256
+ return nil unless value
257
+ @lexer.next
258
+ value
259
+ end
260
+
261
+ # @return [Float, nil]
262
+ def consume_number(error)
263
+ token = @lexer.current
264
+ if token.type == :float
265
+ value = Lexer.parse_float(token.text)
266
+ @lexer.next
267
+ value
268
+ elsif token.type == :integer
269
+ value = Lexer.parse_integer(token.text, UINT64_MAX)
270
+ if value
271
+ value = value.to_f
272
+ elsif token.text.start_with?("0")
273
+ record_error("Integer out of range.")
274
+ value = 0.0
275
+ elsif Lexer.float_text?(token.text)
276
+ value = Lexer.parse_float(token.text)
277
+ else
278
+ record_error("Integer out of range.")
279
+ value = 0.0
280
+ end
281
+ @lexer.next
282
+ value
283
+ elsif looking_at?("inf")
284
+ @lexer.next
285
+ Float::INFINITY
286
+ elsif looking_at?("nan")
287
+ @lexer.next
288
+ Float::NAN
289
+ else
290
+ record_error(error)
291
+ nil
292
+ end
293
+ end
294
+
295
+ # @return [String, nil] binary, adjacent literals concatenated
296
+ def consume_string(error)
297
+ unless looking_at_type?(:string)
298
+ record_error(error)
299
+ return nil
300
+ end
301
+ out = "".b
302
+ Lexer.parse_string(@lexer.current.text, out)
303
+ @lexer.next
304
+ while looking_at_type?(:string)
305
+ Lexer.parse_string(@lexer.current.text, out)
306
+ @lexer.next
307
+ end
308
+ out
309
+ end
310
+
311
+ def try_consume_end_of_declaration(text, location)
312
+ return false unless looking_at?(text)
313
+ leading = +""
314
+ trailing = +""
315
+ detached = []
316
+ @lexer.next_with_comments(trailing, detached, leading)
317
+ leading, @upcoming_doc_comments = @upcoming_doc_comments, leading
318
+ if location
319
+ detached, @upcoming_detached_comments = @upcoming_detached_comments, detached
320
+ location.attach_comments(leading, trailing, detached)
321
+ elsif text == "}"
322
+ @upcoming_detached_comments = detached
323
+ else
324
+ @upcoming_detached_comments.concat(detached)
325
+ end
326
+ true
327
+ end
328
+
329
+ def consume_end_of_declaration(text, location)
330
+ return true if try_consume_end_of_declaration(text, location)
331
+ record_error("Expected \"#{text}\".")
332
+ false
333
+ end
334
+
335
+ def record_error(message)
336
+ @errors << [@lexer.current.line, @lexer.current.column, message]
337
+ end
338
+
339
+ def record_error_at(line, column, message)
340
+ @errors << [line, column, message]
341
+ end
342
+
343
+ def record_warning_at(line, column, message)
344
+ @warnings << [line, column, message]
345
+ end
346
+
347
+ # Runs the block with a location that is finished however the block exits
348
+ def scoped(recorder)
349
+ yield recorder
350
+ ensure
351
+ recorder.finish
352
+ end
353
+
354
+ def skip_statement
355
+ loop do
356
+ return if at_end?
357
+ if looking_at_type?(:symbol)
358
+ return if try_consume_end_of_declaration(";", nil)
359
+ if try_consume("{")
360
+ skip_rest_of_block
361
+ return
362
+ end
363
+ return if looking_at?("}")
364
+ end
365
+ @lexer.next
366
+ end
367
+ end
368
+
369
+ def skip_rest_of_block
370
+ depth = 1
371
+ loop do
372
+ return if at_end?
373
+ if looking_at_type?(:symbol)
374
+ if try_consume_end_of_declaration("}", nil)
375
+ depth -= 1
376
+ return if depth == 0
377
+ elsif try_consume("{")
378
+ depth += 1
379
+ end
380
+ end
381
+ @lexer.next
382
+ end
383
+ end
384
+
385
+ def validate_message(message)
386
+ message.options&.uninterpreted&.each do |option|
387
+ next unless !option.name.empty? && !option.name[0][1] && option.name[0][0] == "map_entry"
388
+ line, column = option.position(:option_name) || [-1, 0]
389
+ record_error_at(line, column, "map_entry should not be set explicitly. Use map<KeyType, ValueType> instead.")
390
+ return false
391
+ end
392
+ true
393
+ end
394
+
395
+ def validate_enum(enum)
396
+ has_allow_alias = false
397
+ allow_alias = false
398
+ enum.options&.uninterpreted&.each do |option|
399
+ next if option.name.size > 1
400
+ next unless !option.name[0][1] && option.name[0][0] == "allow_alias"
401
+ has_allow_alias = true
402
+ allow_alias = true if option.value_kind == :identifier_value && option.value == "true"
403
+ break
404
+ end
405
+ if has_allow_alias && !allow_alias
406
+ record_error("\"#{enum.name}\" declares 'option allow_alias = false;' which has no effect. " \
407
+ "Please remove the declaration.")
408
+ return false
409
+ end
410
+ numbers = enum.value.map(&:number)
411
+ if allow_alias && numbers.uniq.size == numbers.size
412
+ record_error("\"#{enum.name}\" declares support for enum aliases but no enum values share field " \
413
+ "numbers. Please remove the unnecessary 'option allow_alias = true;' declaration.")
414
+ return false
415
+ end
416
+ true
417
+ end
418
+
419
+ def parse_syntax_identifier(file, parent)
420
+ scoped(parent.child(FILE_SYNTAX)) do |location|
421
+ location.record_legacy(file, :editions)
422
+ has_edition = false
423
+ if try_consume("edition")
424
+ has_edition = true
425
+ else
426
+ return false unless consume("syntax",
427
+ "File must begin with an edition or syntax statement, e.g. 'edition = \"2023\";'.")
428
+ end
429
+ return false unless consume("=")
430
+ token = @lexer.current.dup
431
+ syntax = consume_string("Expected syntax identifier.")
432
+ return false if syntax.nil?
433
+ syntax = syntax.force_encoding(Encoding::UTF_8)
434
+ return false unless consume_end_of_declaration(";", location)
435
+
436
+ if has_edition
437
+ edition = :"EDITION_#{syntax}"
438
+ if !Schema::Features::EDITIONS.key?(edition) ||
439
+ %i[EDITION_PROTO2 EDITION_PROTO3 EDITION_UNKNOWN].include?(edition)
440
+ record_error_at(token.line, token.column, "Unknown edition \"#{syntax}\".")
441
+ return false
442
+ end
443
+ @edition = edition
444
+ @syntax_identifier = "editions"
445
+ return true
446
+ end
447
+
448
+ @syntax_identifier = syntax
449
+ if syntax != "proto2" && syntax != "proto3"
450
+ record_error_at(token.line, token.column, "Unrecognized syntax identifier \"#{syntax}\". This parser " \
451
+ "only recognizes \"proto2\" and \"proto3\".")
452
+ return false
453
+ end
454
+ true
455
+ end
456
+ end
457
+
458
+ def edition_number
459
+ Schema::Features::EDITIONS.fetch(@edition, 0)
460
+ end
461
+
462
+ def parse_top_level_statement(file, root)
463
+ return true if try_consume_end_of_declaration(";", nil)
464
+ visibility = nil
465
+ if looking_at?("export") || looking_at?("local")
466
+ ok, visibility = parse_visibility(file)
467
+ return false unless ok
468
+ end
469
+
470
+ if looking_at?("message")
471
+ @recursion_depth = MAX_NESTING
472
+ message = AST::Message.new
473
+ scoped(root.child(FILE_MESSAGE_TYPE, file.message_type.size)) do |location|
474
+ file.message_type << message
475
+ parse_message_definition(message, visibility, location)
476
+ end
477
+ elsif looking_at?("enum")
478
+ enum = AST::Enum.new
479
+ scoped(root.child(FILE_ENUM_TYPE, file.enum_type.size)) do |location|
480
+ file.enum_type << enum
481
+ parse_enum_definition(enum, visibility, location)
482
+ end
483
+ elsif looking_at?("service")
484
+ service = AST::Service.new
485
+ scoped(root.child(FILE_SERVICE, file.service.size)) do |location|
486
+ file.service << service
487
+ parse_service_definition(service, location)
488
+ end
489
+ elsif looking_at?("extend")
490
+ scoped(root.child(FILE_EXTENSION)) do |location|
491
+ parse_extend(file.extension, file.message_type, root, FILE_MESSAGE_TYPE, location)
492
+ end
493
+ elsif looking_at?("import")
494
+ parse_import(file, root)
495
+ elsif looking_at?("package")
496
+ parse_package(file, root)
497
+ elsif looking_at?("option")
498
+ scoped(root.child(FILE_OPTIONS)) do |location|
499
+ parse_option(file, location, :statement)
500
+ end
501
+ else
502
+ record_error("Expected top-level statement (e.g. \"message\").")
503
+ false
504
+ end
505
+ end
506
+
507
+ # protoc's GenerateSyntheticOneofs
508
+ def generate_synthetic_oneofs(message)
509
+ fields = message.field.select(&:proto3_optional)
510
+ return if fields.empty?
511
+ taken = message.field.map(&:name) + message.oneof_decl.map(&:name)
512
+ names = Schema::Draft.synthetic_oneof_names(fields.map { |f| f.name.to_s }, taken)
513
+ fields.zip(names) do |field, name|
514
+ field.oneof_index = message.oneof_decl.size
515
+ oneof = AST::Oneof.new
516
+ oneof.name = name
517
+ message.oneof_decl << oneof
518
+ end
519
+ end
520
+
521
+ def parse_message_definition(message, visibility, message_location)
522
+ @recursion_depth -= 1
523
+ begin
524
+ if @recursion_depth <= 0
525
+ record_error("Reached maximum recursion limit for nested messages.")
526
+ return false
527
+ end
528
+ return false unless consume("message")
529
+ scoped(message_location.child(MESSAGE_NAME)) do |location|
530
+ location.record_legacy(message, :name)
531
+ name = consume_identifier("Expected message name.")
532
+ return false if name.nil?
533
+ message.name = name
534
+ end
535
+ return false unless parse_message_block(message, message_location)
536
+ generate_synthetic_oneofs(message) if @syntax_identifier == "proto3"
537
+ message.visibility = visibility if visibility
538
+ true
539
+ ensure
540
+ @recursion_depth += 1
541
+ end
542
+ end
543
+
544
+ def message_set?(message)
545
+ message.options&.uninterpreted&.any? do |o|
546
+ o.name.size == 1 && !o.name[0][1] && o.name[0][0] == "message_set_wire_format" &&
547
+ o.value_kind == :identifier_value && o.value == "true"
548
+ end
549
+ end
550
+
551
+ def adjust_max_ranges(message, ranges)
552
+ max = message_set?(message) ? INT32_MAX : MAX_FIELD_NUMBER + 1
553
+ ranges.each { |r| r.end = max if r.end == MAX_RANGE_SENTINEL }
554
+ end
555
+
556
+ def parse_message_block(message, message_location)
557
+ return false unless consume_end_of_declaration("{", message_location)
558
+ until try_consume_end_of_declaration("}", nil)
559
+ if at_end?
560
+ record_error("Reached end of input in message definition (missing '}').")
561
+ return false
562
+ end
563
+ skip_statement unless parse_message_statement(message, message_location)
564
+ end
565
+ adjust_max_ranges(message, message.extension_range) unless message.extension_range.empty?
566
+ adjust_max_ranges(message, message.reserved_range) unless message.reserved_range.empty?
567
+ validate_message(message)
568
+ end
569
+
570
+ def parse_message_statement(message, message_location)
571
+ return true if try_consume_end_of_declaration(";", nil)
572
+ visibility = nil
573
+ if looking_at?("export") || looking_at?("local")
574
+ ok, visibility = parse_visibility(@file)
575
+ return false unless ok
576
+ end
577
+
578
+ if looking_at?("message")
579
+ nested = AST::Message.new
580
+ scoped(message_location.child(MESSAGE_NESTED_TYPE, message.nested_type.size)) do |location|
581
+ message.nested_type << nested
582
+ parse_message_definition(nested, visibility, location)
583
+ end
584
+ elsif looking_at?("enum")
585
+ enum = AST::Enum.new
586
+ scoped(message_location.child(MESSAGE_ENUM_TYPE, message.enum_type.size)) do |location|
587
+ message.enum_type << enum
588
+ parse_enum_definition(enum, visibility, location)
589
+ end
590
+ elsif looking_at?("extensions")
591
+ scoped(message_location.child(MESSAGE_EXTENSION_RANGE)) do |location|
592
+ parse_extensions(message, location)
593
+ end
594
+ elsif looking_at?("reserved")
595
+ parse_message_reserved(message, message_location)
596
+ elsif looking_at?("extend")
597
+ scoped(message_location.child(MESSAGE_EXTENSION)) do |location|
598
+ parse_extend(message.extension, message.nested_type, message_location, MESSAGE_NESTED_TYPE, location)
599
+ end
600
+ elsif looking_at?("option")
601
+ scoped(message_location.child(MESSAGE_OPTIONS)) do |location|
602
+ parse_option(message, location, :statement)
603
+ end
604
+ elsif looking_at?("oneof")
605
+ index = message.oneof_decl.size
606
+ oneof = AST::Oneof.new
607
+ scoped(message_location.child(MESSAGE_ONEOF_DECL, index)) do |location|
608
+ message.oneof_decl << oneof
609
+ parse_oneof(oneof, message, index, location, message_location)
610
+ end
611
+ else
612
+ field = AST::Field.new
613
+ scoped(message_location.child(MESSAGE_FIELD, message.field.size)) do |location|
614
+ message.field << field
615
+ parse_message_field(field, message.nested_type, message_location, MESSAGE_NESTED_TYPE, location)
616
+ end
617
+ end
618
+ end
619
+
620
+ def parse_message_field(field, messages, parent_location, nested_type_number, field_location)
621
+ label = parse_label(field_location)
622
+ if label
623
+ field.label = label
624
+ field.proto3_optional = true if label == :LABEL_OPTIONAL && @syntax_identifier == "proto3"
625
+ end
626
+ parse_message_field_no_label(field, messages, parent_location, nested_type_number, field_location)
627
+ end
628
+
629
+ def default_to_optional_fields?
630
+ @syntax_identifier == "editions" || @syntax_identifier == "proto3"
631
+ end
632
+
633
+ def parse_message_field_no_label(field, messages, parent_location, nested_type_number, field_location)
634
+ map_field = nil
635
+ scoped(field_location.child) do |location|
636
+ location.record_legacy(field, :type)
637
+ type_parsed = false
638
+ type = :TYPE_INT32
639
+ type_name = ""
640
+ if try_consume("map")
641
+ if looking_at?("<")
642
+ map_field = {}
643
+ return false unless parse_map_type(map_field, field, location)
644
+ else
645
+ type_parsed = true
646
+ type_name = "map"
647
+ end
648
+ end
649
+ unless map_field
650
+ field.label = :LABEL_OPTIONAL if field.label.nil? && default_to_optional_fields?
651
+ if field.label.nil?
652
+ record_error("Expected \"required\", \"optional\", or \"repeated\".")
653
+ field.label = :LABEL_OPTIONAL
654
+ end
655
+ unless type_parsed
656
+ parsed = parse_type
657
+ return false unless parsed
658
+ type, type_name = parsed
659
+ end
660
+ if type_name.empty?
661
+ location.add_path(FIELD_TYPE)
662
+ field.type = type
663
+ else
664
+ location.add_path(FIELD_TYPE_NAME)
665
+ field.type_name = type_name
666
+ end
667
+ end
668
+ end
669
+
670
+ name_token = @lexer.current.dup
671
+ scoped(field_location.child(FIELD_NAME)) do |location|
672
+ location.record_legacy(field, :name)
673
+ name = consume_identifier("Expected field name.")
674
+ return false if name.nil?
675
+ field.name = name
676
+ end
677
+ return false unless consume("=", "Missing field number.")
678
+ scoped(field_location.child(FIELD_NUMBER)) do |location|
679
+ location.record_legacy(field, :number)
680
+ number = consume_integer("Expected field number.")
681
+ return false if number.nil?
682
+ field.number = number
683
+ end
684
+ return false unless parse_field_options(field, field_location)
685
+
686
+ if field.type == :TYPE_GROUP
687
+ group = AST::Message.new
688
+ group_location = parent_location.child
689
+ begin
690
+ group_location.start_at_recorder(field_location)
691
+ group_location.add_path(nested_type_number)
692
+ group_location.add_path(messages.size)
693
+ messages << group
694
+ group.name = field.name
695
+ scoped(group_location.child(MESSAGE_NAME)) do |location|
696
+ location.start_at(name_token)
697
+ location.end_at(name_token)
698
+ location.record_legacy(group, :name)
699
+ end
700
+ scoped(field_location.child(FIELD_TYPE_NAME)) do |location|
701
+ location.start_at(name_token)
702
+ location.end_at(name_token)
703
+ end
704
+ first = group.name.getbyte(0)
705
+ if first.nil? || first < 65 || first > 90
706
+ record_error_at(name_token.line, name_token.column, "Group names must start with a capital letter.")
707
+ end
708
+ field.name = Schema::Draft.group_field_name(field.name)
709
+ field.type_name = group.name
710
+ if looking_at?("{")
711
+ return false unless parse_message_block(group, group_location)
712
+ else
713
+ record_error("Missing group body.")
714
+ return false
715
+ end
716
+ ensure
717
+ group_location.finish
718
+ end
719
+ else
720
+ return false unless consume_end_of_declaration(";", field_location)
721
+ end
722
+
723
+ generate_map_entry(map_field, field, messages) if map_field
724
+ true
725
+ end
726
+
727
+ def parse_map_type(map_field, field, location)
728
+ if field.oneof_index
729
+ record_error("Map fields are not allowed in oneofs.")
730
+ return false
731
+ end
732
+ if field.label
733
+ record_error("Field labels (required/optional/repeated) are not allowed on map fields.")
734
+ return false
735
+ end
736
+ if field.extendee
737
+ record_error("Map fields are not allowed to be extensions.")
738
+ return false
739
+ end
740
+ field.label = :LABEL_REPEATED
741
+ return false unless consume("<")
742
+ key = parse_type
743
+ return false unless key
744
+ return false unless consume(",")
745
+ value = parse_type
746
+ return false unless value
747
+ return false unless consume(">")
748
+ map_field[:key] = key
749
+ map_field[:value] = value
750
+ location.add_path(FIELD_TYPE_NAME)
751
+ true
752
+ end
753
+
754
+ def generate_map_entry(map_field, field, messages)
755
+ entry = AST::Message.new
756
+ messages << entry
757
+ entry_name = Schema::Draft.map_entry_name(field.name)
758
+ field.type_name = entry_name
759
+ entry.name = entry_name
760
+ entry.options = AST::Options.new
761
+ entry.options.known[:map_entry] = true
762
+ key = map_entry_field("key", 1, map_field[:key])
763
+ value = map_entry_field("value", 2, map_field[:value])
764
+ entry.field << key << value
765
+ field.options&.uninterpreted&.each do |option|
766
+ first, ext = option.name[0]
767
+ if option.name.size == 1 && first == "enforce_utf8" && !ext
768
+ (key.options ||= AST::Options.new).uninterpreted << option if key.type == :TYPE_STRING
769
+ (value.options ||= AST::Options.new).uninterpreted << option if value.type == :TYPE_STRING
770
+ end
771
+ if first == "features" && !ext
772
+ (key.options ||= AST::Options.new).uninterpreted << option
773
+ (value.options ||= AST::Options.new).uninterpreted << option
774
+ end
775
+ end
776
+ end
777
+
778
+ def map_entry_field(name, number, (type, type_name))
779
+ f = AST::Field.new
780
+ f.name = name
781
+ f.label = :LABEL_OPTIONAL
782
+ f.number = number
783
+ if type_name.empty?
784
+ f.type = type
785
+ else
786
+ f.type_name = type_name
787
+ end
788
+ f
789
+ end
790
+
791
+ def parse_field_options(field, field_location)
792
+ return true unless looking_at?("[")
793
+ scoped(field_location.child(FIELD_OPTIONS)) do |location|
794
+ return false unless consume("[")
795
+ loop do
796
+ if looking_at?("default")
797
+ return false unless parse_default_assignment(field, field_location)
798
+ elsif looking_at?("json_name")
799
+ return false unless parse_json_name(field, field_location)
800
+ else
801
+ return false unless parse_option(field, location, :assignment)
802
+ end
803
+ break unless try_consume(",")
804
+ end
805
+ return false unless consume("]")
806
+ end
807
+ true
808
+ end
809
+
810
+ def parse_default_assignment(field, field_location)
811
+ if field.default_value
812
+ record_error("Already set option \"default\".")
813
+ field.default_value = nil
814
+ end
815
+ return false unless consume("default")
816
+ return false unless consume("=")
817
+ scoped(field_location.child(FIELD_DEFAULT_VALUE)) do |location|
818
+ location.record_legacy(field, :default_value)
819
+ unless field.type
820
+ field.default_value = @lexer.current.text.dup.force_encoding(Encoding::UTF_8)
821
+ @lexer.next
822
+ return true
823
+ end
824
+ value = +""
825
+ field.default_value = value
826
+ case field.type
827
+ when :TYPE_INT32, :TYPE_INT64, :TYPE_SINT32, :TYPE_SINT64, :TYPE_SFIXED32, :TYPE_SFIXED64
828
+ max = %i[TYPE_INT32 TYPE_SINT32 TYPE_SFIXED32].include?(field.type) ? INT32_MAX : INT64_MAX
829
+ if try_consume("-")
830
+ value << "-"
831
+ max += 1
832
+ end
833
+ n = consume_integer64(max, "Expected integer for field default value.")
834
+ return false if n.nil?
835
+ value << n.to_s
836
+ when :TYPE_UINT32, :TYPE_UINT64, :TYPE_FIXED32, :TYPE_FIXED64
837
+ max = %i[TYPE_UINT32 TYPE_FIXED32].include?(field.type) ? UINT32_MAX : UINT64_MAX
838
+ record_error("Unsigned field can't have negative default value.") if try_consume("-")
839
+ n = consume_integer64(max, "Expected integer for field default value.")
840
+ return false if n.nil?
841
+ value << n.to_s
842
+ when :TYPE_FLOAT, :TYPE_DOUBLE
843
+ value << "-" if try_consume("-")
844
+ n = consume_number("Expected number.")
845
+ return false if n.nil?
846
+ value << Compiler.simple_dtoa(n)
847
+ when :TYPE_BOOL
848
+ if try_consume("true")
849
+ value.replace("true")
850
+ elsif try_consume("false")
851
+ value.replace("false")
852
+ else
853
+ record_error("Expected \"true\" or \"false\".")
854
+ return false
855
+ end
856
+ when :TYPE_STRING
857
+ s = consume_string("Expected string for field default value.")
858
+ return false if s.nil?
859
+ value.replace(s.force_encoding(Encoding::UTF_8))
860
+ when :TYPE_BYTES
861
+ s = consume_string("Expected string.")
862
+ return false if s.nil?
863
+ value.replace(Compiler.c_escape(s))
864
+ when :TYPE_ENUM
865
+ s = consume_identifier("Expected enum identifier for field default value.")
866
+ return false if s.nil?
867
+ value.replace(s)
868
+ when :TYPE_MESSAGE, :TYPE_GROUP
869
+ record_error("Messages can't have default values.")
870
+ return false
871
+ end
872
+ end
873
+ true
874
+ end
875
+
876
+ def parse_json_name(field, field_location)
877
+ if field.json_name
878
+ record_error("Already set option \"json_name\".")
879
+ field.json_name = nil
880
+ end
881
+ scoped(field_location.child(FIELD_JSON_NAME)) do |location|
882
+ location.record_legacy(field, :option_name)
883
+ return false unless consume("json_name")
884
+ return false unless consume("=")
885
+ scoped(location.child) do |value_location|
886
+ value_location.record_legacy(field, :option_value)
887
+ s = consume_string("Expected string for JSON name.")
888
+ return false if s.nil?
889
+ field.json_name = s.force_encoding(Encoding::UTF_8)
890
+ end
891
+ end
892
+ true
893
+ end
894
+
895
+ def parse_option_name_part(option, part_location)
896
+ part = [+"", false]
897
+ option.name << part
898
+ if looking_at?("(")
899
+ return false unless consume("(")
900
+ scoped(part_location.child(NAME_PART)) do
901
+ if looking_at_type?(:identifier)
902
+ id = consume_identifier("Expected identifier.")
903
+ return false if id.nil?
904
+ part[0] << id
905
+ end
906
+ while looking_at?(".")
907
+ return false unless consume(".")
908
+ part[0] << "."
909
+ id = consume_identifier("Expected identifier.")
910
+ return false if id.nil?
911
+ part[0] << id
912
+ end
913
+ end
914
+ return false unless consume(")")
915
+ part[1] = true
916
+ else
917
+ scoped(part_location.child(NAME_PART)) do
918
+ id = consume_identifier("Expected identifier.")
919
+ return false if id.nil?
920
+ part[0] << id
921
+ end
922
+ end
923
+ true
924
+ end
925
+
926
+ def parse_uninterpreted_block
927
+ unless looking_at?("{")
928
+ record_error("Expected \"{\".")
929
+ return nil
930
+ end
931
+ value = +""
932
+ depth = 1
933
+ @lexer.report_whitespace = true
934
+ @lexer.report_newlines = true
935
+ begin
936
+ @lexer.next
937
+ until at_end?
938
+ if looking_at?("{")
939
+ depth += 1
940
+ elsif looking_at?("}")
941
+ depth -= 1
942
+ break if depth == 0
943
+ end
944
+ cur = @lexer.current
945
+ prev = @lexer.previous
946
+ prev_end_line = (prev.type == :newline) ? prev.line + 1 : prev.line
947
+ if cur.line > prev_end_line
948
+ value << ("\n" * (cur.line - prev_end_line))
949
+ value << (" " * cur.column)
950
+ elsif cur.line == prev_end_line && cur.column > prev.end_column
951
+ value << (" " * (cur.column - prev.end_column))
952
+ end
953
+ value << cur.text
954
+ @lexer.next
955
+ end
956
+ ensure
957
+ @lexer.report_whitespace = false
958
+ @lexer.report_newlines = false
959
+ end
960
+ if depth == 0
961
+ @lexer.next
962
+ value.force_encoding(Encoding::UTF_8)
963
+ else
964
+ record_error("Unexpected end of stream while parsing aggregate value.")
965
+ nil
966
+ end
967
+ end
968
+
969
+ # @param holder [AST::Node] the element whose options this sets
970
+ # @param style [Symbol] :statement (`option x = y;`) or :assignment (`[x = y]`)
971
+ def parse_option(holder, options_location, style)
972
+ options = (holder.options ||= AST::Options.new)
973
+ scoped(options_location.child(UNINTERPRETED_OPTION, options.uninterpreted.size)) do |location|
974
+ if style == :statement
975
+ return false unless consume("option")
976
+ end
977
+ option = AST::Option.new
978
+ options.uninterpreted << option
979
+
980
+ scoped(location.child(OPTION_NAME)) do |name_location|
981
+ name_location.record_legacy(option, :option_name)
982
+ scoped(name_location.child(option.name.size)) do |part_location|
983
+ return false unless parse_option_name_part(option, part_location)
984
+ end
985
+ while looking_at?(".")
986
+ return false unless consume(".")
987
+ scoped(name_location.child(option.name.size)) do |part_location|
988
+ return false unless parse_option_name_part(option, part_location)
989
+ end
990
+ end
991
+ end
992
+
993
+ return false unless consume("=")
994
+
995
+ scoped(location.child) do |value_location|
996
+ value_location.record_legacy(option, :option_value)
997
+ negative = try_consume("-")
998
+ type = @lexer.current.type
999
+ case type
1000
+ when :end
1001
+ record_error("Unexpected end of stream while parsing option value.")
1002
+ return false
1003
+ when :identifier
1004
+ value_location.add_path(OPTION_IDENTIFIER_VALUE)
1005
+ id = consume_identifier("Expected identifier.")
1006
+ return false if id.nil?
1007
+ if negative
1008
+ if id == "inf"
1009
+ option.value_kind = :double_value
1010
+ option.value = -Float::INFINITY
1011
+ elsif id == "nan"
1012
+ option.value_kind = :double_value
1013
+ option.value = Float::NAN
1014
+ else
1015
+ record_error("Identifier after '-' symbol must be inf or nan.")
1016
+ return false
1017
+ end
1018
+ else
1019
+ option.value_kind = :identifier_value
1020
+ option.value = id
1021
+ end
1022
+ when :integer, :float
1023
+ int = if type == :integer
1024
+ try_consume_integer64(negative ? INT64_MAX + 1 : UINT64_MAX)
1025
+ end
1026
+ if int
1027
+ if negative
1028
+ value_location.add_path(OPTION_NEGATIVE_INT_VALUE)
1029
+ option.value_kind = :negative_int_value
1030
+ option.value = -int
1031
+ else
1032
+ value_location.add_path(OPTION_POSITIVE_INT_VALUE)
1033
+ option.value_kind = :positive_int_value
1034
+ option.value = int
1035
+ end
1036
+ else
1037
+ value_location.add_path(OPTION_DOUBLE_VALUE)
1038
+ n = consume_number("Expected number.")
1039
+ return false if n.nil?
1040
+ option.value_kind = :double_value
1041
+ option.value = negative ? -n : n
1042
+ end
1043
+ when :string
1044
+ value_location.add_path(OPTION_STRING_VALUE)
1045
+ if negative
1046
+ record_error("Invalid '-' symbol before string.")
1047
+ return false
1048
+ end
1049
+ s = consume_string("Expected string.")
1050
+ return false if s.nil?
1051
+ option.value_kind = :string_value
1052
+ option.value = s
1053
+ else
1054
+ unless looking_at?("{")
1055
+ record_error("Expected option value.")
1056
+ return false
1057
+ end
1058
+ value_location.add_path(OPTION_AGGREGATE_VALUE)
1059
+ block = parse_uninterpreted_block
1060
+ return false if block.nil?
1061
+ option.value_kind = :aggregate_value
1062
+ option.value = block
1063
+ end
1064
+ end
1065
+
1066
+ if style == :statement
1067
+ return false unless consume_end_of_declaration(";", location)
1068
+ end
1069
+ end
1070
+ true
1071
+ end
1072
+
1073
+ def parse_extensions(message, extensions_location)
1074
+ return false unless consume("extensions")
1075
+ old_size = message.extension_range.size
1076
+ loop do
1077
+ range = AST::Range.new
1078
+ ok = scoped(extensions_location.child(message.extension_range.size)) do |location|
1079
+ message.extension_range << range
1080
+ location.record_legacy(range, :number)
1081
+ start_token = nil
1082
+ start = nil
1083
+ scoped(location.child(RANGE_START)) do
1084
+ start_token = @lexer.current.dup
1085
+ start = consume_integer("Expected field number range.")
1086
+ return false if start.nil?
1087
+ if start == INT32_MAX
1088
+ record_error("Field number out of bounds.")
1089
+ return false
1090
+ end
1091
+ end
1092
+ last = nil
1093
+ if try_consume("to")
1094
+ scoped(location.child(RANGE_END)) do
1095
+ if try_consume("max")
1096
+ last = MAX_RANGE_SENTINEL - 1
1097
+ else
1098
+ last = consume_integer("Expected integer.")
1099
+ return false if last.nil?
1100
+ if last == INT32_MAX
1101
+ record_error("Field number out of bounds.")
1102
+ return false
1103
+ end
1104
+ end
1105
+ end
1106
+ else
1107
+ scoped(location.child(RANGE_END)) do |end_location|
1108
+ end_location.start_at(start_token)
1109
+ end_location.end_at(start_token)
1110
+ last = start
1111
+ end
1112
+ end
1113
+ range.start = start
1114
+ range.end = last + 1
1115
+ true
1116
+ end
1117
+ return false unless ok
1118
+ break unless try_consume(",")
1119
+ end
1120
+
1121
+ if looking_at?("[")
1122
+ range_number_index = extensions_location.path_size
1123
+ info = []
1124
+ first = message.extension_range[old_size]
1125
+ first.options ||= AST::Options.new
1126
+ index_location = extensions_location.child(0, target: info)
1127
+ begin
1128
+ scoped(index_location.child(RANGE_OPTIONS)) do |location|
1129
+ return false unless consume("[")
1130
+ loop do
1131
+ return false unless parse_option(first, location, :assignment)
1132
+ break unless try_consume(",")
1133
+ end
1134
+ return false unless consume("]")
1135
+ end
1136
+ ensure
1137
+ index_location.finish
1138
+ end
1139
+ ((old_size + 1)...message.extension_range.size).each do |i|
1140
+ message.extension_range[i].options = first.options.copy
1141
+ end
1142
+ (old_size...message.extension_range.size).each do |i|
1143
+ info.each do |loc|
1144
+ next if loc.path.size == range_number_index + 1
1145
+ path = loc.path.dup
1146
+ path[range_number_index] = i
1147
+ @locations << loc.dup_with_path(path)
1148
+ end
1149
+ end
1150
+ end
1151
+
1152
+ consume_end_of_declaration(";", extensions_location)
1153
+ end
1154
+
1155
+ def parse_message_reserved(message, message_location)
1156
+ parse_reserved(message, message_location, MESSAGE_RESERVED_NAME, MESSAGE_RESERVED_RANGE, enum: false)
1157
+ end
1158
+
1159
+ def parse_reserved(holder, parent_location, name_number, range_number, enum:)
1160
+ start_token = @lexer.current.dup
1161
+ return false unless consume("reserved")
1162
+ if looking_at_type?(:string)
1163
+ if @syntax_identifier == "editions"
1164
+ record_error("Reserved names must be identifiers in editions, not string literals.")
1165
+ return false
1166
+ end
1167
+ scoped(parent_location.child(name_number)) do |location|
1168
+ location.start_at(start_token)
1169
+ parse_reserved_names(holder, location, enum ? "Expected enum value string literal." : "Expected field name string literal.")
1170
+ end
1171
+ elsif looking_at_type?(:identifier)
1172
+ if @syntax_identifier != "editions"
1173
+ record_error("Reserved names must be string literals. (Only editions supports identifiers.)")
1174
+ return false
1175
+ end
1176
+ scoped(parent_location.child(name_number)) do |location|
1177
+ location.start_at(start_token)
1178
+ parse_reserved_identifiers(holder, location, enum ? "Expected enum value identifier." : "Expected field name identifier.")
1179
+ end
1180
+ else
1181
+ scoped(parent_location.child(range_number)) do |location|
1182
+ location.start_at(start_token)
1183
+ enum ? parse_enum_reserved_numbers(holder, location) : parse_reserved_numbers(holder, location)
1184
+ end
1185
+ end
1186
+ end
1187
+
1188
+ def parse_reserved_names(holder, parent_location, error)
1189
+ loop do
1190
+ scoped(parent_location.child(holder.reserved_name.size)) do
1191
+ line = @lexer.current.line
1192
+ column = @lexer.current.column
1193
+ name = consume_string(error)
1194
+ holder.reserved_name << (name || "".b).dup.force_encoding(Encoding::UTF_8)
1195
+ return false if name.nil?
1196
+ name = holder.reserved_name.last
1197
+ unless Lexer.identifier?(name)
1198
+ record_warning_at(line, column, "Reserved name \"#{name}\" is not a valid identifier.")
1199
+ end
1200
+ end
1201
+ break unless try_consume(",")
1202
+ end
1203
+ consume_end_of_declaration(";", parent_location)
1204
+ end
1205
+
1206
+ def parse_reserved_identifiers(holder, parent_location, error)
1207
+ loop do
1208
+ scoped(parent_location.child(holder.reserved_name.size)) do
1209
+ name = consume_identifier(error)
1210
+ holder.reserved_name << (name || +"")
1211
+ return false if name.nil?
1212
+ end
1213
+ break unless try_consume(",")
1214
+ end
1215
+ consume_end_of_declaration(";", parent_location)
1216
+ end
1217
+
1218
+ def parse_reserved_numbers(message, parent_location)
1219
+ first = true
1220
+ loop do
1221
+ range = AST::Range.new
1222
+ ok = scoped(parent_location.child(message.reserved_range.size)) do |location|
1223
+ message.reserved_range << range
1224
+ location.record_legacy(range, :number)
1225
+ start_token = nil
1226
+ start = nil
1227
+ scoped(location.child(RANGE_START)) do
1228
+ start_token = @lexer.current.dup
1229
+ start = consume_integer(first ? "Expected field name or number range." : "Expected field number range.")
1230
+ return false if start.nil?
1231
+ if start == INT32_MAX
1232
+ record_error("Field number out of bounds.")
1233
+ return false
1234
+ end
1235
+ end
1236
+ last = nil
1237
+ if try_consume("to")
1238
+ scoped(location.child(RANGE_END)) do
1239
+ if try_consume("max")
1240
+ last = MAX_RANGE_SENTINEL - 1
1241
+ else
1242
+ last = consume_integer("Expected integer.")
1243
+ return false if last.nil?
1244
+ if last == INT32_MAX
1245
+ record_error("Field number out of bounds.")
1246
+ return false
1247
+ end
1248
+ end
1249
+ end
1250
+ else
1251
+ scoped(location.child(RANGE_END)) do |end_location|
1252
+ end_location.start_at(start_token)
1253
+ end_location.end_at(start_token)
1254
+ last = start
1255
+ end
1256
+ end
1257
+ range.start = start
1258
+ range.end = last + 1
1259
+ true
1260
+ end
1261
+ return false unless ok
1262
+ first = false
1263
+ break unless try_consume(",")
1264
+ end
1265
+ consume_end_of_declaration(";", parent_location)
1266
+ end
1267
+
1268
+ def parse_enum_reserved_numbers(enum, parent_location)
1269
+ first = true
1270
+ loop do
1271
+ range = AST::Range.new
1272
+ ok = scoped(parent_location.child(enum.reserved_range.size)) do |location|
1273
+ enum.reserved_range << range
1274
+ location.record_legacy(range, :number)
1275
+ start_token = nil
1276
+ start = nil
1277
+ scoped(location.child(RANGE_START)) do
1278
+ start_token = @lexer.current.dup
1279
+ start = consume_signed_integer(first ? "Expected enum value or number range." : "Expected enum number range.")
1280
+ return false if start.nil?
1281
+ end
1282
+ last = nil
1283
+ if try_consume("to")
1284
+ scoped(location.child(RANGE_END)) do
1285
+ if try_consume("max")
1286
+ last = INT32_MAX
1287
+ else
1288
+ last = consume_signed_integer("Expected integer.")
1289
+ return false if last.nil?
1290
+ end
1291
+ end
1292
+ else
1293
+ scoped(location.child(RANGE_END)) do |end_location|
1294
+ end_location.start_at(start_token)
1295
+ end_location.end_at(start_token)
1296
+ last = start
1297
+ end
1298
+ end
1299
+ range.start = start
1300
+ range.end = last
1301
+ true
1302
+ end
1303
+ return false unless ok
1304
+ first = false
1305
+ break unless try_consume(",")
1306
+ end
1307
+ consume_end_of_declaration(";", parent_location)
1308
+ end
1309
+
1310
+ def parse_extend(extensions, messages, parent_location, nested_type_number, extend_location)
1311
+ return false unless consume("extend")
1312
+ extendee_start = @lexer.current.dup
1313
+ extendee = parse_user_defined_type
1314
+ return false if extendee.nil?
1315
+ extendee_end = @lexer.previous.dup
1316
+ return false unless consume_end_of_declaration("{", extend_location)
1317
+ is_first = true
1318
+ loop do
1319
+ if at_end?
1320
+ record_error("Reached end of input in extend definition (missing '}').")
1321
+ return false
1322
+ end
1323
+ field = AST::Field.new
1324
+ scoped(extend_location.child(extensions.size)) do |location|
1325
+ extensions << field
1326
+ scoped(location.child(FIELD_EXTENDEE)) do |extendee_location|
1327
+ extendee_location.start_at(extendee_start)
1328
+ extendee_location.end_at(extendee_end)
1329
+ if is_first
1330
+ extendee_location.record_legacy(field, :extendee)
1331
+ is_first = false
1332
+ end
1333
+ end
1334
+ field.extendee = extendee
1335
+ skip_statement unless parse_message_field(field, messages, parent_location, nested_type_number, location)
1336
+ end
1337
+ break if try_consume_end_of_declaration("}", nil)
1338
+ end
1339
+ true
1340
+ end
1341
+
1342
+ def parse_oneof(oneof, message, index, oneof_location, message_location)
1343
+ return false unless consume("oneof")
1344
+ scoped(oneof_location.child(ONEOF_NAME)) do
1345
+ name = consume_identifier("Expected oneof name.")
1346
+ return false if name.nil?
1347
+ oneof.name = name
1348
+ end
1349
+ return false unless consume_end_of_declaration("{", oneof_location)
1350
+ loop do
1351
+ if at_end?
1352
+ record_error("Reached end of input in oneof definition (missing '}').")
1353
+ return false
1354
+ end
1355
+ if looking_at?("option")
1356
+ ok = scoped(oneof_location.child(ONEOF_OPTIONS)) do |location|
1357
+ parse_option(oneof, location, :statement)
1358
+ end
1359
+ return false unless ok
1360
+ else
1361
+ if looking_at?("required") || looking_at?("optional") || looking_at?("repeated")
1362
+ record_error("Fields in oneofs must not have labels (required / optional / repeated).")
1363
+ @lexer.next
1364
+ end
1365
+ field = AST::Field.new
1366
+ scoped(message_location.child(MESSAGE_FIELD, message.field.size)) do |location|
1367
+ message.field << field
1368
+ field.label = :LABEL_OPTIONAL
1369
+ field.oneof_index = index
1370
+ unless parse_message_field_no_label(field, message.nested_type, message_location, MESSAGE_NESTED_TYPE, location)
1371
+ skip_statement
1372
+ end
1373
+ end
1374
+ end
1375
+ break if try_consume_end_of_declaration("}", nil)
1376
+ end
1377
+ true
1378
+ end
1379
+
1380
+ def parse_enum_definition(enum, visibility, enum_location)
1381
+ return false unless consume("enum")
1382
+ scoped(enum_location.child(ENUM_NAME)) do |location|
1383
+ location.record_legacy(enum, :name)
1384
+ name = consume_identifier("Expected enum name.")
1385
+ return false if name.nil?
1386
+ enum.name = name
1387
+ end
1388
+ return false unless parse_enum_block(enum, enum_location)
1389
+ return false unless validate_enum(enum)
1390
+ enum.visibility = visibility if visibility
1391
+ true
1392
+ end
1393
+
1394
+ def parse_enum_block(enum, enum_location)
1395
+ return false unless consume_end_of_declaration("{", enum_location)
1396
+ until try_consume_end_of_declaration("}", nil)
1397
+ if at_end?
1398
+ record_error("Reached end of input in enum definition (missing '}').")
1399
+ return false
1400
+ end
1401
+ skip_statement unless parse_enum_statement(enum, enum_location)
1402
+ end
1403
+ true
1404
+ end
1405
+
1406
+ def parse_enum_statement(enum, enum_location)
1407
+ if try_consume_end_of_declaration(";", nil)
1408
+ true
1409
+ elsif looking_at?("option")
1410
+ scoped(enum_location.child(ENUM_OPTIONS)) { |location| parse_option(enum, location, :statement) }
1411
+ elsif looking_at?("reserved")
1412
+ parse_reserved(enum, enum_location, ENUM_RESERVED_NAME, ENUM_RESERVED_RANGE, enum: true)
1413
+ else
1414
+ value = AST::EnumValue.new
1415
+ scoped(enum_location.child(ENUM_VALUE, enum.value.size)) do |location|
1416
+ enum.value << value
1417
+ parse_enum_constant(value, location)
1418
+ end
1419
+ end
1420
+ end
1421
+
1422
+ def parse_enum_constant(value, value_location)
1423
+ scoped(value_location.child(ENUM_VALUE_NAME)) do |location|
1424
+ location.record_legacy(value, :name)
1425
+ name = consume_identifier("Expected enum constant name.")
1426
+ return false if name.nil?
1427
+ value.name = name
1428
+ end
1429
+ return false unless consume("=", "Missing numeric value for enum constant.")
1430
+ scoped(value_location.child(ENUM_VALUE_NUMBER)) do |location|
1431
+ location.record_legacy(value, :number)
1432
+ number = consume_signed_integer("Expected integer.")
1433
+ return false if number.nil?
1434
+ value.number = number
1435
+ end
1436
+ if looking_at?("[")
1437
+ ok = scoped(value_location.child(ENUM_VALUE_OPTIONS)) do |location|
1438
+ return false unless consume("[")
1439
+ loop do
1440
+ return false unless parse_option(value, location, :assignment)
1441
+ break unless try_consume(",")
1442
+ end
1443
+ consume("]")
1444
+ end
1445
+ return false unless ok
1446
+ end
1447
+ consume_end_of_declaration(";", value_location)
1448
+ end
1449
+
1450
+ def parse_service_definition(service, service_location)
1451
+ return false unless consume("service")
1452
+ scoped(service_location.child(SERVICE_NAME)) do |location|
1453
+ location.record_legacy(service, :name)
1454
+ name = consume_identifier("Expected service name.")
1455
+ return false if name.nil?
1456
+ service.name = name
1457
+ end
1458
+ return false unless consume_end_of_declaration("{", service_location)
1459
+ until try_consume_end_of_declaration("}", nil)
1460
+ if at_end?
1461
+ record_error("Reached end of input in service definition (missing '}').")
1462
+ return false
1463
+ end
1464
+ skip_statement unless parse_service_statement(service, service_location)
1465
+ end
1466
+ true
1467
+ end
1468
+
1469
+ def parse_service_statement(service, service_location)
1470
+ if try_consume_end_of_declaration(";", nil)
1471
+ true
1472
+ elsif looking_at?("option")
1473
+ scoped(service_location.child(SERVICE_OPTIONS)) { |location| parse_option(service, location, :statement) }
1474
+ else
1475
+ method = AST::Method.new
1476
+ scoped(service_location.child(SERVICE_METHOD, service.rpcs.size)) do |location|
1477
+ service.rpcs << method
1478
+ parse_service_method(method, location)
1479
+ end
1480
+ end
1481
+ end
1482
+
1483
+ def parse_service_method(method, method_location)
1484
+ return false unless consume("rpc")
1485
+ scoped(method_location.child(METHOD_NAME)) do |location|
1486
+ location.record_legacy(method, :name)
1487
+ name = consume_identifier("Expected method name.")
1488
+ return false if name.nil?
1489
+ method.name = name
1490
+ end
1491
+ return false unless consume("(")
1492
+ if looking_at?("stream")
1493
+ scoped(method_location.child(METHOD_CLIENT_STREAMING)) do |location|
1494
+ location.record_legacy(method, :other)
1495
+ method.client_streaming = true
1496
+ return false unless consume("stream")
1497
+ end
1498
+ end
1499
+ scoped(method_location.child(METHOD_INPUT_TYPE)) do |location|
1500
+ location.record_legacy(method, :input_type)
1501
+ type = parse_user_defined_type
1502
+ return false if type.nil?
1503
+ method.input_type = type
1504
+ end
1505
+ return false unless consume(")")
1506
+ return false unless consume("returns")
1507
+ return false unless consume("(")
1508
+ if looking_at?("stream")
1509
+ scoped(method_location.child(METHOD_SERVER_STREAMING)) do |location|
1510
+ location.record_legacy(method, :other)
1511
+ return false unless consume("stream")
1512
+ method.server_streaming = true
1513
+ end
1514
+ end
1515
+ scoped(method_location.child(METHOD_OUTPUT_TYPE)) do |location|
1516
+ location.record_legacy(method, :output_type)
1517
+ type = parse_user_defined_type
1518
+ return false if type.nil?
1519
+ method.output_type = type
1520
+ end
1521
+ return false unless consume(")")
1522
+ if looking_at?("{")
1523
+ parse_method_options(method, method_location)
1524
+ else
1525
+ consume_end_of_declaration(";", method_location)
1526
+ end
1527
+ end
1528
+
1529
+ def parse_method_options(method, parent_location)
1530
+ consume_end_of_declaration("{", parent_location)
1531
+ until try_consume_end_of_declaration("}", nil)
1532
+ if at_end?
1533
+ record_error("Reached end of input in method options (missing '}').")
1534
+ return false
1535
+ end
1536
+ next if try_consume_end_of_declaration(";", nil)
1537
+ ok = scoped(parent_location.child(METHOD_OPTIONS)) do |location|
1538
+ parse_option(method, location, :statement)
1539
+ end
1540
+ skip_statement unless ok
1541
+ end
1542
+ true
1543
+ end
1544
+
1545
+ # @return [Symbol, nil]
1546
+ def parse_label(field_location)
1547
+ return nil unless looking_at?("optional") || looking_at?("repeated") || looking_at?("required")
1548
+ if looking_at?("optional") && @syntax_identifier == "editions"
1549
+ record_error("Label \"optional\" is not supported in editions. By default, all singular fields have " \
1550
+ "presence unless features.field_presence is set.")
1551
+ end
1552
+ if looking_at?("required") && @syntax_identifier == "editions"
1553
+ record_error("Label \"required\" is not supported in editions, use features.field_presence = LEGACY_REQUIRED.")
1554
+ end
1555
+ scoped(field_location.child(FIELD_LABEL)) do
1556
+ if try_consume("optional")
1557
+ :LABEL_OPTIONAL
1558
+ elsif try_consume("repeated")
1559
+ :LABEL_REPEATED
1560
+ else
1561
+ consume("required")
1562
+ :LABEL_REQUIRED
1563
+ end
1564
+ end
1565
+ end
1566
+
1567
+ def fixed_wire_for_int_types?
1568
+ @syntax_identifier == "editions" && @edition == :EDITION_UNSTABLE
1569
+ end
1570
+
1571
+ # @return [Array(Symbol, String), nil] the type, or "" and a type name
1572
+ def parse_type
1573
+ table = fixed_wire_for_int_types? ? UNSTABLE_TYPES : TYPES
1574
+ text = @lexer.current.text
1575
+ if (type = table[text])
1576
+ if @syntax_identifier == "editions" && type == :TYPE_GROUP
1577
+ record_error("Group syntax is no longer supported in editions. To get group behavior you can specify " \
1578
+ "features.message_encoding = DELIMITED on a message field.")
1579
+ end
1580
+ @lexer.next
1581
+ [type, ""]
1582
+ elsif fixed_wire_for_int_types? && (replacement = UNSTABLE_REPLACEMENTS[text])
1583
+ record_error("Type '#{text}' is obsolete in unstable editions, use '#{replacement}' instead.")
1584
+ @lexer.next
1585
+ [:TYPE_INT32, ""]
1586
+ else
1587
+ name = parse_user_defined_type
1588
+ name && [:TYPE_INT32, name]
1589
+ end
1590
+ end
1591
+
1592
+ # @return [String, nil]
1593
+ def parse_user_defined_type
1594
+ if TYPES.key?(@lexer.current.text)
1595
+ record_error("Expected message type.")
1596
+ name = @lexer.current.text.dup
1597
+ @lexer.next
1598
+ return name
1599
+ end
1600
+ name = +""
1601
+ name << "." if try_consume(".")
1602
+ id = consume_identifier("Expected type name.")
1603
+ return nil if id.nil?
1604
+ name << id
1605
+ while try_consume(".")
1606
+ name << "."
1607
+ id = consume_identifier("Expected identifier.")
1608
+ return nil if id.nil?
1609
+ name << id
1610
+ end
1611
+ name
1612
+ end
1613
+
1614
+ def parse_package(file, root)
1615
+ if file.package
1616
+ record_error("Multiple package definitions.")
1617
+ file.package = nil
1618
+ end
1619
+ scoped(root.child(FILE_PACKAGE)) do |location|
1620
+ location.record_legacy(file, :name)
1621
+ return false unless consume("package")
1622
+ package = +""
1623
+ file.package = package
1624
+ loop do
1625
+ id = consume_identifier("Expected identifier.")
1626
+ return false if id.nil?
1627
+ package << id
1628
+ break unless try_consume(".")
1629
+ package << "."
1630
+ end
1631
+ consume_end_of_declaration(";", location)
1632
+ end
1633
+ end
1634
+
1635
+ def parse_import(file, root)
1636
+ import_start = @lexer.current.dup
1637
+ return false unless consume("import")
1638
+ if looking_at?("option")
1639
+ record_error("option import is not supported before edition 2024.") if edition_number < 1001
1640
+ return scoped(root.child(FILE_OPTION_DEPENDENCY, file.option_dependency.size)) do |location|
1641
+ location.start_at(import_start)
1642
+ return false unless consume("option")
1643
+ name = consume_string("Expected a string naming the file to import.")
1644
+ return false if name.nil?
1645
+ name = name.force_encoding(Encoding::UTF_8)
1646
+ file.option_dependency << name
1647
+ file.import_positions[name] = location.start
1648
+ consume_end_of_declaration(";", location)
1649
+ end
1650
+ end
1651
+ scoped(root.child(FILE_DEPENDENCY, file.dependency.size)) do |location|
1652
+ location.start_at(import_start)
1653
+ unless file.option_dependency.empty?
1654
+ record_error("imports should precede any option imports to ensure proto files can roundtrip.")
1655
+ end
1656
+ if looking_at?("public")
1657
+ scoped(root.child(FILE_PUBLIC_DEPENDENCY, file.public_dependency.size)) do
1658
+ return false unless consume("public")
1659
+ file.public_dependency << file.dependency.size
1660
+ end
1661
+ elsif looking_at?("weak")
1662
+ if edition_number >= 1001
1663
+ record_error("weak import is not supported in edition 2024 and above. Consider using option import instead.")
1664
+ end
1665
+ scoped(root.child(FILE_WEAK_DEPENDENCY, file.weak_dependency.size)) do |weak_location|
1666
+ file.import_positions["weak"] = weak_location.start
1667
+ return false unless consume("weak")
1668
+ file.weak_dependency << file.dependency.size
1669
+ end
1670
+ end
1671
+ name = consume_string("Expected a string naming the file to import.")
1672
+ return false if name.nil?
1673
+ name = name.force_encoding(Encoding::UTF_8)
1674
+ file.dependency << name
1675
+ file.import_positions[name] = location.start
1676
+ consume_end_of_declaration(";", location)
1677
+ end
1678
+ end
1679
+
1680
+ # @return [Array(Boolean, Symbol)] success and the visibility, if any
1681
+ def parse_visibility(file)
1682
+ return [true, nil] if Schema::Features::EDITIONS.fetch(file.edition, 0) <= 1000
1683
+ visibility = if try_consume("export")
1684
+ :VISIBILITY_EXPORT
1685
+ elsif try_consume("local")
1686
+ :VISIBILITY_LOCAL
1687
+ end
1688
+ if visibility && !looking_at?("message") && !looking_at?("enum")
1689
+ record_error("'local' and 'export' visibility modifiers are valid only on 'message' and 'enum'")
1690
+ return [false, nil]
1691
+ end
1692
+ [true, visibility]
1693
+ end
1694
+ end
1695
+ end
1696
+ end