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,439 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Protostar
4
+ module Text
5
+ # Reads protoc's text format into a message, with protoc's grammar and
6
+ # its checks: non-repeated fields and oneofs set at most once, enum
7
+ # names and numbers known to closed enums, integers in range.
8
+ # @api private
9
+ class Parser
10
+ F = Message::FieldInfo
11
+ RANGES = Message::Coercion::RANGES
12
+ CLOSE = {"{" => "}", "<" => ">"}.freeze
13
+ TRUE_WORDS = %w[true True t].freeze
14
+ FALSE_WORDS = %w[false False f].freeze
15
+ SIMPLE_ESCAPES = {
16
+ "a" => "\a", "b" => "\b", "f" => "\f", "n" => "\n", "r" => "\r", "t" => "\t", "v" => "\v",
17
+ "\\" => "\\", "'" => "'", "\"" => "\"", "?" => "?"
18
+ }.freeze
19
+ ESCAPE = /\\(?:([0-7]{1,3})|x(\h{1,2})|u(\h{4})|U(\h{8})|(.))/mn
20
+ ANY = "google.protobuf.Any"
21
+ UNSIGNED = [F::UINT32, F::UINT64, F::FIXED32, F::FIXED64].freeze
22
+
23
+ def initialize(text, allow_unknown_fields: false, allow_partial: true, registry: nil, pool: nil)
24
+ @tokens = Tokenizer.new(text)
25
+ @allow_unknown = allow_unknown_fields
26
+ @allow_partial = allow_partial
27
+ @registry = registry
28
+ @extensions = Extensions.new(registry, pool)
29
+ end
30
+
31
+ # @param klass [Class]
32
+ # @return [Message]
33
+ def parse(klass)
34
+ msg = klass.new
35
+ message_body(msg, nil)
36
+ if !@allow_partial && !msg.initialized?
37
+ @tokens.fail!("message of type #{klass.descriptor.full_name} is missing required fields", @tokens.peek.pos)
38
+ end
39
+ msg
40
+ end
41
+
42
+ private
43
+
44
+ def fail!(message, token = @tokens.peek)
45
+ @tokens.fail!(message, token.pos)
46
+ end
47
+
48
+ def accept(text)
49
+ t = @tokens.peek
50
+ return false unless t.type == :symbol && t.text == text
51
+ @tokens.next
52
+ true
53
+ end
54
+
55
+ def expect(text)
56
+ accept(text) || fail!("expected \"#{text}\", got #{shown(@tokens.peek)}")
57
+ end
58
+
59
+ def shown(token)
60
+ (token.type == :eof) ? "end of input" : "\"#{token.text}\""
61
+ end
62
+
63
+ # Fields up to the closing delimiter, or the end of input when there is none
64
+ def message_body(msg, close)
65
+ seen = {}
66
+ extensions = nil
67
+ loop do
68
+ t = @tokens.peek
69
+ if close.nil? ? t.type == :eof : (t.type == :symbol && t.text == close)
70
+ @tokens.next if close
71
+ break
72
+ end
73
+ fail!("expected \"#{close}\", got end of input") if t.type == :eof
74
+ if accept("[")
75
+ name, name_token = bracketed_name
76
+ if name.include?("/")
77
+ any_expansion(msg, name, name_token)
78
+ else
79
+ extensions ||= {}
80
+ extension(msg, name, name_token, extensions)
81
+ end
82
+ else
83
+ field(msg, seen)
84
+ end
85
+ accept(";") || accept(",")
86
+ end
87
+ add_extensions(msg, extensions) if extensions
88
+ end
89
+
90
+ # The text between `[` and `]` as protoc reads it: an extension's full
91
+ # name, or an Any's type URL whose prefix may hold any URL characters
92
+ def bracketed_name
93
+ name, pos = @tokens.url_chars
94
+ start = Tokenizer::Token.new(:identifier, name, pos)
95
+ t = @tokens.peek
96
+ fail!("unexpected #{shown(t)} in a bracketed name", t) unless t.type == :symbol && t.text == "]"
97
+ @tokens.next
98
+ slash = name.rindex("/")
99
+ type_name = slash ? name[(slash + 1)..] : name
100
+ if slash
101
+ fail!("type URL prefix is empty", start) if slash == 0
102
+ bad = name[0..slash].scan(/%.{0,2}/).find { |e| !/\A%\h\h\z/.match?(e) }
103
+ fail!("invalid percent escape \"#{bad}\" in a type URL", start) if bad
104
+ end
105
+ unless type_name.split(".", -1).all? { |part| /\A[A-Za-z_][A-Za-z0-9_]*\z/.match?(part) }
106
+ fail!("invalid type name \"#{type_name}\" in brackets", start)
107
+ end
108
+ [name, start]
109
+ end
110
+
111
+ def field(msg, seen)
112
+ t = @tokens.next
113
+ fail!("expected a field name, got #{shown(t)}", t) unless t.type == :identifier
114
+ klass = msg.class
115
+ info = lookup(klass, t.text)
116
+ unless info
117
+ # protoc skips reserved names as it skips unknown fields when told to
118
+ unless @allow_unknown || klass.descriptor.reserved_names.include?(t.text)
119
+ fail!("message type \"#{klass.descriptor.full_name}\" has no field named \"#{t.text}\"", t)
120
+ end
121
+ return skip_value
122
+ end
123
+ if !info.repeated && !info.map
124
+ if seen[info]
125
+ fail!("non-repeated field \"#{info.name}\" is specified multiple times", t)
126
+ end
127
+ if (oneof = info.descriptor.real_oneof) && (other = seen[oneof])
128
+ fail!("field \"#{info.name}\" is specified along with field \"#{other.name}\", another member of oneof \"#{oneof.name}\"", t)
129
+ end
130
+ seen[info] = true
131
+ seen[oneof] = info if oneof
132
+ end
133
+ values = field_values(info, klass)
134
+ assign(msg, info, values, t)
135
+ end
136
+
137
+ # A field by name; groups also by their type name, as protoc prints them
138
+ def lookup(klass, name)
139
+ info = klass.__by_name[name]
140
+ return info if info
141
+ info = klass.__by_name[name.downcase]
142
+ info if info && Printer.group_like?(info) && info.descriptor.message_type.name == name
143
+ end
144
+
145
+ # The value or values after a field name: `: scalar`, `: [list]`, `{ message }`
146
+ def field_values(info, klass)
147
+ if info.message?
148
+ colon = accept(":")
149
+ return list { message_value(info) } if (info.repeated || info.map) && accept("[")
150
+ fail!("expected \"{\" or \"<\" after \"#{info.name}#{":" if colon}\", got #{shown(@tokens.peek)}") unless open?
151
+ [message_value(info)]
152
+ else
153
+ expect(":")
154
+ if info.repeated && accept("[")
155
+ list { scalar(info) }
156
+ else
157
+ [scalar(info)]
158
+ end
159
+ end
160
+ end
161
+
162
+ def open?
163
+ t = @tokens.peek
164
+ t.type == :symbol && CLOSE.key?(t.text)
165
+ end
166
+
167
+ def list
168
+ out = []
169
+ return out if accept("]")
170
+ loop do
171
+ out << yield
172
+ break if accept("]")
173
+ expect(",")
174
+ end
175
+ out
176
+ end
177
+
178
+ def assign(msg, info, values, token)
179
+ if info.map
180
+ hash = msg.__values[info.index] || {}
181
+ values.each { |entry| hash[entry.first] = entry.last }
182
+ store(msg, info, hash, token)
183
+ elsif info.repeated
184
+ store(msg, info, (msg.__values[info.index] || []) + values, token)
185
+ else
186
+ store(msg, info, values.first, token)
187
+ end
188
+ end
189
+
190
+ def store(msg, info, value, token)
191
+ msg.__set(info.index, value)
192
+ rescue Protostar::TypeError => e
193
+ fail!(e.message, token)
194
+ end
195
+
196
+ def message_value(info)
197
+ t = @tokens.next
198
+ close = CLOSE[t.text] if t.type == :symbol
199
+ fail!("expected \"{\" or \"<\", got #{shown(t)}", t) unless close
200
+ if info.map
201
+ map_entry(info, close)
202
+ else
203
+ msg = info.message_class.new
204
+ message_body(msg, close)
205
+ msg
206
+ end
207
+ end
208
+
209
+ # A map entry as a message with `key` and `value`; either may be left out
210
+ def map_entry(info, close)
211
+ key = nil
212
+ value = nil
213
+ until accept(close)
214
+ t = @tokens.next
215
+ fail!("expected \"#{close}\", got end of input", t) if t.type == :eof
216
+ case t.text
217
+ when "key"
218
+ expect(":")
219
+ key = scalar(info.key)
220
+ when "value"
221
+ if info.value.message?
222
+ accept(":")
223
+ value = message_value(info.value)
224
+ else
225
+ expect(":")
226
+ value = scalar(info.value)
227
+ end
228
+ else
229
+ fail!("map entry has no field named \"#{t.text}\"", t)
230
+ end
231
+ accept(";") || accept(",")
232
+ end
233
+ key = info.key.default if key.nil?
234
+ value = (info.value.message? ? info.value.message_class.new : info.value.default) if value.nil?
235
+ [key, value]
236
+ end
237
+
238
+ def scalar(info)
239
+ case info.kind
240
+ when F::DOUBLE, F::FLOAT then float(info)
241
+ when F::BOOL then bool
242
+ when F::STRING, F::BYTES then string(info)
243
+ when F::ENUM then enum(info)
244
+ else integer(info)
245
+ end
246
+ end
247
+
248
+ def integer(info)
249
+ negative = !UNSIGNED.include?(info.kind) && accept("-")
250
+ t = @tokens.next
251
+ fail!("expected an integer, got #{shown(t)}", t) unless t.type == :integer
252
+ n = integer_value(t)
253
+ n = -n if negative
254
+ range = RANGES.fetch(info.kind)
255
+ fail!("integer out of range (#{"-" if negative}#{t.text})", t) unless range.cover?(n)
256
+ n
257
+ end
258
+
259
+ def integer_value(t)
260
+ text = t.text
261
+ if text.start_with?("0x", "0X")
262
+ text[2..].to_i(16)
263
+ elsif text.length > 1 && text.start_with?("0")
264
+ fail!("numbers starting with a leading zero must be in octal", t) unless /\A[0-7]+\z/.match?(text)
265
+ text.to_i(8)
266
+ else
267
+ text.to_i
268
+ end
269
+ end
270
+
271
+ def float(info)
272
+ negative = accept("-")
273
+ t = @tokens.next
274
+ f = case t.type
275
+ when :float then Float(t.text.delete_suffix("f").delete_suffix("F"))
276
+ when :integer
277
+ fail!("expected a decimal number, got \"#{t.text}\"", t) if t.text.length > 1 && t.text.start_with?("0")
278
+ Float(t.text)
279
+ when :identifier
280
+ case t.text.downcase
281
+ when "inf", "infinity" then Float::INFINITY
282
+ when "nan" then Float::NAN
283
+ else fail!("expected a number, got \"#{t.text}\"", t)
284
+ end
285
+ else fail!("expected a number, got #{shown(t)}", t)
286
+ end
287
+ f = -f if negative
288
+ (info.kind == F::FLOAT) ? Message::Coercion.float32(f) : f
289
+ end
290
+
291
+ def bool
292
+ t = @tokens.next
293
+ if t.type == :identifier
294
+ return true if TRUE_WORDS.include?(t.text)
295
+ return false if FALSE_WORDS.include?(t.text)
296
+ elsif t.type == :integer
297
+ return true if t.text == "1"
298
+ return false if t.text == "0"
299
+ end
300
+ fail!("invalid value for a boolean field: #{shown(t)}", t)
301
+ end
302
+
303
+ # One or more adjacent string literals, concatenated
304
+ def string(info)
305
+ t = @tokens.next
306
+ fail!("expected a string, got #{shown(t)}", t) unless t.type == :string
307
+ out = unescape(t)
308
+ out << unescape(@tokens.next) while @tokens.peek.type == :string
309
+ if info.kind == F::STRING
310
+ out.force_encoding(Encoding::UTF_8)
311
+ fail!("string is not valid UTF-8", t) unless out.valid_encoding?
312
+ end
313
+ out
314
+ end
315
+
316
+ def unescape(t)
317
+ body = t.text[1..-2].b
318
+ out = String.new(encoding: Encoding::BINARY)
319
+ out << body.gsub(ESCAPE) do
320
+ m = $~
321
+ if m[1] then (m[1].to_i(8) & 0xFF).chr
322
+ elsif m[2] then m[2].to_i(16).chr
323
+ elsif m[3] || m[4] then codepoint((m[3] || m[4]).to_i(16), t)
324
+ else
325
+ SIMPLE_ESCAPES.fetch(m[5]) { fail!("invalid escape sequence \"\\#{m[5]}\"", t) }.b
326
+ end
327
+ end
328
+ out
329
+ end
330
+
331
+ def codepoint(n, t)
332
+ fail!("invalid Unicode escape", t) if n > 0x10FFFF
333
+ # protoc writes surrogates as three CESU-8 bytes; the conformance suite
334
+ # wants them refused, paired or not, and they have no UTF-8 form
335
+ fail!("Unicode escape of surrogate U+#{n.to_s(16).upcase}", t) if (0xD800..0xDFFF).cover?(n)
336
+ [n].pack("U").b
337
+ end
338
+
339
+ def enum(info)
340
+ negative = accept("-")
341
+ t = @tokens.next
342
+ if t.type == :identifier && !negative
343
+ number = info.enum_numbers[t.text.to_sym]
344
+ return info.enum_names.fetch(number) if number
345
+ fail!("unknown enumeration value of \"#{t.text}\" for field \"#{info.name}\"", t)
346
+ elsif t.type == :integer
347
+ n = integer_value(t)
348
+ n = -n if negative
349
+ name = info.enum_names[n]
350
+ return name if name
351
+ if info.closed_enum || !Message::Coercion::ENUM_RANGE.cover?(n)
352
+ fail!("unknown enumeration value of \"#{"-" if negative}#{t.text}\" for field \"#{info.name}\"", t)
353
+ end
354
+ n
355
+ else
356
+ fail!("expected an enum name or number, got #{shown(t)}", t)
357
+ end
358
+ end
359
+
360
+ def any_expansion(msg, url, token)
361
+ fail!("\"[#{url}]\" expands an Any, but this is a #{msg.class.descriptor.full_name}", token) unless msg.class.descriptor.full_name == ANY
362
+ name = url[(url.rindex("/") + 1)..]
363
+ klass = Types.find(name, registry: @registry || Types.registry)
364
+ fail!("could not find the type \"#{name}\" of an Any", token) unless klass.is_a?(Class)
365
+ accept(":")
366
+ t = @tokens.next
367
+ close = CLOSE[t.text] if t.type == :symbol
368
+ fail!("expected \"{\" or \"<\", got #{shown(t)}", t) unless close
369
+ inner = klass.new
370
+ message_body(inner, close)
371
+ msg.type_url = url
372
+ msg.value = inner.encode
373
+ end
374
+
375
+ def extension(msg, name, token, collected)
376
+ field = @extensions.named(msg.class.descriptor, name)
377
+ info = field && @extensions.info(field)
378
+ unless info
379
+ fail!("extension \"#{name}\" is not known to #{msg.class.descriptor.full_name}", token) unless @allow_unknown
380
+ return skip_value
381
+ end
382
+ if !info.repeated && collected.key?(info)
383
+ fail!("non-repeated field \"#{name}\" is specified multiple times", token)
384
+ end
385
+ (collected[info] ||= []).concat(field_values(info, msg.class))
386
+ end
387
+
388
+ # Extensions go where the binary codec keeps them, in the unknown fields,
389
+ # in number order as protoc writes them
390
+ def add_extensions(msg, collected)
391
+ collected.sort_by { |info, _| info.number }.each do |info, values|
392
+ @extensions.write(info, values).each { |u| msg.__add_unknown(u) }
393
+ end
394
+ msg.__seal_unknown
395
+ end
396
+
397
+ # Skips the value of a field nobody knows: a scalar, a list or a message
398
+ def skip_value
399
+ if accept(":")
400
+ if accept("[")
401
+ list { skip_single }
402
+ else
403
+ skip_single
404
+ end
405
+ else
406
+ skip_message
407
+ end
408
+ end
409
+
410
+ def skip_single
411
+ return skip_message if open?
412
+ accept("-")
413
+ t = @tokens.next
414
+ case t.type
415
+ when :string then @tokens.next while @tokens.peek.type == :string
416
+ when :identifier, :integer, :float then nil
417
+ else fail!("unexpected #{shown(t)}", t)
418
+ end
419
+ end
420
+
421
+ def skip_message
422
+ t = @tokens.next
423
+ close = CLOSE[t.text] if t.type == :symbol
424
+ fail!("expected \"{\" or \"<\", got #{shown(t)}", t) unless close
425
+ until accept(close)
426
+ n = @tokens.next
427
+ fail!("expected \"#{close}\", got end of input", n) if n.type == :eof
428
+ if n.type == :symbol && n.text == "["
429
+ @tokens.next until accept("]") || @tokens.peek.type == :eof
430
+ elsif n.type != :identifier
431
+ fail!("expected a field name, got #{shown(n)}", n)
432
+ end
433
+ skip_value
434
+ accept(";") || accept(",")
435
+ end
436
+ end
437
+ end
438
+ end
439
+ end