diff options
author | dos-reis <gdr@axiomatics.org> | 2007-08-14 05:14:52 +0000 |
---|---|---|
committer | dos-reis <gdr@axiomatics.org> | 2007-08-14 05:14:52 +0000 |
commit | ab8cc85adde879fb963c94d15675783f2cf4b183 (patch) | |
tree | c202482327f474583b750b2c45dedfc4e4312b1d /src/interp/i-coerce.boot.pamphlet | |
download | open-axiom-ab8cc85adde879fb963c94d15675783f2cf4b183.tar.gz |
Initial population.
Diffstat (limited to 'src/interp/i-coerce.boot.pamphlet')
-rw-r--r-- | src/interp/i-coerce.boot.pamphlet | 1454 |
1 files changed, 1454 insertions, 0 deletions
diff --git a/src/interp/i-coerce.boot.pamphlet b/src/interp/i-coerce.boot.pamphlet new file mode 100644 index 00000000..e69d13b2 --- /dev/null +++ b/src/interp/i-coerce.boot.pamphlet @@ -0,0 +1,1454 @@ +\documentclass{article} +\usepackage{axiom} + +\title{\File{src/interp/i-coerce.boot} Pamphlet} +\author{The Axiom Team} + +\begin{document} +\maketitle +\begin{abstract} +\end{abstract} +\eject +\tableofcontents +\eject + +\section{Coercion conventions} + +\begin{verbatim} +Coercion conventions + +Coercion involves the changing of the datatype of an object. This + can be done for conformality of operations or, for example, to + change the structure of an object into one that is understood by + the printing routines. + +The actual coercion is controlled by the function "coerce" which + takes and delivers wrapped operands. Also see the functions + interpCoerce and coerceInteractive. + +Sometimes one does not want to actually change the datatype but + rather wants to determine whether it is possible to do so. The + controlling function to do this is "canCoerceFrom". The value + passed to specific coercion routines in this case is + "$fromCoerceable$". The value returned is true or false. See + specific examples for more info. + +The special routines that do the coercions typically involve a "2" + in their names. For example, G2E converts type "Gaussian" to + type "Expression". These special routines take and deliver + unwrapped operands. The determination of which special routine + to use is often made by consulting the list $CoerceTable + (currently in COT BOOT) and this is controlled by coerceByTable. + Note that the special routines are in the file COERCEFN BOOT. +\end{verbatim} +\section{Function getConstantFromDomain} +[[getConstantFromDomain]] is used to look up the constants $0$ and $1$ +from the given [[domainForm]]. +\begin{enumerate} +\item if [[isPartialMode]] (see i-funsel.boot) returns true then the +domain modemap contains the constant [[$EmptyMode]] which indicates +that the domain is not fully formed. In this case we return [[NIL]]. +\end{enumerate} +<<getConstantFromDomain>>= +getConstantFromDomain(form,domainForm) == + isPartialMode domainForm => NIL + opAlist := getOperationAlistFromLisplib first domainForm + key := opOf form + entryList := LASSOC(key,opAlist) + entryList isnt [[sig, ., ., .]] => + key = "One" => getConstantFromDomain(["1"], domainForm) + key = "Zero" => getConstantFromDomain(["0"], domainForm) + throwKeyedMsg("S2IC0008",[form,domainForm]) + -- i.e., there should be exactly one item under this key of that form + domain := evalDomain domainForm + SPADCALL compiledLookupCheck(key,sig,domain) + +@ +\section{License} +<<license>>= +-- Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd. +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are +-- met: +-- +-- - Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- +-- - Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in +-- the documentation and/or other materials provided with the +-- distribution. +-- +-- - Neither the name of The Numerical ALgorithms Group Ltd. nor the +-- names of its contributors may be used to endorse or promote products +-- derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +-- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +@ +<<*>>= +<<license>> +--% Algebraic coercions using interactive code + +algCoerceInteractive(p,source,target) == + -- now called in some groebner code + $useConvertForCoercions : local := true + source := devaluate source + target := devaluate target + u := coerceInteractive(objNewWrap(p,source),target) + u => objValUnwrap(u) + error ['"can't convert",p,'"of mode",source,'"to mode",target] + +spad2BootCoerce(x,source,target) == + -- x : source and we wish to coerce to target + -- used in spad code for Any + null isValidType source => throwKeyedMsg("S2IE0004",[source]) + null isValidType target => throwKeyedMsg("S2IE0004",[target]) + x' := coerceInteractive(objNewWrap(x,source),target) => + objValUnwrap(x') + throwKeyedMsgCannotCoerceWithValue(wrap x,source,target) + +--% Functions for Coercion or Else We'll Get Rough + +coerceOrFail(triple,t,mapName) == + -- some code generated for this is in coerceInt0 + t = $NoValueMode => triple + t' := coerceInteractive(triple,t) + t' => objValUnwrap(t') + sayKeyedMsg("S2IC0004",[mapName,objMode triple,t]) + '"failed" + +coerceOrCroak(triple, t, mapName) == + -- this does the coercion and returns the value or dies + t = $NoValueMode => triple + t' := coerceOrConvertOrRetract(triple,t) + t' => objValUnwrap(t') + mapName = 'noMapName => + throwKeyedMsgCannotCoerceWithValue(objVal triple,objMode triple, t) + sayKeyedMsg("S2IC0005",[mapName]) + throwKeyedMsgCannotCoerceWithValue(objVal triple,objMode triple, t) + +coerceOrThrowFailure(value, t1, t2) == + (result := coerceOrRetract(objNewWrap(value, t1), t2)) or + coercionFailure() + objValUnwrap(result) + +--% Retraction functions + +retract object == + type := objMode object + STRINGP type => 'failed + type = $EmptyMode => 'failed + val := objVal object + not isWrapped val and val isnt ['MAP,:.] => 'failed + type' := equiType(type) + (ans := retract1 objNew(val,equiType(type))) = 'failed => ans + objNew(objVal ans,eqType objMode ans) + +retract1 object == + -- this function is the new version of the old "pullback" + -- it first tries to change the datatype of an object to that of + -- largest contained type. Examples: P RN -> RN, RN -> I + -- This is mostly for cases such as constant polynomials or + -- quotients with 1 in the denominator. + type := objMode object + STRINGP type => 'failed + val := objVal object + type = $PositiveInteger => objNew(val,$NonNegativeInteger) + type = $NonNegativeInteger => objNew(val,$Integer) + type = $Integer and SINTP unwrap val => objNew(val, $SingleInteger) + type' := equiType(type) + if not EQ(type,type') then object := objNew(val,type') + (1 = #type') or (type' is ['Union,:.]) or + (type' is ['FunctionCalled,.]) + or (type' is ['OrderedVariableList,.]) or (type is ['Variable,.]) => + (object' := retract2Specialization(object)) => object' + 'failed + null (underDomain := underDomainOf type') => 'failed + -- try to retract the "coefficients" + -- think of P RN -> P I or M RN -> M I + object' := retractUnderDomain(object,type,underDomain) + object' ^= 'failed => object' + -- see if we can use the retract functions + (object' := coerceRetract(object,underDomain)) => object' + -- see if we have a special case here + (object' := retract2Specialization(object)) => object' + 'failed + +retractUnderDomain(object,type,underDomain) == + null (ud := underDomainOf underDomain) => 'failed + [c,:args] := deconstructT type + 1 ^= #args => 'failed + 1 ^= #c => 'failed + type'' := constructT(c,[ud]) + (object' := coerceInt(object,type'')) => object' + 'failed + +retract2Specialization object == + -- handles some specialization retraction cases, like matrices + val := objVal object + val' := unwrap val + type := objMode object + + type = $Any => + [dom,:obj] := val' + objNewWrap(obj,dom) + type is ['Union,:unionDoms] => coerceUnion2Branch object + type = $Symbol => + objNewWrap(1,['OrderedVariableList,[val']]) + type is ['OrderedVariableList,var] => + coerceInt(objNewWrap(var.(val'-1),$Symbol), '(Polynomial (Integer))) +-- !! following retract seems wrong and breaks ug13.input +-- type is ['Variable,var] => +-- coerceInt(object,$Symbol) + type is ['Polynomial,D] => + val' is [ =1,x,:.] => + vl := REMDUP reverse varsInPoly val' + 1 = #vl => coerceInt(object,['UnivariatePolynomial,x,D]) + NIL + val' is [ =0,:.] => coerceInt(object, D) + NIL + type is ['Matrix,D] => + n := # val' + m := # val'.0 + n = m => objNew(val,['SquareMatrix,n,D]) + objNew(val,['RectangularMatrix,n,m,D]) + type is ['RectangularMatrix,n,m,D] => + n = m => objNew(val,['SquareMatrix,n,D]) + NIL + (type is [agg,D]) and (agg in '(Vector Segment UniversalSegment)) => + D = $PositiveInteger => objNew(val,[agg,$NonNegativeInteger]) + D = $NonNegativeInteger => objNew(val,[agg,$Integer]) + NIL + type is ['Array,bds,D] => + D = $PositiveInteger => objNew(val,['Array,bds,$NonNegativeInteger]) + D = $NonNegativeInteger => objNew(val,['Array,bds,$Integer]) + NIL + type is ['List,D] => + D isnt ['List,D'] => + -- try to retract elements + D = $PositiveInteger => objNew(val,['List,$NonNegativeInteger]) + D = $NonNegativeInteger => objNew(val,['List,$Integer]) + null val' => nil +-- null (um := underDomainOf D) => nil +-- objNewWrap(nil,['List,um]) + vl := nil + tl := nil + bad := nil + for e in val' while not bad repeat + (e' := retract objNewWrap(e,D)) = 'failed => bad := true + vl := [objValUnwrap e',:vl] + tl := [objMode e',:tl] + bad => NIL + (m := resolveTypeListAny tl) = D => NIL + D = equiType(m) => NIL + vl' := nil + for e in vl for t in tl repeat + t = m => vl' := [e,:vl'] + e' := coerceInt(objNewWrap(e,t),m) + null e' => return NIL + vl' := [objValUnwrap e',:vl'] + objNewWrap(vl',['List,m]) + D' = $PositiveInteger => + objNew(val,['List,['List,$NonNegativeInteger]]) + D' = $NonNegativeInteger => + objNew(val,['List,['List,$Integer]]) + D' is ['Variable,.] or D' is ['OrderedVariableList,.] => + coerceInt(object,['List,['List,$Symbol]]) + + n := # val' + m := # val'.0 + null isRectangularList(val',n,m) => NIL + coerceInt(object,['Matrix,D']) + type is ['Expression,D] => + [num,:den] := val' + -- coerceRetract already handles case where den = 1 + num isnt [0,:num] => NIL + den isnt [0,:den] => NIL + objNewWrap([num,:den],[$QuotientField, D]) + type is ['SimpleAlgebraicExtension,k,rep,.] => + -- try to retract as an element of rep and see if we can get an + -- element of k + val' := retract objNew(val,rep) + while (val' ^= 'failed) and + (equiType(objMode val') ^= k) repeat + val' := retract val' + val' = 'failed => NIL + val' + + type is ['UnivariatePuiseuxSeries, coef, var, cen] => + coerceInt(object, ['UnivariateLaurentSeries, coef, var, cen]) + type is ['UnivariateLaurentSeries, coef, var, cen] => + coerceInt(object, ['UnivariateTaylorSeries, coef, var, cen]) + + type is ['FunctionCalled,name] => + null (m := get(name,'mode,$e)) => NIL + isPartialMode m => NIL + objNew(val,m) + NIL + +coerceOrConvertOrRetract(T,m) == + $useConvertForCoercions : local := true + coerceOrRetract(T,m) + +coerceOrRetract(T,m) == + (t' := coerceInteractive(T,m)) => t' + t := T + ans := nil + repeat + ans => return ans + t := retract t -- retract is new name for pullback + t = 'failed => return ans + ans := coerceInteractive(t,m) + ans + +coerceRetract(object,t2) == + -- tries to handle cases such as P I -> I + (val := objValUnwrap(object)) = "$fromCoerceable$" => NIL + t1 := objMode object + t2 = $OutputForm => NIL + isEqualOrSubDomain(t1,$Integer) and typeIsASmallInteger(t2) and SMINTP(val) => + objNewWrap(val,t2) + t1 = $Integer => NIL + t1 = $Symbol => NIL + t1 = $OutputForm => NIL + (c := retractByFunction(object, t2)) => c + t1 is [D,:.] => + fun := GETL(D,'retract) or + INTERN STRCONC('"retract",STRINGIMAGE D) + functionp fun => + PUT(D,'retract,fun) + c := CATCH('coerceFailure,FUNCALL(fun,object,t2)) + (c = $coerceFailure) => NIL + c + NIL + NIL + +retractByFunction(object,u) == + -- tries to retract by using function "retractIfCan" + -- if the type belongs to the correct category. + $reportBottomUpFlag: local := NIL + t := objMode object + -- JHD/CRF not ofCategory(t,['RetractableTo,u]) => NIL + val := objValUnwrap object + + -- try to get and apply the function "retractable?" + target := ['Union,u,'"failed"] + funName := 'retractIfCan + if $reportBottomUpFlag then + sayFunctionSelection(funName,[t],target,NIL, + '"coercion facility (retraction)") + -- JHD/CRF if (mms := findFunctionInDomain(funName,t,target,[t],[t],'T,'T)) + -- MCD: changed penultimate variable to NIL. + if (mms := append(findFunctionInDomain(funName,t,target,[t],[t],NIL,'T), + findFunctionInDomain(funName,u,target,[t],[t],NIL,'T))) +-- The above two lines were: (RDJ/BMT 6/95) +-- if (mms := append(findFunctionInDomain(funName,t,target,[t],[t],'T,'T), +-- findFunctionInDomain(funName,u,target,[t],[t],'T,'T))) + then mms := orderMms(funName,mms,[t],[t],target) + if $reportBottomUpFlag then + sayFunctionSelectionResult(funName,[t],mms) + null mms => NIL + + -- [[dc,:.],slot,.]:= CAR mms + dc := CAAAR mms + slot := CADAR mms + dcVector:= evalDomain dc + fun := +--+ + compiledLookup(funName,[target,t],dcVector) + NULL fun => NIL + CAR(fun) = function Undef => NIL +--+ + $: fluid := dcVector + object' := coerceUnion2Branch objNewWrap(SPADCALL(val,fun),target) + u' := objMode object' + u = u' => object' + NIL + +--% Coercion utilities + +-- The next function extracts the structural definition of constants +-- from a given domain. For example, getConstantFromDomain('(One),S) +-- returns the representation of 1 in the domain S. + +constantInDomain?(form,domainForm) == + opAlist := getOperationAlistFromLisplib first domainForm + key := opOf form + entryList := LASSOC(key,opAlist) + entryList is [[., ., ., type]] and type in '(CONST ASCONST) => true + key = "One" => constantInDomain?(["1"], domainForm) + key = "Zero" => constantInDomain?(["0"], domainForm) + false + +<<getConstantFromDomain>> + +domainOne(domain) == getConstantFromDomain('(One),domain) + +domainZero(domain) == getConstantFromDomain('(Zero),domain) + +--------------------> NEW DEFINITION (override in xrun.boot.pamphlet) +equalOne(object, domain) == + -- tries using constant One and "=" from domain + -- object should not be wrapped + eqfunc := getFunctionFromDomain("=",domain,[domain,domain]) + SPADCALL(object,getConstantFromDomain('(One),domain),eqfunc) + +--------------------> NEW DEFINITION (override in xrun.boot.pamphlet) +equalZero(object, domain) == + -- tries using constant Zero and "=" from domain + -- object should not be wrapped + eqfunc := getFunctionFromDomain("=",domain,[domain,domain]) + SPADCALL(object,getConstantFromDomain('(Zero),domain),eqfunc) + +--------------------> NEW DEFINITION (override in xrun.boot.pamphlet) +algEqual(object1, object2, domain) == + -- sees if 2 objects of the same domain are equal by using the + -- "=" from the domain + -- objects should not be wrapped +-- eqfunc := getFunctionFromDomain("=",domain,[domain,domain]) + eqfunc := compiledLookupCheck("=",[$Boolean,domain,domain],evalDomain domain) + SPADCALL(object1,object2, eqfunc) + +--% main algorithms for canCoerceFrom and coerceInteractive + +-- coerceInteractive and canCoerceFrom are the two coercion functions +-- for $InteractiveMode. They translate RN, RF and RR to QF I, QF P +-- and RE RN, respectively, and call coerceInt or canCoerce, which +-- both work in the same way (e.g. coercion from t1 to t2): + +-- 1. they try to coerce t1 to t2 directly (tower coercion), and, if +-- this fails, to coerce t1 to the last argument of t2 and embed +-- this last argument into t2. These embedding functions are now only +-- defined in the algebra code. (RSS 2-27-87) + +-- 2. the tower coercion looks whether there is any applicable local +-- coercion, which means, one defined in boot or in algebra code. +-- If there is an applicable function from a constructor, which is +-- inside the type tower of t1, to the top level constructor of t2, +-- then this constructor is bubbled up inside t1. This means, +-- special coercion functions (defined in boot) are called, which +-- commute two constructors in a tower. Then the local coercion is +-- called on these constructors, which both are on top level now. + +-- example: +-- let t1 = A B C D E (short for (A (B (C (D (E))))), where A ... E are +-- type constructors), and t2 = F D G H I J +-- there is no coercion from t1 to t2 directly, so we try to coerce +-- t1 to s1 = D G H I J, the last argument of t2 +-- we create the type s2 = A D B C E and call a local coercion A2A +-- from t1 to s2, which, by recursively calling coerce, bubbles up +-- the constructor D +-- then we call a commute coerce from s2 to s3 = D A B C E and a local +-- coerce D2D from s3 to s1 +-- finally we embed s1 into t2, which completes the coercion t1 to t2 + +-- the result of canCoerceFrom is TRUE or NIL +-- the result of coerceInteractive is a object or NIL (=failed) +-- all boot coercion functions have the following result: +-- 1. if u=$fromCoerceable$, then TRUE or NIL +-- 2. if the coercion succeeds, the coerced value (this may be NIL) +-- 3. if the coercion fails, they throw to a catch point in +-- coerceByFunction + +--% Interpreter Coercion Query Functions + +canCoerce1(t1,t2) == + -- general test for coercion + -- the result is NIL if it fails + t1 = t2 => true + absolutelyCanCoerceByCheating(t1,t2) or t1 = '(None) or t2 = '(Any) or + t1 in '((Mode) (Domain) (SubDomain (Domain))) => + t2 = $OutputForm => true + NIL + -- next is for tagged union selectors for the time being + t1 is ['Variable,=t2] or t2 is ['Variable,=t1] => true + STRINGP t1 => + t2 = $String => true + t2 = $OutputForm => true + t2 is ['Union,:.] => canCoerceUnion(t1,t2) + t2 is ['Variable,v] and (t1 = PNAME(v)) => true + NIL + STRINGP t2 => + t1 is ['Variable,v] and (t2 = PNAME(v)) => true + NIL + atom t1 or atom t2 => NIL + null isValidType(t2) => NIL + + absolutelyCannotCoerce(t1,t2) => NIL + + nt1 := CAR t1 + nt2 := CAR t2 + + EQ(nt1,'Mapping) => EQ(nt2,'Any) + EQ(nt2,'Mapping) => + EQ(nt1,'Variable) or EQ(nt1,'FunctionCalled) => + canCoerceExplicit2Mapping(t1,t2) + NIL + EQ(nt1,'Union) or EQ(nt2,'Union) => canCoerceUnion(t1,t2) + + -- efficiency hack + t1 is ['Segment, s1] and t2 is ['UniversalSegment, s2] and + (isEqualOrSubDomain(s1, s2) or canCoerce(s1, s2)) => true + + t1 is ['Tuple,S] and t2 ^= '(OutputForm) => canCoerce(['List, S], t2) + + isRingT2 := ofCategory(t2,'(Ring)) + isRingT2 and isEqualOrSubDomain(t1,$Integer) => true + (ans := canCoerceTopMatching(t1,t2,nt1,nt2)) ^= 'maybe => ans + t2 = $Integer => canCoerceLocal(t1,t2) -- is true + ans := canCoerceTower(t1,t2) or + [.,:arg]:= deconstructT t2 + arg and + t:= last arg + canCoerce(t1,t) and canCoerceByFunction(t,t2) and 'T + ans or (t1 in '((PositiveInteger) (NonNegativeInteger)) + and canCoerce($Integer,t2)) + +canCoerceFrom0(t1,t2) == +-- top level test for coercion, which transfers all RN, RF and RR into +-- equivalent types + startTimingProcess 'querycoerce + q := + isEqualOrSubDomain(t1,t2) or t1 = '(None) or t2 = '(Any) or + if t2 = $OutputForm then (s1 := t1; s2 := t2) + else (s1:= equiType(t1); s2:= equiType(t2)) + + -- make sure we are trying to coerce to a legal type + -- in particular, polynomials are repeated, etc. + null isValidType(t2) => NIL + null isLegitimateMode(t2,nil,nil) => NIL + + t1 = $RationalNumber => + isEqualOrSubDomain(t2,$Integer) => NIL + canCoerce(t1,t2) or canCoerce(s1,s2) + canCoerce(s1,s2) + stopTimingProcess 'querycoerce + q + +isSubTowerOf(t1,t2) == + -- assumes RF and RN stuff has been expanded + -- tests whether t1 is somewhere inside t2 + isEqualOrSubDomain(t1,t2) => true + null (u := underDomainOf t2) => nil + isSubTowerOf(t1,u) + +canCoerceTopMatching(t1,t2,tt1,tt2) == + -- returns true, nil or maybe + -- for example, if t1 = P[x] D1 and t2 = P[y] D2 and x = y then + -- canCoerce will only be true if D1 = D2 + not EQ(tt1,tt2) => 'maybe + doms := '(Polynomial List Matrix FiniteSet Vector Stream Gaussian) + MEMQ(tt1,doms) => canCoerce(CADR t1, CADR t2) + not (MEMQ(tt1,$univariateDomains) or MEMQ(tt2,$multivariateDomains)) => + 'maybe + u2 := deconstructT t2 + 1 = #u2 => NIL + u1 := deconstructT t1 + 1 = #u1 => NIL -- no under domain + first(u1) ^= first(u2) => 'maybe + canCoerce(underDomainOf t1, underDomainOf t2) + +canCoerceExplicit2Mapping(t1,t is ['Mapping,target,:argl]) == + -- determines if there a mapping called var with the given args + -- and target + $useCoerceOrCroak: local := nil + t1 is ['Variable,var] => + null (mms :=selectMms1(var,target,argl,[NIL for a in argl],true)) => NIL + mm := CAAR mms + mm is [., targ, :.] => + targ = target => true + false + false + t1 is ['FunctionCalled,fun] => + funNode := mkAtreeNode fun + transferPropsToNode(fun,funNode) + mms := CATCH('coerceOrCroaker, selectLocalMms(funNode,fun,argl,target)) + CONSP mms => + mms is [[['interpOnly,:.],:.]] => nil + mm := CAAR mms + mm is [., targ, :.] => + targ = target => true + false + false + NIL + NIL + +canCoerceUnion(t1,t2) == + -- sees if one can coerce to or from a Union Domain + -- assumes one of t1 and t2 is one + + -- get the domains in the union, checking for tagged unions + if (isUnion1 := t1 is ['Union,:uds1]) then + unionDoms1 := + uds1 and first uds1 is [":",:.] => [t for [.,.,t] in uds1] + uds1 + if (isUnion2 := t2 is ['Union,:uds2]) then + unionDoms2 := + uds2 and first uds2 is [":",:.] => [t for [.,.,t] in uds2] + uds2 + + isUnion2 => + member(t1,unionDoms2) => true + isUnion1 => + and/[or/[canCoerce(ud1,ud2) for ud2 in unionDoms2] + for ud1 in unionDoms1] + or/[canCoerce(t1,ud) for ud in unionDoms2] + -- next, a little lie + t1 is ['Union,d1, ='"failed"] and t2 = d1 => true + isUnion1 => + and/[canCoerce(ud,t2) for ud in unionDoms1] + keyedSystemError("S2GE0016",['"canCoerceUnion", + '"called with 2 non-Unions"]) + +canCoerceByMap(t1,t2) == + -- idea is this: if t1 is D U1 and t2 is D U2, then look for + -- map: (U1 -> U2, D U1) -> D U2. If it exists, then answer true + -- if canCoerceFrom(t1,t2). + u2 := deconstructT t2 + 1 = #u2 => NIL + u1 := deconstructT t1 + 1 = #u1 => NIL -- no under domain + CAR(u1) ^= CAR(u2) => NIL + top := CAAR u1 + u1 := underDomainOf t1 + u2 := underDomainOf t2 + + absolutelyCannotCoerce(u1,u2) => NIL + + -- save some time for those we know about + know := '(List Vector Segment Stream UniversalSegment Array + Polynomial UnivariatePolynomial SquareMatrix Matrix) + top in know => canCoerce(u1,u2) + + null selectMms1('map,t2,[['Mapping,u2,u1],t1], + [['Mapping,u2,u1],u1],NIL) => NIL + -- don't bother checking for Undef, so avoid instantiation + canCoerce(u1,u2) + +canCoerceTower(t1,t2) == +-- tries to find a coercion between top level t2 and somewhere inside t1 +-- builds new bubbled type, for which coercion is called recursively + canCoerceByMap(t1,t2) or newCanCoerceCommute(t1,t2) or + canCoerceLocal(t1,t2) or canCoercePermute(t1,t2) or + [c1,:arg1]:= deconstructT t1 + arg1 and + TL:= NIL + arg:= arg1 + until x or not arg repeat x:= + t:= last arg + [c,:arg]:= deconstructT t + TL:= [c,arg,:TL] + arg and coerceIntTest(t,t2) and + CDDR TL => + s:= constructT(c1,replaceLast(arg1,bubbleConstructor TL)) + canCoerceLocal(t1,s) and + [c2,:arg2]:= deconstructT last s + s1:= bubbleConstructor [c2,arg2,c1,arg1] + canCoerceCommute(s,s1) and canCoerceLocal(s1,t2) + s:= bubbleConstructor [c,arg,c1,arg1] + newCanCoerceCommute(t1,s) and canCoerceLocal(s,t2) + x + +canCoerceLocal(t1,t2) == + -- test for coercion on top level + p:= ASSQ(CAR t1,$CoerceTable) + p and ASSQ(CAR t2,CDR p) is [.,:[tag,fun]] => + tag='partial => NIL + tag='total => true + (functionp(fun) and + (v:=CATCH('coerceFailure,FUNCALL(fun,'_$fromCoerceable_$,t1,t2))) + and v ^= $coerceFailure) or canCoerceByFunction(t1,t2) + canCoerceByFunction(t1,t2) + +canCoerceCommute(t1,t2) == +-- THIS IS OUT-MODED AND WILL GO AWAY SOON RSS 2-87 +-- t1 is t2 with the two top level constructors commuted +-- looks for the existence of a commuting function + CAR(t1) in (l := [$QuotientField, 'Gaussian]) and + CAR(t2) in l => true + p:= ASSQ(CAR t1,$CommuteTable) + p and ASSQ(CAR t2,CDR p) is [.,:['commute,.]] + +newCanCoerceCommute(t1,t2) == + coerceIntCommute(objNewWrap("$fromCoerceable$",t1),t2) + +canCoercePermute(t1,t2) == + -- try to generate a sequence of transpositions that will convert + -- t1 into t2 + t2 in '((Integer) (OutputForm)) => NIL + towers := computeTTTranspositions(t1,t2) + -- at this point, CAR towers = t1 and last towers should be similar + -- to t2 in the sense that the components of t1 are in the same order + -- as in t2. If length towers = 2 and t2 = last towers, we quit to + -- avoid an infinte loop. + NULL towers or NULL CDR towers => NIL + NULL CDDR towers and t2 = CADR towers => NIL + -- do the coercions successively, quitting if any fail + ok := true + for t in CDR towers while ok repeat + ok := canCoerce(t1,t) + if ok then t1 := t + ok + +canConvertByFunction(m1,m2) == + null $useConvertForCoercions => NIL + canCoerceByFunction1(m1,m2,'convert) + +canCoerceByFunction(m1,m2) == canCoerceByFunction1(m1,m2,'coerce) + +canCoerceByFunction1(m1,m2,fun) == + -- calls selectMms with $Coerce=NIL and tests for required target=m2 + $declaredMode:local:= NIL + $reportBottomUpFlag:local:= NIL + -- have to handle cases where we might have changed from RN to QF I + -- make 2 lists of expanded and unexpanded types + l1 := REMDUP [m1,eqType m1] + l2 := REMDUP [m2,eqType m2] + ans := NIL + for t1 in l1 while not ans repeat + for t2 in l2 while not ans repeat + l := selectMms1(fun,t2,[t1],[t1],NIL) + ans := [x for x in l | x is [sig,:.] and CADR sig=t2 and + CADDR sig=t1 and + CAR(sig) isnt ['TypeEquivalence,:.]] and true + ans + +absolutelyCanCoerceByCheating(t1,t2) == + -- this typically involves subdomains and towers where the only + -- difference is a subdomain + isEqualOrSubDomain(t1,t2) => true + typeIsASmallInteger(t1) and t2 = $Integer => true + ATOM(t1) or ATOM(t2) => false + [tl1,:u1] := deconstructT t1 + [tl2,:u2] := deconstructT t2 + tl1 = '(Stream) and tl2 = '(InfiniteTuple) => + #u1 ^= #u2 => false + "and"/[absolutelyCanCoerceByCheating(x1,x2) for x1 in u1 for x2 in u2] + tl1 ^= tl2 => false + #u1 ^= #u2 => false + "and"/[absolutelyCanCoerceByCheating(x1,x2) for x1 in u1 for x2 in u2] + +absolutelyCannotCoerce(t1,t2) == + -- response of true means "definitely cannot coerce" + -- this is largely an efficiency hack + ATOM(t1) or ATOM(t2) => NIL + t2 = '(None) => true + n1 := CAR t1 + n2 := CAR t2 + QFI := [$QuotientField, $Integer] + int2 := isEqualOrSubDomain(t2,$Integer) + scalars := '(BigFloat NewFloat Float DoubleFloat RationalNumber) + + MEMQ(n1,scalars) and int2 => true + (t1 = QFI) and int2 => true + + num2 := int2 or MEMQ(n2,scalars) or (t2 = QFI) + isVar1 := MEMQ(n1,'(Variable Symbol)) + + num2 and isVar1 => true + num2 and MEMQ(n1,$univariateDomains) => true + num2 and MEMQ(n1,$multivariateDomains) => true + miscpols := '(Polynomial ElementaryFunction SimpleAlgebraicExtension) + num2 and MEMQ(n1,miscpols) => true + + aggs := '( + Matrix List Vector Stream Array RectangularMatrix FiniteSet + ) + u1 := underDomainOf t1 + u2 := underDomainOf t2 + MEMQ(n1,aggs) and (u1 = t2) => true + MEMQ(n2,aggs) and (u2 = t1) => true + + algs := '( + SquareMatrix Gaussian RectangularMatrix Quaternion + ) + nonpols := append(aggs,algs) + num2 and MEMQ(n1,nonpols) => true + isVar1 and MEMQ(n2,nonpols) and + absolutelyCannotCoerce(t1,u2) => true + + (MEMQ(n1,scalars) or (t1 = QFI)) and (t2 = '(Polynomial (Integer))) => + true + + v2 := deconstructT t2 + 1 = #v2 => NIL + v1 := deconstructT t1 + 1 = #v1 => NIL + CAR(v1) ^= CAR(v2) => NIL + absolutelyCannotCoerce(u1,u2) + +typeIsASmallInteger x == (x = $SingleInteger) + + +--% Interpreter Coercion Functions + +coerceInteractive(triple,t2) == + -- bind flag for recording/reporting instantiations + -- (see recordInstantiation) + t1 := objMode triple + val := objVal triple + null(t2) or t2 = $EmptyMode => NIL + t2 = t1 => triple + t2 = '$NoValueMode => objNew(val,t2) + if t2 is ['SubDomain,x,.] then t2:= x + -- JHD added category Aug 1996 for BasicMath + t1 in '((Category) (Mode) (Domain) (SubDomain (Domain))) => + t2 = $OutputForm => objNew(val,t2) + NIL + t1 = '$NoValueMode => + if $compilingMap then clearDependentMaps($mapName,nil) + throwKeyedMsg("S2IC0009",[t2,$mapName]) + $insideCoerceInteractive: local := true + expr2 := EQUAL(t2,$OutputForm) + if expr2 then startTimingProcess 'print + else startTimingProcess 'coercion + -- next 2 lines handle cases like '"failed" + result := + expr2 and (t1 = val) => objNew(val,$OutputForm) + expr2 and t1 is ['Variable,var] => objNewWrap(var,$OutputForm) + coerceInt0(triple,t2) + if expr2 then stopTimingProcess 'print + else stopTimingProcess 'coercion + result + +coerceInt0(triple,t2) == + -- top level interactive coercion, which transfers all RN, RF and RR + -- into equivalent types + val := objVal triple + t1 := objMode triple + + val='_$fromCoerceable_$ => canCoerceFrom(t1,t2) + t1 = t2 => triple + if t2 = $OutputForm then + s1 := t1 + s2 := t2 + else + s1 := equiType(t1) + s2 := equiType(t2) + s1 = s2 => return objNew(val,t2) + -- t1 is ['Mapping,:.] and t2 ^= '(Any) => NIL + -- note: may be able to coerce TO mapping + -- treat Exit like Any + -- handle case where we must generate code + null(isWrapped val) and + (t1 isnt ['FunctionCalled,:.] or not $genValue)=> + intCodeGenCOERCE(triple,t2) + t1 = $Any and t2 ^= $OutputForm and ([t1',:val'] := unwrap val) and + (ans := coerceInt0(objNewWrap(val',t1'),t2)) => ans + if not EQ(s1,t1) then triple := objNew(val,s1) + x := coerceInt(triple,s2) => + EQ(s2,t2) => x + objSetMode(x,t2) + x + NIL + +coerceInt(triple, t2) == + val := coerceInt1(triple, t2) => val + t1 := objMode triple + t1 is ['Variable, :.] => + newMode := getMinimalVarMode(unwrap objVal triple, nil) + newVal := coerceInt(triple, newMode) + coerceInt(newVal, t2) + nil + +coerceInt1(triple,t2) == + -- general interactive coercion + -- the result is a new triple with type m2 or NIL (= failed) + $useCoerceOrCroak: local := true + t2 = $EmptyMode => NIL + t1 := objMode triple + t1=t2 => triple + val := objVal triple + absolutelyCanCoerceByCheating(t1,t2) => objNew(val,t2) + isSubDomain(t2, t1) => coerceSubDomain(val, t1, t2) + + if typeIsASmallInteger(t1) then + (t2 = $Integer) or typeIsASmallInteger(t2) => return objNew(val,t2) + sintp := SINTP val + sintp and (t2 = $PositiveInteger) and val > 0 => return objNew(val,t2) + sintp and (t2 = $NonNegativeInteger) and val >= 0 => return objNew(val,t2) + + typeIsASmallInteger(t2) and isEqualOrSubDomain(t1, $Integer) and INTP val => + SINTP val => objNew(val,t2) + NIL + + t2 = $Void => objNew(voidValue(),$Void) + t2 = $Any => objNewWrap([t1,:unwrap val],'(Any)) + + t1 = $Any and t2 ^= $OutputForm and ([t1',:val'] := unwrap val) and + (ans := coerceInt(objNewWrap(val',t1'),t2)) => ans + + -- next is for tagged union selectors for the time being + t1 is ['Variable,=t2] or t2 is ['Variable,=t1] => objNew(val,t2) + + STRINGP t2 => + t1 is ['Variable,v] and (t2 = PNAME(v)) => objNewWrap(t2,t2) + val' := unwrap val + (t2 = val') and ((val' = t1) or (t1 = $String)) => objNew(val,t2) + NIL + -- t1 is ['Tuple,S] and t2 ^= '(OutputForm) => + t1 is ['Tuple,S] => + coerceInt1(objNewWrap(asTupleAsList unwrap val, ['List, S]), t2) + t1 is ['Union,:.] => coerceIntFromUnion(triple,t2) + t2 is ['Union,:.] => coerceInt2Union(triple,t2) + (STRINGP t1) and (t2 = $String) => objNew(val,$String) + (STRINGP t1) and (t2 is ['Variable,v]) => + t1 = PNAME(v) => objNewWrap(v,t2) + NIL + (STRINGP t1) and (t1 = unwrap val) => + t2 = $OutputForm => objNew(t1,$OutputForm) + NIL + atom t1 => NIL + + if t1 = $AnonymousFunction and (t2 is ['Mapping,target,:margl]) then + $useCoerceOrCroak := nil + [.,vars,:body] := unwrap val + vars := + atom vars => [vars] + vars is ['Tuple,:.] => rest vars + vars + #margl ^= #vars => 'continue + tree := mkAtree ['ADEF,vars,[target,:margl],[NIL for x in rest t2],:body] + CATCH('coerceOrCroaker, bottomUp tree) = 'croaked => nil + return getValue tree + + (t1 = $Symbol) and (t2 is ['Mapping,target,:margl]) => + null (mms := selectMms1(unwrap val,nil,margl,margl,target)) => NIL + [dc,targ,:argl] := CAAR mms + targ ^= target => NIL + $genValue => + fun := getFunctionFromDomain(unwrap val,dc,argl) + objNewWrap(fun,t2) + val := NRTcompileEvalForm(unwrap val, CDR CAAR mms, evalDomain dc) + objNew(val, t2) + (t1 is ['Variable,sym]) and (t2 is ['Mapping,target,:margl]) => + null (mms := selectMms1(sym,target,margl,margl,NIL)) => + null (mms := selectMms1(sym,target,margl,margl,true)) => NIL + [dc,targ,:argl] := CAAR mms + targ ^= target => NIL + dc is ["__FreeFunction__",:freeFun] => objNew( freeFun, t2 ) + $genValue => objNewWrap( getFunctionFromDomain(sym,dc,argl), t2 ) + val := NRTcompileEvalForm(sym, CDR CAAR mms, evalDomain dc) + objNew(val, t2) + (t1 is ['FunctionCalled,sym]) and (t2 is ['Mapping,target,:margl]) => + symNode := mkAtreeNode sym + transferPropsToNode(sym,symNode) + null (mms := selectLocalMms(symNode,sym,margl,target)) => NIL + [dc,targ,:argl] := CAAR mms + targ ^= target => NIL + ml := [target,:margl] + intName := + or/[mm for mm in mms | (mm is [[., :ml1],oldName,:.] + and compareTypeLists(ml1,ml))] => [oldName] + NIL + null intName => NIL + objNewWrap(intName,t2) + (t1 is ['FunctionCalled,sym]) => + (t3 := get(sym,'mode,$e)) and t3 is ['Mapping,:.] => + (triple' := coerceInt(triple,t3)) => coerceInt(triple',t2) + NIL + NIL + + EQ(CAR(t1),'Variable) and PAIRP(t2) and + (isEqualOrSubDomain(t2,$Integer) or + (t2 = [$QuotientField, $Integer]) or MEMQ(CAR(t2), + '(RationalNumber BigFloat NewFloat Float DoubleFloat))) => NIL + + ans := coerceRetract(triple,t2) or coerceIntTower(triple,t2) or + [.,:arg]:= deconstructT t2 + arg and + t:= coerceInt(triple,last arg) + t and coerceByFunction(t,t2) + ans or (isSubDomain(t1,$Integer) and + coerceInt(objNew(val,$Integer),t2)) or + coerceIntAlgebraicConstant(triple,t2) or + coerceIntX(val,t1,t2) + +coerceSubDomain(val, tSuper, tSub) == + -- Try to coerce from a sub domain to a super domain + val = '_$fromCoerceable_$ => nil + super := GETDATABASE(first tSub, 'SUPERDOMAIN) + superDomain := first super + superDomain = tSuper => + coerceImmediateSubDomain(val, tSuper, tSub, CADR super) + coerceSubDomain(val, tSuper, superDomain) => + coerceImmediateSubDomain(val, superDomain, tSub, CADR super) + nil + +coerceImmediateSubDomain(val, tSuper, tSub, pred) == + predfn := getSubDomainPredicate(tSuper, tSub, pred) + FUNCALL(predfn, val, nil) => objNew(val, tSub) + nil + +getSubDomainPredicate(tSuper, tSub, pred) == + $env: local := $InteractiveFrame + predfn := HGET($superHash, CONS(tSuper, tSub)) => predfn + name := GENSYM() + decl := ['_:, name, ['Mapping, $Boolean, tSuper]] + interpret(decl, nil) + arg := GENSYM() + pred' := SUBST(arg, "#1", pred) + defn := ['DEF, [name, arg], '(NIL NIL), '(NIL NIL), removeZeroOne pred'] + interpret(defn, nil) + op := mkAtree name + transferPropsToNode(name, op) + predfn := CADAR selectLocalMms(op, name, [tSuper],$Boolean) + HPUT($superHash, CONS(tSuper, tSub), predfn) + predfn + +coerceIntX(val,t1, t2) == + -- some experimental things + t1 = '(List (None)) => + -- this will almost always be an empty list + null unwrap val => + -- try getting a better flavor of List + null (t0 := underDomainOf(t2)) => NIL + coerceInt(objNewWrap(val,['List,t0]),t2) + NIL + NIL + +compareTypeLists(tl1,tl2) == + -- returns true if every type in tl1 is = or is a subdomain of + -- the corresponding type in tl2 + for t1 in tl1 for t2 in tl2 repeat + null isEqualOrSubDomain(t1,t2) => return NIL + true + +coerceIntAlgebraicConstant(object,t2) == + -- should use = from domain, but have to check on defaults code + t1 := objMode object + val := objValUnwrap object + ofCategory(t1,'(Monoid)) and ofCategory(t2,'(Monoid)) and + val = getConstantFromDomain('(One),t1) => + objNewWrap(getConstantFromDomain('(One),t2),t2) + ofCategory(t1,'(AbelianMonoid)) and ofCategory(t2,'(AbelianMonoid)) and + val = getConstantFromDomain('(Zero),t1) => + objNewWrap(getConstantFromDomain('(Zero),t2),t2) + NIL + +stripUnionTags doms == + [if dom is [":",.,dom'] then dom' else dom for dom in doms] + +isTaggedUnion u == + u is ['Union,:tl] and tl and first tl is [":",.,.] and true + +getUnionOrRecordTags u == + tags := nil + if u is ['Union, :tl] or u is ['Record, :tl] then + for t in tl repeat + if t is [":",tag,.] then tags := cons(tag, tags) + tags + +coerceUnion2Branch(object) == + [.,:unionDoms] := objMode object + doms := orderUnionEntries unionDoms + predList:= mkPredList doms + doms := stripUnionTags doms + val' := objValUnwrap object + predicate := NIL + targetType:= NIL + for typ in doms for pred in predList while ^targetType repeat + evalSharpOne(pred,val') => + predicate := pred + targetType := typ + null targetType => keyedSystemError("S2IC0013",NIL) + predicate is ['EQCAR,.,p] => objNewWrap(CDR val',targetType) + objNew(objVal object,targetType) + +coerceBranch2Union(object,union) == + -- assumes type is a member of unionDoms + unionDoms := CDR union + doms := orderUnionEntries unionDoms + predList:= mkPredList doms + doms := stripUnionTags doms + p := position(objMode object,doms) + p = -1 => keyedSystemError("S2IC0014",[objMode object,union]) + val := objVal object + predList.p is ['EQCAR,.,tag] => + objNewWrap([removeQuote tag,:unwrap val],union) + objNew(val,union) + +coerceInt2Union(object,union) == + -- coerces to a Union type, adding numeric tags + -- first cut + unionDoms := stripUnionTags CDR union + t1 := objMode object + member(t1,unionDoms) => coerceBranch2Union(object,union) + val := objVal object + val' := unwrap val + (t1 = $String) and member(val',unionDoms) => + coerceBranch2Union(objNew(val,val'),union) + noCoerce := true + val' := nil + for d in unionDoms while noCoerce repeat + (val' := coerceInt(object,d)) => noCoerce := nil + val' => coerceBranch2Union(val',union) + NIL + +coerceIntFromUnion(object,t2) == + -- coerces from a Union type to something else + coerceInt(coerceUnion2Branch object,t2) + +coerceIntByMap(triple,t2) == + -- idea is this: if t1 is D U1 and t2 is D U2, then look for + -- map: (U1 -> U2, D U1) -> D U2. If it exists, then create a + -- function to do the coercion on the element level and call the + -- map function. + t1 := objMode triple + t2 = t1 => triple + u2 := deconstructT t2 -- compute t2 first because of Expression + 1 = #u2 => NIL -- no under domain + u1 := deconstructT t1 + 1 = #u1 => NIL + CAAR u1 ^= CAAR u2 => nil -- constructors not equal + ^valueArgsEqual?(t1, t2) => NIL +-- CAR u1 ^= CAR u2 => NIL + top := CAAR u1 + u1 := underDomainOf t1 + u2 := underDomainOf t2 + + -- handle a couple of special cases for subdomains of Integer + top in '(List Vector Segment Stream UniversalSegment Array) + and isSubDomain(u1,u2) => objNew(objVal triple, t2) + + args := [['Mapping,u2,u1],t1] + if $reportBottomUpFlag then + sayFunctionSelection('map,args,t2,NIL, + '"coercion facility (map)") + mms := selectMms1('map,t2,args,args,NIL) + if $reportBottomUpFlag then + sayFunctionSelectionResult('map,args,mms) + null mms => NIL + + [[dc,:sig],slot,.]:= CAR mms + fun := compiledLookup('map,sig,evalDomain(dc)) + NULL fun => NIL + [fn,:d]:= fun + fn = function Undef => NIL + -- now compile a function to do the coercion + code := ['SPADCALL,['CONS,["function","coerceIntByMapInner"],MKQ [u1,:u2]], + wrapped2Quote objVal triple,MKQ fun] + -- and apply the function + val := CATCH('coerceFailure,timedEvaluate code) + (val = $coerceFailure) => NIL + objNewWrap(val,t2) + +coerceIntByMapInner(arg,[u1,:u2]) == coerceOrThrowFailure(arg,u1,u2) +-- [u1,:u2] gets passed as the "environment", which is why we have this +-- slightly clumsy locution JHD 31.July,1990 + +valueArgsEqual?(t1, t2) == + -- returns true if the object-valued arguments to t1 and t2 are the same + -- under coercion + coSig := CDR GETDATABASE(CAR t1, 'COSIG) + constrSig := CDR getConstructorSignature CAR t1 + tl1 := replaceSharps(constrSig, t1) + tl2 := replaceSharps(constrSig, t2) + not MEMQ(NIL, coSig) => true + done := false + value := true + for a1 in CDR t1 for a2 in CDR t2 for cs in coSig + for m1 in tl1 for m2 in tl2 while not done repeat + ^cs => + trip := objNewWrap(a1, m1) + newVal := coerceInt(trip, m2) + null newVal => (done := true; value := false) + ^algEqual(a2, objValUnwrap newVal, m2) => + (done := true; value := false) + value + +coerceIntTower(triple,t2) == + -- tries to find a coercion from top level t2 to somewhere inside t1 + -- builds new argument type, for which coercion is called recursively + x := coerceIntByMap(triple,t2) => x + x := coerceIntCommute(triple,t2) => x + x := coerceIntPermute(triple,t2) => x + x := coerceIntSpecial(triple,t2) => x + x := coerceIntTableOrFunction(triple,t2) => x + t1 := objMode triple + [c1,:arg1]:= deconstructT t1 + arg1 and + TL:= NIL + arg:= arg1 + until x or not arg repeat + t:= last arg + [c,:arg]:= deconstructT t + TL:= [c,arg,:TL] + x := arg and coerceIntTest(t,t2) => + CDDR TL => + s := constructT(c1,replaceLast(arg1,bubbleConstructor TL)) + (null isValidType(s)) => (x := NIL) + x := (coerceIntByMap(triple,s) or + coerceIntTableOrFunction(triple,s)) => + [c2,:arg2]:= deconstructT last s + s:= bubbleConstructor [c2,arg2,c1,arg1] + (null isValidType(s)) => (x := NIL) + x:= coerceIntCommute(x,s) => + x := (coerceIntByMap(x,t2) or + coerceIntTableOrFunction(x,t2)) + s:= bubbleConstructor [c,arg,c1,arg1] + (null isValidType(s)) => (x := NIL) + x:= coerceIntCommute(triple,s) => + x:= (coerceIntByMap(x,t2) or + coerceIntTableOrFunction(x,t2)) + x + +coerceIntSpecial(triple,t2) == + t1 := objMode triple + t2 is ['SimpleAlgebraicExtension,R,U,.] and t1 = R => + null (x := coerceInt(triple,U)) => NIL + coerceInt(x,t2) + NIL + +coerceIntTableOrFunction(triple,t2) == + -- this function does the actual coercion to t2, but not to an + -- argument type of t2 + null isValidType t2 => NIL -- added 9-18-85 by RSS + null isLegitimateMode(t2,NIL,NIL) => NIL -- added 6-28-87 by RSS + t1 := objMode triple + p:= ASSQ(CAR t1,$CoerceTable) + p and ASSQ(CAR t2,CDR p) is [.,:[tag,fun]] => + val := objVal triple + fun='Identity => objNew(val,t2) + tag='total => + coerceByTable(fun,val,t1,t2,'T) or coerceByFunction(triple,t2) + coerceByTable(fun,val,t1,t2,NIL) or coerceByFunction(triple,t2) + coerceByFunction(triple,t2) + +coerceCommuteTest(t1,t2) == + null isLegitimateMode(t2,NIL,NIL) => NIL + + -- sees whether t1 = D1 D2 R and t2 = D2 D1 S + null (u1 := underDomainOf t1) => NIL + null (u2 := underDomainOf t2) => NIL + + -- must have underdomains (ie, R and S must be there) + + null (v1 := underDomainOf u1) => NIL + null (v2 := underDomainOf u2) => NIL + + -- now check that cross of constructors is correct + (CAR(deconstructT t1) = CAR(deconstructT u2)) and + (CAR(deconstructT t2) = CAR(deconstructT u1)) + +coerceIntCommute(obj,target) == + -- note that the value in obj may be $fromCoerceable$, for canCoerce + source := objMode obj + null coerceCommuteTest(source,target) => NIL + S := underDomainOf source + T := underDomainOf target + source = T => NIL -- handle in other ways + + source is [D,:.] => + fun := GETL(D,'coerceCommute) or + INTERN STRCONC('"commute",STRINGIMAGE D) + functionp fun => + PUT(D,'coerceCommute,fun) + u := objValUnwrap obj + c := CATCH('coerceFailure,FUNCALL(fun,u,source,S,target,T)) + (c = $coerceFailure) => NIL + u = "$fromCoerceable$" => c + objNewWrap(c,target) + NIL + NIL + +coerceIntPermute(object,t2) == + t2 in '((Integer) (OutputForm)) => NIL + t1 := objMode object + towers := computeTTTranspositions(t1,t2) + -- at this point, CAR towers = t1 and last towers should be similar + -- to t2 in the sense that the components of t1 are in the same order + -- as in t2. If length towers = 2 and t2 = last towers, we quit to + -- avoid an infinte loop. + NULL towers or NULL CDR towers => NIL + NULL CDDR towers and t2 = CADR towers => NIL + -- do the coercions successively, quitting if any fail + ok := true + for t in CDR towers while ok repeat + null (object := coerceInt(object,t)) => ok := NIL + ok => object + NIL + +computeTTTranspositions(t1,t2) == + -- decompose t1 into its tower parts + tl1 := decomposeTypeIntoTower t1 + tl2 := decomposeTypeIntoTower t2 + -- if not at least 2 parts, don't bother working here + null (rest tl1 and rest tl2) => NIL + -- determine the relative order of the parts of t1 in t2 + p2 := [position(d1,tl2) for d1 in tl1] + member(-1,p2) => NIL -- something not present + -- if they are all ascending, this function will do nothing + p2' := MSORT p2 + p2 = p2' => NIL + -- if anything is repeated twice, leave + p2' ^= MSORT REMDUP p2' => NIL + -- create a list of permutations that transform the tower parts + -- of t1 into the order they are in in t2 + n1 := #tl1 + p2 := LIST2VEC compress(p2,0,# REMDUP tl1) where + compress(l,start,len) == + start >= len => l + member(start,l) => compress(l,start+1,len) + compress([(i < start => i; i - 1) for i in l],start,len) + -- p2 now has the same position numbers as p1, we need to determine + -- a list of permutations that takes p1 into p2. + -- them + perms := permuteToOrder(p2,n1-1,0) + towers := [tl1] + tower := LIST2VEC tl1 + for perm in perms repeat + t := tower.(CAR perm) + tower.(CAR perm) := tower.(CDR perm) + tower.(CDR perm) := t + towers := CONS(VEC2LIST tower,towers) + towers := [reassembleTowerIntoType tower for tower in towers] + if CAR(towers) ^= t2 then towers := cons(t2,towers) + NREVERSE towers + +decomposeTypeIntoTower t == + ATOM t => [t] + d := deconstructT t + NULL rest d => [t] + rd := REVERSE t + [reverse QCDR rd,:decomposeTypeIntoTower QCAR rd] + +reassembleTowerIntoType tower == + ATOM tower => tower + NULL rest tower => CAR tower + [:top,t,s] := tower + reassembleTowerIntoType [:top,[:t,s]] + +permuteToOrder(p,n,start) == + -- p is a vector of the numbers 0..n. This function returns a list + -- of swaps of adjacent elements so that p will be in order. We only + -- begin looking at index start + r := n - start + r <= 0 => NIL + r = 1 => + p.r < p.(r+1) => NIL + [[r,:(r+1)]] + p.start = start => permuteToOrder(p,n,start+1) + -- bubble up element start to the top. Find out where it is + stpos := NIL + for i in start+1..n while not stpos repeat + if p.i = start then stpos := i + perms := NIL + while stpos ^= start repeat + x := stpos - 1 + perms := [[x,:stpos],:perms] + t := p.stpos + p.stpos := p.x + p.x := t + stpos := x + APPEND(NREVERSE perms,permuteToOrder(p,n,start+1)) + +coerceIntTest(t1,t2) == + -- looks whether there exists a table entry or a coercion function + -- thus the type can be bubbled before coerceIntTableOrFunction is called + t1=t2 or + b:= + p:= ASSQ(CAR t1,$CoerceTable) + p and ASSQ(CAR t2,CDR p) + b or coerceConvertMmSelection('coerce,t1,t2) or + ($useConvertForCoercions and + coerceConvertMmSelection('convert,t1,t2)) + +coerceByTable(fn,x,t1,t2,isTotalCoerce) == + -- catch point for 'failure in boot coercions + t2 = $OutputForm and ^(newType? t1) => NIL + isWrapped x => + x:= unwrap x + c:= CATCH('coerceFailure,FUNCALL(fn,x,t1,t2)) + c=$coerceFailure => NIL + objNewWrap(c,t2) + isTotalCoerce => objNew([fn,x,MKQ t1,MKQ t2],t2) + objNew(['catchCoerceFailure,MKQ fn,x,MKQ t1,MKQ t2],t2) + +catchCoerceFailure(fn,x,t1,t2) == + -- compiles a catchpoint for compiling boot coercions + c:= CATCH('coerceFailure,FUNCALL(fn,x,t1,t2)) + c = $coerceFailure => + throwKeyedMsgCannotCoerceWithValue(wrap unwrap x,t1,t2) + c + +coercionFailure() == + -- does the throw on coercion failure + THROW('coerceFailure,$coerceFailure) + +--------------------> NEW DEFINITION (override in xrun.boot.pamphlet) +coerceByFunction(T,m2) == + -- using the new modemap selection without coercions + -- should not be called by canCoerceFrom + x := objVal T + x = '_$fromCoerceable_$ => NIL + m2 is ['Union,:.] => NIL + m1 := objMode T + m2 is ['Boolean,:.] and m1 is ['Equation,ud] => + dcVector := evalDomain ud + fun := + isWrapped x => + NRTcompiledLookup("=", [$Boolean, ud, ud], dcVector) + NRTcompileEvalForm("=", [$Boolean, ud, ud], dcVector) + [fn,:d]:= fun + isWrapped x => + x:= unwrap x + objNewWrap(SPADCALL(CAR x,CDR x,fun),m2) + x isnt ['SPADCALL,a,b,:.] => keyedSystemError("S2IC0015",NIL) + code := ['SPADCALL, a, b, fun] + objNew(code,$Boolean) + -- If more than one function is found, any should suffice, I think -scm + if not (mm := coerceConvertMmSelection(funName := 'coerce,m1,m2)) then + mm := coerceConvertMmSelection(funName := 'convert,m1,m2) + mm => + [[dc,tar,:args],slot,.]:= mm + dcVector := evalDomain(dc) + fun:= + isWrapped x => + NRTcompiledLookup(funName,[tar,:args],dcVector) + NRTcompileEvalForm(funName,[tar,:args],dcVector) + [fn,:d]:= fun + fn = function Undef => NIL + isWrapped x => + $: fluid := dcVector + val := CATCH('coerceFailure, SPADCALL(unwrap x,fun)) + (val = $coerceFailure) => NIL + objNewWrap(val,m2) + env := fun + code := ['failCheck, ['SPADCALL, x, env]] +-- tar is ['Union,:.] => objNew(['failCheck,code],m2) + objNew(code,m2) + -- try going back to types like RN instead of QF I + m1' := eqType m1 + m2' := eqType m2 + (m1 ^= m1') or (m2 ^= m2') => coerceByFunction(objNew(x,m1'),m2') + NIL + +hasCorrectTarget(m,sig is [dc,tar,:.]) == + -- tests whether the target of signature sig is either m or a union + -- containing m. It also discards TEQ as it is not meant to be + -- used at top-level + dc is ['TypeEquivalence,:.] => NIL + m=tar => 'T + tar is ['Union,t,'failed] => t=m + tar is ['Union,'failed,t] and t=m + +@ +\eject +\begin{thebibliography}{99} +\bibitem{1} nothing +\end{thebibliography} +\end{document} |