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,659 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
class Compiler
|
|
5
|
+
# protoc's tokenizer (`io/tokenizer.cc`), byte for byte: the same token
|
|
6
|
+
# boundaries, the same 0-based lines and columns (tabs advance to the next
|
|
7
|
+
# multiple of 8), the same error messages and the same comment attachment
|
|
8
|
+
# in {#next_with_comments}.
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
class Lexer
|
|
12
|
+
# A token. The lexer reuses its current token as it advances, as
|
|
13
|
+
# protoc's Tokenizer does, and copies it into {Lexer#previous}.
|
|
14
|
+
class Token
|
|
15
|
+
# @return [Symbol] :start, :identifier, :integer, :float, :string, :symbol, :end,
|
|
16
|
+
# and :whitespace or :newline when asked for
|
|
17
|
+
attr_accessor :type
|
|
18
|
+
# @return [String] the source text, quotes and escapes included for strings
|
|
19
|
+
attr_accessor :text
|
|
20
|
+
# @return [Integer] 0-based
|
|
21
|
+
attr_accessor :line
|
|
22
|
+
# @return [Integer] 0-based, a tab advancing to the next multiple of 8
|
|
23
|
+
attr_accessor :column
|
|
24
|
+
# @return [Integer] 0-based column just after the token
|
|
25
|
+
attr_accessor :end_column
|
|
26
|
+
|
|
27
|
+
def initialize(type:, text:, line:, column:, end_column:)
|
|
28
|
+
@type = type
|
|
29
|
+
@text = text
|
|
30
|
+
@line = line
|
|
31
|
+
@column = column
|
|
32
|
+
@end_column = end_column
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [Array] type, text, line, column and end column
|
|
36
|
+
def to_a
|
|
37
|
+
[@type, @text, @line, @column, @end_column]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def inspect
|
|
41
|
+
"#<#{self.class.name} #{to_a.inspect}>"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
TAB_WIDTH = 8
|
|
46
|
+
|
|
47
|
+
LETTER = Array.new(256) { |b| b.between?(97, 122) || b.between?(65, 90) || b == 95 }.freeze
|
|
48
|
+
DIGIT = Array.new(256) { |b| b.between?(48, 57) }.freeze
|
|
49
|
+
ALNUM = Array.new(256) { |b| LETTER[b] || DIGIT[b] }.freeze
|
|
50
|
+
OCTAL = Array.new(256) { |b| b.between?(48, 55) }.freeze
|
|
51
|
+
HEX = Array.new(256) { |b| DIGIT[b] || b.between?(97, 102) || b.between?(65, 70) }.freeze
|
|
52
|
+
# \t \n \v \f \r and space
|
|
53
|
+
WHITESPACE = Array.new(256) { |b| b == 32 || b.between?(9, 13) }.freeze
|
|
54
|
+
WHITESPACE_NO_NEWLINE = Array.new(256) { |b| WHITESPACE[b] && b != 10 }.freeze
|
|
55
|
+
UNPRINTABLE = Array.new(256) { |b| b >= 1 && b < 32 }.freeze
|
|
56
|
+
ESCAPE = Array.new(256) { |b| 'abfnrtv\\?\'"'.include?(b.chr) }.freeze
|
|
57
|
+
|
|
58
|
+
ESCAPES = {
|
|
59
|
+
"a" => 7, "b" => 8, "f" => 12, "n" => 10, "r" => 13, "t" => 9, "v" => 11,
|
|
60
|
+
"\\" => 92, "?" => 63, "'" => 39, "\"" => 34
|
|
61
|
+
}.freeze
|
|
62
|
+
|
|
63
|
+
# @return [Token]
|
|
64
|
+
attr_reader :current, :previous
|
|
65
|
+
attr_accessor :report_whitespace, :report_newlines
|
|
66
|
+
|
|
67
|
+
# @param source [String] the file's bytes
|
|
68
|
+
# @param errors [#call] receives (line, column, message) for every error
|
|
69
|
+
def initialize(source, errors)
|
|
70
|
+
@src = source.b
|
|
71
|
+
@size = @src.bytesize
|
|
72
|
+
@errors = errors
|
|
73
|
+
@pos = 0
|
|
74
|
+
@line = 0
|
|
75
|
+
@column = 0
|
|
76
|
+
@read_error = @size == 0
|
|
77
|
+
@char = (@size == 0) ? 0 : @src.getbyte(0)
|
|
78
|
+
@record_target = nil
|
|
79
|
+
@record_start = -1
|
|
80
|
+
@report_whitespace = false
|
|
81
|
+
@report_newlines = false
|
|
82
|
+
@current = Token.new(type: :start, text: +"", line: 0, column: 0, end_column: 0)
|
|
83
|
+
@previous = @current.dup
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Advances to the next token, skipping whitespace and comments
|
|
87
|
+
# @return [Boolean] false at the end of input
|
|
88
|
+
def next
|
|
89
|
+
@previous = @current.dup
|
|
90
|
+
until @read_error
|
|
91
|
+
start_token
|
|
92
|
+
report = try_consume_whitespace || try_consume_newline
|
|
93
|
+
end_token
|
|
94
|
+
return true if report
|
|
95
|
+
|
|
96
|
+
case try_consume_comment_start
|
|
97
|
+
when :line
|
|
98
|
+
consume_line_comment(nil)
|
|
99
|
+
next
|
|
100
|
+
when :block
|
|
101
|
+
consume_block_comment(nil)
|
|
102
|
+
next
|
|
103
|
+
when :slash
|
|
104
|
+
return true
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
break if @read_error
|
|
108
|
+
|
|
109
|
+
if UNPRINTABLE[@char] || @char == 0
|
|
110
|
+
add_error("Invalid control characters encountered in text.")
|
|
111
|
+
next_char
|
|
112
|
+
while try_consume_one(UNPRINTABLE) || (!@read_error && try_consume(0))
|
|
113
|
+
end
|
|
114
|
+
else
|
|
115
|
+
start_token
|
|
116
|
+
if try_consume_one(LETTER)
|
|
117
|
+
consume_zero_or_more(ALNUM)
|
|
118
|
+
@current.type = :identifier
|
|
119
|
+
elsif try_consume(48) # 0
|
|
120
|
+
@current.type = consume_number(true, false)
|
|
121
|
+
elsif try_consume(46) # .
|
|
122
|
+
if try_consume_one(DIGIT)
|
|
123
|
+
if @previous.type == :identifier && @current.line == @previous.line &&
|
|
124
|
+
@current.column == @previous.end_column
|
|
125
|
+
@errors.call(@line, @column - 2, "Need space between identifier and decimal point.")
|
|
126
|
+
end
|
|
127
|
+
@current.type = consume_number(false, true)
|
|
128
|
+
else
|
|
129
|
+
@current.type = :symbol
|
|
130
|
+
end
|
|
131
|
+
elsif try_consume_one(DIGIT)
|
|
132
|
+
@current.type = consume_number(false, false)
|
|
133
|
+
elsif try_consume(34) # "
|
|
134
|
+
consume_string(34)
|
|
135
|
+
@current.type = :string
|
|
136
|
+
elsif try_consume(39) # '
|
|
137
|
+
consume_string(39)
|
|
138
|
+
@current.type = :string
|
|
139
|
+
else
|
|
140
|
+
consume_symbol
|
|
141
|
+
@current.type = :symbol
|
|
142
|
+
end
|
|
143
|
+
end_token
|
|
144
|
+
return true
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
@current.type = :end
|
|
149
|
+
@current.text = +""
|
|
150
|
+
@current.line = @line
|
|
151
|
+
@current.column = @column
|
|
152
|
+
@current.end_column = @column
|
|
153
|
+
false
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Like {#next}, but collects the comments around the token boundary the
|
|
157
|
+
# way protoc attaches them to declarations
|
|
158
|
+
# @param prev_trailing [String, nil] receives the previous token's trailing comment
|
|
159
|
+
# @param detached [Array<String>, nil] receives detached comments
|
|
160
|
+
# @param next_leading [String, nil] receives the next token's leading comment
|
|
161
|
+
# @return [Boolean]
|
|
162
|
+
def next_with_comments(prev_trailing, detached, next_leading)
|
|
163
|
+
collector = CommentCollector.new(prev_trailing, detached, next_leading)
|
|
164
|
+
prev_line = @line
|
|
165
|
+
trailing_comment_end_line = -1
|
|
166
|
+
|
|
167
|
+
if @current.type == :start
|
|
168
|
+
if try_consume(0xEF)
|
|
169
|
+
unless try_consume(0xBB) && try_consume(0xBF)
|
|
170
|
+
add_error("Proto file starts with 0xEF but not UTF-8 BOM. Only UTF-8 is accepted for proto file.")
|
|
171
|
+
collector.finish
|
|
172
|
+
return false
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
collector.detach_from_prev
|
|
176
|
+
prev_line = -1
|
|
177
|
+
else
|
|
178
|
+
consume_zero_or_more(WHITESPACE_NO_NEWLINE)
|
|
179
|
+
case try_consume_comment_start
|
|
180
|
+
when :line
|
|
181
|
+
trailing_comment_end_line = @line
|
|
182
|
+
consume_line_comment(collector.buffer_for_line_comment)
|
|
183
|
+
collector.flush
|
|
184
|
+
when :block
|
|
185
|
+
consume_block_comment(collector.buffer_for_block_comment)
|
|
186
|
+
trailing_comment_end_line = @line
|
|
187
|
+
consume_zero_or_more(WHITESPACE_NO_NEWLINE)
|
|
188
|
+
collector.flush
|
|
189
|
+
when :slash
|
|
190
|
+
collector.finish
|
|
191
|
+
return true
|
|
192
|
+
else
|
|
193
|
+
unless try_consume(10)
|
|
194
|
+
collector.finish
|
|
195
|
+
return self.next
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
loop do
|
|
201
|
+
consume_zero_or_more(WHITESPACE_NO_NEWLINE)
|
|
202
|
+
case try_consume_comment_start
|
|
203
|
+
when :line
|
|
204
|
+
consume_line_comment(collector.buffer_for_line_comment)
|
|
205
|
+
when :block
|
|
206
|
+
consume_block_comment(collector.buffer_for_block_comment)
|
|
207
|
+
consume_zero_or_more(WHITESPACE_NO_NEWLINE)
|
|
208
|
+
try_consume(10)
|
|
209
|
+
when :slash
|
|
210
|
+
collector.finish
|
|
211
|
+
return true
|
|
212
|
+
else
|
|
213
|
+
if try_consume(10)
|
|
214
|
+
collector.flush
|
|
215
|
+
collector.detach_from_prev
|
|
216
|
+
else
|
|
217
|
+
result = self.next
|
|
218
|
+
text = @current.text
|
|
219
|
+
collector.flush if !result || text == "}" || text == "]" || text == ")"
|
|
220
|
+
if result && (prev_line == @line || trailing_comment_end_line == @line)
|
|
221
|
+
collector.maybe_detach_comment
|
|
222
|
+
end
|
|
223
|
+
collector.finish
|
|
224
|
+
return result
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# protoc's Tokenizer::ParseInteger: decimal, 0x hex or leading-zero octal
|
|
231
|
+
# @return [Integer, nil] nil when malformed or above `max`
|
|
232
|
+
def self.parse_integer(text, max)
|
|
233
|
+
base = 10
|
|
234
|
+
digits = text
|
|
235
|
+
if text.start_with?("0x", "0X")
|
|
236
|
+
base = 16
|
|
237
|
+
digits = text[2..]
|
|
238
|
+
elsif text.start_with?("0")
|
|
239
|
+
base = 8
|
|
240
|
+
end
|
|
241
|
+
valid = case base
|
|
242
|
+
when 16 then digits.match?(/\A\h*\z/)
|
|
243
|
+
when 8 then digits.match?(/\A[0-7]*\z/)
|
|
244
|
+
else digits.match?(/\A[0-9]*\z/)
|
|
245
|
+
end
|
|
246
|
+
return nil unless valid
|
|
247
|
+
value = digits.empty? ? 0 : digits.to_i(base)
|
|
248
|
+
(value > max) ? nil : value
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# protoc's Tokenizer::ParseFloat over text the tokenizer accepted as a number
|
|
252
|
+
# @return [Float]
|
|
253
|
+
def self.parse_float(text)
|
|
254
|
+
m = text.match(/\A(\d*)(?:\.(\d*))?(?:[eE]([-+]?\d+))?/)
|
|
255
|
+
int = m[1].empty? ? "0" : m[1]
|
|
256
|
+
frac = (m[2].nil? || m[2].empty?) ? "0" : m[2]
|
|
257
|
+
Float("#{int}.#{frac}e#{m[3] || "0"}")
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Whether strtod would consume all of `text` as a float, as
|
|
261
|
+
# Tokenizer::TryParseFloat checks
|
|
262
|
+
def self.float_text?(text)
|
|
263
|
+
text.match?(/\A(\d+\.?\d*|\.\d+)([eE][-+]?\d+)?[fF]?\z/)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# protoc's Tokenizer::ParseStringAppend: the bytes a string token denotes
|
|
267
|
+
# @param text [String] token text including the quotes
|
|
268
|
+
# @return [String] binary
|
|
269
|
+
def self.parse_string(text, out = "".b)
|
|
270
|
+
bytes = text.b
|
|
271
|
+
quote = bytes.getbyte(0)
|
|
272
|
+
n = bytes.bytesize
|
|
273
|
+
i = 1
|
|
274
|
+
while i < n
|
|
275
|
+
b = bytes.getbyte(i)
|
|
276
|
+
if b == 92 && i + 1 < n # backslash
|
|
277
|
+
i += 1
|
|
278
|
+
c = bytes.getbyte(i)
|
|
279
|
+
if OCTAL[c]
|
|
280
|
+
code = c - 48
|
|
281
|
+
if i + 1 < n && OCTAL[bytes.getbyte(i + 1)]
|
|
282
|
+
i += 1
|
|
283
|
+
code = code * 8 + bytes.getbyte(i) - 48
|
|
284
|
+
end
|
|
285
|
+
if i + 1 < n && OCTAL[bytes.getbyte(i + 1)]
|
|
286
|
+
i += 1
|
|
287
|
+
code = code * 8 + bytes.getbyte(i) - 48
|
|
288
|
+
end
|
|
289
|
+
out << (code & 0xFF)
|
|
290
|
+
elsif c == 120 || c == 88 # x X
|
|
291
|
+
code = 0
|
|
292
|
+
if i + 1 < n && HEX[bytes.getbyte(i + 1)]
|
|
293
|
+
i += 1
|
|
294
|
+
code = bytes.getbyte(i).chr.to_i(16)
|
|
295
|
+
end
|
|
296
|
+
if i + 1 < n && HEX[bytes.getbyte(i + 1)]
|
|
297
|
+
i += 1
|
|
298
|
+
code = code * 16 + bytes.getbyte(i).chr.to_i(16)
|
|
299
|
+
end
|
|
300
|
+
out << code
|
|
301
|
+
elsif c == 117 || c == 85 # u U
|
|
302
|
+
len = (c == 117) ? 4 : 8
|
|
303
|
+
hex = bytes.byteslice(i + 1, len)
|
|
304
|
+
if hex.nil? || hex.bytesize < len || !hex.match?(/\A\h+\z/)
|
|
305
|
+
out << c
|
|
306
|
+
else
|
|
307
|
+
code = hex.to_i(16)
|
|
308
|
+
i += len
|
|
309
|
+
if code >= 0xD800 && code < 0xDC00 && bytes.byteslice(i + 1, 2) == "\\u"
|
|
310
|
+
trail = bytes.byteslice(i + 3, 4)
|
|
311
|
+
if trail&.match?(/\A\h{4}\z/) && (t = trail.to_i(16)) >= 0xDC00 && t < 0xE000
|
|
312
|
+
code = 0x10000 + (((code - 0xD800) << 10) | (t - 0xDC00))
|
|
313
|
+
i += 6
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
append_utf8(code, out)
|
|
317
|
+
end
|
|
318
|
+
else
|
|
319
|
+
out << (ESCAPES[c.chr] || 63)
|
|
320
|
+
end
|
|
321
|
+
elsif b == quote && i == n - 1
|
|
322
|
+
# closing quote
|
|
323
|
+
else
|
|
324
|
+
out << b
|
|
325
|
+
end
|
|
326
|
+
i += 1
|
|
327
|
+
end
|
|
328
|
+
out
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def self.append_utf8(code, out)
|
|
332
|
+
if code <= 0x7F
|
|
333
|
+
out << code
|
|
334
|
+
elsif code <= 0x7FF
|
|
335
|
+
out << (0xC0 | (code >> 6)) << (0x80 | (code & 0x3F))
|
|
336
|
+
elsif code <= 0xFFFF
|
|
337
|
+
out << (0xE0 | (code >> 12)) << (0x80 | ((code >> 6) & 0x3F)) << (0x80 | (code & 0x3F))
|
|
338
|
+
elsif code <= 0x10FFFF
|
|
339
|
+
out << (0xF0 | (code >> 18)) << (0x80 | ((code >> 12) & 0x3F)) <<
|
|
340
|
+
(0x80 | ((code >> 6) & 0x3F)) << (0x80 | (code & 0x3F))
|
|
341
|
+
else
|
|
342
|
+
out << format("\\U%08x", code)
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# @return [Boolean] whether `text` is a valid identifier
|
|
347
|
+
def self.identifier?(text)
|
|
348
|
+
text.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
private
|
|
352
|
+
|
|
353
|
+
def add_error(message)
|
|
354
|
+
@errors.call(@line, @column, message)
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def next_char
|
|
358
|
+
if @char == 10
|
|
359
|
+
@line += 1
|
|
360
|
+
@column = 0
|
|
361
|
+
elsif @char == 9
|
|
362
|
+
@column += TAB_WIDTH - @column % TAB_WIDTH
|
|
363
|
+
else
|
|
364
|
+
@column += 1
|
|
365
|
+
end
|
|
366
|
+
@pos += 1
|
|
367
|
+
if @pos < @size
|
|
368
|
+
@char = @src.getbyte(@pos)
|
|
369
|
+
else
|
|
370
|
+
@read_error = true
|
|
371
|
+
@char = 0
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def record_to(target)
|
|
376
|
+
@record_target = target
|
|
377
|
+
@record_start = @pos
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def stop_recording
|
|
381
|
+
if @pos != @record_start
|
|
382
|
+
@record_target << @src.byteslice(@record_start, [@pos, @size].min - @record_start)
|
|
383
|
+
end
|
|
384
|
+
@record_target = nil
|
|
385
|
+
@record_start = -1
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def start_token
|
|
389
|
+
@current.type = :start
|
|
390
|
+
@current.text = +""
|
|
391
|
+
@current.line = @line
|
|
392
|
+
@current.column = @column
|
|
393
|
+
record_to(@current.text)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def end_token
|
|
397
|
+
stop_recording
|
|
398
|
+
@current.end_column = @column
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def try_consume_one(table)
|
|
402
|
+
return false unless table[@char]
|
|
403
|
+
next_char
|
|
404
|
+
true
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def try_consume(byte)
|
|
408
|
+
return false unless @char == byte
|
|
409
|
+
next_char
|
|
410
|
+
true
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def consume_zero_or_more(table)
|
|
414
|
+
next_char while table[@char]
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def consume_one_or_more(table, error)
|
|
418
|
+
if table[@char]
|
|
419
|
+
next_char while table[@char]
|
|
420
|
+
else
|
|
421
|
+
add_error(error)
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# Consumes up to `count` hex digits, stopping at the first that is missing
|
|
426
|
+
def consume_hex(count)
|
|
427
|
+
count.times { return false unless try_consume_one(HEX) }
|
|
428
|
+
true
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def consume_string(delimiter)
|
|
432
|
+
loop do
|
|
433
|
+
case @char
|
|
434
|
+
when 0
|
|
435
|
+
add_error("Unexpected end of string.")
|
|
436
|
+
return
|
|
437
|
+
when 10
|
|
438
|
+
add_error("Multiline strings are not allowed. Did you miss a \"?.")
|
|
439
|
+
return
|
|
440
|
+
when 92 # backslash
|
|
441
|
+
next_char
|
|
442
|
+
if try_consume_one(ESCAPE)
|
|
443
|
+
elsif try_consume_one(OCTAL)
|
|
444
|
+
elsif try_consume(120) || try_consume(88) # x X
|
|
445
|
+
add_error("Expected hex digits for escape sequence.") unless try_consume_one(HEX)
|
|
446
|
+
elsif try_consume(117) # u
|
|
447
|
+
add_error("Expected four hex digits for \\u escape sequence.") unless consume_hex(4)
|
|
448
|
+
elsif try_consume(85) # U
|
|
449
|
+
valid = 2.times.all? { try_consume(48) } && (try_consume(48) || try_consume(49)) && consume_hex(5)
|
|
450
|
+
add_error("Expected eight hex digits up to 10ffff for \\U escape sequence") unless valid
|
|
451
|
+
else
|
|
452
|
+
add_error("Invalid escape sequence in string literal.")
|
|
453
|
+
end
|
|
454
|
+
else
|
|
455
|
+
if @char == delimiter
|
|
456
|
+
next_char
|
|
457
|
+
return
|
|
458
|
+
end
|
|
459
|
+
next_char
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def consume_number(started_with_zero, started_with_dot)
|
|
465
|
+
is_float = false
|
|
466
|
+
if started_with_zero && (try_consume(120) || try_consume(88)) # x X
|
|
467
|
+
consume_one_or_more(HEX, "\"0x\" must be followed by hex digits.")
|
|
468
|
+
elsif started_with_zero && DIGIT[@char]
|
|
469
|
+
consume_zero_or_more(OCTAL)
|
|
470
|
+
if DIGIT[@char]
|
|
471
|
+
add_error("Numbers starting with leading zero must be in octal.")
|
|
472
|
+
consume_zero_or_more(DIGIT)
|
|
473
|
+
end
|
|
474
|
+
else
|
|
475
|
+
if started_with_dot
|
|
476
|
+
is_float = true
|
|
477
|
+
consume_zero_or_more(DIGIT)
|
|
478
|
+
else
|
|
479
|
+
consume_zero_or_more(DIGIT)
|
|
480
|
+
if try_consume(46) # .
|
|
481
|
+
is_float = true
|
|
482
|
+
consume_zero_or_more(DIGIT)
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
if try_consume(101) || try_consume(69) # e E
|
|
486
|
+
is_float = true
|
|
487
|
+
try_consume(45) || try_consume(43) # - +
|
|
488
|
+
consume_one_or_more(DIGIT, "\"e\" must be followed by exponent.")
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
if LETTER[@char]
|
|
493
|
+
add_error("Need space between number and identifier.")
|
|
494
|
+
elsif @char == 46 # .
|
|
495
|
+
if is_float
|
|
496
|
+
add_error("Already saw decimal point or exponent; can't have another one.")
|
|
497
|
+
else
|
|
498
|
+
add_error("Hex and octal numbers must be integers.")
|
|
499
|
+
end
|
|
500
|
+
end
|
|
501
|
+
is_float ? :float : :integer
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def consume_symbol
|
|
505
|
+
@errors.call(@line, @column, "Interpreting non ascii codepoint #{@char}.") if @char >= 0x80
|
|
506
|
+
next_char
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
def consume_line_comment(content)
|
|
510
|
+
record_to(content) if content
|
|
511
|
+
next_char while @char != 0 && @char != 10
|
|
512
|
+
try_consume(10)
|
|
513
|
+
stop_recording if content
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def consume_block_comment(content)
|
|
517
|
+
start_line = @line
|
|
518
|
+
start_column = @column - 2
|
|
519
|
+
record_to(content) if content
|
|
520
|
+
loop do
|
|
521
|
+
next_char while @char != 0 && @char != 42 && @char != 47 && @char != 10 # * / \n
|
|
522
|
+
if try_consume(10)
|
|
523
|
+
stop_recording if content
|
|
524
|
+
consume_zero_or_more(WHITESPACE_NO_NEWLINE)
|
|
525
|
+
if try_consume(42) && try_consume(47) # * /
|
|
526
|
+
break
|
|
527
|
+
end
|
|
528
|
+
record_to(content) if content
|
|
529
|
+
elsif try_consume(42) && try_consume(47)
|
|
530
|
+
if content
|
|
531
|
+
stop_recording
|
|
532
|
+
content.replace(content.byteslice(0, content.bytesize - 2))
|
|
533
|
+
end
|
|
534
|
+
break
|
|
535
|
+
elsif try_consume(47) && @char == 42
|
|
536
|
+
add_error("\"/*\" inside block comment. Block comments cannot be nested.")
|
|
537
|
+
elsif @char == 0
|
|
538
|
+
add_error("End-of-file inside block comment.")
|
|
539
|
+
@errors.call(start_line, start_column, " Comment started here.")
|
|
540
|
+
stop_recording if content
|
|
541
|
+
break
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def try_consume_comment_start
|
|
547
|
+
return nil unless try_consume(47) # /
|
|
548
|
+
if try_consume(47)
|
|
549
|
+
:line
|
|
550
|
+
elsif try_consume(42) # *
|
|
551
|
+
:block
|
|
552
|
+
else
|
|
553
|
+
@current.type = :symbol
|
|
554
|
+
@current.text = +"/"
|
|
555
|
+
@current.line = @line
|
|
556
|
+
@current.column = @column - 1
|
|
557
|
+
@current.end_column = @column
|
|
558
|
+
:slash
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def try_consume_whitespace
|
|
563
|
+
if @report_newlines
|
|
564
|
+
if try_consume_one(WHITESPACE_NO_NEWLINE)
|
|
565
|
+
consume_zero_or_more(WHITESPACE_NO_NEWLINE)
|
|
566
|
+
@current.type = :whitespace
|
|
567
|
+
return true
|
|
568
|
+
end
|
|
569
|
+
return false
|
|
570
|
+
end
|
|
571
|
+
if try_consume_one(WHITESPACE)
|
|
572
|
+
consume_zero_or_more(WHITESPACE)
|
|
573
|
+
@current.type = :whitespace
|
|
574
|
+
return @report_whitespace
|
|
575
|
+
end
|
|
576
|
+
false
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
def try_consume_newline
|
|
580
|
+
return false unless @report_whitespace && @report_newlines
|
|
581
|
+
if try_consume(10)
|
|
582
|
+
@current.type = :newline
|
|
583
|
+
return true
|
|
584
|
+
end
|
|
585
|
+
false
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
# tokenizer.cc's CommentCollector
|
|
589
|
+
class CommentCollector
|
|
590
|
+
def initialize(prev_trailing, detached, next_leading)
|
|
591
|
+
@prev_trailing = prev_trailing
|
|
592
|
+
@detached = detached
|
|
593
|
+
@next_leading = next_leading
|
|
594
|
+
prev_trailing&.clear
|
|
595
|
+
detached&.clear
|
|
596
|
+
next_leading&.clear
|
|
597
|
+
@buffer = "".b
|
|
598
|
+
@num_comments = 0
|
|
599
|
+
@has_trailing_comment = false
|
|
600
|
+
@has_comment = false
|
|
601
|
+
@is_line_comment = false
|
|
602
|
+
@can_attach_to_prev = true
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def buffer_for_line_comment
|
|
606
|
+
flush if @has_comment && !@is_line_comment
|
|
607
|
+
@has_comment = true
|
|
608
|
+
@is_line_comment = true
|
|
609
|
+
@buffer
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
def buffer_for_block_comment
|
|
613
|
+
flush if @has_comment
|
|
614
|
+
@has_comment = true
|
|
615
|
+
@is_line_comment = false
|
|
616
|
+
@buffer
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def clear_buffer
|
|
620
|
+
@buffer = "".b
|
|
621
|
+
@has_comment = false
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
def flush
|
|
625
|
+
return unless @has_comment
|
|
626
|
+
if @can_attach_to_prev
|
|
627
|
+
@prev_trailing&.<<(@buffer)
|
|
628
|
+
@has_trailing_comment = true
|
|
629
|
+
@can_attach_to_prev = false
|
|
630
|
+
else
|
|
631
|
+
@detached&.push(@buffer)
|
|
632
|
+
end
|
|
633
|
+
clear_buffer
|
|
634
|
+
@num_comments += 1
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
def detach_from_prev
|
|
638
|
+
@can_attach_to_prev = false
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def maybe_detach_comment
|
|
642
|
+
count = @num_comments
|
|
643
|
+
count += 1 if @has_comment
|
|
644
|
+
return unless count == 1
|
|
645
|
+
if @has_trailing_comment && @prev_trailing
|
|
646
|
+
@detached&.unshift(@prev_trailing.dup)
|
|
647
|
+
@prev_trailing.clear
|
|
648
|
+
end
|
|
649
|
+
flush
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
# The destructor: whatever is still buffered leads the next token
|
|
653
|
+
def finish
|
|
654
|
+
@next_leading&.replace(@buffer) if @next_leading && @has_comment
|
|
655
|
+
end
|
|
656
|
+
end
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
end
|