package auxiliaryGrammar;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import ast.Ast;
import ast.ErrorMessage;
import ast.ErrorMessage.NotWellFormedMsk;
import ast.Ast.ConcreteHeader;
import ast.Ast.FieldDec;
import ast.Ast.Mdf;
import ast.Ast.MethodSelector;
import ast.Ast.VarDecXE;
import ast.ExpCore;
import ast.Expression;
import ast.Ast.SignalKind;
import ast.Ast.VarDec;
import ast.Expression.*;
import sugarVisitors.CheckNoVarDeclaredTwice;
import sugarVisitors.CheckTerminatingBlock;
import sugarVisitors.CheckVarUsedAreInScope;
import sugarVisitors.CloneVisitor;
import tools.Assertions;
import tools.Map;
/*
class Found extends RuntimeException{
Expression e;Expression ctx;
Found(Expression e,Expression ctx){this.e=e;this.ctx=ctx;}
}*/
public class WellFormedness {
public static boolean checkAll(Expression e){
WellFormedness.operatorPrecedenceCheck(e);
WellFormedness.noVarAndNoTypeCheck(e);
WellFormedness.returnCheck(e);
WellFormedness.blockCheck(e);
WellFormedness.withCheck(e);
CheckNoVarDeclaredTwice.of(e);
CheckVarUsedAreInScope.of(e);
//check all variables used when in scope!
//looks like not use var after assign?
//uno e' intorno, l'altro e' dopo...
WellFormedness.classCheck(e);
return true;
}
//operators at the same level of precedence must be identical,
// i.e. a+b+c is ok, but a+b*c is not
public static void operatorPrecedenceCheck(Expression _e){
_e.accept(new CloneVisitor(){
public Expression visit(BinOp s) {
if(s.getRight() instanceof BinOp){
Ast.Op op=((BinOp)s.getRight()).getOp();
if(s.getOp().kind==op.kind && s.getOp()!=op){
throw new ErrorMessage.NotWellFormedMsk(s,_e, "Here parenthesis are needed to disambiguate operator precedence.");
}
}
if(s.getLeft() instanceof BinOp){
Ast.Op op=((BinOp)s.getLeft()).getOp();
if(s.getOp().kind==op.kind && s.getOp()!=op){
throw new ErrorMessage.NotWellFormedMsk(s,_e, "Here parenthesis are needed to disambiguate operator precedence.");
}
}
return super.visit(s);
}
});
}
public static void noVarAndNoTypeCheck(Expression _e){
_e.accept(new CloneVisitor(){
//Ok liftVarDec since With uses liftVarDecXE
@Override protected ast.Ast.VarDec liftVarDec(ast.Ast.VarDec d) {
return d.match(
vdxe->{
if(vdxe.isVar() & !vdxe.getT().isPresent()){
throw new ErrorMessage.NotWellFormedMsk(vdxe.getInner(),vdxe.getInner(),
"Variable local binding can not infer their type");
}
return this.liftVarDecXE(vdxe);
},
this::liftVarDecE,
this::liftVarDecCE);
}
@Override public Expression visit(Expression.RoundBlock s) {
try{return super.visit(s);}
catch(NotWellFormedMsk nwf){ throw nwf.withCtx(s);}
}
@Override public Expression visit(Expression.CurlyBlock s) {
try{return super.visit(s);}
catch(NotWellFormedMsk nwf){ throw nwf.withCtx(s);}
}
});
}
//returns:
//The \Q@return@ keyword can not be used inside any \Q@if@ or while condition or inside the expression of a $\x$\Q@in@$\e$.
public static void returnCheck(Expression _e){
_e.accept(new CloneVisitor(){
Expression outerDanger=null;
public Expression visit(ClassB s) {
Expression aux=this.outerDanger;
this.outerDanger=null;
try{return super.visit(s);}
finally{this.outerDanger=aux;}
}
public Expression visit(ClassReuse s) {
Expression aux=this.outerDanger;
this.outerDanger=null;
try{return super.visit(s);}
finally{this.outerDanger=aux;}
}
@Override
public Expression visit(If s) {//I avoided try finally in the following since scoping was dumb here
Expression aux=this.outerDanger;
this.outerDanger=s;
Expression e=lift(s.getCond());
this.outerDanger=aux;
return new If(s.getP(),e,lift(s.getThen()),Map.of(this::lift,s.get_else()));
}
@Override
public Expression visit(While s) {
Expression aux=this.outerDanger;
this.outerDanger=s;
Expression e=lift(s.getCond());
this.outerDanger=aux;
return new While(s.getP(),e,lift(s.getThen()));
}
@Override
public Expression visit(With s) {
Expression aux=this.outerDanger;
this.outerDanger=s;
List<VarDecXE> is = Map.of(this::liftVarDecXE, s.getIs());
this.outerDanger=aux;
return new With(s.getP(),s.getXs(),is,Map.of(this::liftVarDecXE, s.getDecs()),Map.of(this::liftO,s.getOns()),Map.of(this::lift, s.getDefaultE()));
}
public Expression visit(Signal s) {
if(this.outerDanger==null){return super.visit(s);}
if(s.getKind()!=SignalKind.Return){return super.visit(s);}
throw new ErrorMessage.NotWellFormedMsk(s, outerDanger, "A return is not allowed here");
}
});
}
public static boolean blockCheck(Expression _e){
_e.accept(new CloneVisitor(){
public Expression visit(RoundBlock s) {
checkBlockContentOk(s,s.getContents());
return super.visit(s);}
public Expression visit(CurlyBlock s) {
if(s.getContents().isEmpty()){
throw new ErrorMessage.NotWellFormedMsk(s, null, "Blocks should not be empty");
}
checkBlockContentOk(s,s.getContents());
CheckTerminatingBlock.of(s);//this can throw!
return super.visit(s);}
});
return true;
}
public static boolean checkBlockContentOk(Expression forErr,List<BlockContent> l) {
if(l.isEmpty()){return true;}
for(BlockContent bc:l.subList(0, l.size()-1)){
if(bc.get_catch().isEmpty()){
throw Assertions.codeNotReachable("Catch should not be empty on multiple contents");
}
if(bc.getDecs().isEmpty()){
throw new ErrorMessage.NotWellFormedMsk(forErr, null, "empty declarations before catch");
}
}
BlockContent last=l.get(l.size()-1);
if(last.getDecs().isEmpty()){
throw Assertions.codeNotReachable("last declarations should not be empty "+forErr);
}
return true;
}
static void withVarCheck(List<String>notEqOp,With w){
w.accept(new CloneVisitor(){
public Expression visit(ClassB s) {return s;}
public Expression visit(ClassReuse s) {return s;}
public Expression visit(BinOp s){
if (s.getOp().kind!= Ast.OpKind.EqOp){return super.visit(s);}
assert s.getLeft() instanceof X;
String x=((X)s.getLeft()).getInner();
if(!notEqOp.contains(x)){return super.visit(s);}
throw new ErrorMessage.NotWellFormedMsk(s, w, "Non var iterator variable should not be updated");
}
});
}
static void withVarCheckNotReadAfterAssign(List<String>notEqOp,With.On on){
//TODO: much much harder than what expected. will call terminating inside? can not be a clone visitor!
}
public static void withCheck(Expression _e){
_e.accept(new CloneVisitor(){
public Expression visit(With s) {//it does take care also of the case SquareWith
//$\withKw\, \emptyset\,\emptyset\, \emptyset\,\_$ is not well formed;
int size=s.getXs().size()+s.getIs().size()+s.getDecs().size();
if (size==0){
throw new ErrorMessage.NotWellFormedMsk(s,null, "With should \"with\" over something, here is empty");
}
//$\withKw\,\xs\,\is\,\es\, %\Opt\onStart\,
//\oRound\Many\onWith\Opt\block\cRound$
//is well formed if the number of types $\T_\vI\ldots\T_\vn$ in each $\onKw$ is the same of the sum of the cardinalities of $\xs$, $\is$ and $\es$.
for(With.On on:s.getOns()){
if(!on.getTs().isEmpty() && on.getTs().size()!=size){
throw new ErrorMessage.NotWellFormedMsk(on.getInner(),s, "on types should be the same number of the with expressions, here on have: "+on.getTs().size()+" while the with have: "+size);
}
}
//In a $\withKw$, variables introduces in the $\is$ can be updated only if they are declared \Q@var@.
List<String>notEqOp=new ArrayList<String>();
for(VarDecXE is:s.getIs()){
if(!is.isVar()){notEqOp.add(is.getX());}
}
withVarCheck(notEqOp,s);
//In a $\onWith$ body, variables whose type have been made more specific can still beeing updated using the more general type, thus
//a well formed $\onWith$ body can not read a variable after updating it.
//TODO: missing, see up!
return super.visit(s);}
});
}
static void checkHeader(ClassB cb,ConcreteHeader h) {
//All fields names in a given header must be unique.
HashSet<String> fnames=new HashSet<String>();
for(FieldDec f:h.getFs()){
fnames.add(f.getName());
if(f.getName().equals("this")){throw new ErrorMessage.NotWellFormedMsk(cb,null, "\"this\" is not a valid field name");}
}
if(fnames.size()!=h.getFs().size()){
throw new ErrorMessage.NotWellFormedMsk(cb,null, "Some field name is repeated.");
}
if(h.getMdf()==Mdf.Class){
throw new ErrorMessage.NotWellFormedMsk(cb,null, "Constructors can not return types");
}
boolean haveVar=false;
for(FieldDec f:h.getFs()){
if(f.isVar()){haveVar=true;break;}
}
if(haveVar && h.getMdf()==Mdf.Readable){//was Immutable, but now the idea is that the mdf can be inferred
throw new ErrorMessage.NotWellFormedMsk(cb,null, "Readable classes can not have a variable fields");
}
}
public static void checkMembers(ClassB s) {
HashSet<Ast.C>usedNestedClassNames=new HashSet<>();
HashSet<MethodSelector>usedSelector=new HashSet<>();
for(ClassB.Member m:s.getMs()){
m.match(
nc->{
if(usedNestedClassNames.contains(nc.getName())){
throw new ErrorMessage.NotWellFormedMsk(s,null, "Some nested class name is repeated: "+nc.getName()+" "+usedNestedClassNames);
}
//All nested class names $\C$ in a class must be unique.
usedNestedClassNames.add(nc.getName());
return null;
},
mi->{checkMs(s,mi.getS(),usedSelector);return null;},
mt->{checkMs(s,mt.getMs(),usedSelector);return null;}
);}
}
static void checkMs(ClassB cb ,MethodSelector s, HashSet<MethodSelector> usedSelector) {
if(usedSelector.contains(s)){
throw new ErrorMessage.NotWellFormedMsk(cb,null, "Some method selector is repeated: "+s+" is repeated. Other selector in the same scope: "+usedSelector);
}
usedSelector.add(s);
//All parameter names declared within a given method header must be unique.
HashSet<String>ps=new HashSet<String>(s.getNames());
if(s.getNames().contains("this")){throw new ErrorMessage.NotWellFormedMsk(cb,null, "\"this\" is not a valid parameter name");}
if(ps.size()!=s.getNames().size()){
throw new ErrorMessage.NotWellFormedMsk(cb,null, "Some parameter name is repeated");
}
}
public static void classCheck(Expression _e){
_e.accept(new CloneVisitor(){
public Expression visit(ClassReuse s) {
checkMembers(s.getInner());
return super.visit(s);}
public Expression visit(ClassB s) {
checkMembers(s);
if(s.getH()instanceof Ast.ConcreteHeader){
checkHeader(s,(Ast.ConcreteHeader)s.getH());
}
return super.visit(s);}
});
}
public static boolean checkCoreVariables(ExpCore.ClassB cb) {
return coreVisitors.CheckNoVarDeclaredTwice.of(cb);
}
//paths:
//well formedness "post": every used path could be present in the future.
}