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