scale_rb 0.4.1 → 0.5.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/devcontainer.json +21 -0
  3. data/Dockerfile +16 -0
  4. data/Gemfile +4 -4
  5. data/README.md +19 -6
  6. data/bin/console +0 -0
  7. data/bin/setup +0 -0
  8. data/examples/http_client_1.rb +0 -2
  9. data/examples/http_client_2.rb +0 -2
  10. data/examples/ws_client_1.rb +0 -2
  11. data/examples/ws_client_3.rb +1 -3
  12. data/examples/ws_client_4.rb +0 -2
  13. data/exe/metadata +9 -11
  14. data/lib/address.rb +1 -1
  15. data/lib/custom_assign.rb +92 -0
  16. data/lib/scale_rb/call_helper.rb +42 -0
  17. data/lib/{client → scale_rb/client}/client_ext.rb +12 -13
  18. data/lib/{client → scale_rb/client}/http_client.rb +3 -5
  19. data/lib/{client → scale_rb/client}/ws_client.rb +13 -20
  20. data/lib/scale_rb/codec.rb +25 -0
  21. data/lib/scale_rb/codec_utils.rb +128 -0
  22. data/lib/scale_rb/decode.rb +164 -0
  23. data/lib/scale_rb/encode.rb +150 -0
  24. data/lib/{hasher.rb → scale_rb/hasher.rb} +10 -8
  25. data/lib/scale_rb/metadata/metadata.rb +114 -0
  26. data/lib/{metadata → scale_rb/metadata}/metadata_v10.rb +0 -17
  27. data/lib/{metadata → scale_rb/metadata}/metadata_v11.rb +0 -17
  28. data/lib/{metadata → scale_rb/metadata}/metadata_v12.rb +0 -17
  29. data/lib/{metadata → scale_rb/metadata}/metadata_v13.rb +0 -17
  30. data/lib/{metadata → scale_rb/metadata}/metadata_v14.rb +18 -18
  31. data/lib/{metadata → scale_rb/metadata}/metadata_v9.rb +0 -17
  32. data/lib/scale_rb/metadata/registry.rb +263 -0
  33. data/lib/scale_rb/metadata/type_exp.rb +286 -0
  34. data/lib/scale_rb/portable_registry.rb +133 -0
  35. data/lib/{storage_helper.rb → scale_rb/storage_helper.rb} +16 -4
  36. data/lib/scale_rb/types.rb +233 -0
  37. data/lib/scale_rb/utils.rb +125 -0
  38. data/lib/scale_rb/version.rb +1 -1
  39. data/lib/scale_rb.rb +22 -30
  40. data/lib/type_enforcer.rb +170 -0
  41. data/scale_rb.gemspec +5 -0
  42. metadata +71 -19
  43. data/lib/codec.rb +0 -450
  44. data/lib/metadata/metadata.rb +0 -137
  45. data/lib/monkey_patching.rb +0 -115
  46. data/lib/portable_codec.rb +0 -285
  47. data/lib/registry.rb +0 -13
@@ -3,23 +3,6 @@
3
3
  module ScaleRb
4
4
  module Metadata
5
5
  module MetadataV13
6
- class << self
7
- def get_module(module_name, metadata)
8
- metadata._get(:metadata)._get(:v13)._get(:modules).find do |m|
9
- m._get(:name) == module_name
10
- end
11
- end
12
-
13
- def get_storage_item(module_name, item_name, metadata)
14
- modula = get_module(module_name, metadata)
15
- raise "Module `#{module_name}` not found" if modula.nil?
16
-
17
- modula._get(:storage)._get(:items).find do |item|
18
- item._get(:name) == item_name
19
- end
20
- end
21
- end
22
-
23
6
  TYPES = {
24
7
  MetadataV13: {
25
8
  modules: 'Vec<ModuleMetadataV13>',
@@ -5,48 +5,48 @@ module ScaleRb
5
5
  module MetadataV14
6
6
  class << self
7
7
  def build_registry(metadata)
8
- types = metadata._get(:lookup)._get(:types)
9
- types.map { |type| [type._get(:id), type._get(:type)] }.to_h
8
+ types = metadata._get(:lookup, :types)
9
+ ScaleRb.build_types(types)
10
10
  end
11
11
 
12
- def get_module(pallet_name, metadata)
13
- metadata._get(:metadata)._get(:v14)._get(:pallets).find do |p|
12
+ def get_module(pallet_name, metadata_prefixed)
13
+ metadata_prefixed._get(:metadata, :V14, :pallets).find do |p|
14
14
  p._get(:name) == pallet_name
15
15
  end
16
16
  end
17
17
 
18
- def get_module_by_index(pallet_index, metadata)
19
- metadata._get(:metadata)._get(:v14)._get(:pallets).find do |p|
18
+ def get_module_by_index(pallet_index, metadata_prefixed)
19
+ metadata_prefixed._get(:metadata, :V14, :pallets).find do |p|
20
20
  p._get(:index) == pallet_index
21
21
  end
22
22
  end
23
23
 
24
- def get_storage_item(pallet_name, item_name, metadata)
25
- pallet = get_module(pallet_name, metadata)
24
+ def get_storage_item(pallet_name, item_name, metadata_prefixed)
25
+ pallet = get_module(pallet_name, metadata_prefixed)
26
26
  raise "Pallet `#{pallet_name}` not found" if pallet.nil?
27
27
 
28
- pallet._get(:storage)._get(:items).find do |item|
28
+ pallet._get(:storage, :items).find do |item|
29
29
  item._get(:name) == item_name
30
30
  end
31
31
  end
32
32
 
33
- def get_calls_type_id(pallet_name, metadata)
34
- pallet = get_module(pallet_name, metadata)
33
+ def get_calls_type_id(pallet_name, metadata_prefixed)
34
+ pallet = get_module(pallet_name, metadata_prefixed)
35
35
  raise "Pallet `#{pallet_name}` not found" if pallet.nil?
36
36
 
37
- pallet._get(:calls)._get(:type)
37
+ pallet._get(:calls, :type)
38
38
  end
39
39
 
40
- def get_calls_type(pallet_name, metadata)
41
- type_id = get_calls_type_id(pallet_name, metadata)
42
- metadata._get(:metadata)._get(:v14)._get(:lookup)._get(:types).find do |type|
40
+ def get_calls_type(pallet_name, metadata_prefixed)
41
+ type_id = get_calls_type_id(pallet_name, metadata_prefixed)
42
+ metadata_prefixed._get(:metadata, :V14, :lookup, :types).find do |type|
43
43
  type._get(:id) == type_id
44
44
  end
45
45
  end
46
46
 
47
- def get_call_type(pallet_name, call_name, metadata)
48
- calls_type = get_calls_type(pallet_name, metadata)
49
- calls_type._get(:type)._get(:def)._get(:variant)._get(:variants).find do |variant|
47
+ def get_call_type(pallet_name, call_name, metadata_prefixed)
48
+ calls_type = get_calls_type(pallet_name, metadata_prefixed)
49
+ calls_type._get(:type, :def, :variant, :variants).find do |variant|
50
50
  variant._get(:name).downcase == call_name.downcase
51
51
  end
52
52
  end
@@ -3,23 +3,6 @@
3
3
  module ScaleRb
4
4
  module Metadata
5
5
  module MetadataV9
6
- class << self
7
- def get_module(module_name, metadata)
8
- metadata._get(:metadata)._get(:v9)._get(:modules).find do |m|
9
- m._get(:name) == module_name
10
- end
11
- end
12
-
13
- def get_storage_item(module_name, item_name, metadata)
14
- modula = get_module(module_name, metadata)
15
- raise "Module `#{module_name}` not found" if modula.nil?
16
-
17
- modula._get(:storage)._get(:items).find do |item|
18
- item._get(:name) == item_name
19
- end
20
- end
21
- end
22
-
23
6
  TYPES = {
24
7
  ErrorMetadataV9: {
25
8
  name: 'Text',
@@ -0,0 +1,263 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './type_exp'
4
+ require_relative '../types'
5
+
6
+ # rubocop:disable all
7
+ module ScaleRb
8
+ module Metadata
9
+ class Registry
10
+ include Types
11
+
12
+ # Map name to index of type in `types` array
13
+ # % lookup :: String -> Integer
14
+ attr_reader :lookup
15
+
16
+ # % keys :: Integer -> String
17
+ attr_reader :keys
18
+
19
+ # % types :: Array<PortableType>
20
+ attr_reader :types
21
+
22
+ attr_reader :old_types
23
+
24
+ # % initialize :: Hash<Symbol, Any> -> void
25
+ def initialize(old_types)
26
+ @old_types = old_types
27
+ @lookup = {}
28
+ @keys = {}
29
+ @types = []
30
+
31
+ build()
32
+ end
33
+
34
+ def build()
35
+ @old_types.keys.each do |name|
36
+ use(name.to_s)
37
+ end
38
+ end
39
+
40
+ def [](index)
41
+ @types[index]
42
+ end
43
+
44
+ def inspect
45
+ "registry(#{@types.length} types)"
46
+ end
47
+
48
+ def to_s
49
+ @types.map.with_index do |type, index|
50
+ "#{@keys[index]} => #{type.to_s}"
51
+ end.join("\n")
52
+ end
53
+
54
+ # % use :: String -> Integer
55
+ def use(old_type_exp)
56
+ raise "Empty old_type_exp: #{old_type_exp}" if old_type_exp.nil? || old_type_exp.strip == ''
57
+
58
+ ast_type = TypeExp.parse(old_type_exp)
59
+ raise "No AST type for #{old_type_exp}" if ast_type.nil?
60
+
61
+ key = ast_type.to_s
62
+ ti = lookup[key]
63
+ return ti if ti
64
+
65
+ ti = @types.length
66
+ @types[ti] = "Placeholder"
67
+ @lookup[key] = ti
68
+ @keys[ti] = key
69
+ @types[ti] = build_portable_type(ast_type)
70
+ ti
71
+ end
72
+
73
+ # % build_portable_type :: NamedType | ArrayType | TupleType -> PortableType
74
+ # __ :build_portable_type, { ast_type: TypedArray[TypeExp::ArrayType | TypeExp::TupleType | TypeExp::NamedType] } => PortableType
75
+ def build_portable_type(ast_type)
76
+ case ast_type
77
+ when TypeExp::ArrayType
78
+ ArrayType.new(use(ast_type.item), ast_type.len, registry: self)
79
+ when TypeExp::TupleType
80
+ TupleType.new(ast_type.params.map { |param| use(param) })
81
+ when TypeExp::NamedType
82
+ build_portable_type_from_named_type(ast_type)
83
+ else
84
+ raise "Unknown type: #{ast_type.class}"
85
+ end
86
+ end
87
+
88
+ # % build_portable_type_from_named_type :: NamedType -> PortableType
89
+ def build_portable_type_from_named_type(named_type)
90
+ name = named_type.name
91
+ params = named_type.params
92
+
93
+ definition = @old_types[name.to_sym]
94
+ return build_from_definition(name, definition) if definition
95
+
96
+ primitive = as_primitive(name)
97
+ return primitive if primitive
98
+
99
+ case name
100
+ when 'Vec'
101
+ item_index = use(params[0].to_s)
102
+ SequenceType.new(type: item_index, registry: self)
103
+ when 'Option'
104
+ item_index = use(params[0].to_s)
105
+ VariantType.option(item_index, self)
106
+ when 'Result'
107
+ ok_index = use(params[0].to_s)
108
+ err_index = use(params[1].to_s)
109
+ VariantType.result(ok_index, err_index, self)
110
+ when 'Compact'
111
+ # item_index = use(params[0].to_s)
112
+ # CompactType.new(type: item_index, registry: self)
113
+ CompactType.new
114
+ when 'Null'
115
+ UnitType.new
116
+ else
117
+ raise "Unknown type: #{name}"
118
+ end
119
+ end
120
+
121
+ # % as_primitive :: String -> PrimitiveType | nil
122
+ def as_primitive(name)
123
+ case name.downcase
124
+ when /^i\d+$/
125
+ PrimitiveType.new(primitive: "I#{name[1..]}".to_sym)
126
+ when /^u\d+$/
127
+ PrimitiveType.new(primitive: "U#{name[1..]}".to_sym)
128
+ when /^bool$/
129
+ PrimitiveType.new(primitive: :Bool)
130
+ when /^str$/, /^text$/
131
+ PrimitiveType.new(primitive: :Str)
132
+ else
133
+ nil
134
+ end
135
+ end
136
+
137
+ # % build_from_definition :: String -> OldTypeDefinition -> PortableType | TypeAlias
138
+ #
139
+ # type OldTypeDefinition = String | OldEnumDefinition | OldStructDefinition
140
+ # type OldEnumDefinition = {
141
+ # _enum: String[] | Hash<Symbol, Any>,
142
+ # }
143
+ # type OldStructDefinition = {
144
+ # _struct: Hash<Symbol, Any>
145
+ # }
146
+ def build_from_definition(name, definition) # rubocop:disable Metrics/MethodLength
147
+ case definition
148
+ when String
149
+ # TypeAlias.new(name, use(definition))
150
+ alias_type_id = use(definition)
151
+ # p "alias_type_id: #{alias_type_id}"
152
+ types[alias_type_id]
153
+ when Hash
154
+ if definition[:_enum]
155
+ _build_portable_type_from_enum_definition(definition)
156
+ elsif definition[:_set]
157
+ raise 'Sets are not supported'
158
+ else
159
+ _build_portable_type_from_struct_definition(definition)
160
+ end
161
+ end
162
+ end
163
+
164
+ private
165
+
166
+ def _indexed_enum?(definition)
167
+ definition[:_enum].is_a?(::Hash) && definition[:_enum].values.all? { |value| value.is_a?(::Integer) }
168
+ end
169
+
170
+ # % _build_portable_type_from_enum_definition :: Hash<Symbol, Any> -> VariantType
171
+ def _build_portable_type_from_enum_definition(definition)
172
+ variants =
173
+ if definition[:_enum].is_a?(::Array)
174
+ # Simple array enum:
175
+ # {
176
+ # _enum: ['A', 'B', 'C']
177
+ # }
178
+ definition[:_enum].map.with_index do |variant_name, index|
179
+ SimpleVariant.new(name: variant_name.to_sym, index:)
180
+ end
181
+ elsif definition[:_enum].is_a?(::Hash)
182
+ if _indexed_enum?(definition)
183
+ # Indexed enum:
184
+ # {
185
+ # _enum: {
186
+ # Variant1: 0,
187
+ # Variant2: 1,
188
+ # Variant3: 2
189
+ # }
190
+ # }
191
+ definition[:_enum].map do |variant_name, index|
192
+ SimpleVariant.new(name: variant_name, index:)
193
+ end
194
+ else
195
+ # Mixed enum:
196
+ # {
197
+ # _enum: {
198
+ # A: 'u32',
199
+ # B: {a: 'u32', b: 'u32'},
200
+ # C: null,
201
+ # D: ['u32', 'u32']
202
+ # }
203
+ # }
204
+ definition[:_enum].map.with_index do |(variant_name, variant_def), index|
205
+ case variant_def
206
+ when ::String
207
+ TupleVariant.new(
208
+ name: variant_name,
209
+ index:,
210
+ tuple: TupleType.new(
211
+ tuple: [use(variant_def)],
212
+ registry: self
213
+ ),
214
+ )
215
+ when ::Array
216
+ TupleVariant.new(
217
+ name: variant_name,
218
+ index:,
219
+ tuple: TupleType.new(
220
+ tuple: variant_def.map { |field_type| use(field_type) },
221
+ registry: self
222
+ )
223
+ )
224
+ when ::Hash
225
+ StructVariant.new(
226
+ name: variant_name,
227
+ index:,
228
+ struct: StructType.new(
229
+ fields: variant_def.map do |field_name, field_type|
230
+ Field.new(name: field_name.to_s, type: use(field_type))
231
+ end,
232
+ registry: self
233
+ )
234
+ )
235
+ else
236
+ raise "Unknown variant type for #{variant_name}: #{variant_def.class}"
237
+ end
238
+ end
239
+ end
240
+ end
241
+ VariantType.new(variants:, registry: self)
242
+ end
243
+
244
+ # % _build_portable_type_from_struct_definition :: Hash<Symbol, Any> -> StructType
245
+ def _build_portable_type_from_struct_definition(definition)
246
+ fields = definition.map do |field_name, field_type|
247
+ Field.new(name: field_name.to_s, type: use(field_type))
248
+ end
249
+ StructType.new(fields:, registry: self)
250
+ end
251
+ end
252
+ end
253
+ end
254
+
255
+ # require_relative '../../metadata/metadata'
256
+
257
+ # begin
258
+ # registry = ScaleRb::Metadata::Registry.new ScaleRb::Metadata::TYPES
259
+ # puts registry
260
+ # rescue StandardError => e
261
+ # puts e.message
262
+ # puts e.backtrace.join("\n")
263
+ # end
@@ -0,0 +1,286 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ScaleRb
4
+ module Metadata
5
+ module TypeExp
6
+ class Tokenizer
7
+ attr_reader :tokens, :index
8
+
9
+ # % tokenize :: String -> [String]
10
+ def initialize(type_exp)
11
+ @tokens = tokenize(type_exp)
12
+ @index = 0
13
+ end
14
+
15
+ # % next_token :: -> String
16
+ def next_token
17
+ token = @tokens[@index]
18
+ @index += 1
19
+ token
20
+ end
21
+
22
+ # % peek_token :: -> String
23
+ def peek_token
24
+ @tokens[@index]
25
+ end
26
+
27
+ # % eof? :: -> Bool
28
+ def eof?
29
+ @index >= @tokens.length
30
+ end
31
+
32
+ private
33
+
34
+ def tokenize(type_exp)
35
+ tokens = []
36
+ current_token = ''
37
+
38
+ type_exp.each_char do |char|
39
+ case char
40
+ when /[a-zA-Z0-9_]/
41
+ current_token += char
42
+ when ':', '<', '>', '(', ')', '[', ']', ',', ';', '&', "'"
43
+ tokens << current_token unless current_token.empty?
44
+ if char == ':' && tokens.last == ':'
45
+ tokens[-1] = '::'
46
+ else
47
+ tokens << char
48
+ end
49
+ current_token = ''
50
+ when /\s/
51
+ tokens << current_token unless current_token.empty?
52
+ current_token = ''
53
+ else
54
+ raise abort
55
+ end
56
+ end
57
+
58
+ tokens << current_token unless current_token.empty?
59
+ tokens
60
+ end
61
+ end
62
+
63
+ class NamedType
64
+ attr_reader :name, :params
65
+
66
+ def initialize(name, params)
67
+ @name = name
68
+ @params = params
69
+ end
70
+
71
+ def to_s
72
+ params.empty? ? name : "#{name}<#{params.map(&:to_s).join(', ')}>"
73
+ end
74
+ end
75
+
76
+ class ArrayType
77
+ attr_reader :item, :len
78
+
79
+ def initialize(item, len)
80
+ @item = item
81
+ @len = len
82
+ end
83
+
84
+ def to_s
85
+ "[#{item}; #{len}]"
86
+ end
87
+ end
88
+
89
+ class TupleType
90
+ attr_reader :params
91
+
92
+ def initialize(params)
93
+ @params = params
94
+ end
95
+
96
+ def to_s
97
+ "(#{params.map(&:to_s).join(', ')})"
98
+ end
99
+ end
100
+
101
+ # % print :: NamedType | ArrayType | TupleType -> String
102
+ def self.print(type)
103
+ type.to_s
104
+ end
105
+
106
+ # % parse :: String -> NamedType | ArrayType | TupleType
107
+ def self.parse(type_exp)
108
+ TypeExpParser.new(type_exp).parse
109
+ end
110
+
111
+ class TypeExpParser
112
+ def initialize(type_exp)
113
+ @type_exp = type_exp
114
+ @tokenizer = Tokenizer.new(type_exp)
115
+ @current_token = @tokenizer.next_token
116
+ end
117
+
118
+ def parse
119
+ build_type
120
+ end
121
+
122
+ private
123
+
124
+ # Consume and return the current token, or nil if it doesn't equal the expected token.
125
+ def expect(token)
126
+ return unless @current_token == token
127
+
128
+ current_token = @current_token
129
+ @current_token = @tokenizer.next_token
130
+ current_token
131
+ end
132
+
133
+ def expect!(token)
134
+ expect(token) || raise("Expected #{token}, got #{@current_token}")
135
+ end
136
+
137
+ # Consume and return the current token if it matches the expected regex pattern.
138
+ def expect_regex(pattern)
139
+ return unless pattern.match?(@current_token)
140
+
141
+ current_token = @current_token
142
+ @current_token = @tokenizer.next_token
143
+ current_token
144
+ end
145
+
146
+ def expect_regex!(pattern)
147
+ expect_regex(pattern) || raise("Expected current token matching #{pattern.inspect}, got #{@current_token}")
148
+ end
149
+
150
+ # Consume and return a natural number (integer) if the current token matches.
151
+ def expect_nat
152
+ expect_regex(/^\d+$/)&.to_i
153
+ end
154
+
155
+ def expect_nat!
156
+ expect_nat || raise("Expected natural number, got #{@current_token}")
157
+ end
158
+
159
+ def expect_name
160
+ expect_regex(/^[a-zA-Z]\w*$/)
161
+ end
162
+
163
+ def expect_name!
164
+ expect_name || raise("Expected name, got #{@current_token}")
165
+ end
166
+
167
+ def list(sep, &block)
168
+ result = []
169
+ item = block.call
170
+ return result if item.nil?
171
+
172
+ result << item
173
+ while expect(sep)
174
+ item = block.call
175
+ break if item.nil? # (A, B,)
176
+
177
+ result << item
178
+ end
179
+ result
180
+ end
181
+
182
+ def build_tuple_type
183
+ return nil unless expect('(')
184
+
185
+ params = list(',') { build_type }
186
+ expect!(')')
187
+
188
+ TupleType.new(params)
189
+ end
190
+
191
+ # [u8; 16; H128]
192
+ # [u8; 16]
193
+ def build_array_type
194
+ return nil unless expect('[')
195
+
196
+ item = build_type
197
+ raise "Expected array item, got #{@current_token}" if item.nil?
198
+
199
+ expect!(';')
200
+ len = expect_nat!
201
+
202
+ # [u8; 16; H128]
203
+ if expect(';')
204
+ expect_name! # Just consume the name
205
+ end
206
+
207
+ expect!(']')
208
+ ArrayType.new(item, len)
209
+ end
210
+
211
+ def build_named_type
212
+ name = nil
213
+ trait = nil
214
+ item = nil
215
+
216
+ if expect('<')
217
+ # Handle trait syntax: <T::Trait as OtherTrait>::Type
218
+ # name trait item
219
+ # '<T::InherentOfflineReport as InherentOfflineReport>::Inherent' -> 'InherentOfflineReport'
220
+ # '<T::Balance as HasCompact>' -> 'Compact<Balance>'
221
+ # '<T as Trait<I>>::Proposal' -> 'Proposal'
222
+ name = build_named_type.name
223
+ expect!('as')
224
+ trait = build_named_type.name
225
+ expect!('>')
226
+ else
227
+ name = expect_name
228
+ return if name.nil?
229
+ end
230
+
231
+ # Consume the :: and get the next name
232
+ item = expect_name while expect('::')
233
+
234
+ # Handle special cases
235
+ # From subsquid's code. But where are these coming from?
236
+ if name == 'InherentOfflineReport' && name == trait && item == 'Inherent'
237
+ # Do nothing
238
+ elsif name == 'exec' && item == 'StorageKey'
239
+ name = 'ContractStorageKey'
240
+ elsif name == 'Lookup' && item == 'Source'
241
+ name = 'LookupSource'
242
+ elsif name == 'Lookup' && item == 'Target'
243
+ name = 'LookupTarget'
244
+ elsif item
245
+ # '<T::Balance as HasCompact>::Item' will raise error
246
+ raise "Expected item, got #{item}" if trait == 'HasCompact'
247
+
248
+ name = item
249
+ elsif trait == 'HasCompact' # '<T::Balance as HasCompact>'
250
+ return NamedType.new('Compact', [NamedType.new(name, [])])
251
+ end
252
+
253
+ NamedType.new(name, type_parameters)
254
+ end
255
+
256
+ def type_parameters
257
+ if expect('<')
258
+ params = list(',') { expect_nat || build_type }
259
+ expect!('>')
260
+ else
261
+ params = []
262
+ end
263
+
264
+ params
265
+ end
266
+
267
+ # &[u8]
268
+ # &'static [u8]
269
+ def build_pointer_bytes
270
+ return nil unless expect('&') # &
271
+
272
+ expect("'") && expect!('static')
273
+ expect!('[')
274
+ expect!('u8')
275
+ expect!(']')
276
+ NamedType.new('Vec', [NamedType.new('u8', [])])
277
+ end
278
+
279
+ # % build_type :: TupleType | ArrayType | NamedType
280
+ def build_type
281
+ build_tuple_type || build_array_type || build_named_type || build_pointer_bytes
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end