package sugarVisitors; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import platformSpecific.fakeInternet.OnLineCode; import privateMangling.RefreshUniqueNames; import tools.Assertions; import ast.Ast; import ast.ErrorMessage; import ast.Ast.ConcreteHeader; import ast.Ast.Doc; import ast.Ast.FieldDec; import ast.Ast.Header; import ast.Ast.Mdf; import ast.Ast.MethodSelector; import ast.Ast.MethodSelectorX; import ast.Ast.MethodType; import ast.Ast.NormType; import ast.Ast.Op; import ast.Ast.Parameters; import ast.Ast.Path; import ast.Ast.Position; import ast.Ast.SignalKind; import ast.Ast.Stage; import ast.Ast.Type; import ast.Ast.VarDec; import ast.Ast.VarDecCE; import ast.Ast.VarDecE; import ast.Ast.VarDecXE; import ast.ExpCore; import ast.Expression; import ast.Expression.BinOp; import ast.Expression.Catch; import ast.Expression.Catch1; import ast.Expression.CatchMany; import ast.Expression.ClassB; import ast.Expression.ClassB.Member; import ast.Expression.ClassB.MethodImplemented; import ast.Expression.ClassB.MethodWithType; import ast.Expression.ClassB.NestedClass; import ast.Expression.ClassReuse; import ast.Expression.CurlyBlock; import ast.Expression.DocE; import ast.Expression.FCall; import ast.Expression.If; import ast.Expression.Literal; import ast.Expression.Loop; import ast.Expression.MCall; import ast.Expression.RoundBlock; import ast.Expression.SquareCall; import ast.Expression.SquareWithCall; import ast.Expression.UnOp; import ast.Expression.UseSquare; import ast.Expression.Using; import ast.Expression.While; import ast.Expression.With; import ast.Expression.X; import ast.Expression._void; import ast.PathAux; import ast.Util.PathMxMx; import auxiliaryGrammar.EncodingHelper; import auxiliaryGrammar.Functions; import programReduction.Program; import coreVisitors.InjectionOnSugar; import coreVisitors.IsCompiled; import coreVisitors.Visitor; import facade.Configuration; import facade.L42; import profiling.Timer; public class Desugar extends CloneVisitor{ public static Expression of(Expression e){ long max=RefreshUniqueNames.maxUnique(e); assert max>=0L; L42.setFreshPrivateCap(max+1); if(L42.path!=null){e=ReplaceDots.of(L42.path, e);} Desugar d=new Desugar(); d.usedVars.addAll(CollectDeclaredVars.of(e)); e=DesugarPaths.of(e); e=DesugarNormalizeReceiver.of(d.usedVars, e); e=DesugarContext.of(d.usedVars, e); assert DesugarContext.checkRemoved(e); e=DesugarW.of(d.usedVars,e); e=DesugarVars.of(d.usedVars,e); assert DesugarVars.assertVarsRemoved(e); //understand what is the current folder //replace ... recursively //replaceDots(currentFolder,e)-> clone visitor d.collectAllUsedLibs(e);//collect all classBreuse in a map url->core version L42.usedNames.addAll(CollectDeclaredClassNamesAndMethodNames.of(e)); d.renameAllPrivatesInUsedLibs(); Expression result= e.accept(d); assert Functions.checkCore(result); return result; } private void renameAllPrivatesInUsedLibs() { for(String s: this.importedLibs.keySet()){ this.importedLibs.put(s,renameAllPrivatesInUsedLibs(s,this.importedLibs.get(s))); } } private ast.ExpCore.ClassB renameAllPrivatesInUsedLibs(String libName,ExpCore.ClassB classB) { return classB;//TODO: when new private system is working modify here } private void collectAllUsedLibs(Expression e) { profiling.Timer.activate("sugarvisitors.collectAllUsed"); e.accept(new CloneVisitor(){ @Override public Expression visit(ClassReuse s) { ClassB data = OnLineCode.getCode(s.getUrl()); L42.usedNames.addAll(CollectDeclaredClassNamesAndMethodNames.of(data)); ast.ExpCore.ClassB dataCore=(ast.ExpCore.ClassB) data.accept(new InjectionOnCore()); Desugar.this.importedLibs.put(s.getUrl(),dataCore); //Using a hash map as over means that if we import the same library twice, we get a single copy. //This is "ok" but then we can not rely on the identities of the positions. //This is why we refresh position identities at the end of desugaring. return super.visit(s); } }); profiling.Timer.deactivate("sugarvisitors.collectAllUsed"); } HashMap<String,ast.ExpCore.ClassB> importedLibs=new HashMap<>(); Set<String> usedVars=new HashSet<String>(); Type t=NormType.immVoid; HashMap<String,Type> varEnv=new HashMap<String,Type>(); public Expression visit(RoundBlock s) { s=blockEtoXE(s); s=blockInferVar(s); HashMap<String, Type> oldVarEnv = new HashMap<String, Type>(varEnv); try{ if(!s.getContents().isEmpty()){addAllDec(s.getContents().get(0).getDecs());} Expression result= super.visit(s); return result; } finally{varEnv=oldVarEnv;} } @Override protected Catch liftK(Catch k){ if(!(k instanceof Expression.Catch1)){return super.liftK(k);} //slower but safer?HashMap<String, Type> oldVarEnv = new HashMap<String, Type>(varEnv); String added=null; try{ Expression.Catch1 k1=(Expression.Catch1)k; added=k1.getX(); varEnv.put(added,k1.getT()); return super.liftK(k); } finally{varEnv.remove(added);} } private void addAllDec(List<VarDec> decs) { for(VarDec _dec:decs){ if(!(_dec instanceof VarDecXE)){continue;} VarDecXE dec=(VarDecXE)_dec; assert dec.getT().isPresent(); this.varEnv.put(dec.getX(), dec.getT().get()); } } private RoundBlock blockEtoXE(RoundBlock s) { if(s.getContents().isEmpty()){return s;} List<VarDec> decs = s.getContents().get(0).getDecs(); List<VarDec> newDecs =new ArrayList<VarDec>(); for(VarDec _dec:decs){ if(!(_dec instanceof VarDecE)){ newDecs.add(_dec);continue;} VarDecE dec=(VarDecE)_dec; String x=Functions.freshName("unused", usedVars); //usedVars.add(x); VarDecXE newXE=new VarDecXE(false,Optional.of(NormType.immVoid),x,dec.getInner()); newDecs.add(newXE); } RoundBlock result = blockWithDec(s,newDecs); //assert L42.checkWellFormedness(s); //assert L42.checkWellFormedness(result); return result; } private RoundBlock blockWithDec(RoundBlock s, List<VarDec> decs) { assert s.getContents().size()==1: s.getContents().size(); List<Expression.BlockContent> ctx = new ArrayList<>(); ctx.add(new Expression.BlockContent(decs, s.getContents().get(0).get_catch())); return s.withContents(ctx); } public Type _computeTypeForClassBForVar(VarDecXE varDec) { Type t=varDec.getT().get(); t=t.match( nt->nt.withPath(computeTypeForClassBForVar(nt.getPath())), h -> h.withPath(computeTypeForClassBForVar(h.getPath())) ); return t; } private Path computeTypeForClassBForVar(Path path) { if (path.isPrimitive()){return path;} if (!path.isCore()){return path;} return path.setNewOuter(path.outerNumber()+1); //return path; } private RoundBlock blockInferVar(RoundBlock s) { if(s.getContents().isEmpty()){return s;} HashMap<String, Type> localVarEnv = new HashMap<String, Type>(this.varEnv); List<VarDec> newDecs =new ArrayList<VarDec>(); for(VarDec _dec:s.getContents().get(0).getDecs()){ if(!(_dec instanceof VarDecXE)){ newDecs.add(_dec);continue;} VarDecXE dec=(VarDecXE)_dec; if(dec.getT().isPresent()){ Type ti=dec.getT().get(); /*TODO: will be in future? if (dec.isVar() & !(ti instanceof NormType)){ throw new ErrorMessage.NotWellFormedMsk(s,new Expression.X(dec.getX()), "Variable bindings need to specify their type."); }*/ localVarEnv.put(dec.getX(),ti); newDecs.add(dec); continue; } /*TODO: will be in future?if (dec.isVar()){ throw new ErrorMessage.NotWellFormedMsk(s,new Expression.X(dec.getX()), "Variable bindings need to specify their type."); } */ Type t=GuessType.of(dec.getInner(),localVarEnv); localVarEnv.put(dec.getX(),t); newDecs.add(dec.withT(Optional.of(t))); } return blockWithDec(s, newDecs); } public Expression visit(ClassB s) { Position pos=s.getP(); if(s.getH() instanceof ConcreteHeader){ List<Member> ms = Desugar.cfType((ConcreteHeader)s.getH(),Doc.empty()); ms.addAll(s.getMs()); s=s.withMs(ms).withH(new Ast.TraitHeader()); } if(!s.getFields().isEmpty()){ List<Member> ms =s.getFields().stream().flatMap(f->Desugar.field(pos,f)).collect(Collectors.toList()); ms.addAll(s.getMs()); s=s.withMs(ms).withH(new Ast.TraitHeader()); } Set<String> oldUsedVars = this.usedVars; HashMap<String, Type> oldVarEnv = this.varEnv; try{ s=(ClassB)super.visit(s); s=FlatFirstLevelLocalNestedClasses.of(s,this); s=DesugarCatchDefault.of(s); return s;} finally{ this.usedVars=oldUsedVars; this.varEnv=oldVarEnv; } } public Expression visit(ClassReuse s) { ClassB res=lift(s.getInner()); //ClassB reused2=OnLineCode.getCode(s.getUrl()); ExpCore.ClassB _reused=this.importedLibs.get(s.getUrl()); assert _reused!=null:s.getUrl()+" "+this.importedLibs.keySet()+this.importedLibs.get(s.getUrl())+this.importedLibs; ExpCore.ClassB reused=RefreshUniqueNames.refreshTopLevel(_reused); for(Member m2:res.getMs()){ m2.match( nc2->{for(ast.ExpCore.ClassB.NestedClass nc1:reused.ns()){ if (nc1.getName().equals(nc2.getName())){ throw new ast.ErrorMessage.NotWellFormedMsk(s, s, "Nested class \""+nc1.getName()+"\" already present in reused library "+s.getUrl()); } }return null;}, mi->{return null;}, mwt2->{for(ast.ExpCore.ClassB.MethodWithType mwt1:reused.mwts()){ if (mwt1.getMs().equals(mwt2.getMs())){ throw new ast.ErrorMessage.NotWellFormedMsk(s, s, "Method with type \""+mwt1.getMs()+"\" already present in reused library "+s.getUrl()); } }return null;}); } return new ClassReuse(res,s.getUrl(),reused); } protected Path liftP(Path s) { assert s.isCore()|| s.isPrimitive(); return s; } protected List<Catch> liftKs(List<Catch> ks) { List<Catch> result=new ArrayList<>(); String x=Functions.freshName("catched", usedVars); for(Catch k:ks){ if( k instanceof DesugarCatchDefault.CatchToComplete){ Catch k2=this.liftK(((DesugarCatchDefault.CatchToComplete) k).catch1); result.add(new DesugarCatchDefault.CatchToComplete((Catch1) k2)); continue; } k.match(k1->result.add(liftK(k1)), kM->{ for(Type t:kM.getTs()){ result.add(liftK(new Expression.Catch1(kM.getP(),kM.getKind(),t,x,kM.getInner()))); } return false; }, kP->{ for(Type t:kP.getTs()){ //S on T e == catch exception T x S e(x) Expression inner=kP.getInner(); inner=new Expression.FCall(kP.getP(),inner,Doc.empty(), new ast.Ast.Parameters(Optional.of(new X(kP.getP(),x)),Collections.emptyList(),Collections.emptyList())); inner=new Expression.Signal(kP.getKind(),inner); result.add(liftK(new Expression.Catch1(kP.getP(),SignalKind.Exception,t,x,inner))); } return false; }); } return result; } protected Parameters liftPs(Parameters ps) { if(!ps.getE().isPresent()){return liftPsPropagate(ps);} List<String> xs = new ArrayList<String>(ps.getXs()); List<Expression> es =new ArrayList<Expression>(ps.getEs()); xs.add(0,"that"); es.add(0,ps.getE().get()); return liftPsPropagate(new Parameters(Optional.empty(),xs,es)); } private Parameters liftPsPropagate(Parameters ps) { assert !ps.getE().isPresent(); List<Expression> es = new ArrayList<Expression>(); {int i=-1;for(Expression ei:ps.getEs()){i+=1; Type ti = expectedTypeFor(ps.getXs().get(i)); es.add(withExpectedType(ti,()->lift(ei))); }} return new Parameters( Optional.empty(), ps.getXs(), es); } public Type expectedTypeFor(String x) { if(this.t instanceof NormType){return this.t;} List<MethodSelectorX> sel = new ArrayList<>(((Ast.HistoricType)this.t).getSelectors()); MethodSelector ms=sel.get(sel.size()-1).getMs(); sel.set(sel.size()-1,new MethodSelectorX(ms,x)); Type ti=((Ast.HistoricType)this.t).withSelectors(sel); return ti; } public Expression visit(While s) { Expression cond=Desugar.getMCall(s.getP(),s.getCond(), "#checkTrue",Desugar.getPs()); RoundBlock b=Desugar.getBlock(s.getP(),cond,s.getThen()); Loop l=new Loop(b); NormType _void=NormType.immVoid; Expression.Catch k=Desugar.getK(s.getP(),SignalKind.Exception, "",_void, Expression._void.instance); RoundBlock b2=Desugar.getBlock(s.getP(),l,Collections.singletonList(k),Expression._void.instance); return b2.accept(this); } public Expression visit(If s) { if(!s.get_else().isPresent()){ return visit(s.with_else(Optional.of(Expression._void.instance))); } Position p=s.getP(); if(!(s.getCond() instanceof Ast.Atom)){ String x=Functions.freshName("cond", usedVars); return visit(getBlock(p,x, s.getCond(),s.withCond(new X(p,x)))); } MCall check=getMCall(p,s.getCond(),"#checkTrue", getPs()); Expression.Catch k = getK(p,SignalKind.Exception,"",NormType.immVoid,s.get_else().get()); return visit(getBlock(p,check,Collections.singletonList(k),s.getThen())); } static Parameters getPs(){ return new Parameters(Optional.empty(),Collections.emptyList(),Collections.emptyList()); } static Parameters getPs(Expression e){ return new Parameters(Optional.of(e), Collections.emptyList(),Collections.emptyList()); } static Parameters getPs(String pName,Expression e){ List<String> ps=new ArrayList<>(); List<Expression> es=new ArrayList<>(); ps.add(pName); es.add(e); return new Parameters(Optional.empty(), ps,es); } static Position getPosition(Expression src){ if(src instanceof Ast.HasPos){return ((Ast.HasPos)src).getP();} else{return CollapsePositions.of(src);} } static MCall getMCall(Position p,Expression rec,String name,Parameters ps){ return new MCall(rec,name,Doc.empty(),ps,p); } static RoundBlock getBlock(Position p,String x,Expression xe,Expression inner){ List<Expression.BlockContent> bc=new ArrayList<>(); List<VarDec> decs = new ArrayList<VarDec>(); decs.add(new VarDecXE(false,Optional.empty(),x,xe)); bc.add(new Expression.BlockContent(decs,Collections.emptyList())); return new RoundBlock(p,Doc.empty(),inner,bc); } static RoundBlock getBlock(Position p,List<? extends VarDec> _decs,Expression inner){ if(_decs.isEmpty()){return new RoundBlock(p,Doc.empty(),inner,Collections.emptyList());} List<VarDec> decs=new ArrayList<VarDec>(_decs); List<Expression.BlockContent> bc=new ArrayList<>(); bc.add(new Expression.BlockContent(decs,Collections.emptyList())); return new RoundBlock(p,Doc.empty(),inner,bc); } static RoundBlock getBlock(Position p,Expression xe,Expression inner){ List<Expression.BlockContent> bc=new ArrayList<>(); List<VarDec> decs = new ArrayList<VarDec>(); decs.add(new VarDecE(xe)); bc.add(new Expression.BlockContent(decs,Collections.emptyList())); return new RoundBlock(p,Doc.empty(),inner,bc); } static RoundBlock getBlock(Position p,Expression xe,List<Expression.Catch> ks,Expression inner){ List<Expression.BlockContent> bc=new ArrayList<>(); List<VarDec> decs = new ArrayList<VarDec>(); decs.add(new VarDecE(xe)); bc.add(new Expression.BlockContent(decs,ks)); return new RoundBlock(p,Doc.empty(),inner,bc); } static Expression.Catch getK(Position pos,SignalKind kind, String x, Type t,Expression inner){ if (x==""){return new Expression.CatchMany(pos,kind,Collections.singletonList(t),inner);} return new Expression.Catch1(pos,kind,t,x,inner); } public Expression visit(CurlyBlock s) { assert s.getContents().size()==1; assert s.getContents().get(0).get_catch().isEmpty(); assert s.getContents().get(0).getDecs().size()==1; assert s.getContents().get(0).getDecs().get(0) instanceof VarDecE; Expression inner=((VarDecE)s.getContents().get(0).getDecs().get(0)).getInner(); String y=Functions.freshName("result",this.usedVars); Expression.Catch k=getK(s.getP(),SignalKind.Return,y,this.t,new X(s.getP(),y)); Expression termination= Desugar.errorMsg("CurlyBlock-Should be unreachable code"); RoundBlock outer=getBlock(s.getP(),inner, Collections.singletonList(k),termination); return visit(outer); } public Expression visit(DocE s) { return new RoundBlock(getPosition(s),s.getDoc(),lift(s.getInner()),Collections.emptyList()); } public Expression visit(BinOp s) { Op op=s.getOp(); if(op==Op.ColonEqual){ return visit(getMCall(s.getP(),s.getLeft(),"inner",getPs(s.getRight()))); } if(op.kind==Ast.OpKind.EqOp){ //go from, for example ++= into ++ Op op2=Op.fromString(s.getOp().inner.substring(0,s.getOp().inner.length()-1)); BinOp s2=s.withOp(op2); s2=s2.withLeft(getMCall(s.getP(),s.getLeft(),"#inner",getPs())); return visit(new BinOp(s.getP(),s.getLeft(),Op.ColonEqual,s2)); } if (op.negated){ BinOp s2=s.withOp(op.nonNegatedVersion()); return visit(getMCall(s.getP(),s2,desugarName(Op.Bang.inner),getPs())); } if (op.normalized){ return visit(getMCall(s.getP(),s.getLeft(),desugarName(s.getOp().inner),getPs(s.getRight()))); } String x=Functions.freshName("opNorm", usedVars); BinOp s2=new BinOp(s.getP(),s.getRight(),op.normalizedVersion(),new Expression.X(s.getP(),x)); return visit(getBlock(s.getP(),x, s.getLeft(),s2)); } public Expression visit(UnOp s) { return visit(visit1Step(s)); } static public MCall visit1Step(UnOp s) { return getMCall(s.getP(),s.getInner(),desugarName(s.getOp().inner),getPs()); } public Expression visit(FCall s) { return visit(visit1Step(s)); } static public MCall visit1Step(FCall s) { return getMCall(s.getP(),s.getReceiver(),"#apply",s.getPs()); } public Expression visit(SquareCall s) { return visit(visit1Step(s)); } public MCall visit1Step(SquareCall s) { //we can assumethe receivers are normalized after DesugarContext //assert s.getReceiver() instanceof Ast.Atom: s.getReceiver(); //but nor really, since vars accesses are replaced by meth calls. In that case is fine to not have an atom. //(b=r.builder() b.a() b.b() b.c() .... b) List<VarDec> vd=new ArrayList<>(); Expression k=getMCall(s.getP(),s.getReceiver(),"#seqBuilder",getPs()); String x=Functions.freshName("b", usedVars); X b=new X(s.getP(),x); vd.add(new VarDecXE(false, Optional.empty(), x, k)); for(Parameters ps:s.getPss()){ vd.add(new VarDecE(getMCall(s.getP(),b,"#add",ps))); } Expression inner=getBlock(s.getP(), vd, b); Parameters ps=new Parameters(Optional.empty(), Collections.singletonList("seqBuilder"),Collections.singletonList(inner)); return getMCall(s.getP(),s.getReceiver(),"#from",ps); } public static Expression appendAddMethods(SquareCall s, Expression result) { for(Parameters ps: s.getPss()){ result=getMCall(s.getP(),result,"#add",ps); } return result; } public static Expression appendEndMethod(Position pos,Expression x,Expression inner) { MCall result=new MCall(x,"#end",Doc.empty(),Desugar.getPs(),pos); return result; //return x; } @Override public Expression visit(UseSquare s) { assert s.getInner() instanceof Expression.SquareCall:"The other shape is stupid: use[with...]== with..."; return super.visit(s); } public Expression visit(Literal s) { //we can assumethe receivers are normalized after DesugarContext return visit1Step(s).accept(this); } public MCall visit1Step(Literal s) { //(b=r.builder() b.a() b.b() b.c() .... b) List<VarDec> vd=new ArrayList<>(); Expression k=getMCall(s.getP(),s.getReceiver(),"#builder",getPs()); String x=Functions.freshName("b", usedVars); X b=new X(s.getP(),x); vd.add(new VarDecXE(false, Optional.empty(), x, k)); for(char ch:s.getInner().toCharArray()){ String name=Character.toString(ch); if(!Character.isAlphabetic(ch) && ! Character.isDigit(ch) ){ name=desugarSymbol(name); } vd.add(new VarDecE(getMCall(s.getP(),b,"#"+name,getPs()))); } Expression inner=getBlock(s.getP(), vd, b); Parameters ps=new Parameters(Optional.empty(), Collections.singletonList("builder"),Collections.singletonList(inner)); return getMCall(s.getP(),s.getReceiver(),"#from",ps); } static Ast.MethodSelector literalGuessedSelector(){ return Ast.MethodSelector.of("#from",Collections.singletonList("builder")); } static Ast.MethodSelector squareGuessedSelector(){ return Ast.MethodSelector.of("#from",Collections.singletonList("seqBuilder")); } protected ast.Ast.VarDecXE liftVarDecXE(ast.Ast.VarDecXE d) { assert !d.isVar(): d; assert d.getT().isPresent(); return withExpectedType(d.getT().get(),()->super.liftVarDecXE(d)); } protected ast.Ast.VarDecE liftVarDecE(ast.Ast.VarDecE d) { throw Assertions.codeNotReachable(); } protected ast.Ast.VarDecCE liftVarDecCE(ast.Ast.VarDecCE d) { return d;//ok, it have to happen after, when is lifted out of the expression } protected Doc liftDoc(Doc doc) { return super.liftDoc(doc.toMultiline()); } static ClassB encodePrimitiveString(String s){ //return EncodingHelper.wrapStringU(s);//no, this produces a ExpCoreClassB return new ClassB(Doc.factory(true,"@stringU\n"+EncodingHelper.produceStringUnicode(s)+"\n"),new Ast.TraitHeader(),Collections.emptyList(),Collections.emptyList(),Collections.emptyList(),Position.noInfo); } public static String desugarName(String n){ if(n.isEmpty())return "#apply"; if(isNormalName(n)){return n;} return "#"+desugarSymbol(n); } public static String desugarSymbol(String n){ String res=""; for(char c:n.toCharArray()){ switch (c){ case '+':res+="plus";break; case '-':res+="less";break; case '~':res+="tilde";break; case '!':res+="bang";break; case '&':res+="and";break; case '|':res+="or";break; case '<':res+="left";break; case '>':res+="right";break; case '=':res+="equal";break; case '*':res+="times";break; case '/':res+="divide";break; case '(':res+="oRound";break; case ')':res+="cRound";break; case '[':res+="oSquare";break; case ']':res+="cSquare";break; case '{':res+="oCurly";break; case '}':res+="cCurly";break; case '\"':res+="dQuote";break; case '\'':res+="sQuote";break; case '`':res+="hQuote";break; case '?':res+="qMark";break; case '^':res+="hat";break; case ',':res+="comma";break; case ';':res+="semicolon";break; case ':':res+="colon";break; case '.':res+="dot";break; case '_':res+="underscore";break; case '#':res+="hash";break; case '@':res+="at";break; case '$':res+="$";break;//yes, is not an operator, but is not alphabetic :( case '%':res+="%";break;//yes, is not an operator, but is not alphabetic :( case '\\':res+="backslash";break; case ' ':res+="space";break; case '\n':res+="newline";break; case '\t': throw new AssertionError("Tab in string?"); } } assert res.length()>0:"\""+n+"\""; return res; } private static boolean isNormalName(String n) { for(char c:n.toCharArray()){ if ("+-~!&|<>=*/".indexOf(c)!=-1){return false;} } return true; } //--------- protected Header liftH(Header h) { if(!(h instanceof Ast.ConcreteHeader)){return super.liftH(h);} Ast.ConcreteHeader ch=(Ast.ConcreteHeader) h; return super.liftH(ch.withName(desugarName(ch.getName()))); } protected MethodSelector liftMs(MethodSelector ms) { return ms.withName(desugarName(ms.nameToS())); } private<T0,T> T withExpectedType(Type t,Supplier<T> f){ Type aux=this.t; this.t=t; T result=f.get(); this.t=aux; return result; } public Expression visit(MCall s) { Type recT=GuessType.of(s.getReceiver(), varEnv); List<String> names=new ArrayList<String>(); if(s.getPs().getE().isPresent()){names.add("that");} names.addAll(s.getPs().getXs()); MethodSelector ms=MethodSelector.of(s.getName(),names); Type tt=recT.match( nt->{ Path path=nt.getPath(); List<MethodSelectorX> selectors=new ArrayList<>(); selectors.add(new MethodSelectorX(ms,"")); return new Ast.HistoricType(path,selectors,Doc.empty()); }, ht->{ List<MethodSelectorX> selectors=new ArrayList<>(ht.getSelectors()); selectors.add(new MethodSelectorX(ms,"")); return ht.withSelectors(selectors); } ); //Type tt=new Ast.HistoricType(); return new MCall(lift(s.getReceiver()),s.getName(),s.getDoc(), withExpectedType(tt,()->liftPs(s.getPs())),s.getP() ); } public Expression visit(Using s) { Type aux=this.t; this.t=NormType.immVoid; Parameters ps = liftPs(s.getPs()); this.t=aux; return new Using(liftP(s.getPath()),s.getName(),s.getDocs(),ps,lift(s.getInner())); } public NestedClass visit(NestedClass nc){ while(nc.getInner() instanceof Expression.DocE){ nc=nc.withInner(((Expression.DocE)nc.getInner()).getInner()); }//TODO: document stripping of comments and decide scope NestedClass nc1=nc; this.usedVars=new HashSet<String>(); this.varEnv=new HashMap<String, Type>(); usedVars.addAll(CollectDeclaredVars.of(nc.getInner())); return withExpectedType( NormType.immLibrary, ()->super.visit(nc1)); } public MethodImplemented visit(MethodImplemented mi){ this.usedVars=new HashSet<String>(); this.varEnv=new HashMap<String, Type>(); String mName=desugarName(mi.getS().nameToS()); mi=mi.withS(mi.getS().withName(mName)); for(String name:mi.getS().getNames()){ usedVars.add(name); List<Ast.MethodSelectorX> msxsi=new ArrayList<>(); msxsi.add(new Ast.MethodSelectorX(mi.getS(),name)); varEnv.put(name,new Ast.HistoricType(Path.outer(0),msxsi,Doc.empty())); } usedVars.add("this"); List<Ast.MethodSelectorX> msxsi=new ArrayList<>(); msxsi.add(new Ast.MethodSelectorX(mi.getS(),"this")); varEnv.put("this",new Ast.HistoricType(Path.outer(0),msxsi,Doc.empty())); List<Ast.MethodSelectorX> msxs=new ArrayList<>(); msxs.add(new Ast.MethodSelectorX(mi.getS(),"")); usedVars.addAll(CollectDeclaredVars.of(mi.getInner())); final MethodImplemented mi2=mi;//final restrictions return withExpectedType( new Ast.HistoricType(Path.outer(0),msxs,Doc.empty()), ()->super.visit(mi2)); } public MethodWithType visit(MethodWithType mt){ this.usedVars=new HashSet<String>(); this.varEnv=new HashMap<String, Type>(); String mName=desugarName(mt.getMs().nameToS()); mt=mt.withMs(mt.getMs().withName(mName)); if(!mt.getInner().isPresent()){return super.visit(mt);} {int i=-1;for(String name:mt.getMs().getNames()){i+=1; this.usedVars.add(name); this.varEnv.put(name,mt.getMt().getTs().get(i)); }} usedVars.add("this"); varEnv.put("this",new NormType(mt.getMt().getMdf(),Path.outer(0),mt.getDoc())); usedVars.addAll(CollectDeclaredVars.of(mt.getInner().get())); final MethodWithType mt2=mt;//final restrictions return withExpectedType( liftT(mt.getMt().getReturnType()), ()->super.visit(mt2)); } public Expression visit(With e){ throw Assertions.codeNotReachable(); } public static Expression.BlockContent getBlockContent(Expression e) { List<VarDec> single= new ArrayList<VarDec>(); single.add(new VarDecE(e)); return new Expression.BlockContent(single,Collections.emptyList()); } public static Expression.BlockContent getBlockContent(Expression e,Expression.Catch k) { List<VarDec> single= new ArrayList<VarDec>(); single.add(new VarDecE(e)); return new Expression.BlockContent(single,Collections.singletonList(k)); } public Expression visit(SquareWithCall s) { throw Assertions.codeNotReachable(); } public static Expression errorMsg(String msg){//could be error void, but this is more informative for debugging ExpCore core=EncodingHelper.wrapError(msg); return core.accept(new InjectionOnSugar()); } //private static final Doc consistentDoc=Doc.factory("@consistent\n"); public static List<Member> cfType(ConcreteHeader h,Doc doc){ //doc=Doc.factory("@private"); List<Member> result=new ArrayList<Member>(); MethodWithType k = cfMutK(doc,h); Mdf nameMdf=mdfForNamedK(h); if(nameMdf==Mdf.Lent){k=cfLentK(k);} MethodWithType kOut =cfNameK(doc, nameMdf, h, k.getMs()); result.add(k); result.add(kOut); //cfType1(h,doc, result); for(FieldDec f:h.getFs()){ Doc fDoc=doc.sum(f.getDoc()); cfSetter(h.getP(),f,fDoc,result); cfExposer(h.getP(),f,fDoc,result); cfGetter(h.getP(),f,fDoc,result); } return result; } static private MethodWithType cfNameK(Doc doc,Mdf mdf,ast.Ast.ConcreteHeader h,MethodSelector called) { List<Type> ts=new ArrayList<Type>(); for(FieldDec fi:h.getFs()){ Type ti=fi.getT(); ts.add(ti.withDoc(ti.getDoc().sum(fi.getDoc()))); } MethodSelector ms=called.withName(h.getName()); NormType resT=new ast.Ast.NormType(mdf,ast.Ast.Path.outer(0),Doc.empty()); MethodType mt=new MethodType(false,ast.Ast.Mdf.Class,ts,resT,Collections.emptyList()); Parameters ps=new Parameters(Optional.empty(),called.getNames(), called.getNames().stream().map(n->new X(Position.noInfo,n)).collect(Collectors.toList())); MCall body=new MCall(new Expression.EPath(h.getP(),Path.outer(0)),called.nameToS(),Doc.empty(),ps,h.getP()); return new MethodWithType(doc, ms,mt, Optional.of(body),h.getP()); } static public MethodWithType cfLentK(MethodWithType mutK) { mutK=mutK.withMs(mutK.getMs().withName("#lentK")); NormType resT=new ast.Ast.NormType(Mdf.Lent,ast.Ast.Path.outer(0),Doc.empty()); MethodType mt = mutK.getMt(); mt=mt.withReturnType(resT).withTs(mt.getTs().stream() .map(t->(NormType)t) .map(nt->nt.withMdf(nt.getMdf()==Mdf.Mutable?Mdf.Lent:nt.getMdf())) .collect(Collectors.toList())); mutK=mutK.withMt(mt); return mutK; } static public MethodWithType cfMutK(Doc doc,ast.Ast.ConcreteHeader h) { return cfMutK(doc,h.getFs(),h.getP()); } static public MethodWithType cfMutK(Doc doc,List<FieldDec>fields,Position pos) { List<String> names= new ArrayList<String>(); List<Type> ts=new ArrayList<Type>(); for(FieldDec fi:fields){ Type ti=fi.getT().match(nt->{ if(nt.getMdf()==Mdf.Capsule){nt=nt.withMdf(Mdf.Mutable);} return nt; },ht->ht); ti=ti.withDoc(ti.getDoc().sum(fi.getDoc())); ts.add(ti); names.add(fi.getName()); } MethodSelector ms=MethodSelector.of("#mutK",names); NormType resT=new ast.Ast.NormType(Mdf.Mutable,ast.Ast.Path.outer(0),Doc.empty()); MethodType mt=new MethodType(false,ast.Ast.Mdf.Class,ts,resT,Collections.emptyList()); return new MethodWithType(doc, ms,mt, Optional.empty(),pos); } static private Mdf mdfForNamedK(ast.Ast.ConcreteHeader h){ boolean canImm=true; for(FieldDec f:h.getFs()){ if(!(f.getT() instanceof NormType)){return Mdf.Mutable;}//TODO: will disappear? NormType nt=(NormType)f.getT(); Mdf m=nt.getMdf(); if(m==Mdf.Lent || m==Mdf.Readable){return Mdf.Lent;} if(m!=Mdf.Immutable && m!=Mdf.Class && m!=Mdf.ImmutableFwd){canImm=false;} if(f.isVar()){canImm=false;} } if(canImm){return Mdf.Immutable;} return Mdf.Mutable; } static private Stream<Member> field(Position pos,Ast.FieldDec f){ Stream<Member> s=Stream.of(); //if var, do setter if(f.isVar()){s=Stream.concat(s,Stream.of(generateSetter(pos,f,f.getDoc())));} //if #, do getter and exposer, else only exposer //if(f.getName().startsWith("#")){ s=Stream.concat(s, Stream.of( generateExposer(pos,f,f.getDoc()), generateGetter(pos,f,f.getDoc()) )); /* } else if (requireExposer(f.getT())){ s=Stream.concat(s, Stream.of( generateExposer(pos,f,f.getDoc()) )); } else {s=Stream.concat(s, Stream.of( generateGetter(pos,f,f.getDoc()) ));} *///Careful with capsule return s; } private static boolean requireExposer(Type t) { Mdf mdf= ((NormType)t).getMdf(); return mdf==Mdf.Mutable || mdf==Mdf.Capsule|| mdf==Mdf.Lent; } static private void cfSetter(Expression.Position pos,ast.Ast.FieldDec f, Doc doc,List<Member> result) { if(!f.isVar()){return;} result.add(generateSetter(pos, f, doc)); } private static MethodWithType generateSetter(Expression.Position pos, ast.Ast.FieldDec f, Doc doc) { Type tt=f.getT().match(nt->Functions.toComplete(nt), hType->hType); MethodType mti=new MethodType(false,Mdf.Mutable,Collections.singletonList(tt),NormType.immVoid,Collections.emptyList()); MethodSelector msi=MethodSelector.of(f.getName(),Collections.singletonList("that")); MethodWithType mwt = new MethodWithType(doc, msi, mti, Optional.empty(),pos); return mwt; } //left cfExposer generating exposer since is different from generateExposer code for # and capsule static private void cfExposer(Expression.Position pos,FieldDec f,Doc doc, List<Member> result) { Type tt=f.getT().match(nt->Functions.toComplete(nt), hType->hType); MethodType mti=new MethodType(false,Mdf.Mutable,Collections.emptyList(),tt,Collections.emptyList()); MethodSelector msi=MethodSelector.of("#"+f.getName(),Collections.emptyList()); result.add(new MethodWithType(doc, msi, mti, Optional.empty(),pos)); } private static MethodWithType generateExposer(Expression.Position pos, FieldDec f, Doc doc) { Type tt=f.getT().match(nt->{ nt=Functions.toComplete(nt); if(nt.getMdf()==Mdf.Capsule){nt=nt.withMdf(Mdf.Lent);} return nt; }, hType->hType); MethodType mti=new MethodType(false,Mdf.Mutable,Collections.emptyList(),tt,Collections.emptyList()); MethodSelector msi=MethodSelector.of("#"+f.getName(),Collections.emptyList()); MethodWithType mwt = new MethodWithType(doc, msi, mti, Optional.empty(),pos); return mwt; } static private void cfGetter(Expression.Position pos,FieldDec f,Doc doc, List<Member> result) { if(!( f.getT() instanceof NormType)){return;} MethodWithType mwt = generateGetter(pos, f, doc); result.add(mwt); } private static MethodWithType generateGetter(Expression.Position pos, FieldDec f, Doc doc) { NormType fieldNt=(NormType)f.getT(); fieldNt=Functions.toComplete(fieldNt); Mdf mdf=fieldNt.getMdf(); if(mdf==Mdf.Capsule || mdf==Mdf.Mutable || mdf==Mdf.Lent){ fieldNt=fieldNt.withMdf(Mdf.Readable); } MethodType mti=new MethodType(false,Mdf.Readable,Collections.emptyList(),fieldNt,Collections.emptyList()); String name=f.getName(); //if(name.startsWith("#")){name=name.substring(1);} MethodSelector msi=MethodSelector.of(name,Collections.emptyList()); MethodWithType mwt=new MethodWithType(doc, msi, mti, Optional.empty(),pos); return mwt; } }