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.
@@ -0,0 +1,186 @@
1
+ module Types
2
+ class Typed ## note: use class Typed as namespace (all metatype etc. nested here - the beginning)
3
+
4
+
5
+
6
+ ## todo - break-up literal_norm and move into types - why? why not?
7
+ class Type
8
+
9
+ def raise_type_error(literal)
10
+ ## change to typeerror or such - why? why not?
11
+ raise TypeError, "expected type #{self.format}; got #{literal.inspect}"
12
+ end
13
+
14
+
15
+ def parse_integer(literal)
16
+ base = literal.start_with?( '0x' ) ? 16 : 10
17
+
18
+ begin
19
+ Integer(literal, base)
20
+ rescue ArgumentError
21
+ raise_type_error( literal )
22
+ end
23
+ end
24
+
25
+
26
+ def check_and_normalize_literal( literal )
27
+ ### todo/check - split up and move to type classes - why? why not?
28
+
29
+ ## fix fix fix: allow typed passed in as literals here?
30
+ ## if literal.is_a?(TypedVariable)
31
+ ## raise TypeError, "Only literals can be passed to check_and_normalize_literal: #{literal.inspect}"
32
+ ## end
33
+
34
+
35
+ if is_a?(AddressType)
36
+ ## fix fix fix: add contract support
37
+ # if literal.is_a?(ContractType::Proxy)
38
+ # return literal.address
39
+ # end
40
+
41
+ unless literal.is_a?(::String) && literal.match?(/\A0x[a-f0-9]{40}\z/i)
42
+ raise_type_error(literal)
43
+ end
44
+
45
+ ## note: always downcase & freeze address - why? why not?
46
+ return literal == ADDRESS_ZERO ? literal : literal.downcase.freeze
47
+ elsif is_a?( ContractType )
48
+ ## elsif is_contract_type?
49
+ ## uses in original.
50
+ ## def is_contract_type?
51
+ ## ContractImplementation.valid_contract_types.include?(name)
52
+ ## end
53
+
54
+ ## todo/check - use a different base class for contracts - why? why not?
55
+ ## fix fix fix: check matching contract type/class too - why? why not?
56
+ if literal.is_a?( ContractBase )
57
+ return literal
58
+ else
59
+ raise TypeError, "No literals allowed for contract types got: #{literal}; sorry"
60
+ end
61
+
62
+
63
+ elsif is_a?(UIntType)
64
+ if literal.is_a?(::String)
65
+ literal = parse_integer(literal)
66
+ end
67
+
68
+ if literal.is_a?(::Integer) && literal.between?(0, 2 ** 256 - 1)
69
+ return literal
70
+ end
71
+
72
+ raise_type_error(literal)
73
+ elsif is_a?( IntType )
74
+ if literal.is_a?(::String)
75
+ literal = parse_integer(literal)
76
+ end
77
+
78
+ if literal.is_a?(::Integer) && literal.between?(-2 ** 255, 2 ** 255 - 1)
79
+ return literal
80
+ end
81
+
82
+ raise_type_error(literal)
83
+ elsif is_a?( EnumType )
84
+ if literal.is_a?( ::Integer ) ## todo - check literal is withing min/max
85
+ return literal
86
+ end
87
+ raise_type_error(literal)
88
+ elsif is_a?( StringType )
89
+ unless literal.is_a?( ::String)
90
+ raise_type_error(literal)
91
+ end
92
+
93
+ return literal.freeze
94
+ elsif is_a?( BoolType )
95
+ ## fix-fix-fix- check if solidity support 0|1 for bools in function args???
96
+ unless literal == true || literal == false
97
+ raise_type_error(literal)
98
+ end
99
+
100
+ return literal
101
+ elsif is_a?( InscriptionIdType ) || is_a?( Bytes32Type )
102
+ unless literal.is_a?( ::String) && literal.match?(/\A0x[a-f0-9]{64}\z/i)
103
+ raise_type_error(literal)
104
+ end
105
+
106
+ ## note: always downcase & freeze address - why? why not?
107
+ ## fix-fix-fix - check - use BYTES32_ZERO - why? why not?
108
+ return literal == INSCRIPTION_ID_ZERO ? literal : literal.downcase.freeze
109
+
110
+ elsif is_a?( BytesType )
111
+ ## note: assume empty string is bytes literal!!!
112
+ if literal.is_a?( ::String) && literal.length == 0
113
+ return literal
114
+ end
115
+
116
+ unless literal.is_a?( ::String) &&
117
+ literal.match?(/\A0x[a-fA-F0-9]*\z/) &&
118
+ literal.size.even?
119
+ raise_type_error( literal )
120
+ end
121
+
122
+ ##
123
+ ## check if dynamic? (like bytebuffer) - freeze? why? why not?
124
+ return literal.downcase
125
+
126
+
127
+ elsif is_a?( TimestampType ) || is_a?( TimedeltaType )
128
+ dummy_uint = UIntType.instance
129
+
130
+ begin
131
+ return dummy_uint.check_and_normalize_literal(literal)
132
+ rescue TypeError ## TypeError => e
133
+ raise_type_error(literal)
134
+ end
135
+ elsif is_a?( MappingType )
136
+ if literal.is_a?( TypedMapping) ## todo - check if possible (literal) typed mapping
137
+ return literal
138
+ end
139
+
140
+ unless literal.is_a?(Hash)
141
+ raise_type_error(literal)
142
+ end
143
+
144
+ ## add types (wrap literal in types)
145
+ ## todo - do a quick check - if hash populated with vars - why? why not?
146
+ ## todo/fix: check for nested arrays/mappings!!!
147
+ ## do NOT wrap in SafeMapping/SafeArray
148
+ data = literal.map do |key, value|
149
+ [
150
+ key_type.check_and_normalize_literal( key ),
151
+ value_type.check_and_normalize_literal( value )
152
+ ]
153
+ end.to_h
154
+
155
+ return data
156
+ elsif is_a?( ArrayType )
157
+
158
+ ## todo/fix: check for matching sub_type!!!!
159
+ ## check if possible to get TypedArray passed in as literal!!!
160
+ if literal.is_a?(TypedArray)
161
+ return literal ## .data ## note: return nested (inside) data e.g. array!!!
162
+ end
163
+
164
+ unless literal.is_a?(::Array)
165
+ raise_type_error(literal)
166
+ end
167
+
168
+ ## check types only - wrap literal in types - why? why not?
169
+ data = literal.map do |value|
170
+ sub_type.check_and_normalize_literal( value )
171
+ end
172
+
173
+ return data
174
+ end
175
+
176
+
177
+ raise TypeError, "invalid type; expected #{self.format}; got #{literal.inspect}"
178
+ end
179
+
180
+
181
+ end # class Type
182
+
183
+
184
+ end # class Typed ## note: use class Typed as namespace (all metatype etc. nested here - the beginning)
185
+ end # module Types
186
+
@@ -0,0 +1,46 @@
1
+ module Types
2
+ class Typed ## note: use class Typed as namespace (all metatype etc. nested here - the beginning)
3
+
4
+
5
+ class MappingType < ReferenceType
6
+ def self.instance( key_type, value_type )
7
+ raise ArgumentError, "[MappingType.instance] key_type not a type - got #{key_type}; sorry" unless key_type.is_a?( Type )
8
+ raise ArgumentError, "[MappingType.instance] value_type not a type - got #{value_type}; sorry" unless value_type.is_a?( Type )
9
+ @instances ||= {}
10
+ @instances[ key_type.format+"=>"+value_type.format ] ||= new(key_type, value_type)
11
+ end
12
+
13
+
14
+ attr_reader :key_type
15
+ attr_reader :value_type
16
+ def initialize( key_type, value_type )
17
+ @key_type = key_type
18
+ @value_type = value_type
19
+ end
20
+ def format() "mapping(#{@key_type.format}=>#{@value_type.format})"; end
21
+ alias_method :to_s, :format
22
+
23
+ def ==(other)
24
+ other.is_a?( MappingType ) &&
25
+ @key_type == other.key_type &&
26
+ @value_type == other.value_type
27
+ end
28
+
29
+
30
+ def typedclass_name
31
+ ## return/use symbol (not string here) - why? why not?
32
+ ## or use TypedMappingOf<key-type.typedclass_name><value_type...> - why? why not?
33
+ key_name = _sanitize_class_name( key_type.typedclass_name )
34
+ value_name = _sanitize_class_name( value_type.typedclass_name )
35
+ "Mapping‹#{key_name}→#{value_name}›"
36
+ end
37
+ def typedclass() Types.const_get( typedclass_name ); end
38
+
39
+ def mut?() true; end
40
+ def new_zero() typedclass.new; end
41
+ def new( initial_value ) typedclass.new( initial_value ); end
42
+ end # class MappingType
43
+
44
+
45
+ end # class Typed ## note: use class Typed as namespace (all metatype etc. nested here - the beginning)
46
+ end # module Types