package programReduction;
import ast.Ast.NormType;
import ast.Ast.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ast.ExpCore;
import ast.ExpCore.ClassB;
import ast.ExpCore.ClassB.MethodWithType;
import ast.ExpCore.ClassB.NestedClass;
import ast.ExpCore.ClassB.Phase;
import coreVisitors.CloneVisitor;
import tools.Assertions;
import tools.Map;
import ast.Ast;
import ast.Ast.HistoricType;
import ast.Ast.MethodType;
import ast.Ast.Type;
public class Norm {
static Type nextT(Ast.Path p,MethodWithType mwt,Ast.MethodSelectorX msx){
String x=msx.getX();
if(x.isEmpty()){return mwt.getMt().getReturnType();}
if(x.equals("this")){
return new NormType(mwt.getMt().getMdf(),p,mwt.getDoc());
}
int i=mwt.getMs().getNames().indexOf(x);
assert i!=-1:
mwt.getMs()+" but searched for "+ x+" in "+p;
return mwt.getMt().getTs().get(i);
}
static ExpCore.ClassB.MethodWithType select(Ast.MethodSelector ms, List<ExpCore.ClassB.MethodWithType> list){
for(MethodWithType mwt:list){if(mwt.getMs().equals(ms)){return mwt;}}
return null;
}
public static NormType resolve(Program p,Type t){
try{
return _resolve(p,t);
}
catch(StackOverflowError err){
throw new ast.ErrorMessage.HistoricTypeCircularDefinition((HistoricType)t);
}
}
static NormType _resolve(Program p,Type t){//may stack overflow
//-resolve(p,mdf P)=mdf P
if( t instanceof NormType){return (NormType) t;}
HistoricType ht=(HistoricType) t;
//-resolve(p,P::ms)=resolve(p,T)//and avoid circularity
// methods(p,P)(ms)=refine? _ method T ms e?
List<MethodWithType> methods = p.methods(ht.getPath());
MethodWithType mwt = select(ht.getSelectors().get(0).getMs(),methods);
if(mwt==null){
//throw new ast.ErrorMessage.NormImpossible(t, cause, null);
throw new ast.ErrorMessage.HistoricTypeNoTarget(ht,methods );
//TODO: what about the name targetType
}
Type nextT=nextT(ht.getPath(),mwt,ht.getSelectors().get(0));
NormType nextNT= _resolve(p,nextT);
if (ht.getSelectors().size()==1){return nextNT.withDoc(ht.getDoc().sum(nextNT.getDoc()));}
return _resolve(p,new HistoricType(nextNT.getPath(),ht.getSelectors().subList(1, ht.getSelectors().size()),ht.getDoc()));
//-resolve(p,P::ms::x)=resolve(p,T)//and avoid circularity
// methods(p,P)(ms)=refine? _ method _ _( _ T x _) e?
//-resolve(p,P::msx::msxs)=resolve(p,P'::msxs) //here be carefull for possible infinite recursion
// resolve(p,P::msx)= _ P'
}
private static MethodType resolve(Program p, MethodType mt) {
Type rt=resolve(p,mt.getReturnType());
List<Type>pts=Map.of(t->resolve(p,t),mt.getTs());
return mt.withReturnType(rt).withTs(pts);
}
ExpCore norm(Program p,ExpCore e){
return e.accept(new CloneVisitor(){
protected Type liftT(Type t){ return resolve(p,t); }
public ExpCore visit(ClassB s) {
//TODO: enable when there is new TS if(s.getPhase()!=Phase.None){return s;}
return norm(p.evilPush(s));
}});
}
public ExpCore.ClassB norm(Program p){
//-norm(p)={interface? implements Ps' norm(p,Ms') }
//p.top()={interface? implements Ps Ms} //Ms is free var and is ok
ClassB l=p.top();
//Ps'=collect(p,Ps)
List<Path> ps1 = Methods.collect(p,l.getSuperPaths());
//Ms'=methods(p,This0), {C:e in Ms} //norm now put all the nested classes in the back.
List<ClassB.Member> ms1 = Stream.concat(
p.methods(Path.outer(0)).stream(),
l.getMs().stream().filter(m->m instanceof ClassB.NestedClass)
).map(m->norm(p,m)).collect(Collectors.toList());
//return l.withSupertypes(ps1).withMs(ms1).withUniqueId(p.getFreshId()).withPhase(Phase.Norm);
return new ClassB(l.getDoc1(),l.isInterface(),Map.of(pi->pi.toImmNT(),ps1),ms1,l.getP(),Phase.Norm,p.getFreshId());
}
@SuppressWarnings("unchecked")
<T extends ExpCore.ClassB.Member> T norm(Program p,T m){
return m.match(
nc->(T)normNC(p,nc),
mi->(T)Assertions.codeNotReachable(),
mwt->(T)normMwt(p,mwt)
);
}
protected NestedClass normNC(Program p,NestedClass nc){
return nc.withE(norm(p,nc.getE()));
//modify here to decrese performance but reduce evilpushes, by doing push(C) in case e is L
}
protected MethodWithType normMwt(Program p,MethodWithType mwt){
return mwt.withMt(resolve(p,mwt.getMt()))
.with_inner(mwt.get_inner().map(e->norm(p,e)));
}
static Program auxMultiNorm(Program p, List<List<Ast.C>>topPaths){
ClassB lTop=p.top();
for(List<Ast.C> csi:topPaths){
// pi = p.navigate(Csi)
// Li = norm(pi)//norming the top
// L = p.top()[Cs1=L1..Csn=Ln] //replace the nested classes in paths Csi with libraries Li.
Program pi=p.navigate(csi);
ClassB li=new Norm().norm(pi);
lTop=lTop.onClassNavigateToPathAndDo(csi, _l->li);
}
return p.updateTop(lTop);
}
static Program multiNorm(Program p, Paths paths){
//- multiNorm(p,empty) = p
if(paths.isEmpty()){return p;}
//- multiNorm(p, Cs1..Csn)/*a single Css*/= p.update(L)
Paths popped=paths.pop();
if(popped.isEmpty()){
return auxMultiNorm(p,paths.top());
}
//- multiNorm(p, Css,Csss) =multiNorm(p',Css)
// p'=p.growFellow(multiNorm(p.pop(), Csss))
Program rec=multiNorm(p.pop(),popped);
Program p1=p.growFellow(rec);
return auxMultiNorm(p1,paths.current);
}
}