package ast; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; import ast.Ast.Atom; import ast.Ast.Doc; import ast.Ast.Mdf; import ast.Ast.NormType; import ast.Ast.Op; import ast.Ast.Path; import sugarVisitors.Desugar; import tools.Assertions; import tools.StringBuilders; //import ast.Ast.Doc; //import ast.Ast.MethodSelector; import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.Value; import lombok.experimental.Wither; import platformSpecific.javaTranslation.Resources; public interface Ast { public interface Atom { } @Value public class Parameters { Optional<Expression> e; List<String> xs; List<Expression> es; } public interface VarDec { <T> T match(Function<VarDecXE, T> xe, Function<VarDecE, T> e, Function<VarDecCE, T> ce); } @Value @Wither public class VarDecXE implements VarDec { boolean isVar; Optional<Type> t; String x; Expression inner; public <T> T match(Function<VarDecXE, T> xe, Function<VarDecE, T> e, Function<VarDecCE, T> ce) { return xe.apply(this); } } @Value public class VarDecE implements VarDec { Expression inner; public <T> T match(Function<VarDecXE, T> xe, Function<VarDecE, T> e, Function<VarDecCE, T> ce) { return e.apply(this); } } @Value public class VarDecCE implements VarDec { Expression.ClassB.NestedClass inner; public <T> T match(Function<VarDecXE, T> xe, Function<VarDecE, T> e, Function<VarDecCE, T> ce) { return ce.apply(this); } } public interface Header { <T> T match(Function<ConcreteHeader, T> concreteH, Function<TraitHeader, T> traitH, Function<InterfaceHeader, T> interfH); } @Value @Wither @EqualsAndHashCode(exclude = "p") @ToString(exclude = "p") public class ConcreteHeader implements Header, Expression.HasPos { Mdf mdf; String name; List<FieldDec> fs; Position p; public <T> T match(Function<ConcreteHeader, T> concreteH, Function<TraitHeader, T> traitH, Function<InterfaceHeader, T> interfH) { return concreteH.apply(this); } } @Value public class TraitHeader implements Header { public <T> T match(Function<ConcreteHeader, T> concreteH, Function<TraitHeader, T> traitH, Function<InterfaceHeader, T> interfH) { return traitH.apply(this); } } @Value public class InterfaceHeader implements Header { public <T> T match(Function<ConcreteHeader, T> concreteH, Function<TraitHeader, T> traitH, Function<InterfaceHeader, T> interfH) { return interfH.apply(this); } } @Value @Wither public class FieldDec { boolean isVar; Type t; String name; Doc doc; } public interface Type { <T> T match(Function<NormType, T> normType, Function<HistoricType, T> hType); default ast.Ast.NormType getNT() { assert this instanceof ast.Ast.NormType : this; return (ast.Ast.NormType) this; } Doc getDoc(); Type withDoc(Doc doc); } @Value @Wither public class NormType implements Type { Mdf mdf; Path path; Doc doc; public static final NormType mutThis0=new NormType(Mdf.Mutable,Path.outer(0),Doc.empty()); public static final NormType immThis0=new NormType(Mdf.Immutable,Path.outer(0),Doc.empty()); public static final NormType immVoid=new NormType(Mdf.Immutable,Path.Void(),Doc.empty()); public static final NormType immLibrary=new NormType(Mdf.Immutable,Path.Library(),Doc.empty()); public static final NormType immAny=new NormType(Mdf.Immutable,Path.Any(),Doc.empty()); public static final NormType classAny=new NormType(Mdf.Class,Path.Any(),Doc.empty()); public String toString() { return mdf.name() + "[" + this.path.toString()+"]"; } public <T> T match(Function<NormType, T> normType, Function<HistoricType, T> hType) { return normType.apply(this); } } @Value @Wither public class MethodSelectorX { MethodSelector ms; String x; } @Value @Wither public class HistoricType implements Type { Path path; List<MethodSelectorX> selectors; Doc doc; public <T> T match(Function<NormType, T> normType, Function<HistoricType, T> hType) { return hType.apply(this); } } @Value public class FreeType implements Type { public <T> T match(Function<NormType, T> normType, Function<HistoricType, T> hType) { throw tools.Assertions.codeNotReachable(); } public Doc getDoc(){return Doc.empty();} public Type withDoc(Doc doc){return this;} } @Value @Wither public class MethodSelector { String name; long uniqueNum; public boolean isUnique(){return uniqueNum!=-1;} List<String> names; public static MethodSelector of(String name,List<String>names){ int index=name.lastIndexOf("_$_"); long uniqueNum=C.sToNumber(name, index); if (uniqueNum!=-1){name=name.substring(0,index);} names=java.util.Collections.unmodifiableList(names); MethodSelector res=new MethodSelector(name,uniqueNum,names); assert res.invariant(); return res; } public boolean isOperator(){ return this.name.startsWith("#");//for now, to improve later } public boolean invariant(){ // not good enought, it can also be empty or operator // assert checkX(name,true); assert !name.contains("\t"): name; if(!( name.isEmpty() && this.uniqueNum==-1)){ assert checkX(Desugar.desugarName(name),true); } for(String n:names){assert checkX(n,false);} return true; } public String toSrcEquivalent() { String result = "ast.Ast.MethodSelector.of(\"" + nameToS() + "\",java.util.Arrays.asList("; result += String.join(",", tools.Map.of(ni -> "\"" + ni + "\"", names)); return result + "))"; } public String nameToS(){ if(uniqueNum==-1){return name;} return name+"_$_"+uniqueNum; } public String toString() { String nameU=nameToS(); if (nameU.isEmpty() && names.isEmpty()) { return "()"; } if (names.isEmpty()) { return nameU + "()"; } StringBuilder result = new StringBuilder(); result.append(nameU + "("); tools.StringBuilders.formatSequence(result, names.iterator(), ",", result::append); result.append(")"); return result.toString(); } public static MethodSelector parse(String s) { if (s.equals("()")) { return MethodSelector.of(Desugar.desugarName(""), Collections.emptyList()); } String name = s; List<String> xs = new ArrayList<String>(); assert!s.isEmpty(); char last = s.charAt(s.length() - 1); if (last != ')') { throw new Resources.Error("InvalidSelector: " + s); } int i = s.indexOf('('); if (i == -1) { throw new Resources.Error("InvalidSelector: " + s); } name = s.substring(0, i); String parenthesis = s.substring(i + 1, s.length() - 1).trim(); if (!parenthesis.isEmpty()) { String[] names = parenthesis.split(",");// single representation // required for (String si : names) { if (!checkX(si, false)) { throw new Resources.Error("InvalidSelector: " + s); } if(xs.contains(si)){ throw new Resources.Error("InvalidSelector: " + s+" a parameter is repeted: "+si); } xs.add(si); } } name = Desugar.desugarName(name); if (!checkX(name, true)) { throw new Resources.Error("InvalidSelector: " + s); } return MethodSelector.of(name, xs); } public static boolean checkX(String s, boolean allowHash) { if (s.isEmpty()) {return false;} char c0 = s.charAt(0); if (allowHash && c0 == '#') { if (s.length() == 1) {return false;} char c1 = s.charAt(1); if (c1 == '#') {return false;} //return checkX(s.substring(1), allowHash); } for (char c : s.toCharArray()) { if (allowHash && c == '#') {continue;} if (ast.PathAux.isValidPathChar(c)) {continue;} return false; } return c0 == '_' ||c0 == '#' || (c0 >= 'a' && c0 <= 'z'); } } @Value @Wither public class MethodType { boolean refine; Mdf mdf; List<Type> ts; Type returnType; List<Type> exceptions; @Override public String toString(){ String res= this.mdf+":"+ts.stream().map(e->e.toString()).collect(Collectors.joining(",")) +"->"+returnType; if (!exceptions.isEmpty()){res=res+" exceptions:"+exceptions.stream().map(e->e.toString()).collect(Collectors.joining(","));} return res; } } @Value @Wither public static class C{ String inner; long uniqueNum; public boolean isUnique(){return uniqueNum!=-1;} public C(String inner,long uniqueNum){ this.inner=inner;this.uniqueNum=uniqueNum; assert PathAux.isValidClassName(inner); } public static long sToNumber(String name,int index){ if(index==-1){return -1L;} String numS=name.substring(index+3,name.length()); try{ long num=Long.parseLong(numS); return num; } catch(NumberFormatException nfe){return -1L;} } public static C of(String name){ int index=name.lastIndexOf("_$_"); long uniqueNum=sToNumber(name,index); if(uniqueNum==-1){return new C(name,-1);} return new C(name.substring(0,index),uniqueNum); } public String toString(){ if(uniqueNum==-1){return inner;} return inner+"_$_"+uniqueNum; } } //------------------- public static abstract class Path { public static Path sugarParse(List<String> rowData){ Path res=PathSugar._instance(rowData); if (res!=null){return res;} return parse(rowData); } public static Path sugarParse(String path) { List<String> rowData = Arrays.asList(path.split("\\.")); return sugarParse(rowData); } public static Path parse(List<String> rowData){ Path res=PathPrimitive._parsePrimitive(rowData); if (res!=null){return res;} res=PathCore._parsePathCode(rowData); if (res!=null){return res;} throw Assertions.codeNotReachable("InvalidPath: " + rowData); //this does not accept the sugarPath } public static Path parse(String path) { List<String> rowData = Arrays.asList(path.split("\\.")); return parse(rowData); } public static Path outer(int n, List<C> cs){ return PathCore.instance(n,cs); } public static Path outer(int n){ return PathCore.instance(n,Collections.emptyList()); } public static Path Void() {return PathPrimitive._Void;} public static Path Any() {return PathPrimitive._Any;} public static Path Library() {return PathPrimitive._Library;} public NormType toImmNT(){return new NormType(Mdf.Immutable,this,Doc.empty());} public boolean isPrimitive() {return false;} public boolean isCore() { return false; } public String toString() { return sugarVisitors.ToFormattedText.of(this);} public Path popC(){throw Assertions.codeNotReachable("path.pocC on not core:"+this);} public Path pushC(C c){throw Assertions.codeNotReachable("path.pushC on not core:"+this);} public List<C> getCBar(){throw Assertions.codeNotReachable("path.getCBar on not core:"+this);} public Path setNewOuter(int n){throw Assertions.codeNotReachable("path.setNewOuter on not core:"+this);} public int outerNumber(){throw Assertions.codeNotReachable("path.outerNumber on not core:"+this);} public List<C> sugarNames(){throw Assertions.codeNotReachable("path.outerNumber on not core:"+this);} } //----------------------------- @Value @EqualsAndHashCode(exclude = "p") /*to string manually defined so @ToString(exclude = "p") not needed*/ public static class Doc implements Expression.HasPos{ boolean multiline; String s; List<Object> annotations; List<String> parameters; Position p; public List<Path> getPaths() { List<Path> result = new ArrayList<>(); for (Object o : annotations) { if (o instanceof Path) { result.add((Path) o); } } return result; } public String _getParameterFor(Object annotation){ int i=annotations.indexOf(annotation); if(i==-1){return null;} return parameters.get(i); } public Doc withNewlineTerminator(){ if(s.endsWith("\n")){return this;} return this.withS(s+"\n"); } /*public boolean isPrivate() { if (this.annotations.contains("private")) { return true; } // if(this.toString().startsWith("@private")){return true;} return false; }*/ //public static Doc getPrivate(){return privateInstance;} //private static final Doc privateInstance=Doc.factory(true,"@private"); public static Doc factory(Path single) { return new Doc(true,"%s\n", Collections.singletonList((Object) single), Collections.singletonList(""),Position.noInfo); } public static Doc factory(boolean multiline,String s) { return factory(multiline, s,Position.noInfo ); } public static Doc factory(boolean multiline,String s, Position pos) { if (!multiline & !s.endsWith("\n")) { s += "\n";} List<Object> annotations = new ArrayList<>(); List<String> parameters = new ArrayList<>(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char ci = s.charAt(i); if (ci == '%') { sb.append('%'); sb.append('%');//NOTE: this is to allow String.format in toString continue; } if (ci != '@') { sb.append(ci); continue; } else {// ci=='@' char next = '\n'; if (i + 1 < s.length()) { next = s.charAt(i + 1); } if (next == '.' || PathAux.isValidPathChar(next)) { sb.append("%s"); i = readAnnotation(s, i + 1, annotations); readParameter(s, i + 1, parameters); } else { throw Assertions.codeNotReachable("invalid use of @ in |" + next + "| " + s); } // if(!PathAux.isValidPathStart(next)){sb.append(ci);continue;} } } return new Doc(multiline,sb.toString(), annotations,parameters,pos); } private static final Doc empty = new Doc(true,"", Collections.emptyList(), Collections.emptyList(),Position.noInfo); public static Doc empty() { return empty; } public Doc withAnnotations(final List<Object> ann) { //customized to make the string change not break the parameters structure assert ann.size()==this.annotations.size(); assert ann.stream().allMatch(Doc::isValidAnnotation):// ""; Doc tmp = new Doc(this.multiline, this.s, ann, this.parameters, this.p); assert Doc.factory(this.multiline,tmp.toStringWeak()).equals(tmp):// ""; return tmp; } public static boolean isValidAnnotation(Object o){ if (o instanceof Path){return true;} if (!(o instanceof String)){return false;} String s=(String)o; if(s.isEmpty()){return true;} if (PathAux.isValidPathStart(s.charAt(0))){return false;} for(char c : s.toCharArray()) { if(c=='.'){continue;} if(!PathAux.isValidPathChar(c)){return false;} } return true; } public Doc toMultiline(){ Doc tmp=new Doc(true, s, this.annotations, this.parameters, this.p); assert Doc.factory(true,tmp.toStringWeak()).equals(tmp); return tmp; } public Doc withS(final String s) { //customized to make the string change not break the parameters structure if (this.s==s || this.s.equals(s)){return this;} Doc tmp=new Doc(this.multiline, s, this.annotations, this.parameters, this.p); return Doc.factory(this.multiline,tmp.toStringWeak()); } public String _getParameterForPlugin(){return _getParameterFor("plugin");} public String _getParameterForPluginPart(){return _getParameterFor("pluginPart");} public String toStringWeak() { List<Object> paths = new ArrayList<>(); for (Object pi : this.annotations) { if (pi instanceof Path) { paths.add("@" + sugarVisitors.ToFormattedText.of((Path) pi)); } else { paths.add("@" + (String) pi); } } return String.format(this.s, paths.toArray()); } public String toString() { String text=toStringWeak(); assert Doc.factory(this.multiline, text).equals(this):// ""; return text; } public String toCodeFormattedString() { String text=toString(); if(text.isEmpty()){return text;} if(this.multiline){return "/*"+text+"*/";} assert text.endsWith("\n"):"|"+text+"|"; String[] splitted=text.substring(0,text.length()-1).split("\n",-1);//on its line for ease of testing//This was a bad move, javaSplit, you despicable bastard StringBuffer res=new StringBuffer(); {int i=-1;for(String s:splitted){i+=1; if(s.isEmpty()&& i==0){continue;} res.append("//"); res.append(s); res.append("\n"); }} return res.toString(); } public boolean isEmpty() { return this.s.isEmpty(); } public Doc sum(Doc that) { //be carefull, a more optimized implementation would mess up annotations as in // /*@a b*/ +/* c*/ would create annotations=[" b"] while // /*@a b c*/ would create annotations=[" b c"] String tosThis=this.toString(); String tosThat=that.toString(); return Doc.factory(true, tosThis+tosThat,this.p.sum(that.p)); } public Doc formatNewLinesAsList() { String newS=this.s.trim(); newS=newS.replace("\n", ", "); newS="["+newS+"]\n"; return this.withS(newS); } private static int readAnnotation(String s, int start, List<Object> paths) { boolean isPath=PathAux.isValidPathStart(s.charAt(start)); StringBuilder sb = new StringBuilder(); for (int i = start; i < s.length(); i++) { char ci = s.charAt(i); if (PathAux.isValidPathChar(ci)) {sb.append(ci);} else if (ci == '.' && isPath && i+1<s.length()&& PathAux.isValidPathStart(s.charAt(i+1))) {sb.append(ci);} else if (ci == '.' && !isPath) {sb.append(ci);} else {break;} } String res = sb.toString(); if (isPath) {paths.add(Path.sugarParse(res));} else {paths.add(res);} return start + res.length() - 1; } private static void readParameter(String s, int start, List<String> parameters) { int i=s.indexOf('@',start); if (i==-1){i=s.length();} String par=s.substring(start, i); while(par.endsWith("\n")){ par=par.substring(0, par.length()-1); } parameters.add(par); } } public static enum SignalKind { Error("error"), Exception("exception"), Return("return"); public final String content; SignalKind(String content) { this.content = content; } public static SignalKind fromString(String s) { for (SignalKind sk : SignalKind.values()) { if (sk.content.equals(s)) return sk; } throw tools.Assertions.codeNotReachable(); } } public static enum Mdf { Immutable(""), Mutable("mut"), Readable("read"), Lent("lent"), Capsule("capsule"), Class("class"), ImmutableFwd("fwd"),ImmutablePFwd("%fwd"),MutableFwd("fwd mut"),MutablePFwd("%fwd mut"); public final String inner; Mdf(String inner) { this.inner = inner; } public static Mdf fromString(String s) { for (Mdf mdf : Mdf.values()) { if (mdf.inner.equals(s)) return mdf; } throw tools.Assertions.codeNotReachable(); } public static List<Mdf> muts=Arrays.asList(Mdf.Mutable,Mdf.MutablePFwd,Mdf.MutableFwd); public static List<Mdf> imms=Arrays.asList(Mdf.Immutable,Mdf.ImmutablePFwd,Mdf.ImmutableFwd); } public static enum OpKind { Unary, BoolOp, RelationalOp, DataOp, EqOp } public static enum Op { Tilde("~", OpKind.Unary, true, true,false),// Bang("!", OpKind.Unary, true, true,false),// And("&", OpKind.BoolOp, true,true,false),// Or("|", OpKind.BoolOp, true, true,false),// LTEqual("<=", OpKind.RelationalOp, false, true,false),// GTEqual(">=", OpKind.RelationalOp, true, true,false),// LT("<", OpKind.RelationalOp, false, true,false),// GT(">",OpKind.RelationalOp, true,true,false),// LTLT("<<", OpKind.RelationalOp, false, false,false),// GTGT(">>",OpKind.RelationalOp, true, false,false),// LTLTEqual("<<=",OpKind.RelationalOp,false,true,false),// GTGTEqual(">>=",OpKind.RelationalOp,true,true,false),// EqualEqual("==", OpKind.RelationalOp, true, false,false),// BangLTEqual("!<=", OpKind.RelationalOp, false, true,true),// BangGTEqual("!>=", OpKind.RelationalOp, true, true,true),// BangLT("!<", OpKind.RelationalOp, false, true,true),// BangGT("!>",OpKind.RelationalOp, true,true,true),// BangLTLT("!<<", OpKind.RelationalOp, false, false,true),// BangGTGT("!>>",OpKind.RelationalOp, true, false,true),// BangLTLTEqual("!<<=",OpKind.RelationalOp,false,true,true),// BangGTGTEqual("!>>=",OpKind.RelationalOp,true,true,true),// BangEqual("!=",OpKind.RelationalOp, true,true,true),// Plus("+", OpKind.DataOp, true, true,false),// Minus("-", OpKind.DataOp, true,true,false),// Times("*", OpKind.DataOp, true, true,false),// Divide("/", OpKind.DataOp,true, true,false),// PlusPlus("++",OpKind.DataOp, true, false,false),// MinusMinus("--",OpKind.DataOp, true, false,false),// TimesTimes("**",OpKind.DataOp, true,false,false),// BabelFishL("<><",OpKind.DataOp, true,false,false),// BabelFishR("><>",OpKind.DataOp, false,false,false),// PlusEqual("+=", OpKind.EqOp,true, true,false),// MinusEqual("-=",OpKind.EqOp, true,true,false),// TimesEqual("*=",OpKind.EqOp,true,true,false),// DivideEqual("/=",OpKind.EqOp,true,true,false),// AndEqual("&=",OpKind.EqOp,true,true,false),// OrEqual("|=",OpKind.EqOp,true,true,false),// PlusPlusEqual("++=",OpKind.EqOp,true,true,false),// MinusMinusEqual("--=",OpKind.EqOp,true,true,false),// TimesTimesEqual("**=",OpKind.EqOp,true,true,false),// ColonEqual(":=",OpKind.EqOp,true,true,false),// BabelFishLEqual("<><=",OpKind.EqOp, true,false,false),// BabelFishREqual("><>=",OpKind.EqOp, true,false,false);// public final String inner; public final OpKind kind; public final boolean normalized;// false for <<,<, <=,<<=,><> etc public final boolean leftAssociative;// false for ++ << >> ** == public final boolean negated;//true for all starting with ! Op(String inner, OpKind kind, boolean normalized, boolean leftAssociative, boolean negated) { this.inner = inner; this.kind = kind; this.normalized = normalized; this.leftAssociative = leftAssociative; this.negated=negated; } public static Op fromString(String s) { for (Op op : Op.values()) { if (op.inner.equals(s)) return op; } throw tools.Assertions.codeNotReachable(); } public Op nonNegatedVersion(){ if (!this.negated){return this;} String ops=this.inner.substring(1); if(ops.equals("=")){ops="==";} return Op.fromString(ops); } public Op normalizedVersion(){ if(this.normalized){return this;} String ops=inner.replace('<',' ').replace('>','<').replace(' ','>'); return Op.fromString(ops); } } public static enum Stage { None(""), Less("##less"), ToIterate("##toIterateTemp"), // Meta("##meta"), Plus("##plus"), Star("##star"); // Needed("##needed"), // Needable("##needable"); public final String inner; Stage(String inner) { this.inner = inner; } public static Stage fromString(String s) { for (Stage st : Stage.values()) { if (st.inner.equals(s)) return st; } throw tools.Assertions.codeNotReachable(); } } /*public static enum Ph { None, Ph, Partial }*/ public static @Wither @Value class Position { public static final Position noInfo = new Position(null, Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2, 0, 0,null); public Position sum(Position that){ if (this==noInfo){return that;} if (that==noInfo || that==null){return this;} if(this._next==null){return this.with_next(that);} return this.with_next(this._next.sum(that)); } String file; int line1; int pos1; int line2; int pos2; Position _next; public String toString() { int line1 = this.line1 - 1; int line2 = this.line2 - 1; String res = ""; if (line1 == line2) { res = "line:" + line1 + ", pos:" + pos1 + "--" + pos2; } else { res = "from line:" + line1 + "(pos:" + pos1 + ") to line:" + line2 + "(pos:" + pos2 + ")"; } if (file == null) { return "fileUnknown; " + res; } String fileName = null; // if(file!=null){fileName="..."+file.substring(file.lastIndexOf("\\"));} int pos = file.lastIndexOf("\\"); if (pos != -1) { pos = file.substring(0, pos).lastIndexOf("\\"); } fileName = "..." + ((pos == -1) ? file : file.substring(pos)); return "file: " + fileName + "; " + res; } } public static interface HasPos { Position getP(); } public static interface HasReceiver extends HasPos,Expression{ Expression getReceiver(); Expression withReceiver(Expression receiver); } }