33
44import toolz
55
6+ from collections import Sequence
7+
68from multipledispatch import dispatch
79
810from kanren .term import operator , arguments
1315etuple_repr .maxother = 100
1416
1517
16- class KwdPair (tuple ):
18+ class KwdPair (object ):
1719 """A class used to indicate a keyword + value mapping.
1820
1921 TODO: Could subclass `ast.keyword`.
2022
2123 """
2224
23- def __new__ (cls , arg , value ):
25+ __slots__ = ("arg" , "value" )
26+
27+ def __init__ (self , arg , value ):
2428 assert isinstance (arg , str )
25- obj = super (). __new__ ( cls , ( arg , value ))
26- return obj
29+ self . arg = arg
30+ self . value = value
2731
2832 @property
2933 def eval_obj (self ):
30- return KwdPair (self [ 0 ] , getattr (self [ 1 ] , "eval_obj" , self [ 1 ] ))
34+ return KwdPair (self . arg , getattr (self . value , "eval_obj" , self . value ))
3135
3236 def __repr__ (self ):
33- return f"{ str (self [0 ])} ={ repr (self [1 ])} "
37+ return f"{ self .__class__ .__name__ } ({ repr (self .arg )} , { repr (self .value )} )"
38+
39+ def __str__ (self ):
40+ return f"{ self .arg } ={ self .value } "
41+
42+ def _repr_pretty_ (self , p , cycle ):
43+ p .text (str (self ))
3444
3545
36- class ExpressionTuple (tuple ):
37- """A tuple object that represents an expression.
46+ class ExpressionTuple (Sequence ):
47+ """A tuple-like object that represents an expression.
3848
39- This object carries the underlying object, if any, and preserves it
40- through limited forms of concatenation/cons-ing.
49+ This object caches the return value resulting from evaluation of the
50+ expression it represents. Likewise, it holds onto the "parent" expression
51+ from which it was derived (e.g. as a slice), if any, so that it can
52+ preserve the return value through limited forms of concatenation/cons-ing
53+ that would reproduce the parent expression.
4154
55+ TODO: Should probably use weakrefs for that.
4256 """
4357
58+ __slots__ = ("_eval_obj" , "_tuple" , "_orig_expr" )
4459 null = object ()
4560
46- def __new__ (cls , * args , ** kwargs ):
47- obj = super ().__new__ (cls , * args , ** kwargs )
48- # TODO: Consider making this a weakref.
49- obj ._eval_obj = cls .null
50- return obj
61+ def __new__ (cls , seq = None , ** kwargs ):
62+
63+ # XXX: This doesn't actually remove the entry from the kwargs
64+ # passed to __init__!
65+ # It does, however, remove it for the check below.
66+ kwargs .pop ("eval_obj" , None )
67+
68+ if seq is None and not kwargs and isinstance (seq , cls ):
69+ return seq
70+
71+ res = super ().__new__ (cls )
72+
73+ return res
74+
75+ def __init__ (self , seq = None , ** kwargs ):
76+ """Create an expression tuple.
77+
78+ If the keyword 'eval_obj' is given, the `ExpressionTuple`'s
79+ evaluated object is set to the corresponding value.
80+ XXX: There is no verification/check that the arguments evaluate to the
81+ user-specified 'eval_obj', so be careful.
82+ """
83+
84+ _eval_obj = kwargs .pop ("eval_obj" , self .null )
85+ etuple_kwargs = tuple (KwdPair (k , v ) for k , v in kwargs .items ())
86+
87+ if seq :
88+ self ._tuple = tuple (seq ) + etuple_kwargs
89+ else :
90+ self ._tuple = etuple_kwargs
91+
92+ # TODO: Consider making these a weakrefs.
93+ self ._eval_obj = _eval_obj
94+ self ._orig_expr = None
5195
5296 @property
5397 def eval_obj (self ):
5498 """Return the evaluation of this expression tuple.
5599
56- XXX: If the object isn't cached, it will be evaluated recursively.
100+ Warning: If the evaluation value isn't cached, it will be evaluated
101+ recursively.
57102
58103 """
59- if self ._eval_obj is not ExpressionTuple .null :
104+ if self ._eval_obj is not self .null :
60105 return self ._eval_obj
61106 else :
62- evaled_args = [getattr (i , "eval_obj" , i ) for i in self [1 :]]
107+ evaled_args = [getattr (i , "eval_obj" , i ) for i in self . _tuple [1 :]]
63108 arg_grps = toolz .groupby (lambda x : isinstance (x , KwdPair ), evaled_args )
64109 evaled_args = arg_grps .get (False , [])
65110 evaled_kwargs = arg_grps .get (True , [])
66111
67- op = self [0 ]
112+ op = self . _tuple [0 ]
68113 try :
69114 op_sig = inspect .signature (op )
70115 except ValueError :
71- _eval_obj = op (* (evaled_args + [kw [1 ] for kw in evaled_kwargs ]))
116+ # This handles some builtin function types
117+ _eval_obj = op (* (evaled_args + [kw .value for kw in evaled_kwargs ]))
72118 else :
73- op_args = op_sig .bind (* evaled_args , ** dict ( evaled_kwargs ) )
119+ op_args = op_sig .bind (* evaled_args , ** { kw . arg : kw . value for kw in evaled_kwargs } )
74120 op_args .apply_defaults ()
75121
76122 _eval_obj = op (* op_args .args , ** op_args .kwargs )
@@ -84,87 +130,104 @@ def eval_obj(self):
84130 def eval_obj (self , obj ):
85131 raise ValueError ("Value of evaluated expression cannot be set!" )
86132
133+ def __add__ (self , x ):
134+ res = self ._tuple + x
135+ if self ._orig_expr is not None and res == self ._orig_expr ._tuple :
136+ return self ._orig_expr
137+ return type (self )(res )
138+
139+ def __contains__ (self , * args ):
140+ return self ._tuple .__contains__ (* args )
141+
142+ def __ge__ (self , * args ):
143+ return self ._tuple .__ge__ (* args )
144+
87145 def __getitem__ (self , key ):
88- # if isinstance(key, slice):
89- # return [self.list[i] for i in xrange(key.start, key.stop, key.step)]
90- # return self.list[key]
91- tuple_res = super ().__getitem__ (key )
146+ tuple_res = self ._tuple [key ]
92147 if isinstance (key , slice ) and isinstance (tuple_res , tuple ):
93148 tuple_res = type (self )(tuple_res )
94- tuple_res .orig_expr = self
149+ tuple_res ._orig_expr = self
95150 return tuple_res
96151
97- def __add__ (self , x ):
98- res = type (self )(super ().__add__ (x ))
99- if res == getattr (self , "orig_expr" , None ):
100- return self .orig_expr
101- return res
152+ def __gt__ (self , * args ):
153+ return self ._tuple .__gt__ (* args )
154+
155+ def __iter__ (self , * args ):
156+ return self ._tuple .__iter__ (* args )
157+
158+ def __le__ (self , * args ):
159+ return self ._tuple .__le__ (* args )
160+
161+ def __len__ (self , * args ):
162+ return self ._tuple .__len__ (* args )
163+
164+ def __lt__ (self , * args ):
165+ return self ._tuple .__lt__ (* args )
166+
167+ def __mul__ (self , * args ):
168+ return self ._tuple .__mul__ (* args )
169+
170+ def __rmul__ (self , * args ):
171+ return self ._tuple .__rmul__ (* args )
102172
103173 def __radd__ (self , x ):
104- res = type (self )(x + tuple ( self ) )
105- if res == getattr ( self , "orig_expr" , None ) :
106- return self .orig_expr
107- return res
174+ res = x + self . _tuple # type(self)(x + self._tuple )
175+ if self . _orig_expr is not None and res == self . _orig_expr . _tuple :
176+ return self ._orig_expr
177+ return type ( self )( res )
108178
109179 def __str__ (self ):
110- return f"e({ ', ' .join (tuple (str (i ) for i in self ))} )"
180+ return f"e({ ', ' .join (tuple (str (i ) for i in self . _tuple ))} )"
111181
112182 def __repr__ (self ):
113- return f"ExpressionTuple({ etuple_repr .repr (tuple ( self ) )} )"
183+ return f"ExpressionTuple({ etuple_repr .repr (self . _tuple )} )"
114184
115185 def _repr_pretty_ (self , p , cycle ):
116186 if cycle :
117- p .text (f"{ self . __class__ . __name__ } (...)" )
187+ p .text (f"e (...)" )
118188 else :
119- with p .group (2 , f" { self . __class__ . __name__ } (( " , ") )" ):
120- p .breakable ()
121- for idx , item in enumerate (self ):
189+ with p .group (2 , "e( " , ")" ):
190+ p .breakable (sep = "" )
191+ for idx , item in enumerate (self . _tuple ):
122192 if idx :
123193 p .text ("," )
124194 p .breakable ()
125195 p .pretty (item )
126196
197+ def __eq__ (self , other ):
198+ return self ._tuple == other
127199
128- def etuple ( * args , ** kwargs ):
129- """Create an expression tuple from the arguments.
200+ def __hash__ ( self ):
201+ return hash ( self . _tuple )
130202
131- If the keyword 'eval_obj' is given, the `ExpressionTuple`'s
132- evaluated object is set to the corresponding value.
133- XXX: There is no verification/check that the arguments evaluate to the
134- user-specified 'eval_obj', so be careful.
135203
136- """
137- _eval_obj = kwargs .pop ("eval_obj" , ExpressionTuple .null )
138-
139- etuple_kwargs = tuple (KwdPair (k , v ) for k , v in kwargs .items ())
140-
141- res = ExpressionTuple (args + etuple_kwargs )
204+ def etuple (* args , ** kwargs ):
205+ """Create an ExpressionTuple from the argument list.
142206
143- res ._eval_obj = _eval_obj
207+ In other words:
208+ etuple(1, 2, 3) == ExpressionTuple((1, 2, 3))
144209
145- return res
210+ """
211+ return ExpressionTuple (args , ** kwargs )
146212
147213
148214@dispatch (object )
149- def etuplize (x , shallow = False ):
215+ def etuplize (x , shallow = False , return_bad_args = False ):
150216 """Return an expression-tuple for an object (i.e. a tuple of rand and rators).
151217
152- When evaluated, the rand and rators should [re-]construct the object. When the
153- object cannot be given such a form, the object itself is returned.
154-
155- NOTE: `etuplize(...)[2:]` and `arguments(...)` will *not* return
156- the same thing by default, because the former is recursive and the latter
157- is not. In other words, this S-expression-like "decomposition" is
158- recursive, and, as such, it requires an inside-out evaluation to
159- re-construct a "decomposed" object. In contrast, `operator` and
160- `arguments` is necessarily a shallow "decomposition".
218+ When evaluated, the rand and rators should [re-]construct the object. When
219+ the object cannot be given such a form, it is simply converted to an
220+ `ExpressionTuple` and returned.
161221
162222 Parameters
163223 ----------
164224 x: object
165225 Object to convert to expression-tuple form.
166226 shallow: bool
167227 Whether or not to do a shallow conversion.
228+ return_bad_args: bool
229+ Return the passed argument when its type is not appropriate, instead
230+ of raising an exception.
168231
169232 """
170233 if isinstance (x , ExpressionTuple ):
@@ -176,18 +239,23 @@ def etuplize(x, shallow=False):
176239 op = operator (x )
177240 args = arguments (x )
178241 except (IndexError , NotImplementedError ):
179- return x
242+ op = None
243+ args = x
180244
181- assert isinstance (args , (list , tuple ))
245+ if not isinstance (args , Sequence ) or isinstance (args , str ):
246+ if return_bad_args :
247+ return x
248+ else :
249+ raise TypeError (f"x is neither a non-str Sequence nor term: { type (x )} " )
182250
183251 # Not everything in a list/tuple should be considered an expression.
184252 if not callable (op ):
185- return x
253+ return etuple ( * x )
186254
187255 if shallow :
188256 et_args = args
189257 else :
190- et_args = tuple (etuplize (a ) for a in args )
258+ et_args = tuple (etuplize (a , return_bad_args = True ) for a in args )
191259
192260 res = etuple (op , * et_args , eval_obj = x )
193261 return res
0 commit comments