solidity-typed 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,492 @@
1
+
2
+ ###
3
+ # Metatype classes like Class in ruby
4
+ # every Typed<> has a (Meta)Type class singleton (shared) instance that is_a?( Type )
5
+
6
+
7
+ ## note:
8
+ ## make bytes a reference type?
9
+ ## bytes NOT like string frozen?
10
+ ## more like a stringbuffer / bytesbuffer -
11
+ ## double check if "in-place" updates to value possible!!!
12
+
13
+
14
+
15
+ ###
16
+ # global helper(s) - move to ??? - why? why not?
17
+
18
+ def _sanitize_class_name( name )
19
+ name = name.sub( /\bContractBase::/, '' ) ## remove contract module from name if present
20
+ name = name.sub( /\bContract::/, '' ) ## remove contract module from name if present
21
+ name = name.sub( /\bTyped::/, '' )
22
+ name = name.sub( /\bTypes::/, '' )
23
+ name = name.sub( /\bTypedArray::/, '' )
24
+ name = name.sub( /\bTypedMapping::/, '' )
25
+ name = name.gsub( '::', '' ) ## remove module separator if present
26
+ name
27
+ end
28
+
29
+
30
+
31
+ module Types
32
+ class Typed ## note: use class Typed as namespace (all metatype etc. nested here - the beginning)
33
+
34
+ #######
35
+ ## global literal constants
36
+ ## add LITERAL_ eg. LITERAL_STRING_ZERO - why? why not?
37
+
38
+ STRING_ZERO = ''.freeze ## string with utf-8 encoding
39
+ BYTES_ZERO = String.new().freeze ## string with binary encoding
40
+ BYTES20_ZERO = ('0x'+'00'*20).freeze ## 20 bytes (40 hexchars) ## care about string encoding here - why? why not?
41
+ BYTES32_ZERO = ('0x'+'00'*32).freeze ## 32 bytes (64 hexchars)
42
+
43
+ ADDRESS_ZERO = BYTES20_ZERO
44
+ INSCRIPTION_ID_ZERO = INSCRIPTIONID_ZERO = BYTES32_ZERO
45
+
46
+
47
+
48
+ class Type
49
+ ## change format to type or typesig/type_sig
50
+ ## or sig or signature or typespec - why? why not?
51
+ def format
52
+ ## check/todo: what error to raise for not implemented / method not defined???
53
+ ### note: raise (will use RuntimeError/Exception?)
54
+ raise "no required format method defined for Type subclass #{self.class.name}; sorry"
55
+ end
56
+ ## return type signature string - why? why not?
57
+ ## e.g. string
58
+ ## mapping(address=>uint256)
59
+ ## string[]
60
+ ## and so on...
61
+ def pretty_print( printer ) printer.text( "<type #{format}>" ); end
62
+
63
+ def zero ## change to zero_literal (returns 0, [], {} - NOT typed version) - why? why not?
64
+ # ## check/todo: what error to raise for not implemented / method not defined???
65
+ raise "no required zero method defined for Type subclass #{self.class.name}; sorry"
66
+ end
67
+
68
+ def mut? # is mutable (read/write)
69
+ raise "no require mut(ability)? helper for Type subclass #{self.class.name}; sorry"
70
+ end
71
+
72
+
73
+ # ## todo/check - make "dynamic" e.g. structs with all value types still value type? - why? why not?
74
+ # ## check if is_value_type is used anywhere? remove!!!! - why? why not?
75
+ # def is_value_type?() is_a?( ValueType ); end
76
+
77
+
78
+ def mapping?() is_a?( MappingType ); end
79
+ def array?() is_a?( ArrayType ); end
80
+ ## add more metatype helpers here - why? why not?
81
+
82
+
83
+ ### todo/check for minimal required methods required for
84
+ ## compare and equal support??
85
+ def ==(other)
86
+ raise "no required == method defined for Type subclass #{self.class.name}; sorry"
87
+ end
88
+
89
+
90
+ ######
91
+ ## note: format MUST be unique for types
92
+ ## if self.format == other.format than same type
93
+ ## use for hash?
94
+ ## check default eql? is self.object_id == other.object_id ???
95
+ ### change to hash == other.hash; why? why not?
96
+
97
+ def hash() [format].hash; end ## add Type prefix or such - why? why not?
98
+ def eql?(other) hash == other.hash; end ## check eql? used for what?
99
+ end # class Type
100
+
101
+
102
+ ## use for (abstract) data types
103
+ ## e.g. bool, enums - why? why not?
104
+ ## can only (re)use constants (true|false or defined enums)
105
+ ## BUT not any new values!!!!
106
+ class DataType < Type; end
107
+
108
+ class ValueType < Type; end ## add value & reference type base - why? why not?
109
+
110
+ class ReferenceType < Type; end
111
+
112
+
113
+ ##
114
+ ## ruby note: is_a? and kind_of? are alias - the same
115
+ ## for "strict" checking use instance_of?
116
+
117
+
118
+ class StringType < ValueType ## note: strings are frozen / immutable - check again!!
119
+
120
+ def self.instance() @instance ||= new; end
121
+
122
+
123
+ def format() 'string'; end
124
+ alias_method :to_s, :format
125
+
126
+ def ==(other) other.is_a?( StringType ); end
127
+
128
+
129
+ ## note: use Types::String here to avoid confusion with ::String - why? why not?
130
+ ### -fix-fix-fix- remove typedclass for "primitives" - used anywhere - why? why not?
131
+ def typedclass_name() Types::String.name; end
132
+ def typedclass() Types::String; end
133
+
134
+ def mut?() false; end
135
+ def zero() Types::String.zero; end
136
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
137
+ def new( initial_value ) Types::String.new( initial_value ); end
138
+ end
139
+
140
+
141
+
142
+ class AddressType < ValueType
143
+
144
+ def self.instance() @instance ||= new; end
145
+
146
+ def format() 'address'; end
147
+ alias_method :to_s, :format
148
+
149
+ def ==(other) other.is_a?( AddressType ); end
150
+
151
+ def typedclass_name() Address.name; end
152
+ def typedclass() Address; end
153
+
154
+ def mut?() false; end
155
+ def zero() Address.zero; end
156
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
157
+ def new( initial_value ) Address.new( initial_value ); end
158
+ end
159
+
160
+
161
+
162
+
163
+ class InscriptionIdType < ValueType ## todo/check: rename to inscripeId or inscriptionId
164
+
165
+ def self.instance() @instance ||= new; end
166
+
167
+ def format() 'inscriptionId'; end
168
+ alias_method :to_s, :format
169
+
170
+ def ==(other) other.is_a?( InscriptionIdType ); end
171
+
172
+ def typedclass_name() InscriptionId.name; end
173
+ def typedclass() InscriptionId; end
174
+
175
+ def mut?() false; end
176
+ def zero() InscriptionId.zero; end
177
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
178
+ def new( initial_value ) InscriptionId.new( initial_value ); end
179
+ end
180
+
181
+
182
+ class Bytes32Type < ValueType
183
+ def self.instance() @instance ||= new; end
184
+
185
+
186
+ def format() 'bytes32'; end
187
+ alias_method :to_s, :format
188
+
189
+ def ==(other) other.is_a?( Bytes32Type ); end
190
+
191
+
192
+ def typedclass_name() Bytes32.name; end
193
+ def typedclass() Bytes32; end
194
+
195
+ def mut?() false; end
196
+ def zero() Bytes32.zero; end
197
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
198
+ def new( initial_value ) Bytes32.new( initial_value ); end
199
+ end
200
+
201
+
202
+ class BytesType < ValueType ### fix: see comments above - is bytes dynamic? or frozen?
203
+ def self.instance() @instance ||= new; end
204
+
205
+ def format() 'bytes'; end
206
+ alias_method :to_s, :format
207
+
208
+ def ==(other) other.is_a?( BytesType ); end
209
+
210
+ def typedclass_name() Bytes.name; end
211
+ def typedclass() Bytes; end
212
+
213
+ def mut?() false; end
214
+ def zero() Bytes.zero; end
215
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
216
+ def new( initial_value ) Bytes.new( initial_value ); end
217
+ end
218
+
219
+
220
+ class UIntType < ValueType
221
+ def self.instance() @instance ||= new; end
222
+
223
+ def format() 'uint'; end
224
+ alias_method :to_s, :format
225
+
226
+ ## note abi requires uint256!!! (not uint)
227
+ ## todo/check - rename to sig or abisig or selector or ???
228
+ def abi() 'uint256'; end
229
+
230
+ def ==(other) other.is_a?( UIntType ); end
231
+
232
+
233
+ def typedclass_name() UInt.name; end
234
+ def typedclass() UInt; end
235
+
236
+ def mut?() false; end
237
+ def zero() UInt.zero; end
238
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
239
+ def new( initial_value ) UInt.new( initial_value ); end
240
+ end
241
+
242
+
243
+ class IntType < ValueType
244
+ def self.instance() @instance ||= new; end
245
+
246
+ def format() 'int'; end
247
+ alias_method :to_s, :format
248
+
249
+ ## note abi requires uint256!!! (not uint)
250
+ ## todo/check - rename to sig or abisig or selector or ???
251
+ def abi() 'int256'; end
252
+
253
+
254
+ def ==(other) other.is_a?( IntType ); end
255
+
256
+
257
+ def typedclass_name() Int.name; end
258
+ def typedclass() Int; end
259
+
260
+ def mut?() false; end
261
+ def zero() Int.zero; end
262
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
263
+ def new( initial_value ) Int.new( initial_value ); end
264
+ end
265
+
266
+
267
+ class TimestampType < ValueType ## note: datetime is int (epoch time since 1970 in seconds in utc)
268
+ def self.instance() @instance ||= new; end
269
+
270
+ def format() 'timestamp'; end
271
+ alias_method :to_s, :format
272
+
273
+ ## check for abi sig format is it uint32 ???
274
+
275
+ def ==(other) other.is_a?( TimestampType ); end
276
+
277
+
278
+ def typedclass_name() Timestamp.name; end
279
+ def typedclass() Timestamp; end
280
+
281
+ def mut?() false; end
282
+ def zero() Timestamp.zero; end
283
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
284
+ def new( initial_value ) Timestamp.new( initial_value ); end
285
+ end
286
+
287
+
288
+ class TimedeltaType < ValueType
289
+ def self.instance() @instance ||= new; end
290
+
291
+ def format() 'timedelta'; end
292
+ alias_method :to_s, :format
293
+
294
+ def ==(other) other.is_a?( TimedeltaType ); end
295
+
296
+
297
+ def typedclass_name() Timedelta.name; end
298
+ def typedclass() Timedelta; end
299
+
300
+ def mut?() false; end
301
+ def zero() Timedelta.zero; end
302
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
303
+ def new( initial_value ) Timedelta.new( initial_value ); end
304
+ end
305
+
306
+
307
+
308
+
309
+
310
+ ### note: for bool and enum use DataType or AbstractDataType (ADT)
311
+ ## values must always be from existing set (CANNOT create new values/ones)
312
+ ## all instances are immutable/frozen and shared
313
+ class EnumType < Type
314
+ attr_reader :enum_name
315
+ attr_reader :enum_class ## reference enum_class here - why? why not?
316
+ def initialize( enum_name, enum_class )
317
+ @enum_name = enum_name
318
+ @enum_class = enum_class
319
+ end
320
+ def format
321
+ "#{enum_name} enum(#{enum_class.keys.join(',')})"
322
+ end
323
+ alias_method :to_s, :format
324
+
325
+ ## note abi requires uint8!!! 0-255 (8bit)
326
+ ## todo/check - rename to sig or abisig or selector or ???
327
+ def abi() 'uint8'; end
328
+
329
+
330
+ def ==(other)
331
+ other.is_a?( EnumType ) &&
332
+ @enum_name == other.enum_name && ## check for name too - why? why not?
333
+ @enum_class == other.enum_class
334
+ end
335
+
336
+
337
+ def typedclass_name() @enum_class.name; end
338
+ def typedclass() @enum_class; end
339
+
340
+ def mut?() false; end
341
+ def zero() @enum_class.zero; end
342
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
343
+ def new( initial_value )
344
+ ## allow new use here - why? why not?
345
+ @enum_class.members[ initial_value ]
346
+ end
347
+ end
348
+
349
+
350
+
351
+ class StructType < ReferenceType
352
+ attr_reader :struct_name
353
+ attr_reader :struct_class ## reference struct_class here - why? why not?
354
+ def initialize( struct_name, struct_class )
355
+ @struct_name = struct_name
356
+ @struct_class = struct_class
357
+ end
358
+ def format
359
+ ## use tuple here (not struct) - why? why not?
360
+ named_types = @struct_class.attributes.map {|key,type| "#{key} #{type.format}" }
361
+ "#{@struct_name} struct(#{named_types.join(',')})"
362
+ end
363
+ alias_method :to_s, :format
364
+
365
+
366
+ ### note: abi requires "tuple()"
367
+ def abi
368
+ types = @struct_class.attributes.map {|_,type| type.abi }
369
+ "tuples(#{types.join(',')})"
370
+ end
371
+
372
+ def ==(other)
373
+ other.is_a?( StructType ) &&
374
+ @struct_name == other.struct_name && ## check for name too - why? why not?
375
+ @struct_class == other.struct_class
376
+ end
377
+
378
+
379
+ def typedclass_name() @struct_class.name; end
380
+ def typedclass() @struct_class; end
381
+
382
+ ## note: mut? == true MUST use new_zero (dup)
383
+ ## mut? == false MUST use zero (frozen/shared/singelton)
384
+ def mut?() true; end
385
+ def new_zero() @struct_class.new_zero; end
386
+ def new( initial_values ) ## todo/check: change to values with splat - why? why not?
387
+ ## note: use "splat" here - must be empty or matching number of fields/attributes
388
+ ## change - why? why not?
389
+ @struct_class.new( *initial_values )
390
+ end
391
+ end # class StructType
392
+
393
+
394
+
395
+ ###
396
+ # event for now kind of like a struct - why? why not?
397
+ ## but MUST be initialized (and than frozen)
398
+ ## and no zero possible etc.
399
+
400
+ class EventType < ReferenceType
401
+ attr_reader :event_name
402
+ attr_reader :event_class ## reference event_class here - why? why not?
403
+ def initialize( event_name, event_class )
404
+ @event_name = event_name
405
+ @event_class = event_class
406
+ end
407
+ def format
408
+ ## use tuple here (not event) - why? why not?
409
+ named_types = @event_class.attributes.map {|key,type| "#{key} #{type.format}" }
410
+ "#{@event_name} event(#{named_types.join(',')})"
411
+ end
412
+ alias_method :to_s, :format
413
+
414
+ ## check what abi looks like if possible for event
415
+ ## is like tuple?
416
+
417
+ def ==(other)
418
+ other.is_a?( EventType ) &&
419
+ @event_name == other.event_name && ## check for name too - why? why not?
420
+ @event_class == other.event_class
421
+ end
422
+
423
+
424
+ def typedclass_name() @event_class.name; end
425
+ def typedclass() @event_class; end
426
+
427
+ ## note: mut? == true MUST use new_zero (dup)
428
+ ## mut? == false MUST use zero (frozen/shared/singelton)
429
+ def mut?() false; end
430
+ def zero
431
+ raise "event cannot be zero (by defintion); sorry"
432
+ end
433
+ alias_method :new_zero, :zero
434
+
435
+ def new( initial_values ) ## todo/check: change to values with splat - why? why not?
436
+ ## note: use "splat" here - must be empty or matching number of fields/attributes
437
+ ## change - why? why not?
438
+ @event_class.new( *initial_values )
439
+ end
440
+ end # class EventType
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+
449
+ ## todo/check
450
+ ## keep (internal) contract type??
451
+ ## - raise error on create?
452
+ ## - check what operations to support
453
+ ## - is like address (value type??)
454
+
455
+
456
+
457
+ class ContractType < ValueType
458
+
459
+ def self.instance( contract_type )
460
+ raise ArgumentError, "[ContractType.insntance] class expected for contract_type arg" unless contract_type.is_a?( Class )
461
+ @instances ||= {}
462
+ @instances[ contract_type.name ] ||= new( contract_type )
463
+ end
464
+
465
+ attr_reader :contract_type
466
+ ## note: assume for now contract_type is a (contract) class!!!!!
467
+ def initialize( contract_type )
468
+ raise ArgumentError, "[ContractType#initialize] class expected for contract_type arg" unless contract_type.is_a?( Class )
469
+ @contract_type = contract_type
470
+ end
471
+
472
+ def format() "contract(#{@contract_type.name})"; end
473
+ alias_method :to_s, :format
474
+
475
+ def ==(other)
476
+ other.is_a?( ContractType ) &&
477
+ @contract_type == other.contract_type
478
+ end
479
+
480
+
481
+ def mut?() false; end
482
+ ## add support with passed in address - why? why not?
483
+ def new( initial_value )
484
+ raise NameError, "no method create for ContractType; sorry"
485
+ end
486
+ end # class ContractType
487
+
488
+
489
+ ## note: use class Typed as namespace (all metatype etc. nested here - the end)
490
+ end # class Typed
491
+
492
+ end ## module Types
@@ -0,0 +1,108 @@
1
+ module Types
2
+
3
+
4
+ class UInt < TypedValue
5
+ def self.type() UIntType.instance; end
6
+ def self.zero() @zero ||= UInt.new; end
7
+ def zero?() @value == 0; end
8
+
9
+ def initialize( initial_value = 0 )
10
+ ## was: initial_value ||= type.zero
11
+ ## check if nil gets passed in - default not used?
12
+
13
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
14
+
15
+ @value = type.check_and_normalize_literal( initial_value )
16
+ @value.freeze ## freeze here (and freeze self!) - why? why not?
17
+ @value
18
+ end
19
+
20
+ include Comparable
21
+ def <=>(other) @value <=> other.to_int; end
22
+
23
+ def +(other ) UInt.new( @value + other.to_int); end
24
+ def -(other) UInt.new( @value - other.to_int); end
25
+ def *(other) UInt.new( @value * other.to_int); end
26
+ def /(other) UInt.new( @value / other.to_int); end
27
+ ## add more Integer forwards here!!!!
28
+ ##def_delegators :@value, :+, :-
29
+
30
+ ##
31
+ ## undefined method `>=' for #<UInt @value=21000000> (NoMethodError)
32
+ ## undefined method `-' for #<UInt @value=21000000> (NoMethodError)
33
+ ## def to_i() @value; end
34
+ def to_int() @value; end ## "automagilally" support implicit integer conversion - why? why not?
35
+ def to_i() @value; end
36
+ end # class UInt
37
+
38
+
39
+
40
+ class Int < TypedValue
41
+ def self.type() IntType.instance; end
42
+ def self.zero() @zero ||= Int.new; end
43
+ def zero?() @value == 0; end
44
+
45
+ def initialize( initial_value = 0 )
46
+ ## was: initial_value ||= type.zero
47
+ ## check if nil gets passed in - default not used?
48
+
49
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
50
+
51
+ @value = type.check_and_normalize_literal( initial_value )
52
+ @value.freeze ## freeze here (and freeze self!) - why? why not?
53
+ @value
54
+ end
55
+
56
+ include Comparable
57
+ def <=>(other) @value <=> other.to_int; end
58
+
59
+ def +(other ) Int.new( @value + other.to_int); end
60
+ def -(other) Int.new( @value - other.to_int); end
61
+ def *(other) Int.new( @value * other.to_int); end
62
+ def /(other) Int.new( @value / other.to_int); end
63
+
64
+
65
+ def to_int() @value; end ## "automagilally" support implicit integer conversion - why? why not?
66
+ def to_i() @value; end
67
+ end # class Int
68
+
69
+
70
+
71
+ class Timestamp < TypedValue
72
+ def self.type() TimestampType.instance; end
73
+ def self.zero() @zero ||= new; end
74
+ def zero?() @value == 0; end
75
+
76
+ def initialize( initial_value = 0 )
77
+ ## was: initial_value ||= type.zero
78
+ ## check if nil gets passed in - default not used?
79
+
80
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
81
+
82
+ @value = type.check_and_normalize_literal( initial_value )
83
+ @value.freeze ## freeze here (and freeze self!) - why? why not?
84
+ @value
85
+ end
86
+ end # class Timestamp
87
+
88
+
89
+ class Timedelta < TypedValue
90
+ def self.type() TimedeltaType.instance; end
91
+ def self.zero() @zero ||= new; end
92
+ def zero?() @value == 0; end
93
+
94
+ def initialize( initial_value = 0 )
95
+ ## was: initial_value ||= type.zero
96
+ ## check if nil gets passed in - default not used?
97
+
98
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
99
+
100
+ @value = type.check_and_normalize_literal( initial_value )
101
+ @value.freeze ## freeze here (and freeze self!) - why? why not?
102
+ @value
103
+ end
104
+ end # class Timedelta
105
+
106
+
107
+ end # module Types
108
+
@@ -0,0 +1,73 @@
1
+
2
+ module Types
3
+ class Struct < Typed
4
+
5
+ def self.zero
6
+ ## note: freeze return new zero (for "singelton" & "immutable" zero instance)
7
+ ## todo/fix:
8
+ ## in build_class add freeze for composite/reference objects
9
+ ## that is, arrays, hash mappings, structs etc.
10
+ ## freeze only works for now for "value" objects e.g. integer, bool, etc.
11
+ @zero ||= new_zero.freeze
12
+ end
13
+
14
+ def zero?() self == self.class.zero; end
15
+
16
+
17
+
18
+ def initialize( *args, **kwargs )
19
+ ## fix-fix-fix: first check for matching args - why? why not?
20
+ if kwargs.size > 0 ## assume kwargs init
21
+ self.class.attributes.each do |key, type|
22
+ ## note: allow unused keys (will get set to zero)
23
+ value = kwargs.has_key?( key ) ? kwargs[ key ] : type.new_zero
24
+ value = if value.is_a?( Typed )
25
+ ## fix-fix-fix - check type match here!!!
26
+ value
27
+ else
28
+ type.new( value )
29
+ end
30
+ instance_variable_set( "@#{key}", value )
31
+ end
32
+ else
33
+ self.class.attributes.zip( args ).each do |(key, type), value|
34
+ value = if value.is_a?(Typed)
35
+ ## fix-fix-fix - check type match here!!!
36
+ value
37
+ else
38
+ type.new( value )
39
+ end
40
+ instance_variable_set( "@#{key}", value )
41
+ end
42
+ end
43
+ self ## note: return reference to self for chaining method calls
44
+ end
45
+
46
+
47
+ def as_data
48
+ self.class.attributes.keys.map do |key|
49
+ ivar = instance_variable_get( "@#{key}" )
50
+ puts " @#{key}:"
51
+ pp ivar
52
+ ivar.as_data
53
+ end
54
+ end
55
+
56
+
57
+
58
+ def ==(other)
59
+ if other.is_a?( self.class )
60
+ self.class.attributes.keys.all? do |key|
61
+ __send__( key ) == other.__send__( key )
62
+ end
63
+ else
64
+ false
65
+ end
66
+ end
67
+ ## do note override eql? why? why not?
68
+ # default is object id equality???
69
+ ## alias_method :eql?, :==
70
+
71
+
72
+ end # class Struct
73
+ end # module Types