d52cc3f2ef4c3d493170fb3205c7e5238ea55b1c
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / expressions / EAmbiguous.java
1 package org.simantics.scl.compiler.elaboration.expressions;
2
3 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
4 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
5 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
6 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
7 import org.simantics.scl.compiler.errors.ErrorLog;
8 import org.simantics.scl.compiler.errors.Locations;
9 import org.simantics.scl.compiler.types.Skeletons;
10 import org.simantics.scl.compiler.types.TMetaVar;
11 import org.simantics.scl.compiler.types.Type;
12 import org.simantics.scl.compiler.types.Types;
13 import org.simantics.scl.compiler.types.exceptions.MatchException;
14 import org.simantics.scl.compiler.types.exceptions.UnificationException;
15 import org.simantics.scl.compiler.types.util.TypeListener;
16 import org.simantics.scl.compiler.types.util.TypeUnparsingContext;
17
18 import gnu.trove.map.hash.THashMap;
19 import gnu.trove.map.hash.TObjectIntHashMap;
20 import gnu.trove.set.hash.THashSet;
21 import gnu.trove.set.hash.TIntHashSet;
22
23 public class EAmbiguous extends SimplifiableExpression {
24     public static final boolean DEBUG = false;
25     
26     Alternative[] alternatives;
27     boolean[] active;
28     int activeCount;
29     transient TypingContext context;
30     Expression resolvedExpression;
31     
32     public abstract static class Alternative {
33         public abstract Type getType();
34         public abstract Expression realize();
35     }
36     
37     public EAmbiguous(Alternative[] alternatives) {
38         this.alternatives = alternatives;
39         this.active = new boolean[alternatives.length];
40         for(int i=0;i<alternatives.length;++i)
41             this.active[i] = true;
42         this.activeCount = alternatives.length;
43     }
44
45     @Override
46     public void collectVars(TObjectIntHashMap<Variable> allVars,
47             TIntHashSet vars) {
48     }
49
50     @Override
51     protected void updateType() throws MatchException {
52         throw new InternalCompilerError();
53     }
54     
55     private Type getCommonSkeleton() {
56         Type[] types = new Type[activeCount];
57         for(int i=0,j=0;i<alternatives.length;++i)
58             if(active[i])
59                 types[j++] = Types.instantiateAndStrip(alternatives[i].getType());
60         return Skeletons.commonSkeleton(context.getEnvironment(), types);
61     }
62
63     private void filterActive() {
64         THashMap<TMetaVar,Type> unifications = new THashMap<TMetaVar,Type>(); 
65         Type requiredType = getType();
66         if(DEBUG)
67             System.out.println("EAmbigious.filterActive with " + requiredType);
68         for(int i=0;i<alternatives.length;++i)
69             if(active[i]) {
70                 unifications.clear();
71                 Type alternativeType = Types.instantiateAndStrip(alternatives[i].getType());
72                 if(DEBUG)
73                     System.out.println("    " + alternativeType);
74                 if(!Skeletons.areSkeletonsCompatible(unifications, alternativeType, requiredType)) {
75                     active[i] = false;
76                     --activeCount;
77                 }
78             }
79         if(DEBUG)
80             System.out.println("    activeCount = " + activeCount);
81     }
82
83     private String getNoMatchDescription(Type requiredType) {
84         StringBuilder b = new StringBuilder();
85         b.append("Expected <");
86         requiredType.toString(new TypeUnparsingContext(), b);
87         b.append(">, but no alteratives match the type: ");
88         for(int i=0;i<alternatives.length;++i) {
89             b.append("\n    ");
90             b.append(alternatives[i]);
91             b.append(" :: ");
92             alternatives[i].getType().toString(new TypeUnparsingContext(), b);
93         }
94         b.append('.');
95         return b.toString();
96     }
97     
98     private String getAmbiguousDescription(Type requiredType) {
99         StringBuilder b = new StringBuilder();
100         b.append("Expected <");
101         requiredType.toString(new TypeUnparsingContext(), b);
102         b.append(">, but multiple values match the type: ");
103         for(int i=0;i<alternatives.length;++i) {
104             b.append("\n    ");
105             b.append(alternatives[i]);
106             b.append(" :: ");
107             alternatives[i].getType().toString(new TypeUnparsingContext(), b);
108         }
109         b.append('.');
110         return b.toString();
111     }
112
113
114     private void resolveTo(int i) {
115         if(DEBUG)
116             System.out.println("EAmbigious.resolve to " + alternatives[i]);
117         resolvedExpression = context.instantiate(alternatives[i].realize());
118         Type requiredType = getType();
119         try {
120             Types.unify(resolvedExpression.getType(), requiredType);
121         } catch (UnificationException e) {
122             context.getErrorLog().log(location, getNoMatchDescription(requiredType));
123         }
124     }
125     
126     private void listenType() {
127         if(DEBUG)
128             System.out.println("EAmbigious.listenType " + getType());
129         new TypeListener() {
130             @Override
131             public void notifyAboutChange() {
132                 if(DEBUG)
133                     System.out.println("EAmbigious.notifyAboutChange " + getType());
134                 Type requiredType = getType();
135                 filterActive();
136                 if(activeCount == 0) {
137                     context.getErrorLog().log(location, getNoMatchDescription(requiredType));
138                     return;
139                 }   
140                 else if(activeCount == 1) {
141                     for(int i=0;i<alternatives.length;++i)
142                         if(active[i]) {
143                             resolveTo(i);
144                             return;
145                         }
146                 }
147                 Type commonType = getCommonSkeleton();
148                 try {
149                     Skeletons.unifySkeletons(requiredType, commonType);
150                     listenType();
151                 } catch (UnificationException e) {
152                     context.getErrorLog().log(location, getNoMatchDescription(requiredType));
153                 }
154             }
155         }.listenSkeleton(getType());
156     }
157     
158     @Override
159     public Expression inferType(TypingContext context) {
160         this.context = context;
161         context.overloadedExpressions.add(this);
162         setType(getCommonSkeleton());
163         listenType();
164         return this;
165     }
166     
167     @Override
168     public void collectFreeVariables(THashSet<Variable> vars) {
169     }
170
171     @Override
172     public Expression resolve(TranslationContext context) {
173         throw new InternalCompilerError("EAmbiguousConstant should not exist in resolve phase.");
174     }
175
176     @Override
177     public void setLocationDeep(long loc) {
178         if(location == Locations.NO_LOCATION)
179             location = loc;
180     }
181
182     @Override
183     public void accept(ExpressionVisitor visitor) {
184         visitor.visit(this);
185     }
186     
187     @Override
188     public Expression simplify(SimplificationContext context) {
189         if(resolvedExpression != null)
190             return resolvedExpression;
191         else {
192             context.getErrorLog().log(location, getAmbiguousDescription(getType()));
193             return this;
194         }
195     }
196     
197     public void assertResolved(ErrorLog errorLog) {
198         if(resolvedExpression == null)
199             errorLog.log(location, getAmbiguousDescription(getType()));
200     }
201     
202     @Override
203     public Expression accept(ExpressionTransformer transformer) {
204         return transformer.transform(this);
205     }
206
207 }