package coreVisitors;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import tools.Assertions;
import ast.Ast.Path;
import ast.ErrorMessage;
import ast.ExpCore;
import ast.ExpCore.Block;
import ast.ExpCore.ClassB;
import ast.ExpCore.Loop;
import ast.ExpCore.MCall;
import ast.ExpCore.Signal;
import ast.ExpCore.Using;
import ast.ExpCore.WalkBy;
import ast.ExpCore.X;
import ast.ExpCore._void;
import ast.ExpCore.Block.Dec;
import ast.Redex;
import auxiliaryGrammar.Ctx;
import auxiliaryGrammar.Functions;
import programReduction.Program;
public class ExtractCtxVal implements Visitor<Ctx<Redex>>{
Program p;
private ExtractCtxVal(Program p){this.p=p;}
public Ctx<Redex> visit(ExpCore.EPath s) {return null;}
public Ctx<Redex> visit(X s) {return null;}
public Ctx<Redex> visit(_void s) { return null; }
public Ctx<Redex> visit(WalkBy s) {throw Assertions.codeNotReachable();}
public Ctx<Redex> visit(ClassB s) {
return checkRedex(s);
}
public Ctx<Redex> visit(Signal s) {
return lift(s.getInner().accept(this),
ctx->s.withInner(ctx));
}
public Ctx<Redex> visit(Loop s) {
return checkRedex(s);
}
public Ctx<Redex> visit(Using s) {
Ctx<Redex> res=checkRedex(s);
if(res!=null){return res;}
for(ExpCore ei:s.getEs()){
if(IsValue.of(p,ei)){continue;}
return lift(ei.accept(this),ctx->{
List<ExpCore> es=new ArrayList<ExpCore>(s.getEs());
es.set(es.indexOf(ei), ctx);
return s.withEs(es);
});
}
return lift(s.getInner().accept(this),
ctx->s.withInner(ctx));
}
public Ctx<Redex> visit(MCall s) {
Ctx<Redex> res=checkRedex(s);
if(res!=null){return res;}
if(!IsValue.of(p, s.getInner())){
return lift(s.getInner().accept(this),
ctx->s.withInner(ctx));}
for(ExpCore ei:s.getEs()){
if(IsValue.of(p,ei)){continue;}
return lift(ei.accept(this),ctx->{
List<ExpCore> es=new ArrayList<ExpCore>(s.getEs());
es.set(es.indexOf(ei), ctx);
return s.withEs(es);
});
}
return null;
}
public Ctx<Redex> visit(Block s) {
Ctx<Redex> res=checkRedex(s);
if(res!=null){return res;}
for(int i=0;i<s.getDecs().size();i++){
int ii=i;//final restrictions
Dec di=s.getDecs().get(i);
boolean isDv=new IsValue(p).validDv(di);
Redex isGarbage=null;
if(isDv && di.getInner() instanceof Block){
Block diB=(Block)di.getInner();
isGarbage=IsValue.nestedGarbage(diB);
}
if(isGarbage!=null){
List<Block.Dec> ds=new ArrayList<Block.Dec>(s.getDecs());
ds.set(ii,di.withInner(new WalkBy()));
return new Ctx<Redex>(s.withDecs(ds),isGarbage);
}
if(isDv){continue;}
//otherwise, i is the first non dv
return lift(di.getInner().accept(this),ctx->{
List<Block.Dec> es=new ArrayList<Block.Dec>(s.getDecs());
es.set(ii,es.get(ii).withInner(ctx));
return s.withDecs(es);
});
}
if(!s.getOns().isEmpty()){return null;}
return lift(s.getInner().accept(this),
ctx->s.withInner(ctx));
}
private Ctx<Redex> lift(Ctx<Redex> res,Function<ExpCore,ExpCore> f){
if(res!=null){res.ctx=f.apply(res.ctx);}
return res;
}
private Ctx<Redex> checkRedex(ExpCore s) {
Redex r=IsRedex.of(p, s);
if(!(r instanceof Redex.NoRedex)){
return new Ctx<Redex>(new WalkBy(),r);}
return null;
}
public static Ctx<Redex> of(Program p,ExpCore e){
//what if check garbage first?
Ctx<Redex> result=e.accept(new ExtractCtxVal(p));
if(result!=null){return result;}
throw new ErrorMessage.CtxExtractImpossible(e,null);
}
}