package sugarVisitors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import tools.Map;
import ast.Ast.VarDecXE;
import ast.ErrorMessage;
import ast.Expression;
import ast.Ast.VarDec;
import ast.Expression.*;
import ast.Expression.ClassB.Member;
public class CheckVarUsedAreInScope extends CloneVisitor{
private static Expression ctx=null;
public static boolean of(Expression e){
of(e,Collections.emptyList());
return true;
}
public static void of(Expression e,List<String>names){
Expression old=ctx;
ctx=e;
try{
CheckVarUsedAreInScope cdv=new CheckVarUsedAreInScope();
cdv.xs.addAll(names);
e.accept(cdv);
}
finally{ctx=old;}
}
Set<String> xs=new HashSet<String>();
public Expression visit(Expression.X s){
if(this.xs.contains(s.getInner())){return super.visit(s);}
throw new ErrorMessage.VariableUsedNotInScope(s, ctx,"Variable used not in scope");
}
protected Expression.BlockContent liftBC(Expression.BlockContent c) {
Set<String> aux = this.xs;
Set<String> aux2=new HashSet<String>(aux);
for(VarDec vd:c.getDecs()){vd.match(
xe->{aux2.add(xe.getX());return null;},
e->{return null;},
ce->{return null;});}
this.xs=aux2;
List<VarDec> liftVarDecs = liftVarDecs(c.getDecs());
this.xs=aux;
List<Catch> liftK;
try{liftK=Map.of(this::liftK,c.get_catch());}
catch(ErrorMessage.VariableUsedNotInScope nis){
Expression.X x=nis.getE();
assert !aux.contains(x.getInner());
if (aux2.contains(x.getInner())){
throw nis.withReason(nis.getReason()+"\nThe variable is in lexical scope, but is used in a catch of the declaring block. It may not be initialized at this stage");
}
throw nis;
}
this.xs=aux2;//add again for later contents
return new Expression.BlockContent(liftVarDecs,liftK);
}
protected Expression.Catch liftK(Expression.Catch k){
Set<String> aux = this.xs;
Set<String> aux2=new HashSet<String>(aux);
aux2.add(k.getX());
this.xs=aux2;
try{return super.liftK(k);}
finally{this.xs=aux;}
}
public Expression visit(CurlyBlock s) {
Set<String> aux = this.xs;
this.xs=new HashSet<String>(aux);
try{return super.visit(s);}
finally{this.xs=aux;}
}
public Expression visit(RoundBlock s) {
Set<String> aux = this.xs;
this.xs=new HashSet<String>(aux);
List<BlockContent> content = Map.of(this::liftBC,s.getContents());
//here should have all the accumulated vardecs
Expression inner = lift(s.getInner());
this.xs=aux;
Expression result= new RoundBlock(s.getP(),s.getDoc(), inner,content);
return result;
}
public Expression visit(With s) {
Set<String> aux = this.xs;
this.xs=new HashSet<String>(aux);
List<VarDecXE> is = Map.of(this::liftVarDecXE, s.getIs());
for(VarDecXE vd:is){this.xs.add(vd.getX());}
List<VarDecXE> es = Map.of(this::liftVarDecXE, s.getDecs());
for(VarDecXE vd:es){this.xs.add(vd.getX());}
try{return new With(s.getP(),s.getXs(),is,es,Map.of(this::liftO,s.getOns()),Map.of(this::lift, s.getDefaultE()));}
finally{this.xs=aux;}
}
public Expression visit(ClassB s) {
for(Member m:s.getMs()){
m.match(
nc->{CheckVarUsedAreInScope.of(nc.getInner(),Collections.emptyList());return null;},
mi->{
List<String> names = new ArrayList<>(mi.getS().getNames());
names.add("this");
CheckVarUsedAreInScope.of(mi.getInner(),names);
return null;},
mt->{
if(mt.getInner().isPresent()){
List<String> names =new ArrayList<>( mt.getMs().getNames());
names.add("this");
CheckVarUsedAreInScope.of(mt.getInner().get(),names);
};
return null;
}
);
}
return s;
}
}