scale_rb 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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