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,52 @@
1
+ ###
2
+ # add global type conversion functions here
3
+ #
4
+ # include via Kernel module - why? why not?
5
+
6
+
7
+ module ConversionFunctions
8
+ #####
9
+ # todo/check: use AddressType.try_convert( literal_or_obj ) or such - why? why not?
10
+ def address( literal=0 )
11
+ ## hack for now support address(0)
12
+ ## todo/fix: address( '0x0' ) too!!!!
13
+ return Types::Typed::AddressType.instance.zero if literal.is_a?(::Integer) && literal == 0
14
+
15
+ Types::Typed::AddressType.instance.check_and_normalize_literal( literal )
16
+ end # methdod address
17
+
18
+
19
+ =begin
20
+ def uint( obj=0 )
21
+ ## check if typed?
22
+ ## raise exception on error
23
+ end
24
+ =end
25
+
26
+
27
+
28
+ =begin
29
+ def string(i)
30
+ if i.is_a?(TypedVariable) && i.type.is_value_type?
31
+ return TypedVariable.create(:string, i.value.to_s)
32
+ else
33
+ raise "Input must be typed"
34
+ end
35
+ end
36
+
37
+ def address(i)
38
+ return TypedVariable.create(:address) if i == 0
39
+
40
+ if i.is_a?(TypedVariable) && i.type == Type.create(:addressOrDumbContract)
41
+ return TypedVariable.create(:address, i.value)
42
+ end
43
+
44
+ raise "Not implemented"
45
+ end
46
+ =end
47
+
48
+ end # module ConversionFunctions
49
+
50
+
51
+
52
+ Kernel.include( ConversionFunctions )
@@ -0,0 +1,116 @@
1
+ ###############################
2
+ ## base class for enum
3
+ #
4
+ #
5
+ # notes on enum:
6
+ # do NOT ever create new typed enums with new/create!!!!
7
+ # only create zeros with new_zero!!!!!
8
+ # otherwise always reuse and assign CONSTANTS!!!!
9
+ ##
10
+ ## todo - fix-fix-fix - enforce rules in code here - why? why not?
11
+ ## make new private or such - why? why not?
12
+ ## check TrueClass|FalseClass as example - why? why not?
13
+
14
+ ## for bool and enum
15
+ ## use TypedData - only allow assignment of existing instances
16
+ ## no NEW possible!!!
17
+ ## thus - deserialize has a switch for TypedData!!
18
+
19
+ module Types
20
+ class Enum < Typed
21
+
22
+ def self.zero() members[0]; end
23
+ def zero?() self == self.class.zero; end ## note: use compare by identity (object_id) and NOT value e.g. 0
24
+
25
+
26
+ ## return a new Enum read-only class
27
+ attr_reader :key
28
+ attr_reader :value
29
+
30
+
31
+ def initialize( key, value )
32
+ @key = key
33
+ @value = value
34
+ self.freeze ## make "immutable"
35
+ self
36
+ end
37
+
38
+ def self._typecheck_enum!( o )
39
+ if o.instance_of?( self )
40
+ o
41
+ else
42
+ raise TypeError.new( "[enum] enum >#{name}< type expected; got >#{o.class.inspect}<" )
43
+ end
44
+ end
45
+ def _typecheck_enum!( o ) self.class._typecheck_enum!( o ); end
46
+
47
+
48
+ def ==( other )
49
+ if other.is_a?( Integer ) && other == 0 ## note: only allow compare by zero (0) integer - why? why not?
50
+ @value == 0
51
+ else
52
+ @value == _typecheck_enum!( other ).value
53
+ end
54
+ end
55
+ ## keep eql? compare by object_id(entity) - why? why not?
56
+ ### alias_method :eql?, :==
57
+
58
+
59
+ def self.keys
60
+ @keys ||= members.map {|member| member.key}.freeze
61
+ @keys
62
+ end
63
+
64
+ def self.key( key )
65
+ ## note: returns nil now for unknown keys
66
+ ## use/raise IndexError or something - why? why not?
67
+ @hash_by_key ||= Hash[ keys.zip( members ) ].freeze
68
+ @hash_by_key[key]
69
+ end
70
+
71
+ class << self
72
+ alias_method :[], :key ## convenience alias for key
73
+ end
74
+
75
+ def self.values
76
+ @values ||= members.map {|member| member.value}.freeze
77
+ @values
78
+ end
79
+
80
+ def self.value( value )
81
+ ## note: returns nil now for unknown values
82
+ ## use/raise IndexError or something - why? why not?
83
+ @hash_by_value ||= Hash[ values.zip( members ) ].freeze
84
+ @hash_by_value[value]
85
+ end
86
+
87
+ def self.min() members[0]; end
88
+ def self.max() members[-1]; end
89
+
90
+ def self.size() keys.size; end
91
+ class << self
92
+ alias_method :length, :size ## alias (as is the ruby tradition)
93
+ end
94
+
95
+ def self.convert( arg )
96
+ ## todo/check: support keys too - why? why not?
97
+ ## e.g. Color(0), Color(1)
98
+ ## Color(:red), Color(:blue) - why? why not?
99
+ ## note: will ALWAYS look-up by (member) index and NOT by value (integer number value might be different!!)
100
+ members[ arg ]
101
+ end
102
+
103
+ ## add to_i, to_int - why? why not?
104
+ def to_i() @value; end
105
+ def to_int() @value; end ## allows Integer( .. )
106
+
107
+ ## add to_b/to_bool support (see safebool @ https://github.com/s6ruby/safebool) - why? why not?
108
+ # def parse_bool() @value != 0; end ## nonzero == true, zero == false like numbers
109
+
110
+ def as_data() @value; end
111
+
112
+ def pretty_print( printer )
113
+ printer.text( "<val #{type}:#{@key}(#{@value})>" );
114
+ end
115
+ end # class Enum
116
+ end # module Types
@@ -0,0 +1,101 @@
1
+
2
+ module Types
3
+ class Enum
4
+
5
+ ###################
6
+ ## meta-programming "macro" - build class (on the fly)
7
+ #
8
+ # todo/fix: scope: keep empty by default
9
+
10
+
11
+
12
+ def self.build_class( class_name, *args, scope: Types )
13
+ if args.size > 0
14
+ ## e.g. :Color, :red, :green, :blue
15
+ ## or
16
+ ## :Color, [:red, :green, :blue]
17
+ ## note: start counting a zero (0) !!!
18
+ keys = args
19
+ values = (0...keys.size).to_a # note: use ... (exclusive) range
20
+ e = Hash[ keys.zip( values ) ]
21
+ else
22
+ raise ArgumentError, "[enum] no members declared - min. one member required; got #{args}; sorry"
23
+ end
24
+
25
+ ## todo/fix:
26
+ ## check class name MUST start with uppercase letter
27
+
28
+ ## check if all keys are symbols and follow the ruby id(entifier) naming rules
29
+ ## todo/check - allow uppercase fist A-Z letters - why? why not?
30
+ e.keys.each do |key|
31
+ if key.is_a?( Symbol ) && key =~ /\A[a-z][a-zA-Z0-9_]*\z/
32
+ else
33
+ raise ArgumentError.new( "[enum] members to enum must be all symbols following the ruby/solidity id naming rules; >#{key}< failed" )
34
+ end
35
+ end
36
+
37
+ klass = Class.new( Enum )
38
+
39
+
40
+ ## add self.new too - note: call/forward to "old" orginal self.new of Event (base) class
41
+ klass.define_singleton_method( :new ) do |*new_args|
42
+ raise ArgumentError, "enum #{klass.name} - do NOT call new EVER; reuse existing enum members! sorry"
43
+ end
44
+ ## make new - private too - why? why not?
45
+
46
+
47
+ e.each do |key,value|
48
+ klass.class_eval( <<RUBY )
49
+ #{key.upcase} = old_new( :#{key}, #{value} )
50
+
51
+ def #{key}?
52
+ self == #{key.upcase}
53
+ end
54
+
55
+ def self.#{key}
56
+ #{key.upcase}
57
+ end
58
+ RUBY
59
+ end
60
+
61
+ klass.class_eval( <<RUBY )
62
+ def self.members
63
+ @members ||= [#{e.keys.map {|key|key.upcase}.join(',')}].freeze
64
+ end
65
+ RUBY
66
+
67
+
68
+ type = EnumType.new( class_name, klass )
69
+ klass.define_singleton_method( :type ) do
70
+ @type ||= type
71
+ end
72
+
73
+ =begin
74
+ ## note: use Kernel for "namespacing"
75
+ ## make all enums Kernel convenience converters (always) global
76
+ ## including uppercase methods (e.g. State(), Color(), etc.) does NOT work otherwise (with other module includes)
77
+
78
+ ## add global convenience converter function
79
+ ## e.g. State(0) is same as State.convert(0)
80
+ ## Color(0) is same as Color.convert(0)
81
+ Kernel.class_eval( <<RUBY )
82
+ def #{class_name}( arg )
83
+ #{class_name}.convert( arg )
84
+ end
85
+ RUBY
86
+ =end
87
+ ## note: use scoped(class) and NO Object for namespacing
88
+ scope.const_set( class_name, klass ) ## returns klass (plus sets global constant class name)
89
+ klass
90
+ end
91
+
92
+
93
+ class << self
94
+ alias_method :old_new, :new # note: store "old" orginal version of new
95
+ alias_method :new, :build_class # replace original version with create
96
+ end
97
+
98
+
99
+
100
+ end # class Enum
101
+ end # module Types
@@ -0,0 +1,108 @@
1
+
2
+ module Types
3
+ class Mapping < TypedReference
4
+
5
+ def self.zero() @zero ||= new; end
6
+ def zero?() @data.empty? end
7
+
8
+ ## add short-cut helpers why? why not?
9
+ def key_type() self.class.type.key_type; end
10
+ def value_type() self.class.type.value_type; end
11
+
12
+ ## todo/check: make "internal" data/hash available? why? why not?
13
+ ## attr_reader :data
14
+
15
+ def initialize( initial_value = {} )
16
+ ## was: initial_value ||= {}
17
+ ## check if nil gets passed in - default not used?
18
+
19
+ raise ArgumentError, "expected literal of type #{type}; got typed #{initial_value.pretty_print_inspect}" if initial_value.is_a?( Typed )
20
+
21
+ @data = type.check_and_normalize_literal( initial_value ).map do |key, value|
22
+ [
23
+ type.key_type.new( key ),
24
+ type.value_type.new( value )
25
+ ]
26
+ end.to_h
27
+ end
28
+
29
+
30
+ extend Forwardable ## pulls in def_delegator
31
+ ## add more Hash forwards here!!!!
32
+ def_delegators :@data, :size, :empty?, :clear
33
+
34
+
35
+
36
+ def [](key)
37
+ puts "[debug] Mapping#[]( #{key} )"
38
+ key_var = key.is_a?( Typed ) ? key : key_type.new( key )
39
+ obj = @data[key_var]
40
+
41
+ ## was:
42
+ ## if value_type.mapping? && obj.nil?
43
+ ## obj =
44
+ ## @data[key_var] = obj
45
+ ## end
46
+ ##
47
+ ## note:
48
+ ## change to
49
+ ## allow access to ref to struct and than update change to assign !!=!!!!!!
50
+
51
+ if obj.nil?
52
+ obj = value_type.new_zero
53
+ @data[ key_var ] = obj
54
+ end
55
+
56
+ obj
57
+ end
58
+
59
+
60
+
61
+ def []=(key, new_value)
62
+ puts "[debug] Mapping#[]=( #{key}:#{key.class.name}, #{new_value}:#{new_value.class.name})"
63
+ pp type.key_type
64
+ key_var = key.is_a?( Typed ) ? key : key_type.new( key )
65
+ pp key_var
66
+ obj = new_value.is_a?( Typed ) ? new_value : value_type.new( new_value )
67
+ pp obj
68
+
69
+ if value_type.mapping?
70
+ ## val_var = Proxy.new(keytype: valuetype.keytype, valuetype: valuetype.valuetype)
71
+ raise 'What?'
72
+ end
73
+
74
+ @data[key_var] = obj
75
+ end
76
+
77
+
78
+ def key?( key )
79
+ key_var = key.is_a?( Typed ) ? key : key_type.new( key )
80
+ @data.key?( key_var )
81
+ end
82
+ alias_method :has_key?, :key?
83
+
84
+ def delete( key )
85
+ key_var = key.is_a?( Typed ) ? key : key_type.new( key )
86
+ @data.delete( key_var )
87
+ end
88
+
89
+
90
+ ## note: pass through value!!!!
91
+ ## in new scheme - only "plain" ruby arrays/hash/string/integer/bool used!!!!
92
+ ## no wrappers!!! - no need to convert!!!
93
+ def as_data
94
+ puts "[debug] Mapping#as_data"
95
+ @data.reduce({}) do |h, (k, v)|
96
+ ## todo/fix:
97
+ ## check if value is zero/ do not serialize zero - why? why not?
98
+ h[k.as_data] = v.as_data
99
+ h
100
+ end
101
+ end
102
+
103
+ def pretty_print( printer )
104
+ printer.text( "<ref #{type}:#{@data.pretty_print_inspect}>" );
105
+ end
106
+ end # class Mapping
107
+ end # module Types
108
+
@@ -0,0 +1,54 @@
1
+
2
+ module Types
3
+
4
+ class Mapping
5
+
6
+ def self.build_class( key_type, value_type )
7
+
8
+ # key_type = Type.create( key_type ) if key_type.is_a?( Symbol ) ||
9
+ # key_type.is_a?( String )
10
+ # value_type = Type.create( value_type ) if value_type.is_a?( Symbol ) ||
11
+ # value_type.is_a?( String )
12
+ # key_type = key_type.type if key_type.is_a?( Class ) && key_type.ancestors.include?( Typed )
13
+ # value_type = value_type.type if value_type.is_a?( Class ) && value_type.ancestors.include?( Typed )
14
+
15
+ key_type = typeof( key_type )
16
+ value_type = typeof( value_type )
17
+
18
+ type = MappingType.instance( key_type, value_type )
19
+
20
+ class_name = type.typedclass_name
21
+
22
+ ## note: keep a class cache
23
+ ## note: klasses may have different init sizes (default 0)
24
+ cache = @@cache ||= {}
25
+ klass = cache[ class_name ]
26
+ ## fix-fix-fix - check if const klass defined for cache (no cache needed)!!!!!!!!
27
+
28
+ if klass.nil?
29
+ klass = Class.new( Mapping )
30
+ klass.define_singleton_method( :type ) do
31
+ @type ||= type
32
+ end
33
+
34
+ ## add self.new too - note: call/forward to "old" orginal self.new of Event (base) class
35
+ klass.define_singleton_method( :new ) do |*args|
36
+ old_new( *args )
37
+ end
38
+
39
+ ## add to cache for later (re)use
40
+ cache[ class_name ] = klass
41
+ Types.const_set( class_name, klass )
42
+ end
43
+
44
+ klass
45
+ end # method self.build_class
46
+
47
+ class << self
48
+ alias_method :old_new, :new # note: store "old" orginal version of new
49
+ alias_method :new, :build_class # replace original version with create
50
+ end
51
+
52
+
53
+ end # class Mapping
54
+ end # module Types
@@ -0,0 +1,56 @@
1
+ module Types
2
+ class Typed ## note: use class Typed as namespace (all metatype etc. nested here - the beginning)
3
+
4
+
5
+ class ArrayType < ReferenceType ## note: dynamic array for now (NOT fixed!!!! - add FixedArray - why? why not?)
6
+ def self.instance( sub_type, size=0 )
7
+ raise ArgumentError, "[ArrayType.instance] sub_type not a type - got #{sub_type}; sorry" unless sub_type.is_a?( Type )
8
+ @instances ||= {}
9
+ key = '' ## note: String.new('') will pick-up TypedString!!; thus, use literal for now
10
+ key += sub_type.format
11
+ key += "×#{size}" if size != 0
12
+ @instances[ key ] ||= new( sub_type, size )
13
+ end
14
+
15
+
16
+ attr_reader :sub_type
17
+ attr_reader :size ## rename size to dim(ension) or such - why? why not?
18
+ def initialize( sub_type, size=0 )
19
+ @sub_type = sub_type
20
+ @size = size
21
+ end
22
+ def format
23
+ if @size == 0 ## assume dynamic
24
+ "#{@sub_type.format}[]"
25
+ else
26
+ "#{@sub_type.format}[#{@size}]"
27
+ end
28
+ end
29
+ alias_method :to_s, :format
30
+
31
+ def ==(other)
32
+ other.is_a?( ArrayType ) &&
33
+ @sub_type == other.sub_type &&
34
+ @size == other.size ### add check for size too - why? why not?
35
+ end
36
+
37
+
38
+ def typedclass_name
39
+ ## return/use symbol (not string here) - why? why not?
40
+ ## or use TypedArrayOf<sub-type.typedclass_name> - why? why not?
41
+ sub_name = _sanitize_class_name( @sub_type.typedclass_name )
42
+ name = '' ## note: String.new('') will pick-up TypedString!!; thus, use literal for now
43
+ name += "Array‹#{sub_name}›"
44
+ name += "×#{size}" if @size != 0
45
+ name
46
+ end
47
+ def typedclass() Types.const_get( typedclass_name ); end
48
+
49
+ def mut?() true; end
50
+ def new_zero() typedclass.new; end
51
+ def new( initial_value ) typedclass.new( initial_value ); end
52
+ end # class ArrayType
53
+
54
+
55
+ end # class Typed ## note: use class Typed as namespace (all metatype etc. nested here - the beginning)
56
+ end # module Types
@@ -0,0 +1,39 @@
1
+ module Types
2
+ class Typed ## note: use class Typed as namespace (all metatype etc. nested here - the beginning)
3
+
4
+
5
+ class BoolType < DataType
6
+ def self.instance() @instance ||= new; end
7
+
8
+ def format() 'bool'; end
9
+ alias_method :to_s, :format
10
+
11
+ def ==(other) other.is_a?( BoolType ); end
12
+
13
+ ## note: class is really a module for bool - hack!!!
14
+ ## remove class from methods (typedclass_name|typedclass) - why? why not?
15
+ ## change typedclass_name to typed_name !!!
16
+ ## and typedclass to typed !!! - why? why not??
17
+ def typedclass_name() Bool.name; end
18
+ def typedclass() Bool; end
19
+
20
+ def mut?() false; end
21
+ def zero() Bool.zero; end ## note: Bool.zero is false
22
+ alias_method :new_zero, :zero ## add/keep (convenience) alias for new_zero - why? why not?
23
+ def new( initial_value ) ## check - add default zero value - why? why not?
24
+ ## allow integer 0|1 here too - why? why not?
25
+ ## fix-fix-fix check solidity functions with bool (if they use 0|1 or true|false)!!!
26
+ if initial_value == true # instance_of( TrueClass )
27
+ true
28
+ elsif initial_value == false # instance_of?( FalseClass )
29
+ false
30
+ else
31
+ ## use value error?
32
+ raise ArgumentError, "BoolType.new - true or false arg expected; got: #{initial_value}"
33
+ end
34
+ end
35
+ end # class BoolType
36
+
37
+ end # class Typed ## note: use class Typed as namespace (all metatype etc. nested here - the beginning)
38
+ end # module Types
39
+