def func2((elt, subindex)): #070320 if is_expr_Instance(elt): res = elt elif is_pure_expr(elt): # new feature 070320: if function(element) gives an expr, instantiate it with a suitable choice of index eli = self.instance_function( elt, (subindex, index), permit_expr_to_vary = True) assert is_expr_Instance(eli) #e error message with elt and eli res = eli else: assert is_expr_Instance(elt) or is_pure_expr(elt) #e message assert is_expr_Instance(res) # sanity check, can remove when works return res
def _i_grabarg(self, attr, argpos, dflt_expr, _arglist=False): """ #doc, especially the special values for some of these args """ debug = False ## _arglist if debug: print_compact_stack( "_i_grabarg called with ( self %r, attr %r, argpos %r, dflt_expr %r, _arglist %r): " % \ (self, attr, argpos, dflt_expr, _arglist) ) print " and the data it grabs from is _e_kws = %r, _e_args = %r" % ( self._e_kws, self._e_args) #k below should not _e_eval or canon_expr without review -- should return an arg expr or dflt expr, not its value # (tho for now it also can return None on error -- probably causing bugs in callers) assert is_pure_expr( dflt_expr), "_i_grabarg dflt_expr should be an expr, not %r" % ( dflt_expr, ) #061105 - or use canon_expr? assert self._e_is_instance if not self._e_has_args: print "warning: possible bug: not self._e_has_args in _i_grabarg%r in %r" % ( (attr, argpos), self) assert attr is None or isinstance(attr, str) assert argpos is None or (isinstance(argpos, int) and argpos >= 0) assert _arglist in (False, True) external_flag, res0 = self._i_grabarg_0(attr, argpos, dflt_expr, _arglist=_arglist) if external_flag: # flag and this condition added 061116 443p to try to fix '_self dflt_expr bug'; seems to work in testexpr_9a; # I'm guessing type_expr doesn't have a similar bug since it's applied outside this call. #k index = attr or argpos #k I guess, for now [070105 stub -- not always equal to index in ipath, esp for ArgOrOption] env_for_arg = self.env_for_arg(index) # revised 070105 if debug070120: print "grabarg %r in %r is wrapping %r with %r containing _self %r" % \ ((attr, argpos), self, res0, env_for_arg, getattr(env_for_arg,'_self','<none>')) res = lexenv_Expr( env_for_arg, res0 ) ##k lexenv_Expr is guess, 061110 late, seems confirmed; env_for_args 061114 #e possible good optim: use self.env instead, unless res0 contains a use of _this; # or we could get a similar effect (slower when this runs, but better when the arg instance is used) by simplifying res, # so that only the used parts of the env provided by lexenv_Expr remained (better due to other effects of simplify) else: res = res0 #k (I *think* it's right to not even add self.env here, tho I recall I had lexenv_Expr(self.env, res0) at first -- # because the self.env was for changing _self, which we want to avoid here, and the _for_args is for supporting _this.) # Note: there was a '_self dflt_expr bug' in the above, until i added 'if external_flag:'] [diagnosed & fixed 061116]: # it's wrong for the parts of this expr res0 that came from our class, i.e. dflt_expr and type_expr. # That is hard to fix here, since they're mixed into other parts of the same expr, # but nontrivial to fix where we supply them, since when we do, the specific env needed is not known -- # we'd have to save it up or push it here, then access it there (or just say "escape from the most local lexenv_Expr" # if that's well-defined & possible & safe -- maybe the most local one of a certain type is better). # We also want whatever we do to be robust, and to not make it too hard to someday simplify the expr. # So why not fix it by wrapping only the parts that actually come from _e_args and _e_kws, right when we access them? # I guess that is done inside _i_grabarg_0... ok, it's easy to add external_flag to its retval to tell us what to do. [done] ###k ARE THERE any other accesses of _e_args or _e_kws that need similar protection? Worry about this if I add # support for iterating specific args (tho that might just end up calling _i_grabarg and being fine). if debug: print "_i_grabarg returns %r" % (res, ) return res
def set_default_attrs( obj, **kws ): # e if this was general, we could refile into py_utils, but it turns out it's not general enough """For each attr=val pair in **kws, if attr is not set in obj, set it, without doing usage or change tracking. If obj supports __setattr_default__ (not a special name to Python itself), use that, otherwise use hasattr and setattr (with a temporary dynamic suppression of usage or change tracking, if possible -- nim for now except as an assertion). Note: this is not very efficient (or self-contained) for general use, for which pure hasattr/setattr would be sufficient. """ # Note about why we disable tracking: hasattr itself does legitimately track use of the attr, # since testing thereness does use its "value" in a general sense (as if value was _UNSET_ when not there). # But, in this specific code, we don't want client (caller) to see us using attr, # since from client POV, all we're doing is changing _UNSET_ to v, which are equivalent values # (as client is declaring by calling us). # So we need to discard tracked usage of attr here. # We also need to discard any change from the set to default value, # except that we can probably ignore that issue for now (due to an accident of how current code uses this), # since if the attr is unset, it was never yet used, so invalidating it should have no effect. if 1: from exprs.Exprs import is_pure_expr if is_pure_expr(obj): assert 0, ( "set_default_attrs called on pure_expr %r is almost surely a bug; normally do it in _init_instance" % (obj,) ) import foundation.changes as changes try: method = obj.__setattr_default__ except AttributeError: ## print "fyi: set_default_attrs using general case for",obj,kws.keys() # this happens for all non-tracked StatePlaces # general case - let's hope the object doesn't have tracked attrs; the temporary assertion here might not catch this ###e instead, we should actively suppress usage/change tracking, so we can permit it in obj # (tho careless uses of that would lead to bugs) mc = changes.begin_disallowing_usage_tracking("set_default_attrs (hasattr/setattr) for %r" % obj) # note: the argument is just an explanation for use in error messages ##e OPTIM: don't precompute that arg try: for k, v in kws.iteritems(): if not hasattr(obj, k): setattr(obj, k, v) finally: changes.end_disallowing_usage_tracking(mc) else: # use the special method mc = changes.begin_disallowing_usage_tracking("set_default_attrs (__setattr_default__) for %r" % obj) # e optim: remove this check; it only catches usage tracking, not change tracking (ie inval of something by this), anyway try: for k, v in kws.iteritems(): method( k, v ) # does its own usage/change-tracking suppression (we assume); this is partially checked (for now) finally: changes.end_disallowing_usage_tracking(mc) return # from set_default_attrs
def set_default_attrs( obj, **kws ): #e if this was general, we could refile into py_utils, but it turns out it's not general enough """For each attr=val pair in **kws, if attr is not set in obj, set it, without doing usage or change tracking. If obj supports __setattr_default__ (not a special name to Python itself), use that, otherwise use hasattr and setattr (with a temporary dynamic suppression of usage or change tracking, if possible -- nim for now except as an assertion). Note: this is not very efficient (or self-contained) for general use, for which pure hasattr/setattr would be sufficient. """ # Note about why we disable tracking: hasattr itself does legitimately track use of the attr, # since testing thereness does use its "value" in a general sense (as if value was _UNSET_ when not there). # But, in this specific code, we don't want client (caller) to see us using attr, # since from client POV, all we're doing is changing _UNSET_ to v, which are equivalent values # (as client is declaring by calling us). # So we need to discard tracked usage of attr here. # We also need to discard any change from the set to default value, # except that we can probably ignore that issue for now (due to an accident of how current code uses this), # since if the attr is unset, it was never yet used, so invalidating it should have no effect. if 1: from exprs.Exprs import is_pure_expr if is_pure_expr(obj): assert 0, "set_default_attrs called on pure_expr %r is almost surely a bug; normally do it in _init_instance" % ( obj, ) import foundation.changes as changes try: method = obj.__setattr_default__ except AttributeError: ## print "fyi: set_default_attrs using general case for",obj,kws.keys() # this happens for all non-tracked StatePlaces # general case - let's hope the object doesn't have tracked attrs; the temporary assertion here might not catch this ###e instead, we should actively suppress usage/change tracking, so we can permit it in obj # (tho careless uses of that would lead to bugs) mc = changes.begin_disallowing_usage_tracking( 'set_default_attrs (hasattr/setattr) for %r' % obj) # note: the argument is just an explanation for use in error messages ##e OPTIM: don't precompute that arg try: for k, v in kws.iteritems(): if not hasattr(obj, k): setattr(obj, k, v) finally: changes.end_disallowing_usage_tracking(mc) else: # use the special method mc = changes.begin_disallowing_usage_tracking( 'set_default_attrs (__setattr_default__) for %r' % obj) #e optim: remove this check; it only catches usage tracking, not change tracking (ie inval of something by this), anyway try: for k, v in kws.iteritems(): method( k, v ) # does its own usage/change-tracking suppression (we assume); this is partially checked (for now) finally: changes.end_disallowing_usage_tracking(mc) return # from set_default_attrs
def _i_grabarg( self, attr, argpos, dflt_expr, _arglist = False): """ #doc, especially the special values for some of these args """ debug = False ## _arglist if debug: print_compact_stack( "_i_grabarg called with ( self %r, attr %r, argpos %r, dflt_expr %r, _arglist %r): " % \ (self, attr, argpos, dflt_expr, _arglist) ) print " and the data it grabs from is _e_kws = %r, _e_args = %r" % (self._e_kws, self._e_args) #k below should not _e_eval or canon_expr without review -- should return an arg expr or dflt expr, not its value # (tho for now it also can return None on error -- probably causing bugs in callers) assert is_pure_expr(dflt_expr), "_i_grabarg dflt_expr should be an expr, not %r" % (dflt_expr,) #061105 - or use canon_expr? assert self._e_is_instance if not self._e_has_args: print "warning: possible bug: not self._e_has_args in _i_grabarg%r in %r" % ((attr, argpos), self) assert attr is None or isinstance(attr, str) assert argpos is None or (isinstance(argpos, int) and argpos >= 0) assert _arglist in (False, True) external_flag, res0 = self._i_grabarg_0(attr, argpos, dflt_expr, _arglist = _arglist) if external_flag: # flag and this condition added 061116 443p to try to fix '_self dflt_expr bug'; seems to work in testexpr_9a; # I'm guessing type_expr doesn't have a similar bug since it's applied outside this call. #k index = attr or argpos #k I guess, for now [070105 stub -- not always equal to index in ipath, esp for ArgOrOption] env_for_arg = self.env_for_arg(index) # revised 070105 if debug070120: print "grabarg %r in %r is wrapping %r with %r containing _self %r" % \ ((attr, argpos), self, res0, env_for_arg, getattr(env_for_arg,'_self','<none>')) res = lexenv_Expr(env_for_arg, res0) ##k lexenv_Expr is guess, 061110 late, seems confirmed; env_for_args 061114 #e possible good optim: use self.env instead, unless res0 contains a use of _this; # or we could get a similar effect (slower when this runs, but better when the arg instance is used) by simplifying res, # so that only the used parts of the env provided by lexenv_Expr remained (better due to other effects of simplify) else: res = res0 #k (I *think* it's right to not even add self.env here, tho I recall I had lexenv_Expr(self.env, res0) at first -- # because the self.env was for changing _self, which we want to avoid here, and the _for_args is for supporting _this.) # Note: there was a '_self dflt_expr bug' in the above, until i added 'if external_flag:'] [diagnosed & fixed 061116]: # it's wrong for the parts of this expr res0 that came from our class, i.e. dflt_expr and type_expr. # That is hard to fix here, since they're mixed into other parts of the same expr, # but nontrivial to fix where we supply them, since when we do, the specific env needed is not known -- # we'd have to save it up or push it here, then access it there (or just say "escape from the most local lexenv_Expr" # if that's well-defined & possible & safe -- maybe the most local one of a certain type is better). # We also want whatever we do to be robust, and to not make it too hard to someday simplify the expr. # So why not fix it by wrapping only the parts that actually come from _e_args and _e_kws, right when we access them? # I guess that is done inside _i_grabarg_0... ok, it's easy to add external_flag to its retval to tell us what to do. [done] ###k ARE THERE any other accesses of _e_args or _e_kws that need similar protection? Worry about this if I add # support for iterating specific args (tho that might just end up calling _i_grabarg and being fine). if debug: print "_i_grabarg returns %r" % (res,) return res
def _debug_i_instance_retval(self, res): #070212 """ [private] res is about to be returned from self._i_instance; perform debug checks [#e someday maybe do other things] """ ## NumericArrayType = type(ORIGIN) ## if isinstance(res, NumericArrayType): ## return res # has no __class__, but legitimate try: ## assert res.__class__.__name__ != 'lexenv_ipath_Expr', "should not be returned from _i_instance: %r" % (res,) assert not is_pure_expr(res), "pure exprs should not be returned from _i_instance: %r" % (res,) except: print "bug: exception in _debug_i_instance_retval for this res (reraising): %s" % safe_repr(res) raise return
def _ArgOption_helper(attr_expr, argpos_expr, type_expr, dflt_expr, _lvalue_flag=False, _arglist=False, **moreopts): """ [private helper for Arg, Option, and maybe ArgOrOption] attr_expr should be None, or some sort of expr (in practice always _E_ATTR so far) that will get replaced by a constant_Expr for the current attr (in ExprsMeta's FormulaScanner), according to whether the current attr should be part of the index and a public option-name for supplying the arg (we make sure those conditions are the same). [#e Note that if someday we wanted to include f(attr) in the index, but still use attr alone as an option name, we'd have to modify this to permit both f(attr) (or f) and attr to be passed.] argpos_expr should similarly be None, or some sort of expr (in practice a private subclass of internal_Expr) that will get replaced by a constant_Expr for the argument position (an int) that should be allocated to the current attr's arg (determined in ExprsMeta's FormulaScanner by allocating posns 0,1,2,etc to newly seen arg-attrs, whether or not the attr itself is public for that arg). type_expr ###doc, passed herein to canon_type dflt_expr ###doc, can also be _E_DFLT_FROM_TYPE_ or [handled in caller i think, but survives here unmatteringly] _E_REQUIRED_ARG_; will be passed through canon_expr _lvalue_flag is a private option used by LvalueArg. _arglist is a private option used by ArgList. """ if _lvalue_flag: printnim("_lvalue_flag's proper interaction with dflt_expr is nim" ) # in all cases below ### guess: we want it to be an expr for a default stateref global _self # fyi type_expr = canon_type(type_expr) printnim( "can type_expr legally be self-dependent and/or time-dependent? ###k I guess that's nim in current code!" ) #070115 comment if _arglist: # new feature 070321. The type is applied to each element, but the default value is for the entire list -- # OTOH, when would it ever be used, since even if no args are supplied, the list can be formed?? # Probably it would only be used when the list was 0 length, and could meaningfully be [], (), or another list-like thing... # this is all a guess and I probably won't even review this code for this issue now, unless it fails when tried. ####k type_expr = tuple_Expr( type_expr ) # type-coerce the value to a list of the given type [070321 guess] ###e or list_Expr??? if dflt_expr is _E_DFLT_FROM_TYPE_: dflt_expr = default_expr_from_type_expr(type_expr) ## note [070115], this would be impossible for time-dependent types! and for self-dep ones, possible but harder than current code. assert is_pure_expr(dflt_expr) #k guess 061105 else: dflt_expr = canon_expr( dflt_expr ) # hopefully this finally will fix dflt 10 bug, 061105 guesshope ###k [works for None, 061114] assert is_pure_expr( dflt_expr ) # not sure this is redundant, since not sure if canon_expr checks for Instance ###k printnim("not sure if canon_expr checks for Instance") # Note on why we use explicit call_Expr & getattr_Expr below, # rather than () and . notation like you can use in user-level formulae (which python turns into __call__ and getattr), # to construct Exprs like _self._i_grabarg( attr_expr, ...): # it's only to work around safety features which normally detect that kind of Expr-formation (getattr on _i_* or _e_*, # or getattr then call) as a likely error. These safety features are very important, catching errors that would often lead # to hard-to-diagnose bugs (when our code has an Expr but thinks it has an Instance), so it's worth the trouble. held_dflt_expr = hold_Expr(dflt_expr) # Note, this gets evalled back into dflt_expr (treated as inert, may or may not be an expr depending on what it is right here) # by the time _i_grabarg sees it (the eval is done when the call_Expr evals its args before doing the call). # So if we wanted _i_grabarg to want None rather than _E_REQUIRED_ARG_ as a special case, we could change to that (there & here). grabopts = {} if _arglist: grabopts.update(dict(_arglist=constant_Expr(_arglist))) grabarg_expr = call_Expr(getattr_Expr(_self, '_i_grabarg'), attr_expr, argpos_expr, held_dflt_expr, **grabopts) # comments 070115: # - This will eval to an expr which depends on self but not on time. We could optim by wrapping it # (or declaring it final) in a way which effectively replaced it with its value-expr when first used. # (But it's not obvious where to store the result of that, since the exprs being returned now are assigned to classes # and will never be specific to single selfs. Do we need an expr to use here, which can cache its own info in self?? # Note: AFAIK, self will be the same as what _self gets replaced with when this is used. (We ought to assert that.) ###e) # - Further, grabarg_expr is probably supposed to be wrapped *directly* by eval_Expr, not with type_expr inside. I think I'll # make that change right now and test it with EVAL_REFORM still False, since I think it's always been required, as said # in other comments here. DOING THIS NOW. if attr_expr is not None and argpos_expr is not None: # for ArgOrOption, use a tuple of a string and int (attr and argpos) as the index index_expr = tuple_Expr(attr_expr, argpos_expr) elif attr_expr is None and argpos_expr is None: assert 0, "attr_expr is None and argpos_expr is None ..." elif attr_expr is not None: # for Option, use a plain attr string as the index index_expr = attr_expr else: assert argpos_expr is not None # for Arg, use a plain int as the index # (note: ExprsMeta replaces argpos_expr with that int wrapped in constant_Expr, but later eval pulls out the raw int) index_expr = argpos_expr # see if this is fixed now, not that it means much since we were using a stub... but who knows, maybe the stub was buggy # and we compensated for that and this could if so cause a bug: ## printnim("I suspect type_expr (stub now) is included wrongly re eval_Expr in _ArgOption_helper, in hindsight 061117") ## ### I suspect the above, because grabarg expr needs to be evalled to get the expr whose type coercion we want to instantiate res = Instance(_type_coercion_expr(type_expr, eval_Expr(grabarg_expr)), _index_expr=index_expr, _lvalue_flag=_lvalue_flag, **moreopts) # note: moreopts might contain _noinstance = True, and if so, Instance normally returns its first arg unchanged # (depending on other options). # 070115 replaced eval_Expr( type_expr( grabarg_expr)) with _type_coercion_expr( type_expr, eval_Expr(grabarg_expr) ) return res # from _ArgOption_helper
def _CV__i_instance_CVdict(self, index): """ [private] value-recomputing function for self._i_instance_CVdict. Before calling this, the caller must store an expr for this instance into self._i_instance_decl_data[index] (an ordinary dict). If it's permitted for that expr to change with time (which I doubt, but don't know yet for sure #k), then whenever the caller changes it (other than when initially setting it), the caller must invalidate the entry with the same key (our index arg) in the LvalDict2 that implements self._i_instance_CVdict (but the API for the caller to do that is not yet worked out #e). (Implem note: _CV_ stands for "compute value" to ExprsMeta, which implements the LvalDict2 associated with this. This method needs no corresponding _CK_ keylist function, since any key (instance index) asked for is assumed valid.) """ assert self._e_is_instance # This method needs to create a new instance, by instantiating expr and giving it index. # Implem notes: # - the glue code added by _CV_ from self._i_instance_CVdict to this method (by ExprsMeta) uses LvalDict2 # to cache values computed by this method, and recompute them if needed (which may never happen, I'm not sure). # - the access to self._i_instance_decl_data[index] is not usage tracked; # thus if we change it above w/o error, an inval is needed. data = self._i_instance_decl_data[index] expr, lvalflag = data # 061204 ## print "fyi, using kid expr %r" % expr # (these tend to be longish, and start with eval_Expr -- what we'd rather print is the result of the first eval it does) # Note, this expr can be very simplifiable, if it came from an Arg macro, # but present code [061105] reevals the full macro expansion each time -- suboptimal. # To fix, we'd need an "expr simplify", whether we used it only once or not... # the reason to use it more than once is if it becomes partly final at some later point than when it starts out. # The simplify implem would need decls about finality of, e.g., getting of some bound methods and (determinism of) # their retvals -- namely (for Arg macro etc) for _i_instance and _i_grabarg and maybe more. # Feasible but not simple; worthwhile optim someday but not right away. ##e if not EVAL_REFORM: # three increasingly strict asserts: assert expr is not None assert is_Expr(expr), "not is_Expr: %r" % (expr, ) assert is_pure_expr( expr ) ###k?? what if someone passes an instance -- is that permitted, but a noop for instantiation?? assert is_Expr_pyinstance( expr), "can't instantiate a class: %r" % ( expr, ) # new, 070117, untested### -- i'm not even sure it's correct # also assume expr is "canonicalized" and even "understood" -- not sure if this is justified printnim( "don't we need understand_expr somewhere in here? (before kidmaking in IorE)" ) ###@@@ else: # EVAL_REFORM case 070117 if not (is_pure_expr(expr) and is_Expr_pyinstance(expr)): print "this should never happen as of 070118 since we handle it above: non pure expr %r in _CV__i_instance_CVdict" % ( expr, ) ## print "FYI: EVAL_REFORM: _CV__i_instance_CVdict is identity on %r" % (expr,) # this is routine on e.g. None, small ints, colors, other tuples... and presumably Instances (not tested) assert not lvalflag # see comments at start of _i_instance return expr pass ####e: [061105] is _e_eval actually needing to be different from _e_make_in?? yes, _e_eval needs to be told _self # and the other needs to make one... no wait, wrong, different selves -- # the _self told to eval is the one _make_in *should make something inside of*! (ie should make a kid of) # So it may be that these are actually the same thing. (See paper notes from today for more about this.) # For now, tho, we'll define _e_make_in on OpExpr to use eval. [not done, where i am] # actually this is even faster -- review sometime (note, in constant_Expr they're both there & equiv #k): ###@@@ ## this is just the kid expr: print "_CV__i_instance_CVdict needs to make expr %r" % (expr,) ## if hasattr(expr, '_e_make_in'): ## print("REJECTED using _e_make_in case, on a pyinstance of class %s" % expr.__class__.__name__)###was printfyi til 061208 921p ## ## res = expr._e_make_in(env, index_path) ## #k we might have to fix bugs caused by not using this case, by defining (or modifying?) defs of _e_eval on some classes; ## # addendum 061212, we now do that on If_expr. if not EVAL_REFORM: # WARNING: following code is very similar to _i_eval_dfltval_expr as of 061203 # printfyi("used _e_eval case (via _e_compute_method)") # this case is usually used, as of 061108 -- now always, 061110 # note: this (used-to-be-redundantly) grabs env from self try: res = expr._e_compute_method( self, index, _lvalue_flag=lvalflag)() ##e optim someday: inline this # 061105 bug3, if bug2 was in held_dflt_expr and bug1 was 'dflt 10' except: # we expect caller to exit now, so we might as well print this first: [061114] print "following exception concerns self = %r, index = %r in _CV__i_instance_CVdict calling _e_compute_method" % \ (self, index) raise else: # EVAL_REFORM case, 070117 if kluge070119: # also pass an env retrieved from... env = self._i_instance_decl_env[index] env, ipath = self._i_env_ipath_for_formula_at_index( index, env) # note: retval's env is modified from the one passed # THIS MUST BE WHERE THE INCORRECT BINDING _self = self gets added, in the kluge070119 ###BUG in _5x! [070120 844p] # GUESS at basic bug: an Instance self really needs to know two envs: the one for its args (_self refers to # whoeever lexically created the expr they were made from), and the one for their internal formulae (_self = self). # self.env is the one for the args (since it's thought of as "self's (outer) environment"), # and the one for internal formulae has (until now -- this should change #e) been recreated as needed in # _e_compute_method and now in _i_env_ipath_for_formula_at_index, by extending self.env by _self = self. # But to be correct, this should be done BEFORE the burrowing and env-extension done by the kluge070119, # but in this wrong current code it's done after it. The old code also had wrongness of order, in principle # (doing this _self addition here rather than in env_for_args, with env_for_args done first when it should # be done 2nd), but it didn't matter since env_for_args only added _this_xxx and _my. WAIT, THAT'S WRONG -- # env_for_args is for external (in lexenv) args, so it should have SAME _self as self.env, so it's correct now, # and never gets or needs _self = self, either before or after its _my and _this_xxx bindings. # What we need here is NOT env_for_args but env_for_internal_formulae. It should be used for dflt exprs in Arg, # and for all exprs sitting on class assignments. We've been making it before *using* those formulae # (probably getting it onto dflt exprs only because they end up being *used* in the right place for that). # We've been making it in each call of _i_env_ipath_for_formula_at_index or _e_compute_method, but we ought to # make it once and let those use it, and make sure that happens before the env mods from the burrowing done by # kluge070119. (We can't make it in the class (eg ExprsMeta) -- we have to wait until _init_instance or so, # because it depends on self which is only known around then.) # # SUGGESTED FIX ###e: make self.env_for_internal_formulae (#e shorter name -- env_for_formulae?) # before (??) calling _init_instance [or after if _init_instance can modify self.env ##k]; # use it in _i_env_ipath_for_formula_at_index and some of our uses of _e_compute_method; # and review all uses of self.env for whether they ought to be env_for_formulae. # Doing it 070120 circa 942p -- I made _C_env_for_formulae instead of setting it in _init_instance # (optim, since some Instances never need it (I hope -- not sure) and since it creates a cyclic ref. else: env, ipath = self._i_env_ipath_for_formula_at_index( index) # equivalent to how above other case computes them assert not lvalflag # see comments at start of _i_instance res = expr._e_make_in( env, ipath ) # only pure IorE exprs have this method; should be ok since only they are returned from expr evals return res # from _CV__i_instance_CVdict
def _i_instance(self, expr, index, _lvalue_flag=False, permit_expr_to_vary=False, skip_expr_compare=False): # see also def Instance (the method, just above, not the macro) """ [semi-private; used by macros like Instance, Arg, Option] Find or create (or perhaps recompute if invalid, but only the latest version is memoized) (and return) an Instance of expr, contained in self, at the given relative index, and in the same env [#e generalize env later?]. Error (perhaps not detected) if called with same index and different other args; when a cached instance is returned, the other args are not used except perhaps to check for this error. (When an instance is recomputed, if this error happens, are new args used? undefined.) (#e Should we change this to make the expr effectively part of the index, for caching? Probably not; not sure.) (#e Should we change this to make it legal to pass a new expr? Probably not... hard for subsequent callers to be consistent...) WARNING: this docstring is probably out of date regarding whether expr can change and what happens if it does. [070301 comment] """ # Note: Before EVAL_REFORM, this is used to do all evals, including instantiations (treated as how IorE evals). # It also does "eval to lval" when _lvalue_flag is true (passed only from LvalueArg, so far used only in Set). # After EVAL_REFORM, instantiation can't be done by eval (evals of IorE exprs return them unchanged); # evals are done before this is called, then this either returns constants (numbers, Instances) unchanged, # or instantiates exprs. It can no longer be called with _lvalue_flag when EVAL_REFORM (as of late 070119). def __debug_frame_repr__(locals): """ return a string for inclusion in some calls of print_compact_stack """ return "_i_instance(index = %r, expr = %r), self = %r" % ( index, expr, self) assert self._e_is_instance if not EVAL_REFORM: # this is redundant with a later assert in _i_instance_CVdict in all cases (EVAL_REFORM or not), # and it causes trouble in the EVAL_REFORM case. To be conservative I'll only disable it in that case # (so I'll leave it untouched in this case). 070117 assert is_pure_expr(expr), "who passed non-pure-expr %r to _i_instance? index %r, self %r, _e_args %r" % \ (expr, index, self, self._e_args) #k guess 061105 else: if _lvalue_flag: print "SHOULD NEVER HAPPEN: saw _lvalue_flag: _i_instance(index = %r, expr = %r), self = %r" % ( index, expr, self) # new behavior 070118; might be needed to fix "bug: expr or lvalflag for instance changed" in testexpr_9fx4 click, # or might just be hiding an underlying bug which will show up for pure exprs in If clauses -- not yet sure. #####k # (Note that its only effects are an optim and to remove some error messages -- what's unknown is whether the detected # "errors" were real errors or not, in this case. # Guess: we'll still have a ###BUG when exprs are involved, which to fix # will require noticing local ipath modifiers (lexenv_ipath_Expr's ipath) not only in _e_make_in but in the upcoming # determination of index; the best fix will be to dispense with the local cache of exprs per-index in favor of a global one # indexed by ipath, after those local ipath mods are included -- the same one needed for the "find" in the find or make # of instantiation (this find presently being nim, as mentioned somewhere else in this file in the last day or two), # meaning that it takes into account everything needed to create the ipath to find or make at (local mods, # state-sharing transforms, maybe more).) if is_constant_for_instantiation(expr): #revised 070131 self._debug_i_instance_retval(expr) #070212 return expr # [#k review whether this comment is still needed/correct; 061204 semiobs due to newdata change below; even more obs other ways] # hmm, calling Instance macro evals the expr first... can't it turn out that it changes over time? # I think yes... not only that, a lot of change in it should be buried inside the instance! (if it's in an arg formula) # as if we need to "instantiate the expr" before actually passing it... hmm, if so this is a SERIOUS LOGIC BUG. ####@@@@ # WAIT -- can it be that the expr never changes? only its value does? and that we should pass the unevaluated expr? YES. # But i forgot, eval of an expr is that expr! I get confused since _e_eval evals an implicit instance -- rename it soon! ###@@@ # [addendum 061212: see also the comments in the new overriding method If_expr._e_eval.] # 070119 Q: what if, right here, we burrowed into expr, and used a cvdict on a central object? as a kluge? # just since fast to code vs a central instantiator obj? # or at *least* we could do it locally -- no sharing but should fix bugs? if EVAL_REFORM and kluge070119: assert not _lvalue_flag # don't know how otherwise ## env = self.env # like in _i_env_ipath_for_formula_at_index [like in the old wrong version, that is] env = self.env_for_formulae # like in _i_env_ipath_for_formula_at_index [like in the new corrected version, 070120 940p] expr, env, ipath = expr, env, index oldstuff = expr, env, ipath expr, env, ipath = expr._e_burrow_for_find_or_make(env, ipath) if debug070120 and (expr, env, ipath) != oldstuff: print "fyi EVAL_REFORM kluge070119: worked, %r => %r" % ( oldstuff, (expr, env, ipath)) index = ipath del ipath self._i_instance_decl_env[index] = env del env pass newdata = ( expr, _lvalue_flag ) # revised 061204, was just expr, also renamed; cmt above is semiobs due to this olddata = self._i_instance_decl_data.get(index, None) # see above comment if skip_expr_compare: #070411 new optim option (public) ###UNTESTED; might or might not use in confirmation corner ###e should use in Arg, Option -- might be a big speedup -- but is it safe?? maybe only with IorE If, not eval If!! ###k expr_is_new = (olddata is None) else: expr_is_new = (olddata != newdata) if expr_is_new: if olddata is not None: if permit_expr_to_vary: # new feature 070207 ### how do we remove the old instance so it gets remade? ## del self._i_instance_CVdict[index] ##k guess at how -- but del might be nim in this object -- # good thing it was, since inval_at_key [new feature] is what we need -- del would fail to propogate invals # to prior usage trackers of this now-obsolete instance! ###BUG: changes to the info used to compute expr, by whatever decides to call this method (_i_instance), # are usage tracked into that caller, not into our lval in our lvaldict. We depend on the caller # someday getting recomputed before it will realize it wants to pass us a new expr and cause this inval. # (Example of this use: a kid-column delegate in MT_try2.) # THIS LATE PROPOGATION OF INVAL MIGHT LEAD TO BUGS -- not reviewed in detail. # Much better would be for the callers "usage tracked so far" to somehow get subscribed to (for invals) # by the thing we manually inval here. The potential bugs in the current scheme include: # - inval during recompute can make more pieces of it need recompute than did when it started # - if the caller's recompute never happens, the invals that need to propogate never occur at all, # though they need to. # I am not sure if this can cause bugs in MT_try2 -- maybe not -- but surely it can in general! ###FIX SOMEHOW (maybe as suggested above, by stealing or copying usage tracking info from caller -- # but it's not enough, we'd need our lval's recompute to get the caller to re-decide to call us # so we'd know the new expr to recompute with! So, details of any fix are unclear.) self._i_instance_CVdict.inval_at_key(index) # print "fyi: made use of permit_expr_to_vary for index = %r, expr = %r" % (index, expr) # remove when works else: print "bug: expr or lvalflag for instance changed: self = %r, index = %r, new data = %r, old data = %r" % \ (self,index,newdata,olddata) #e more info? i think this is an error and should not happen normally # update 070122: it usually indicates an error, but it's a false alarm in the latest bugfixed testexpr_21g, # since pure-expr equality should be based on formal structure, not pyobj identity. ###e ####e need to print a diff of the exprs, so we see what the problem is... [070321 comment; happens with ArgList] #e if it does happen, should we inval that instance? yes, if this ever happens without error. # [addendum 061212: see also the comments in the new overriding method If_expr._e_eval.] # [one way to do that: have _i_instance_decl_data contain changetracked state vars, changed above, # usage-tracked by _CV__i_instance_CVdict. But the env matters too, so we might want to compare it too, # or compare the expr before burrowing into it, and do the # burrowing later then?? But we can't, due to the index... ###e 070122] self._i_instance_decl_data[index] = newdata else: # they're equal (or we were told not to bother comparing them, in which case, use the old one) if 0 and (not skip_expr_compare) and olddata[0] is not expr: # this print worked, but it's not usually useful (not enough is printed) so I disabled it # they're only equal because __eq__ is working on non-identical exprs [implemented in Expr late 070122] print "fyi: non-identical exprs compared equal (did we get it right?): %r and %r" % ( olddata[0], expr) ### remove sometime pass res = self._i_instance_CVdict[ index] # takes care of invals in making process? or are they impossible? ##k [see above] self._debug_i_instance_retval(res) #070212 return res
def _ArgOption_helper( attr_expr, argpos_expr, type_expr, dflt_expr, _lvalue_flag = False, _arglist = False, **moreopts ): """ [private helper for Arg, Option, and maybe ArgOrOption] attr_expr should be None, or some sort of expr (in practice always _E_ATTR so far) that will get replaced by a constant_Expr for the current attr (in ExprsMeta's FormulaScanner), according to whether the current attr should be part of the index and a public option-name for supplying the arg (we make sure those conditions are the same). [#e Note that if someday we wanted to include f(attr) in the index, but still use attr alone as an option name, we'd have to modify this to permit both f(attr) (or f) and attr to be passed.] argpos_expr should similarly be None, or some sort of expr (in practice a private subclass of internal_Expr) that will get replaced by a constant_Expr for the argument position (an int) that should be allocated to the current attr's arg (determined in ExprsMeta's FormulaScanner by allocating posns 0,1,2,etc to newly seen arg-attrs, whether or not the attr itself is public for that arg). type_expr ###doc, passed herein to canon_type dflt_expr ###doc, can also be _E_DFLT_FROM_TYPE_ or [handled in caller i think, but survives here unmatteringly] _E_REQUIRED_ARG_; will be passed through canon_expr _lvalue_flag is a private option used by LvalueArg. _arglist is a private option used by ArgList. """ if _lvalue_flag: printnim("_lvalue_flag's proper interaction with dflt_expr is nim") # in all cases below ### guess: we want it to be an expr for a default stateref global _self # fyi type_expr = canon_type( type_expr) printnim("can type_expr legally be self-dependent and/or time-dependent? ###k I guess that's nim in current code!")#070115 comment if _arglist: # new feature 070321. The type is applied to each element, but the default value is for the entire list -- # OTOH, when would it ever be used, since even if no args are supplied, the list can be formed?? # Probably it would only be used when the list was 0 length, and could meaningfully be [], (), or another list-like thing... # this is all a guess and I probably won't even review this code for this issue now, unless it fails when tried. ####k type_expr = tuple_Expr(type_expr) # type-coerce the value to a list of the given type [070321 guess] ###e or list_Expr??? if dflt_expr is _E_DFLT_FROM_TYPE_: dflt_expr = default_expr_from_type_expr( type_expr) ## note [070115], this would be impossible for time-dependent types! and for self-dep ones, possible but harder than current code. assert is_pure_expr(dflt_expr) #k guess 061105 else: dflt_expr = canon_expr(dflt_expr) # hopefully this finally will fix dflt 10 bug, 061105 guesshope ###k [works for None, 061114] assert is_pure_expr(dflt_expr) # not sure this is redundant, since not sure if canon_expr checks for Instance ###k printnim("not sure if canon_expr checks for Instance") # Note on why we use explicit call_Expr & getattr_Expr below, # rather than () and . notation like you can use in user-level formulae (which python turns into __call__ and getattr), # to construct Exprs like _self._i_grabarg( attr_expr, ...): # it's only to work around safety features which normally detect that kind of Expr-formation (getattr on _i_* or _e_*, # or getattr then call) as a likely error. These safety features are very important, catching errors that would often lead # to hard-to-diagnose bugs (when our code has an Expr but thinks it has an Instance), so it's worth the trouble. held_dflt_expr = hold_Expr(dflt_expr) # Note, this gets evalled back into dflt_expr (treated as inert, may or may not be an expr depending on what it is right here) # by the time _i_grabarg sees it (the eval is done when the call_Expr evals its args before doing the call). # So if we wanted _i_grabarg to want None rather than _E_REQUIRED_ARG_ as a special case, we could change to that (there & here). grabopts = {} if _arglist: grabopts.update(dict(_arglist = constant_Expr(_arglist))) grabarg_expr = call_Expr( getattr_Expr(_self, '_i_grabarg'), attr_expr, argpos_expr, held_dflt_expr, **grabopts ) # comments 070115: # - This will eval to an expr which depends on self but not on time. We could optim by wrapping it # (or declaring it final) in a way which effectively replaced it with its value-expr when first used. # (But it's not obvious where to store the result of that, since the exprs being returned now are assigned to classes # and will never be specific to single selfs. Do we need an expr to use here, which can cache its own info in self?? # Note: AFAIK, self will be the same as what _self gets replaced with when this is used. (We ought to assert that.) ###e) # - Further, grabarg_expr is probably supposed to be wrapped *directly* by eval_Expr, not with type_expr inside. I think I'll # make that change right now and test it with EVAL_REFORM still False, since I think it's always been required, as said # in other comments here. DOING THIS NOW. if attr_expr is not None and argpos_expr is not None: # for ArgOrOption, use a tuple of a string and int (attr and argpos) as the index index_expr = tuple_Expr( attr_expr, argpos_expr ) elif attr_expr is None and argpos_expr is None: assert 0, "attr_expr is None and argpos_expr is None ..." elif attr_expr is not None: # for Option, use a plain attr string as the index index_expr = attr_expr else: assert argpos_expr is not None # for Arg, use a plain int as the index # (note: ExprsMeta replaces argpos_expr with that int wrapped in constant_Expr, but later eval pulls out the raw int) index_expr = argpos_expr # see if this is fixed now, not that it means much since we were using a stub... but who knows, maybe the stub was buggy # and we compensated for that and this could if so cause a bug: ## printnim("I suspect type_expr (stub now) is included wrongly re eval_Expr in _ArgOption_helper, in hindsight 061117") ## ### I suspect the above, because grabarg expr needs to be evalled to get the expr whose type coercion we want to instantiate res = Instance( _type_coercion_expr( type_expr, eval_Expr(grabarg_expr) ), _index_expr = index_expr, _lvalue_flag = _lvalue_flag, **moreopts ) # note: moreopts might contain _noinstance = True, and if so, Instance normally returns its first arg unchanged # (depending on other options). # 070115 replaced eval_Expr( type_expr( grabarg_expr)) with _type_coercion_expr( type_expr, eval_Expr(grabarg_expr) ) return res # from _ArgOption_helper
def _CV__i_instance_CVdict(self, index): """ [private] value-recomputing function for self._i_instance_CVdict. Before calling this, the caller must store an expr for this instance into self._i_instance_decl_data[index] (an ordinary dict). If it's permitted for that expr to change with time (which I doubt, but don't know yet for sure #k), then whenever the caller changes it (other than when initially setting it), the caller must invalidate the entry with the same key (our index arg) in the LvalDict2 that implements self._i_instance_CVdict (but the API for the caller to do that is not yet worked out #e). (Implem note: _CV_ stands for "compute value" to ExprsMeta, which implements the LvalDict2 associated with this. This method needs no corresponding _CK_ keylist function, since any key (instance index) asked for is assumed valid.) """ assert self._e_is_instance # This method needs to create a new instance, by instantiating expr and giving it index. # Implem notes: # - the glue code added by _CV_ from self._i_instance_CVdict to this method (by ExprsMeta) uses LvalDict2 # to cache values computed by this method, and recompute them if needed (which may never happen, I'm not sure). # - the access to self._i_instance_decl_data[index] is not usage tracked; # thus if we change it above w/o error, an inval is needed. data = self._i_instance_decl_data[index] expr, lvalflag = data # 061204 ## print "fyi, using kid expr %r" % expr # (these tend to be longish, and start with eval_Expr -- what we'd rather print is the result of the first eval it does) # Note, this expr can be very simplifiable, if it came from an Arg macro, # but present code [061105] reevals the full macro expansion each time -- suboptimal. # To fix, we'd need an "expr simplify", whether we used it only once or not... # the reason to use it more than once is if it becomes partly final at some later point than when it starts out. # The simplify implem would need decls about finality of, e.g., getting of some bound methods and (determinism of) # their retvals -- namely (for Arg macro etc) for _i_instance and _i_grabarg and maybe more. # Feasible but not simple; worthwhile optim someday but not right away. ##e if not EVAL_REFORM: # three increasingly strict asserts: assert expr is not None assert is_Expr(expr), "not is_Expr: %r" % (expr,) assert is_pure_expr(expr) ###k?? what if someone passes an instance -- is that permitted, but a noop for instantiation?? assert is_Expr_pyinstance(expr), "can't instantiate a class: %r" % (expr,) # new, 070117, untested### -- i'm not even sure it's correct # also assume expr is "canonicalized" and even "understood" -- not sure if this is justified printnim("don't we need understand_expr somewhere in here? (before kidmaking in IorE)") ###@@@ else: # EVAL_REFORM case 070117 if not (is_pure_expr(expr) and is_Expr_pyinstance(expr)): print "this should never happen as of 070118 since we handle it above: non pure expr %r in _CV__i_instance_CVdict" % (expr,) ## print "FYI: EVAL_REFORM: _CV__i_instance_CVdict is identity on %r" % (expr,) # this is routine on e.g. None, small ints, colors, other tuples... and presumably Instances (not tested) assert not lvalflag # see comments at start of _i_instance return expr pass ####e: [061105] is _e_eval actually needing to be different from _e_make_in?? yes, _e_eval needs to be told _self # and the other needs to make one... no wait, wrong, different selves -- # the _self told to eval is the one _make_in *should make something inside of*! (ie should make a kid of) # So it may be that these are actually the same thing. (See paper notes from today for more about this.) # For now, tho, we'll define _e_make_in on OpExpr to use eval. [not done, where i am] # actually this is even faster -- review sometime (note, in constant_Expr they're both there & equiv #k): ###@@@ ## this is just the kid expr: print "_CV__i_instance_CVdict needs to make expr %r" % (expr,) ## if hasattr(expr, '_e_make_in'): ## print("REJECTED using _e_make_in case, on a pyinstance of class %s" % expr.__class__.__name__)###was printfyi til 061208 921p ## ## res = expr._e_make_in(env, index_path) ## #k we might have to fix bugs caused by not using this case, by defining (or modifying?) defs of _e_eval on some classes; ## # addendum 061212, we now do that on If_expr. if not EVAL_REFORM: # WARNING: following code is very similar to _i_eval_dfltval_expr as of 061203 # printfyi("used _e_eval case (via _e_compute_method)") # this case is usually used, as of 061108 -- now always, 061110 # note: this (used-to-be-redundantly) grabs env from self try: res = expr._e_compute_method(self, index, _lvalue_flag = lvalflag)() ##e optim someday: inline this # 061105 bug3, if bug2 was in held_dflt_expr and bug1 was 'dflt 10' except: # we expect caller to exit now, so we might as well print this first: [061114] print "following exception concerns self = %r, index = %r in _CV__i_instance_CVdict calling _e_compute_method" % \ (self, index) raise else: # EVAL_REFORM case, 070117 if kluge070119: # also pass an env retrieved from... env = self._i_instance_decl_env[index] env, ipath = self._i_env_ipath_for_formula_at_index( index, env) # note: retval's env is modified from the one passed # THIS MUST BE WHERE THE INCORRECT BINDING _self = self gets added, in the kluge070119 ###BUG in _5x! [070120 844p] # GUESS at basic bug: an Instance self really needs to know two envs: the one for its args (_self refers to # whoeever lexically created the expr they were made from), and the one for their internal formulae (_self = self). # self.env is the one for the args (since it's thought of as "self's (outer) environment"), # and the one for internal formulae has (until now -- this should change #e) been recreated as needed in # _e_compute_method and now in _i_env_ipath_for_formula_at_index, by extending self.env by _self = self. # But to be correct, this should be done BEFORE the burrowing and env-extension done by the kluge070119, # but in this wrong current code it's done after it. The old code also had wrongness of order, in principle # (doing this _self addition here rather than in env_for_args, with env_for_args done first when it should # be done 2nd), but it didn't matter since env_for_args only added _this_xxx and _my. WAIT, THAT'S WRONG -- # env_for_args is for external (in lexenv) args, so it should have SAME _self as self.env, so it's correct now, # and never gets or needs _self = self, either before or after its _my and _this_xxx bindings. # What we need here is NOT env_for_args but env_for_internal_formulae. It should be used for dflt exprs in Arg, # and for all exprs sitting on class assignments. We've been making it before *using* those formulae # (probably getting it onto dflt exprs only because they end up being *used* in the right place for that). # We've been making it in each call of _i_env_ipath_for_formula_at_index or _e_compute_method, but we ought to # make it once and let those use it, and make sure that happens before the env mods from the burrowing done by # kluge070119. (We can't make it in the class (eg ExprsMeta) -- we have to wait until _init_instance or so, # because it depends on self which is only known around then.) # # SUGGESTED FIX ###e: make self.env_for_internal_formulae (#e shorter name -- env_for_formulae?) # before (??) calling _init_instance [or after if _init_instance can modify self.env ##k]; # use it in _i_env_ipath_for_formula_at_index and some of our uses of _e_compute_method; # and review all uses of self.env for whether they ought to be env_for_formulae. # Doing it 070120 circa 942p -- I made _C_env_for_formulae instead of setting it in _init_instance # (optim, since some Instances never need it (I hope -- not sure) and since it creates a cyclic ref. else: env, ipath = self._i_env_ipath_for_formula_at_index( index) # equivalent to how above other case computes them assert not lvalflag # see comments at start of _i_instance res = expr._e_make_in(env,ipath) # only pure IorE exprs have this method; should be ok since only they are returned from expr evals return res # from _CV__i_instance_CVdict
def _i_instance( self, expr, index, _lvalue_flag = False, permit_expr_to_vary = False, skip_expr_compare = False ): # see also def Instance (the method, just above, not the macro) """ [semi-private; used by macros like Instance, Arg, Option] Find or create (or perhaps recompute if invalid, but only the latest version is memoized) (and return) an Instance of expr, contained in self, at the given relative index, and in the same env [#e generalize env later?]. Error (perhaps not detected) if called with same index and different other args; when a cached instance is returned, the other args are not used except perhaps to check for this error. (When an instance is recomputed, if this error happens, are new args used? undefined.) (#e Should we change this to make the expr effectively part of the index, for caching? Probably not; not sure.) (#e Should we change this to make it legal to pass a new expr? Probably not... hard for subsequent callers to be consistent...) WARNING: this docstring is probably out of date regarding whether expr can change and what happens if it does. [070301 comment] """ # Note: Before EVAL_REFORM, this is used to do all evals, including instantiations (treated as how IorE evals). # It also does "eval to lval" when _lvalue_flag is true (passed only from LvalueArg, so far used only in Set). # After EVAL_REFORM, instantiation can't be done by eval (evals of IorE exprs return them unchanged); # evals are done before this is called, then this either returns constants (numbers, Instances) unchanged, # or instantiates exprs. It can no longer be called with _lvalue_flag when EVAL_REFORM (as of late 070119). def __debug_frame_repr__(locals): """ return a string for inclusion in some calls of print_compact_stack """ return "_i_instance(index = %r, expr = %r), self = %r" % (index,expr,self) assert self._e_is_instance if not EVAL_REFORM: # this is redundant with a later assert in _i_instance_CVdict in all cases (EVAL_REFORM or not), # and it causes trouble in the EVAL_REFORM case. To be conservative I'll only disable it in that case # (so I'll leave it untouched in this case). 070117 assert is_pure_expr(expr), "who passed non-pure-expr %r to _i_instance? index %r, self %r, _e_args %r" % \ (expr, index, self, self._e_args) #k guess 061105 else: if _lvalue_flag: print "SHOULD NEVER HAPPEN: saw _lvalue_flag: _i_instance(index = %r, expr = %r), self = %r" % (index,expr,self) # new behavior 070118; might be needed to fix "bug: expr or lvalflag for instance changed" in testexpr_9fx4 click, # or might just be hiding an underlying bug which will show up for pure exprs in If clauses -- not yet sure. #####k # (Note that its only effects are an optim and to remove some error messages -- what's unknown is whether the detected # "errors" were real errors or not, in this case. # Guess: we'll still have a ###BUG when exprs are involved, which to fix # will require noticing local ipath modifiers (lexenv_ipath_Expr's ipath) not only in _e_make_in but in the upcoming # determination of index; the best fix will be to dispense with the local cache of exprs per-index in favor of a global one # indexed by ipath, after those local ipath mods are included -- the same one needed for the "find" in the find or make # of instantiation (this find presently being nim, as mentioned somewhere else in this file in the last day or two), # meaning that it takes into account everything needed to create the ipath to find or make at (local mods, # state-sharing transforms, maybe more).) if is_constant_for_instantiation(expr): #revised 070131 self._debug_i_instance_retval(expr) #070212 return expr # [#k review whether this comment is still needed/correct; 061204 semiobs due to newdata change below; even more obs other ways] # hmm, calling Instance macro evals the expr first... can't it turn out that it changes over time? # I think yes... not only that, a lot of change in it should be buried inside the instance! (if it's in an arg formula) # as if we need to "instantiate the expr" before actually passing it... hmm, if so this is a SERIOUS LOGIC BUG. ####@@@@ # WAIT -- can it be that the expr never changes? only its value does? and that we should pass the unevaluated expr? YES. # But i forgot, eval of an expr is that expr! I get confused since _e_eval evals an implicit instance -- rename it soon! ###@@@ # [addendum 061212: see also the comments in the new overriding method If_expr._e_eval.] # 070119 Q: what if, right here, we burrowed into expr, and used a cvdict on a central object? as a kluge? # just since fast to code vs a central instantiator obj? # or at *least* we could do it locally -- no sharing but should fix bugs? if EVAL_REFORM and kluge070119: assert not _lvalue_flag # don't know how otherwise ## env = self.env # like in _i_env_ipath_for_formula_at_index [like in the old wrong version, that is] env = self.env_for_formulae # like in _i_env_ipath_for_formula_at_index [like in the new corrected version, 070120 940p] expr, env, ipath = expr, env, index oldstuff = expr, env, ipath expr, env, ipath = expr._e_burrow_for_find_or_make(env, ipath) if debug070120 and (expr, env, ipath) != oldstuff: print "fyi EVAL_REFORM kluge070119: worked, %r => %r" % (oldstuff, (expr, env, ipath)) index = ipath del ipath self._i_instance_decl_env[index] = env del env pass newdata = (expr, _lvalue_flag) # revised 061204, was just expr, also renamed; cmt above is semiobs due to this olddata = self._i_instance_decl_data.get(index, None) # see above comment if skip_expr_compare: #070411 new optim option (public) ###UNTESTED; might or might not use in confirmation corner ###e should use in Arg, Option -- might be a big speedup -- but is it safe?? maybe only with IorE If, not eval If!! ###k expr_is_new = (olddata is None) else: expr_is_new = (olddata != newdata) if expr_is_new: if olddata is not None: if permit_expr_to_vary: # new feature 070207 ### how do we remove the old instance so it gets remade? ## del self._i_instance_CVdict[index] ##k guess at how -- but del might be nim in this object -- # good thing it was, since inval_at_key [new feature] is what we need -- del would fail to propogate invals # to prior usage trackers of this now-obsolete instance! ###BUG: changes to the info used to compute expr, by whatever decides to call this method (_i_instance), # are usage tracked into that caller, not into our lval in our lvaldict. We depend on the caller # someday getting recomputed before it will realize it wants to pass us a new expr and cause this inval. # (Example of this use: a kid-column delegate in MT_try2.) # THIS LATE PROPOGATION OF INVAL MIGHT LEAD TO BUGS -- not reviewed in detail. # Much better would be for the callers "usage tracked so far" to somehow get subscribed to (for invals) # by the thing we manually inval here. The potential bugs in the current scheme include: # - inval during recompute can make more pieces of it need recompute than did when it started # - if the caller's recompute never happens, the invals that need to propogate never occur at all, # though they need to. # I am not sure if this can cause bugs in MT_try2 -- maybe not -- but surely it can in general! ###FIX SOMEHOW (maybe as suggested above, by stealing or copying usage tracking info from caller -- # but it's not enough, we'd need our lval's recompute to get the caller to re-decide to call us # so we'd know the new expr to recompute with! So, details of any fix are unclear.) self._i_instance_CVdict.inval_at_key(index) # print "fyi: made use of permit_expr_to_vary for index = %r, expr = %r" % (index, expr) # remove when works else: print "bug: expr or lvalflag for instance changed: self = %r, index = %r, new data = %r, old data = %r" % \ (self,index,newdata,olddata) #e more info? i think this is an error and should not happen normally # update 070122: it usually indicates an error, but it's a false alarm in the latest bugfixed testexpr_21g, # since pure-expr equality should be based on formal structure, not pyobj identity. ###e ####e need to print a diff of the exprs, so we see what the problem is... [070321 comment; happens with ArgList] #e if it does happen, should we inval that instance? yes, if this ever happens without error. # [addendum 061212: see also the comments in the new overriding method If_expr._e_eval.] # [one way to do that: have _i_instance_decl_data contain changetracked state vars, changed above, # usage-tracked by _CV__i_instance_CVdict. But the env matters too, so we might want to compare it too, # or compare the expr before burrowing into it, and do the # burrowing later then?? But we can't, due to the index... ###e 070122] self._i_instance_decl_data[index] = newdata else: # they're equal (or we were told not to bother comparing them, in which case, use the old one) if 0 and (not skip_expr_compare) and olddata[0] is not expr: # this print worked, but it's not usually useful (not enough is printed) so I disabled it # they're only equal because __eq__ is working on non-identical exprs [implemented in Expr late 070122] print "fyi: non-identical exprs compared equal (did we get it right?): %r and %r" % (olddata[0], expr) ### remove sometime pass res = self._i_instance_CVdict[index] # takes care of invals in making process? or are they impossible? ##k [see above] self._debug_i_instance_retval( res) #070212 return res