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.
- checksums.yaml +4 -4
- data/.devcontainer/devcontainer.json +21 -0
- data/Dockerfile +16 -0
- data/Gemfile +4 -4
- data/README.md +19 -6
- data/bin/console +0 -0
- data/bin/setup +0 -0
- data/examples/http_client_1.rb +0 -2
- data/examples/http_client_2.rb +0 -2
- data/examples/ws_client_1.rb +0 -2
- data/examples/ws_client_3.rb +1 -3
- data/examples/ws_client_4.rb +0 -2
- data/exe/metadata +9 -11
- data/lib/address.rb +1 -1
- data/lib/custom_assign.rb +92 -0
- data/lib/scale_rb/call_helper.rb +42 -0
- data/lib/{client → scale_rb/client}/client_ext.rb +12 -13
- data/lib/{client → scale_rb/client}/http_client.rb +3 -5
- data/lib/{client → scale_rb/client}/ws_client.rb +13 -20
- data/lib/scale_rb/codec.rb +25 -0
- data/lib/scale_rb/codec_utils.rb +128 -0
- data/lib/scale_rb/decode.rb +164 -0
- data/lib/scale_rb/encode.rb +150 -0
- data/lib/{hasher.rb → scale_rb/hasher.rb} +10 -8
- data/lib/scale_rb/metadata/metadata.rb +114 -0
- data/lib/{metadata → scale_rb/metadata}/metadata_v10.rb +0 -17
- data/lib/{metadata → scale_rb/metadata}/metadata_v11.rb +0 -17
- data/lib/{metadata → scale_rb/metadata}/metadata_v12.rb +0 -17
- data/lib/{metadata → scale_rb/metadata}/metadata_v13.rb +0 -17
- data/lib/{metadata → scale_rb/metadata}/metadata_v14.rb +18 -18
- data/lib/{metadata → scale_rb/metadata}/metadata_v9.rb +0 -17
- data/lib/scale_rb/metadata/registry.rb +263 -0
- data/lib/scale_rb/metadata/type_exp.rb +286 -0
- data/lib/scale_rb/portable_registry.rb +133 -0
- data/lib/{storage_helper.rb → scale_rb/storage_helper.rb} +16 -4
- data/lib/scale_rb/types.rb +233 -0
- data/lib/scale_rb/utils.rb +125 -0
- data/lib/scale_rb/version.rb +1 -1
- data/lib/scale_rb.rb +22 -30
- data/lib/type_enforcer.rb +170 -0
- data/scale_rb.gemspec +5 -0
- metadata +71 -19
- data/lib/codec.rb +0 -450
- data/lib/metadata/metadata.rb +0 -137
- data/lib/monkey_patching.rb +0 -115
- data/lib/portable_codec.rb +0 -285
- 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
|
9
|
-
|
8
|
+
types = metadata._get(:lookup, :types)
|
9
|
+
ScaleRb.build_types(types)
|
10
10
|
end
|
11
11
|
|
12
|
-
def get_module(pallet_name,
|
13
|
-
|
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,
|
19
|
-
|
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,
|
25
|
-
pallet = get_module(pallet_name,
|
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
|
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,
|
34
|
-
pallet = get_module(pallet_name,
|
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
|
37
|
+
pallet._get(:calls, :type)
|
38
38
|
end
|
39
39
|
|
40
|
-
def get_calls_type(pallet_name,
|
41
|
-
type_id = get_calls_type_id(pallet_name,
|
42
|
-
|
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,
|
48
|
-
calls_type = get_calls_type(pallet_name,
|
49
|
-
calls_type._get(:type
|
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
|