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,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