solidity-typed 0.1.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 +7 -0
- data/CHANGELOG.md +4 -0
- data/Manifest.txt +24 -0
- data/README.md +318 -0
- data/Rakefile +27 -0
- data/lib/solidity/typed/array.rb +166 -0
- data/lib/solidity/typed/array_builder.rb +53 -0
- data/lib/solidity/typed/bool.rb +47 -0
- data/lib/solidity/typed/conversion.rb +52 -0
- data/lib/solidity/typed/enum.rb +116 -0
- data/lib/solidity/typed/enum_builder.rb +101 -0
- data/lib/solidity/typed/mapping.rb +108 -0
- data/lib/solidity/typed/mapping_builder.rb +54 -0
- data/lib/solidity/typed/metatypes/array.rb +56 -0
- data/lib/solidity/typed/metatypes/bool.rb +39 -0
- data/lib/solidity/typed/metatypes/literals.rb +186 -0
- data/lib/solidity/typed/metatypes/mapping.rb +46 -0
- data/lib/solidity/typed/metatypes/types.rb +492 -0
- data/lib/solidity/typed/numbers.rb +108 -0
- data/lib/solidity/typed/struct.rb +73 -0
- data/lib/solidity/typed/struct_builder.rb +145 -0
- data/lib/solidity/typed/typed.rb +114 -0
- data/lib/solidity/typed/values.rb +113 -0
- data/lib/solidity/typed/version.rb +23 -0
- data/lib/solidity/typed.rb +128 -0
- metadata +111 -0
@@ -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
|