rbs 0.8.0 → 0.12.0
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 +4 -4
- data/CHANGELOG.md +32 -0
- data/Gemfile +1 -0
- data/README.md +1 -1
- data/Rakefile +9 -4
- data/Steepfile +28 -0
- data/bin/steep +4 -0
- data/bin/test_runner.rb +10 -5
- data/docs/syntax.md +14 -1
- data/lib/rbs/ast/comment.rb +12 -0
- data/lib/rbs/ast/declarations.rb +15 -9
- data/lib/rbs/ast/members.rb +3 -8
- data/lib/rbs/buffer.rb +1 -1
- data/lib/rbs/cli.rb +66 -2
- data/lib/rbs/definition.rb +35 -16
- data/lib/rbs/definition_builder.rb +111 -68
- data/lib/rbs/environment.rb +24 -11
- data/lib/rbs/environment_loader.rb +55 -35
- data/lib/rbs/location.rb +14 -3
- data/lib/rbs/method_type.rb +5 -5
- data/lib/rbs/namespace.rb +14 -3
- data/lib/rbs/parser.y +8 -20
- data/lib/rbs/prototype/rb.rb +3 -5
- data/lib/rbs/prototype/rbi.rb +1 -4
- data/lib/rbs/prototype/runtime.rb +0 -4
- data/lib/rbs/substitution.rb +4 -3
- data/lib/rbs/test/hook.rb +1 -0
- data/lib/rbs/test/setup.rb +17 -12
- data/lib/rbs/test/setup_helper.rb +15 -0
- data/lib/rbs/test/tester.rb +59 -13
- data/lib/rbs/test/type_check.rb +43 -14
- data/lib/rbs/type_name.rb +18 -1
- data/lib/rbs/type_name_resolver.rb +10 -3
- data/lib/rbs/types.rb +27 -21
- data/lib/rbs/validator.rb +4 -0
- data/lib/rbs/variance_calculator.rb +8 -5
- data/lib/rbs/version.rb +1 -1
- data/lib/rbs/writer.rb +7 -3
- data/sig/annotation.rbs +26 -0
- data/sig/buffer.rbs +28 -0
- data/sig/builtin_names.rbs +41 -0
- data/sig/comment.rbs +26 -0
- data/sig/declarations.rbs +202 -0
- data/sig/definition.rbs +129 -0
- data/sig/definition_builder.rbs +95 -0
- data/sig/environment.rbs +94 -0
- data/sig/environment_loader.rbs +4 -0
- data/sig/location.rbs +52 -0
- data/sig/members.rbs +160 -0
- data/sig/method_types.rbs +40 -0
- data/sig/namespace.rbs +124 -0
- data/sig/polyfill.rbs +3 -0
- data/sig/rbs.rbs +3 -0
- data/sig/substitution.rbs +39 -0
- data/sig/type_name_resolver.rbs +24 -0
- data/sig/typename.rbs +70 -0
- data/sig/types.rbs +361 -0
- data/sig/util.rbs +13 -0
- data/sig/variance_calculator.rbs +35 -0
- data/stdlib/bigdecimal/big_decimal.rbs +887 -0
- data/stdlib/bigdecimal/math/big_math.rbs +142 -0
- data/stdlib/builtin/array.rbs +2 -1
- data/stdlib/builtin/builtin.rbs +0 -3
- data/stdlib/builtin/hash.rbs +1 -1
- data/stdlib/builtin/math.rbs +26 -26
- data/stdlib/builtin/struct.rbs +9 -10
- data/stdlib/date/date.rbs +1056 -0
- data/stdlib/date/date_time.rbs +582 -0
- data/stdlib/forwardable/forwardable.rbs +204 -0
- data/stdlib/json/json.rbs +6 -0
- data/stdlib/set/set.rbs +1 -1
- data/stdlib/uri/file.rbs +167 -0
- data/stdlib/uri/generic.rbs +875 -0
- data/stdlib/zlib/zlib.rbs +392 -0
- data/steep/Gemfile +3 -0
- data/steep/Gemfile.lock +55 -0
- metadata +39 -6
@@ -75,29 +75,16 @@ module RBS
|
|
75
75
|
self.class.gem_sig_path(name, version)
|
76
76
|
end
|
77
77
|
|
78
|
-
def each_signature(path
|
78
|
+
def each_signature(path, immediate: true, &block)
|
79
79
|
if block_given?
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
yield path
|
85
|
-
end
|
86
|
-
when path.directory?
|
87
|
-
path.children.each do |child|
|
88
|
-
each_signature child, immediate: false, &block
|
89
|
-
end
|
80
|
+
case
|
81
|
+
when path.file?
|
82
|
+
if path.extname == ".rbs" || immediate
|
83
|
+
yield path
|
90
84
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
when Pathname
|
95
|
-
each_signature path, immediate: immediate, &block
|
96
|
-
when LibraryPath
|
97
|
-
each_signature path.path, immediate: immediate, &block
|
98
|
-
when GemPath
|
99
|
-
each_signature path.path, immediate: immediate, &block
|
100
|
-
end
|
85
|
+
when path.directory?
|
86
|
+
path.children.each do |child|
|
87
|
+
each_signature child, immediate: false, &block
|
101
88
|
end
|
102
89
|
end
|
103
90
|
else
|
@@ -105,32 +92,65 @@ module RBS
|
|
105
92
|
end
|
106
93
|
end
|
107
94
|
|
108
|
-
def
|
109
|
-
|
95
|
+
def each_library_path
|
96
|
+
paths.each do |path|
|
97
|
+
case path
|
98
|
+
when Pathname
|
99
|
+
yield path, path
|
100
|
+
when LibraryPath
|
101
|
+
yield path, path.path
|
102
|
+
when GemPath
|
103
|
+
yield path, path.path
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def no_builtin!(skip = true)
|
109
|
+
@no_builtin = skip
|
110
|
+
self
|
110
111
|
end
|
111
112
|
|
112
113
|
def no_builtin?
|
113
114
|
@no_builtin
|
114
115
|
end
|
115
116
|
|
116
|
-
def
|
117
|
-
|
117
|
+
def each_decl
|
118
|
+
if block_given?
|
119
|
+
signature_files = []
|
118
120
|
|
119
|
-
|
120
|
-
|
121
|
-
|
121
|
+
unless no_builtin?
|
122
|
+
each_signature(stdlib_root + "builtin") do |path|
|
123
|
+
signature_files << [:stdlib, path]
|
124
|
+
end
|
125
|
+
end
|
122
126
|
|
123
|
-
|
124
|
-
|
127
|
+
each_library_path do |library_path, pathname|
|
128
|
+
each_signature(pathname) do |path|
|
129
|
+
signature_files << [library_path, path]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
signature_files.each do |lib_path, file_path|
|
134
|
+
buffer = Buffer.new(name: file_path.to_s, content: file_path.read)
|
135
|
+
Parser.parse_signature(buffer).each do |decl|
|
136
|
+
yield decl, buffer, file_path, lib_path
|
137
|
+
end
|
138
|
+
end
|
139
|
+
else
|
140
|
+
enum_for :each_decl
|
125
141
|
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def load(env:)
|
145
|
+
loadeds = []
|
126
146
|
|
127
|
-
|
128
|
-
buffer = Buffer.new(name: file.to_s, content: file.read)
|
147
|
+
each_decl do |decl, buffer, file_path, lib_path|
|
129
148
|
env.buffers.push(buffer)
|
130
|
-
|
131
|
-
|
132
|
-
end
|
149
|
+
env << decl
|
150
|
+
loadeds << [decl, file_path, lib_path]
|
133
151
|
end
|
152
|
+
|
153
|
+
loadeds
|
134
154
|
end
|
135
155
|
end
|
136
156
|
end
|
data/lib/rbs/location.rb
CHANGED
@@ -43,7 +43,7 @@ module RBS
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def source
|
46
|
-
@source ||= buffer.content[start_pos...end_pos]
|
46
|
+
@source ||= buffer.content[start_pos...end_pos] or raise
|
47
47
|
end
|
48
48
|
|
49
49
|
def to_s
|
@@ -73,8 +73,19 @@ module RBS
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
-
def
|
77
|
-
|
76
|
+
def concat(*others)
|
77
|
+
others.each { |other| self << other }
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def <<(other)
|
82
|
+
if other
|
83
|
+
raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
|
84
|
+
@end_pos = other.end_pos
|
85
|
+
@source = nil
|
86
|
+
@end_loc = nil
|
87
|
+
end
|
88
|
+
self
|
78
89
|
end
|
79
90
|
|
80
91
|
def pred?(loc)
|
data/lib/rbs/method_type.rb
CHANGED
@@ -93,7 +93,7 @@ module RBS
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def each_type(&block)
|
96
|
-
if
|
96
|
+
if block
|
97
97
|
type.each_type(&block)
|
98
98
|
self.block&.yield_self do |b|
|
99
99
|
b.type.each_type(&block)
|
@@ -105,10 +105,10 @@ module RBS
|
|
105
105
|
|
106
106
|
def to_s
|
107
107
|
s = case
|
108
|
-
when block &&
|
109
|
-
"(#{type.param_to_s}) { (#{
|
110
|
-
when block
|
111
|
-
"(#{type.param_to_s}) ?{ (#{
|
108
|
+
when (b = block) && b.required
|
109
|
+
"(#{type.param_to_s}) { (#{b.type.param_to_s}) -> #{b.type.return_to_s} } -> #{type.return_to_s}"
|
110
|
+
when b = block
|
111
|
+
"(#{type.param_to_s}) ?{ (#{b.type.param_to_s}) -> #{b.type.return_to_s} } -> #{type.return_to_s}"
|
112
112
|
else
|
113
113
|
"(#{type.param_to_s}) -> #{type.return_to_s}"
|
114
114
|
end
|
data/lib/rbs/namespace.rb
CHANGED
@@ -63,7 +63,9 @@ module RBS
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def split
|
66
|
-
|
66
|
+
last = path.last or return
|
67
|
+
parent = self.parent
|
68
|
+
[parent, last]
|
67
69
|
end
|
68
70
|
|
69
71
|
def to_s
|
@@ -77,6 +79,10 @@ module RBS
|
|
77
79
|
|
78
80
|
def to_type_name
|
79
81
|
parent, name = split
|
82
|
+
|
83
|
+
raise unless name
|
84
|
+
raise unless parent
|
85
|
+
|
80
86
|
TypeName.new(name: name, namespace: parent)
|
81
87
|
end
|
82
88
|
|
@@ -88,14 +94,13 @@ module RBS
|
|
88
94
|
end
|
89
95
|
end
|
90
96
|
|
91
|
-
|
92
97
|
def ascend
|
93
98
|
if block_given?
|
94
99
|
current = self
|
95
100
|
|
96
101
|
until current.empty?
|
97
102
|
yield current
|
98
|
-
current = current.parent
|
103
|
+
current = _ = current.parent
|
99
104
|
end
|
100
105
|
|
101
106
|
yield current
|
@@ -107,3 +112,9 @@ module RBS
|
|
107
112
|
end
|
108
113
|
end
|
109
114
|
end
|
115
|
+
|
116
|
+
module Kernel
|
117
|
+
def Namespace(name)
|
118
|
+
RBS::Namespace.parse(name)
|
119
|
+
end
|
120
|
+
end
|
data/lib/rbs/parser.y
CHANGED
@@ -435,14 +435,6 @@ rule
|
|
435
435
|
method_member:
|
436
436
|
annotations attributes overload kDEF method_kind def_name method_types {
|
437
437
|
location = val[3].location + val[6].last.location
|
438
|
-
types = val[6].map do |type|
|
439
|
-
case type
|
440
|
-
when LocatedValue
|
441
|
-
type.value
|
442
|
-
else
|
443
|
-
type
|
444
|
-
end
|
445
|
-
end
|
446
438
|
|
447
439
|
last_type = val[6].last
|
448
440
|
if last_type.is_a?(LocatedValue) && last_type.value == :dot3
|
@@ -458,16 +450,14 @@ rule
|
|
458
450
|
types: val[6],
|
459
451
|
annotations: val[0],
|
460
452
|
location: location,
|
461
|
-
comment: leading_comment(val[0].first&.location || val[
|
462
|
-
attributes: val[1].map(&:value),
|
453
|
+
comment: leading_comment(val[0].first&.location || val[2]&.location || val[3].location),
|
463
454
|
overload: overload || !!val[2]
|
464
455
|
)
|
465
456
|
}
|
466
457
|
|
467
458
|
attributes:
|
468
|
-
{ result = [] }
|
469
459
|
| attributes kINCOMPATIBLE {
|
470
|
-
|
460
|
+
RBS.logger.warn "`incompatible` method attribute is deprecated and ignored."
|
471
461
|
}
|
472
462
|
|
473
463
|
method_kind:
|
@@ -1164,15 +1154,13 @@ def leading_comment(location)
|
|
1164
1154
|
end
|
1165
1155
|
|
1166
1156
|
def push_comment(string, location)
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
new_comment = AST::Comment.new(string:
|
1172
|
-
|
1157
|
+
if (comment = leading_comment(location)) && comment.location.start_column == location.start_column
|
1158
|
+
comment.concat(string: "#{string}\n", location: location)
|
1159
|
+
@comments[comment.location.end_line] = comment
|
1160
|
+
else
|
1161
|
+
new_comment = AST::Comment.new(string: "#{string}\n", location: location)
|
1162
|
+
@comments[new_comment.location.end_line] = new_comment
|
1173
1163
|
end
|
1174
|
-
|
1175
|
-
@comments[new_comment.location.end_line] = new_comment
|
1176
1164
|
end
|
1177
1165
|
|
1178
1166
|
def new_token(type, value = input.matched)
|
data/lib/rbs/prototype/rb.rb
CHANGED
@@ -36,7 +36,7 @@ module RBS
|
|
36
36
|
tokens.each.with_object({}) do |token, hash|
|
37
37
|
if token[1] == :on_comment
|
38
38
|
line = token[0][0]
|
39
|
-
body = token[2][2
|
39
|
+
body = token[2][2..-1]
|
40
40
|
|
41
41
|
body = "\n" if body.empty?
|
42
42
|
|
@@ -130,7 +130,6 @@ module RBS
|
|
130
130
|
types: types,
|
131
131
|
kind: kind,
|
132
132
|
comment: comments[node.first_lineno - 1],
|
133
|
-
attributes: [],
|
134
133
|
overload: false
|
135
134
|
)
|
136
135
|
|
@@ -354,7 +353,6 @@ module RBS
|
|
354
353
|
Types::Bases::Nil.new(location: nil)
|
355
354
|
when :LIT
|
356
355
|
lit = node.children[0]
|
357
|
-
name = lit.class.name
|
358
356
|
case lit
|
359
357
|
when Symbol
|
360
358
|
if lit.match?(/\A[ -~]+\z/)
|
@@ -365,7 +363,7 @@ module RBS
|
|
365
363
|
when Integer
|
366
364
|
Types::Literal.new(literal: lit, location: nil)
|
367
365
|
else
|
368
|
-
type_name = TypeName.new(name: name, namespace: Namespace.root)
|
366
|
+
type_name = TypeName.new(name: lit.class.name.to_sym, namespace: Namespace.root)
|
369
367
|
Types::ClassInstance.new(name: type_name, args: [], location: nil)
|
370
368
|
end
|
371
369
|
when :ZLIST, :ZARRAY
|
@@ -421,7 +419,7 @@ module RBS
|
|
421
419
|
|
422
420
|
types = types.map do |t|
|
423
421
|
if t.is_a?(Types::Literal)
|
424
|
-
type_name = TypeName.new(name: t.literal.class.name, namespace: Namespace.root)
|
422
|
+
type_name = TypeName.new(name: t.literal.class.name.to_sym, namespace: Namespace.root)
|
425
423
|
Types::ClassInstance.new(name: type_name, args: [], location: nil)
|
426
424
|
else
|
427
425
|
t
|
data/lib/rbs/prototype/rbi.rb
CHANGED
@@ -16,7 +16,7 @@ module RBS
|
|
16
16
|
tokens.each.with_object({}) do |token, hash|
|
17
17
|
if token[1] == :on_comment
|
18
18
|
line = token[0][0]
|
19
|
-
body = token[2][2
|
19
|
+
body = token[2][2..-1]
|
20
20
|
|
21
21
|
body = "\n" if body.empty?
|
22
22
|
|
@@ -173,7 +173,6 @@ module RBS
|
|
173
173
|
types: types,
|
174
174
|
kind: :singleton,
|
175
175
|
comment: comment,
|
176
|
-
attributes: [],
|
177
176
|
overload: false
|
178
177
|
)
|
179
178
|
end
|
@@ -194,7 +193,6 @@ module RBS
|
|
194
193
|
types: types,
|
195
194
|
kind: :instance,
|
196
195
|
comment: comment,
|
197
|
-
attributes: [],
|
198
196
|
overload: false
|
199
197
|
)
|
200
198
|
end
|
@@ -502,7 +500,6 @@ module RBS
|
|
502
500
|
else
|
503
501
|
type_node.type == :CALL && proc_type?(type_node.children[0])
|
504
502
|
end
|
505
|
-
|
506
503
|
end
|
507
504
|
|
508
505
|
def call_node?(node, name:, receiver: -> (node) { node.type == :CONST && node.children[0] == :T }, args: -> (node) { true })
|
@@ -154,7 +154,6 @@ module RBS
|
|
154
154
|
location: nil,
|
155
155
|
comment: method.comments[0],
|
156
156
|
annotations: method.annotations,
|
157
|
-
attributes: method.attributes,
|
158
157
|
overload: false
|
159
158
|
)
|
160
159
|
return
|
@@ -193,7 +192,6 @@ module RBS
|
|
193
192
|
location: nil,
|
194
193
|
comment: nil,
|
195
194
|
annotations: [],
|
196
|
-
attributes: [],
|
197
195
|
overload: false
|
198
196
|
)
|
199
197
|
end
|
@@ -227,7 +225,6 @@ module RBS
|
|
227
225
|
location: nil,
|
228
226
|
comment: nil,
|
229
227
|
annotations: [],
|
230
|
-
attributes: [],
|
231
228
|
overload: false
|
232
229
|
)
|
233
230
|
end
|
@@ -262,7 +259,6 @@ module RBS
|
|
262
259
|
location: nil,
|
263
260
|
comment: nil,
|
264
261
|
annotations: [],
|
265
|
-
attributes: [],
|
266
262
|
overload: false
|
267
263
|
)
|
268
264
|
end
|
data/lib/rbs/substitution.rb
CHANGED
@@ -31,10 +31,11 @@ module RBS
|
|
31
31
|
def apply(ty)
|
32
32
|
case ty
|
33
33
|
when Types::Variable
|
34
|
+
# @type var ty: Types::Variable
|
34
35
|
mapping[ty.name] || ty
|
35
36
|
when Types::Bases::Instance
|
36
|
-
if instance_type
|
37
|
-
|
37
|
+
if t = instance_type
|
38
|
+
t
|
38
39
|
else
|
39
40
|
ty
|
40
41
|
end
|
@@ -44,7 +45,7 @@ module RBS
|
|
44
45
|
end
|
45
46
|
|
46
47
|
def without(*vars)
|
47
|
-
|
48
|
+
Substitution.new.tap do |subst|
|
48
49
|
subst.mapping.merge!(mapping)
|
49
50
|
vars.each do |var|
|
50
51
|
subst.mapping.delete(var)
|
data/lib/rbs/test/hook.rb
CHANGED
data/lib/rbs/test/setup.rb
CHANGED
@@ -9,21 +9,26 @@ logger = Logger.new(STDERR)
|
|
9
9
|
|
10
10
|
begin
|
11
11
|
opts = Shellwords.shellsplit(ENV["RBS_TEST_OPT"] || "-I sig")
|
12
|
-
filter = ENV.fetch('RBS_TEST_TARGET').split(',').map! { |e| e.strip }
|
12
|
+
filter = ENV.fetch('RBS_TEST_TARGET', "").split(',').map! { |e| e.strip }
|
13
13
|
skips = (ENV['RBS_TEST_SKIP'] || '').split(',').map! { |e| e.strip }
|
14
14
|
RBS.logger_level = (ENV["RBS_TEST_LOGLEVEL"] || "info")
|
15
15
|
sample_size = get_sample_size(ENV['RBS_TEST_SAMPLE_SIZE'] || '')
|
16
|
+
double_class = to_double_class(ENV['RBS_TEST_DOUBLE_SUITE'])
|
17
|
+
unchecked_classes = (ENV['RBS_TEST_UNCHECKED_CLASSES'] || '').split(',').map! { |unchecked_class| unchecked_class.strip }.push(*double_class)
|
16
18
|
rescue InvalidSampleSizeError => exception
|
17
19
|
RBS.logger.error exception.message
|
18
20
|
exit 1
|
19
|
-
|
20
|
-
|
21
|
+
end
|
22
|
+
|
23
|
+
if filter.empty?
|
21
24
|
STDERR.puts "rbs/test/setup handles the following environment variables:"
|
22
25
|
STDERR.puts " [REQUIRED] RBS_TEST_TARGET: test target class name, `Foo::Bar,Foo::Baz` for each class or `Foo::*` for all classes under `Foo`"
|
23
26
|
STDERR.puts " [OPTIONAL] RBS_TEST_SKIP: skip testing classes"
|
24
27
|
STDERR.puts " [OPTIONAL] RBS_TEST_OPT: options for signatures (`-r` for libraries or `-I` for signatures)"
|
25
28
|
STDERR.puts " [OPTIONAL] RBS_TEST_LOGLEVEL: one of debug|info|warn|error|fatal (defaults to info)"
|
26
29
|
STDERR.puts " [OPTIONAL] RBS_TEST_SAMPLE_SIZE: sets the amount of values in a collection to be type-checked (Set to `ALL` to type check all the values)"
|
30
|
+
STDERR.puts " [OPTIONAL] RBS_TEST_DOUBLE_SUITE: sets the double suite in use (currently supported: minitest | rspec)"
|
31
|
+
STDERR.puts " [OPTIONAL] RBS_TEST_UNCHECKED_CLASSES: sets the classes that would not be checked"
|
27
32
|
exit 1
|
28
33
|
end
|
29
34
|
|
@@ -50,16 +55,16 @@ end
|
|
50
55
|
|
51
56
|
tester = RBS::Test::Tester.new(env: env)
|
52
57
|
|
58
|
+
module_name = Module.instance_method(:name)
|
59
|
+
|
53
60
|
TracePoint.trace :end do |tp|
|
54
|
-
class_name = tp.self.
|
61
|
+
class_name = module_name.bind(tp.self).call&.yield_self {|name| to_absolute_typename name }
|
55
62
|
|
56
63
|
if class_name
|
57
64
|
if filter.any? {|f| match(to_absolute_typename(f).to_s, class_name.to_s) } && skips.none? {|f| match(f, class_name.to_s) }
|
58
|
-
if
|
59
|
-
|
60
|
-
|
61
|
-
tester.install!(tp.self, sample_size: sample_size)
|
62
|
-
end
|
65
|
+
if env.class_decls.key?(class_name)
|
66
|
+
logger.info "Setting up hooks for #{class_name}"
|
67
|
+
tester.install!(tp.self, sample_size: sample_size, unchecked_classes: unchecked_classes)
|
63
68
|
end
|
64
69
|
end
|
65
70
|
end
|
@@ -67,8 +72,8 @@ end
|
|
67
72
|
|
68
73
|
at_exit do
|
69
74
|
if $!.nil? || $!.is_a?(SystemExit) && $!.success?
|
70
|
-
|
75
|
+
if tester.targets.empty?
|
76
|
+
logger.debug { "No type checker was installed!" }
|
77
|
+
end
|
71
78
|
end
|
72
79
|
end
|
73
|
-
|
74
|
-
|