4
4
from blspy import G1Element
5
5
6
6
from .as_python import as_python
7
- from .CLVMObject import CLVMObject , SExpType
7
+ from .CLVMObject import CLVMObject
8
8
9
9
from .EvalError import EvalError
10
10
17
17
18
18
CastableType = typing .Union [
19
19
"SExp" ,
20
- CLVMObject ,
20
+ " CLVMObject" ,
21
21
bytes ,
22
+ str ,
22
23
int ,
23
24
None ,
24
- SExpType ,
25
25
G1Element ,
26
+ list ,
26
27
typing .Tuple [typing .Any , typing .Any ],
27
28
]
28
29
30
+
29
31
NULL = b""
30
32
31
33
34
+ def looks_like_clvm_object (o : typing .Any ) -> bool :
35
+ d = dir (o )
36
+ return "atom" in d and "pair" in d
37
+
38
+
39
+ # this function recognizes some common types and turns them into plain bytes,
40
+ def convert_atom_to_bytes (
41
+ v : typing .Union [bytes , str , int , G1Element , None , list ],
42
+ ) -> bytes :
43
+
44
+ if isinstance (v , bytes ):
45
+ return v
46
+ if isinstance (v , str ):
47
+ return v .encode ()
48
+ if isinstance (v , int ):
49
+ return int_to_bytes (v )
50
+ if isinstance (v , G1Element ):
51
+ return bytes (v )
52
+ if v is None :
53
+ return b""
54
+ if v == []:
55
+ return b""
56
+
57
+ raise ValueError ("can't cast %s (%s) to bytes" % (type (v ), v ))
58
+
59
+
60
+ # returns a clvm-object like object
32
61
def to_sexp_type (
33
62
v : CastableType ,
34
- ) -> SExpType :
63
+ ):
35
64
stack = [v ]
36
65
ops = [(0 , None )] # convert
37
66
38
67
while len (ops ) > 0 :
39
68
op , target = ops .pop ()
40
69
# convert value
41
70
if op == 0 :
71
+ if looks_like_clvm_object (stack [- 1 ]):
72
+ continue
42
73
v = stack .pop ()
43
74
if isinstance (v , tuple ):
44
75
if len (v ) != 2 :
45
76
raise ValueError ("can't cast tuple of size %d" % len (v ))
46
77
left , right = v
47
78
target = len (stack )
48
- stack .append (( left , right ))
49
- if type (right ) != CLVMObject :
79
+ stack .append (CLVMObject (( left , right ) ))
80
+ if not looks_like_clvm_object (right ):
50
81
stack .append (right )
51
82
ops .append ((2 , target )) # set right
52
83
ops .append ((0 , None )) # convert
53
- if type (left ) != CLVMObject :
84
+ if not looks_like_clvm_object (left ):
54
85
stack .append (left )
55
86
ops .append ((1 , target )) # set left
56
87
ops .append ((0 , None )) # convert
57
88
continue
58
- if isinstance (v , CLVMObject ):
59
- stack .append (v .pair or v .atom )
60
- continue
61
- if isinstance (v , bytes ):
62
- stack .append (v )
63
- continue
64
- if isinstance (v , str ):
65
- stack .append (v .encode ())
66
- continue
67
- if isinstance (v , int ):
68
- stack .append (int_to_bytes (v ))
69
- continue
70
- if isinstance (v , G1Element ):
71
- stack .append (bytes (v ))
72
- continue
73
- if v is None :
74
- stack .append (NULL )
75
- continue
76
- if v == []:
77
- stack .append (NULL )
78
- continue
79
-
80
- if hasattr (v , "__iter__" ):
89
+ if isinstance (v , list ):
81
90
target = len (stack )
82
- stack .append (NULL )
91
+ stack .append (CLVMObject ( NULL ) )
83
92
for _ in v :
84
93
stack .append (_ )
85
94
ops .append ((3 , target )) # prepend list
86
95
# we only need to convert if it's not already the right
87
96
# type
88
- if type (_ ) != CLVMObject :
97
+ if not looks_like_clvm_object (_ ):
89
98
ops .append ((0 , None )) # convert
90
99
continue
100
+ stack .append (CLVMObject (convert_atom_to_bytes (v )))
101
+ continue
91
102
92
- raise ValueError ("can't cast to CLVMObject: %s" % v )
93
103
if op == 1 : # set left
94
- stack [target ] = (CLVMObject (stack .pop ()), stack [target ][1 ])
104
+ stack [target ]. pair = (CLVMObject (stack .pop ()), stack [target ]. pair [1 ])
95
105
continue
96
106
if op == 2 : # set right
97
- stack [target ] = (stack [target ][0 ], CLVMObject (stack .pop ()))
107
+ stack [target ]. pair = (stack [target ]. pair [0 ], CLVMObject (stack .pop ()))
98
108
continue
99
109
if op == 3 : # prepend list
100
- stack [target ] = ( CLVMObject (stack .pop ()), CLVMObject ( stack [target ]))
110
+ stack [target ] = CLVMObject (( stack .pop (), stack [target ]))
101
111
continue
102
112
# there's exactly one item left at this point
103
113
if len (stack ) != 1 :
104
114
raise ValueError ("internal error" )
115
+
116
+ # stack[0] implements the clvm object protocol and can be wrapped by an SExp
105
117
return stack [0 ]
106
118
107
119
108
- class SExp (CLVMObject ):
120
+ class SExp :
121
+ """
122
+ SExp provides higher level API on top of any object implementing the CLVM
123
+ object protocol.
124
+ The tree of values is not a tree of SExp objects, it's a tree of CLVMObject
125
+ like objects. SExp simply wraps them to privide a uniform view of any
126
+ underlying conforming tree structure.
127
+
128
+ The CLVM object protocol (concept) exposes two attributes:
129
+ 1. "atom" which is either None or bytes
130
+ 2. "pair" which is either None or a tuple of exactly two elements. Both
131
+ elements implementing the CLVM object protocol.
132
+ Exactly one of "atom" and "pair" must be None.
133
+ """
109
134
true : "SExp"
110
135
false : "SExp"
111
136
__null__ : "SExp"
112
137
113
- def as_pair (self ):
138
+ # the underlying object implementing the clvm object protocol
139
+ atom : typing .Optional [bytes ]
140
+
141
+ # this is a tuple of the otherlying CLVMObject-like objects. i.e. not
142
+ # SExp objects with higher level functions, or None
143
+ pair : typing .Optional [typing .Tuple [typing .Any , typing .Any ]]
144
+
145
+ def __init__ (self , obj ):
146
+ self .atom = obj .atom
147
+ self .pair = obj .pair
148
+
149
+ # this returns a tuple of two SExp objects, or None
150
+ def as_pair (self ) -> typing .Tuple ["SExp" , "SExp" ]:
114
151
pair = self .pair
115
152
if pair is None :
116
153
return pair
117
- return (self .to (pair [0 ]), self .to (pair [1 ]))
154
+ return (self .__class__ (pair [0 ]), self .__class__ (pair [1 ]))
118
155
156
+ # TODO: deprecate this. Same as .atom property
119
157
def as_atom (self ):
120
158
return self .atom
121
159
122
160
def listp (self ):
123
161
return self .pair is not None
124
162
125
163
def nullp (self ):
126
- return self .atom == b""
164
+ v = self .atom
165
+ return v is not None and len (v ) == 0
127
166
128
167
def as_int (self ):
129
168
return int_from_bytes (self .atom )
@@ -134,26 +173,29 @@ def as_bin(self):
134
173
return f .getvalue ()
135
174
136
175
@classmethod
137
- def to (class_ , v : CastableType ):
176
+ def to (class_ , v : CastableType ) -> "SExp" :
138
177
if isinstance (v , class_ ):
139
178
return v
140
- v1 = to_sexp_type (v )
141
- return class_ (v1 )
142
179
143
- def cons (self , right : "CLVMObject" ):
144
- s = (self , right )
145
- return self .to (s )
180
+ if looks_like_clvm_object (v ):
181
+ return class_ (v )
182
+
183
+ # this will lazily convert elements
184
+ return class_ (to_sexp_type (v ))
185
+
186
+ def cons (self , right ):
187
+ return self .to ((self , right ))
146
188
147
189
def first (self ):
148
190
pair = self .pair
149
191
if pair :
150
- return self .to (pair [0 ])
192
+ return self .__class__ (pair [0 ])
151
193
raise EvalError ("first of non-cons" , self )
152
194
153
195
def rest (self ):
154
196
pair = self .pair
155
197
if pair :
156
- return self .to (pair [1 ])
198
+ return self .__class__ (pair [1 ])
157
199
raise EvalError ("rest of non-cons" , self )
158
200
159
201
@classmethod
@@ -169,22 +211,22 @@ def as_iter(self):
169
211
def __eq__ (self , other : CastableType ):
170
212
try :
171
213
other = self .to (other )
214
+ to_compare_stack = [(self , other )]
215
+ while to_compare_stack :
216
+ s1 , s2 = to_compare_stack .pop ()
217
+ p1 = s1 .as_pair ()
218
+ if p1 :
219
+ p2 = s2 .as_pair ()
220
+ if p2 :
221
+ to_compare_stack .append ((p1 [0 ], p2 [0 ]))
222
+ to_compare_stack .append ((p1 [1 ], p2 [1 ]))
223
+ else :
224
+ return False
225
+ elif s2 .as_pair () or s1 .as_atom () != s2 .as_atom ():
226
+ return False
227
+ return True
172
228
except ValueError :
173
229
return False
174
- to_compare_stack = [(self , other )]
175
- while to_compare_stack :
176
- s1 , s2 = to_compare_stack .pop ()
177
- p1 = s1 .as_pair ()
178
- if p1 :
179
- p2 = s2 .as_pair ()
180
- if p2 :
181
- to_compare_stack .append ((p1 [0 ], p2 [0 ]))
182
- to_compare_stack .append ((p1 [1 ], p2 [1 ]))
183
- else :
184
- return False
185
- elif s2 .as_pair () or s1 .as_atom () != s2 .as_atom ():
186
- return False
187
- return True
188
230
189
231
def list_len (self ):
190
232
v = self
@@ -204,5 +246,5 @@ def __repr__(self):
204
246
return "%s(%s)" % (self .__class__ .__name__ , str (self ))
205
247
206
248
207
- SExp .false = SExp .__null__ = SExp (b"" )
208
- SExp .true = SExp (b"\1 " )
249
+ SExp .false = SExp .__null__ = SExp (CLVMObject ( b"" ) )
250
+ SExp .true = SExp (CLVMObject ( b"\1 " ) )
0 commit comments