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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +60 -0
- data/LICENSE.txt +21 -0
- data/README.md +647 -0
- data/THIRD_PARTY_NOTICES.md +61 -0
- data/exe/protoc-gen-protostar +7 -0
- data/exe/protostar +7 -0
- data/lib/protostar/builder/generator.rb +71 -0
- data/lib/protostar/builder/sink.rb +35 -0
- data/lib/protostar/builder.rb +965 -0
- data/lib/protostar/cli.rb +267 -0
- data/lib/protostar/codec/decode_generator.rb +199 -0
- data/lib/protostar/codec/decoder.rb +296 -0
- data/lib/protostar/codec/encode_generator.rb +224 -0
- data/lib/protostar/codec/encoder.rb +121 -0
- data/lib/protostar/codec/sizer.rb +91 -0
- data/lib/protostar/codec.rb +15 -0
- data/lib/protostar/compiler/ast.rb +193 -0
- data/lib/protostar/compiler/emitters/descriptor_set.rb +57 -0
- data/lib/protostar/compiler/emitters/google_ruby.rb +103 -0
- data/lib/protostar/compiler/emitters/grpc.rb +175 -0
- data/lib/protostar/compiler/emitters/protostar_rbs.rb +252 -0
- data/lib/protostar/compiler/emitters/rbs.rb +394 -0
- data/lib/protostar/compiler/emitters/ruby.rb +503 -0
- data/lib/protostar/compiler/emitters.rb +268 -0
- data/lib/protostar/compiler/include/LICENSE +32 -0
- data/lib/protostar/compiler/include/google/protobuf/any.proto +106 -0
- data/lib/protostar/compiler/include/google/protobuf/api.proto +229 -0
- data/lib/protostar/compiler/include/google/protobuf/compiler/plugin.proto +180 -0
- data/lib/protostar/compiler/include/google/protobuf/cpp_features.proto +87 -0
- data/lib/protostar/compiler/include/google/protobuf/descriptor.proto +1532 -0
- data/lib/protostar/compiler/include/google/protobuf/duration.proto +115 -0
- data/lib/protostar/compiler/include/google/protobuf/empty.proto +51 -0
- data/lib/protostar/compiler/include/google/protobuf/field_mask.proto +243 -0
- data/lib/protostar/compiler/include/google/protobuf/java_features.proto +132 -0
- data/lib/protostar/compiler/include/google/protobuf/source_context.proto +48 -0
- data/lib/protostar/compiler/include/google/protobuf/struct.proto +111 -0
- data/lib/protostar/compiler/include/google/protobuf/timestamp.proto +145 -0
- data/lib/protostar/compiler/include/google/protobuf/type.proto +217 -0
- data/lib/protostar/compiler/include/google/protobuf/wrappers.proto +157 -0
- data/lib/protostar/compiler/lexer.rb +659 -0
- data/lib/protostar/compiler/linker.rb +894 -0
- data/lib/protostar/compiler/option_interpreter.rb +363 -0
- data/lib/protostar/compiler/parser.rb +1696 -0
- data/lib/protostar/compiler/retention.rb +104 -0
- data/lib/protostar/compiler/run.rb +184 -0
- data/lib/protostar/compiler/source_info.rb +161 -0
- data/lib/protostar/compiler/symbol_table.rb +223 -0
- data/lib/protostar/compiler/text_format.rb +340 -0
- data/lib/protostar/compiler/validator.rb +944 -0
- data/lib/protostar/compiler.rb +304 -0
- data/lib/protostar/diagnostics/codes.rb +1307 -0
- data/lib/protostar/diagnostics/formats.rb +64 -0
- data/lib/protostar/diagnostics/renderer.rb +240 -0
- data/lib/protostar/diagnostics/source.rb +153 -0
- data/lib/protostar/diagnostics.rb +117 -0
- data/lib/protostar/dsl.rb +367 -0
- data/lib/protostar/enum.rb +70 -0
- data/lib/protostar/errors.rb +23 -0
- data/lib/protostar/google/protobuf/any_pb.rb +90 -0
- data/lib/protostar/google/protobuf/api_pb.rb +199 -0
- data/lib/protostar/google/protobuf/compiler/plugin_pb.rb +168 -0
- data/lib/protostar/google/protobuf/descriptor_pb.rb +1239 -0
- data/lib/protostar/google/protobuf/duration_pb.rb +99 -0
- data/lib/protostar/google/protobuf/empty_pb.rb +36 -0
- data/lib/protostar/google/protobuf/field_mask_pb.rb +228 -0
- data/lib/protostar/google/protobuf/source_context_pb.rb +33 -0
- data/lib/protostar/google/protobuf/struct_pb.rb +91 -0
- data/lib/protostar/google/protobuf/timestamp_pb.rb +129 -0
- data/lib/protostar/google/protobuf/type_pb.rb +202 -0
- data/lib/protostar/google/protobuf/wrappers_pb.rb +125 -0
- data/lib/protostar/google_protobuf_shim.rb +24 -0
- data/lib/protostar/json/encoder.rb +340 -0
- data/lib/protostar/json/errors.rb +34 -0
- data/lib/protostar/json/parser.rb +437 -0
- data/lib/protostar/json/path.rb +39 -0
- data/lib/protostar/json.rb +95 -0
- data/lib/protostar/lint/command.rb +197 -0
- data/lib/protostar/lint/config.rb +98 -0
- data/lib/protostar/lint/context.rb +126 -0
- data/lib/protostar/lint/directives.rb +56 -0
- data/lib/protostar/lint/fixer.rb +165 -0
- data/lib/protostar/lint/naming.rb +54 -0
- data/lib/protostar/lint/rule.rb +120 -0
- data/lib/protostar/lint/rules/comment_enum.rb +31 -0
- data/lib/protostar/lint/rules/comment_enum_value.rb +32 -0
- data/lib/protostar/lint/rules/comment_field.rb +33 -0
- data/lib/protostar/lint/rules/comment_message.rb +31 -0
- data/lib/protostar/lint/rules/comment_rpc.rb +31 -0
- data/lib/protostar/lint/rules/comment_service.rb +31 -0
- data/lib/protostar/lint/rules/edition_recommended.rb +31 -0
- data/lib/protostar/lint/rules/enum_first_value_zero.rb +34 -0
- data/lib/protostar/lint/rules/enum_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/enum_value_prefix.rb +38 -0
- data/lib/protostar/lint/rules/enum_value_upper_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/enum_zero_value_suffix.rb +41 -0
- data/lib/protostar/lint/rules/field_lower_snake_case.rb +34 -0
- data/lib/protostar/lint/rules/field_not_required.rb +39 -0
- data/lib/protostar/lint/rules/field_number_reuse_risk.rb +45 -0
- data/lib/protostar/lint/rules/field_numbers_hot_range.rb +44 -0
- data/lib/protostar/lint/rules/file_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/import_no_public.rb +29 -0
- data/lib/protostar/lint/rules/import_no_weak.rb +28 -0
- data/lib/protostar/lint/rules/import_used.rb +33 -0
- data/lib/protostar/lint/rules/message_pascal_case.rb +27 -0
- data/lib/protostar/lint/rules/no_deprecated_features.rb +50 -0
- data/lib/protostar/lint/rules/no_groups.rb +34 -0
- data/lib/protostar/lint/rules/oneof_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/package_defined.rb +28 -0
- data/lib/protostar/lint/rules/package_directory_match.rb +33 -0
- data/lib/protostar/lint/rules/package_lower_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/package_same_directory.rb +39 -0
- data/lib/protostar/lint/rules/package_version_suffix.rb +31 -0
- data/lib/protostar/lint/rules/reserved_names_with_numbers.rb +44 -0
- data/lib/protostar/lint/rules/rpc_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/rpc_request_response_unique.rb +40 -0
- data/lib/protostar/lint/rules/rpc_request_standard_name.rb +32 -0
- data/lib/protostar/lint/rules/rpc_response_standard_name.rb +31 -0
- data/lib/protostar/lint/rules/service_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/service_suffix.rb +28 -0
- data/lib/protostar/lint/rules/syntax_specified.rb +35 -0
- data/lib/protostar/lint/runner.rb +135 -0
- data/lib/protostar/lint.rb +122 -0
- data/lib/protostar/message/accessors.rb +86 -0
- data/lib/protostar/message/coercion.rb +171 -0
- data/lib/protostar/message/field_info.rb +145 -0
- data/lib/protostar/message/reflection.rb +149 -0
- data/lib/protostar/message.rb +572 -0
- data/lib/protostar/plugin.rb +94 -0
- data/lib/protostar/schema/default_value.rb +113 -0
- data/lib/protostar/schema/descriptor.rb +41 -0
- data/lib/protostar/schema/draft/copy.rb +88 -0
- data/lib/protostar/schema/draft/resolution.rb +177 -0
- data/lib/protostar/schema/draft.rb +779 -0
- data/lib/protostar/schema/enum.rb +106 -0
- data/lib/protostar/schema/extension_range.rb +37 -0
- data/lib/protostar/schema/features.rb +153 -0
- data/lib/protostar/schema/field.rb +201 -0
- data/lib/protostar/schema/file.rb +217 -0
- data/lib/protostar/schema/freezer.rb +61 -0
- data/lib/protostar/schema/message.rb +104 -0
- data/lib/protostar/schema/oneof.rb +38 -0
- data/lib/protostar/schema/options.rb +367 -0
- data/lib/protostar/schema/pool.rb +179 -0
- data/lib/protostar/schema/proto.rb +576 -0
- data/lib/protostar/schema/resolver.rb +118 -0
- data/lib/protostar/schema/service.rb +89 -0
- data/lib/protostar/schema/source_info.rb +53 -0
- data/lib/protostar/schema.rb +65 -0
- data/lib/protostar/short_names/any.rb +7 -0
- data/lib/protostar/short_names/duration.rb +7 -0
- data/lib/protostar/short_names/empty.rb +7 -0
- data/lib/protostar/short_names/field_mask.rb +7 -0
- data/lib/protostar/short_names/struct.rb +9 -0
- data/lib/protostar/short_names/timestamp.rb +7 -0
- data/lib/protostar/short_names/wrappers.rb +15 -0
- data/lib/protostar/text/extensions.rb +114 -0
- data/lib/protostar/text/parser.rb +439 -0
- data/lib/protostar/text/printer.rb +304 -0
- data/lib/protostar/text/tokenizer.rb +116 -0
- data/lib/protostar/text.rb +67 -0
- data/lib/protostar/types.rb +152 -0
- data/lib/protostar/unknown_field.rb +58 -0
- data/lib/protostar/version.rb +5 -0
- data/lib/protostar/view/generator.rb +298 -0
- data/lib/protostar/view/map.rb +194 -0
- data/lib/protostar/view/repeated.rb +143 -0
- data/lib/protostar/view/scan.rb +183 -0
- data/lib/protostar/view.rb +283 -0
- data/lib/protostar/well_known/any.rb +52 -0
- data/lib/protostar/well_known/duration.rb +53 -0
- data/lib/protostar/well_known/list_value.rb +63 -0
- data/lib/protostar/well_known/struct.rb +55 -0
- data/lib/protostar/well_known/timestamp.rb +64 -0
- data/lib/protostar/well_known/value.rb +60 -0
- data/lib/protostar/well_known.rb +38 -0
- data/lib/protostar/wire/io_source.rb +40 -0
- data/lib/protostar/wire/reader.rb +254 -0
- data/lib/protostar/wire/writer.rb +201 -0
- data/lib/protostar/wire.rb +96 -0
- data/lib/protostar.rb +95 -0
- data/shims/google-protobuf/lib/google/protobuf/any_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/api_pb.rb +22 -0
- data/shims/google-protobuf/lib/google/protobuf/compiler/plugin_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pb.rb +72 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pool.rb +164 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptors.rb +553 -0
- data/shims/google-protobuf/lib/google/protobuf/duration_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/empty_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/field_mask_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/convert.rb +180 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/extensions.rb +154 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/json.rb +104 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/well_known.rb +140 -0
- data/shims/google-protobuf/lib/google/protobuf/map.rb +216 -0
- data/shims/google-protobuf/lib/google/protobuf/message.rb +544 -0
- data/shims/google-protobuf/lib/google/protobuf/message_exts.rb +41 -0
- data/shims/google-protobuf/lib/google/protobuf/plugin_pb.rb +4 -0
- data/shims/google-protobuf/lib/google/protobuf/repeated_field.rb +366 -0
- data/shims/google-protobuf/lib/google/protobuf/source_context_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/struct_pb.rb +20 -0
- data/shims/google-protobuf/lib/google/protobuf/timestamp_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/type_pb.rb +27 -0
- data/shims/google-protobuf/lib/google/protobuf/well_known_types.rb +37 -0
- data/shims/google-protobuf/lib/google/protobuf/wrappers_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf.rb +107 -0
- data/shims/google-protobuf/lib/google/protobuf_ffi.rb +5 -0
- data/shims/google-protobuf/lib/google/protobuf_native.rb +5 -0
- data/sig/protostar/google/protobuf/any_pb.rbs +77 -0
- data/sig/protostar/google/protobuf/api_pb.rbs +242 -0
- data/sig/protostar/google/protobuf/compiler/plugin_pb.rbs +264 -0
- data/sig/protostar/google/protobuf/descriptor_pb.rbs +2220 -0
- data/sig/protostar/google/protobuf/duration_pb.rbs +82 -0
- data/sig/protostar/google/protobuf/empty_pb.rbs +18 -0
- data/sig/protostar/google/protobuf/field_mask_pb.rbs +170 -0
- data/sig/protostar/google/protobuf/source_context_pb.rbs +24 -0
- data/sig/protostar/google/protobuf/struct_pb.rbs +135 -0
- data/sig/protostar/google/protobuf/timestamp_pb.rbs +102 -0
- data/sig/protostar/google/protobuf/type_pb.rbs +304 -0
- data/sig/protostar/google/protobuf/wrappers_pb.rbs +169 -0
- data/sig/protostar/lint.rbs +216 -0
- data/sig/protostar.rbs +326 -0
- 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
|