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,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
|