]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TranslationContext.java
migrated to svn revision 33108
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / contexts / TranslationContext.java
1 package org.simantics.scl.compiler.elaboration.contexts;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5
6 import org.simantics.scl.compiler.common.names.Name;
7 import org.simantics.scl.compiler.common.precedence.Associativity;
8 import org.simantics.scl.compiler.common.precedence.Precedence;
9 import org.simantics.scl.compiler.elaboration.expressions.Case;
10 import org.simantics.scl.compiler.elaboration.expressions.EAmbiguous;
11 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
12 import org.simantics.scl.compiler.elaboration.expressions.EEntityTypeAnnotation;
13 import org.simantics.scl.compiler.elaboration.expressions.EError;
14 import org.simantics.scl.compiler.elaboration.expressions.EFieldAccess;
15 import org.simantics.scl.compiler.elaboration.expressions.ELambda;
16 import org.simantics.scl.compiler.elaboration.expressions.EVar;
17 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
18 import org.simantics.scl.compiler.elaboration.expressions.Expression;
19 import org.simantics.scl.compiler.elaboration.expressions.Variable;
20 import org.simantics.scl.compiler.elaboration.expressions.accessor.FieldAccessor;
21 import org.simantics.scl.compiler.elaboration.expressions.accessor.IdAccessor;
22 import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement;
23 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
24 import org.simantics.scl.compiler.elaboration.query.pre.PreQuery;
25 import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
26 import org.simantics.scl.compiler.environment.AmbiguousNameException;
27 import org.simantics.scl.compiler.environment.Environment;
28 import org.simantics.scl.compiler.environment.Environments;
29 import org.simantics.scl.compiler.environment.LocalEnvironment;
30 import org.simantics.scl.compiler.environment.Namespace;
31 import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter;
32 import org.simantics.scl.compiler.errors.ErrorLog;
33 import org.simantics.scl.compiler.errors.Locations;
34 import org.simantics.scl.compiler.internal.parsing.declarations.DValueAst;
35 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
36 import org.simantics.scl.compiler.types.Type;
37
38 import gnu.trove.list.array.TIntArrayList;
39 import gnu.trove.map.hash.THashMap;
40 import gnu.trove.procedure.TObjectProcedure;
41 import gnu.trove.set.hash.THashSet;
42
43 public class TranslationContext extends TypeTranslationContext implements EnvironmentalContext {
44
45     THashMap<String, Variable> variables = new THashMap<String, Variable>();
46     ArrayList<Entry> variableEntries = new ArrayList<Entry>();
47     LocalEnvironment localEnvironment;
48     TIntArrayList frames = new TIntArrayList();
49     ArrayList<THashSet<String>> frameNameSets = new ArrayList<THashSet<String>>(); 
50     ArrayList<THashSet<String>> existentialFrames = new ArrayList<THashSet<String>>();
51     ArrayList<ArrayList<Variable>> blanksInExistentialFrame = new ArrayList<ArrayList<Variable>>();
52     SCLValue bindFunction;
53     
54     public EEntityTypeAnnotation currentEntityTypeAnnotation;
55     public PreQuery currentPreQuery;
56     
57     THashMap<String, SCLRelation> relations = new THashMap<String, SCLRelation>();
58     TIntArrayList relationFrames = new TIntArrayList();
59     ArrayList<RelationEntry> relationEntries = new ArrayList<RelationEntry>();
60     
61     static class Entry {
62         String name;
63         Variable variable;
64         public Entry(String name, Variable variable) {
65             this.name = name;
66             this.variable = variable;
67         }
68     }
69     
70     static class RelationEntry {
71         String name;
72         SCLRelation relation;
73         public RelationEntry(String name, SCLRelation relation) {
74             this.name = name;
75             this.relation = relation;
76         }
77     }
78     
79     public TranslationContext(ErrorLog errorLog, 
80             Environment environment, LocalEnvironment localEnvironment) {
81         super(errorLog, environment);
82         this.localEnvironment = localEnvironment;
83     }
84     
85     public static boolean isConstructorName(String name) {
86         char firstChar = name.charAt(0);
87         return Character.isUpperCase(firstChar);
88     }
89     
90     /* Tries to resolve name as a local variable. It is assumed
91      * that name does not contain '.'.
92      */
93     private Expression resolveLocalVariable(long location, String name) {
94         Variable variable = variables.get(name);
95         if(variable != null)
96             return new EVariable(location, variable);
97         
98         char c = name.charAt(0);
99         switch(c) {
100         case '?':
101             if(existentialFrames.isEmpty()) {
102                 errorLog.log(location, "Existential variables can be used only in queries.");
103                 return new EError(location);
104             }
105             variable = new Variable(name);
106             variables.put(name, variable);
107             existentialFrames.get(existentialFrames.size()-1).add(name);
108             return new EVariable(variable);
109         case '_':
110             if(name.length()==1) {
111                 variable = new Variable("_");
112                 if(blanksInExistentialFrame.isEmpty()) {
113                     errorLog.log(location, "Cannot use blank variables in this context.");
114                     return new EError(location);
115                 }
116                 blanksInExistentialFrame.get(blanksInExistentialFrame.size()-1).add(variable);
117                 return new EVariable(variable);
118             }
119             break;
120         case '#':
121             if(name.length() > 1 && Character.isLetter(name.charAt(1))) {
122                 if(currentEntityTypeAnnotation == null) {
123                     errorLog.log(location, "Attribute references cannot be made in this context.");
124                     return new EError(location);
125                 }
126                 return currentEntityTypeAnnotation.resolveAttribute(this, location, name.substring(1));
127             }
128             break;
129         }
130         return null;
131     }
132     
133     private FieldAccessor createFieldAccessor(char accessSeparator, String name) {
134         IdAccessor accessor = new IdAccessor(name);
135         accessor.accessSeparator = accessSeparator;
136         return accessor;
137     }
138     
139     private Expression resolveFieldAccess(Expression base, int pos, String name) {
140         ArrayList<FieldAccessor> accessors = new ArrayList<FieldAccessor>(2);
141         while(pos != -1) {
142             int p = findSeparator(name, pos+1);
143             accessors.add(createFieldAccessor(
144                     name.charAt(pos),
145                     name.substring(pos+1, p==-1 ? name.length() : p-1)));
146             pos = p;
147         }
148         return new EFieldAccess(base,
149                 accessors.toArray(new FieldAccessor[accessors.size()]));
150     }
151     
152     private Expression resolveIn(long location, Namespace namespace, String name) {
153         SCLValue value;
154         try {
155             value = resolveValueIn(location, namespace, name);
156         } catch (AmbiguousNameException e) {
157             if(SCLCompilerConfiguration.ALLOW_OVERLOADING) {
158                 EAmbiguous.Alternative[] alternatives = new EAmbiguous.Alternative[e.conflictingModules.length];
159                 //System.out.println("Overloading:");
160                 for(int i=0;i<e.conflictingModules.length;++i) {
161                     Name altName = Name.create(e.conflictingModules[i], e.name);
162                     //System.out.println("    " + altName);
163                     SCLValue altValue = environment.getValue(altName);
164                     alternatives[i] = new EAmbiguous.Alternative() {
165                         @Override
166                         public Type getType() {
167                             return altValue.getType();
168                         }
169     
170                         @Override
171                         public Expression realize() {
172                             EConstant expression = new EConstant(altValue);
173                             expression.location = location;
174                             return expression;
175                         }
176                         
177                         @Override
178                         public String toString() {
179                             return altValue.getName().toString().replace('/', '.');
180                         }
181                     };
182                 }
183                 EAmbiguous expression = new EAmbiguous(alternatives);
184                 expression.location = location;
185                 return expression;
186             }
187             else {
188                 errorLog.log(location, e.getMessage());
189                 value = null;
190             }
191         }
192         if(value == null)
193             return new EError(location);
194         return new EConstant(location, value);
195     }
196     
197     private Expression resolveComplexNameIn(long location, Namespace namespace, int startPos, String name) {
198         int pos = name.length();
199         {
200             int hashPos = name.lastIndexOf('#');
201             if(hashPos >= 0)
202                 pos = hashPos;
203         }
204         while(pos > startPos) {
205             SCLValue value;
206             try {
207                 value = namespace.getValue(name.substring(startPos, pos));
208             } catch (AmbiguousNameException e) {
209                 errorLog.log(location, e.getMessage());
210                 return new EError(location);
211             }
212             if(value != null) {
213                 Expression result = new EConstant(location, value);
214                 if(pos < name.length())
215                     result = resolveFieldAccess(result, pos, name);
216                 return result;
217             }
218             pos = name.lastIndexOf('.', pos-1);
219         }
220         errorLog.log(location, "Couldn't resolve variable " + name + ".");
221         return new EError(location);
222     }
223     
224     private static int findSeparator(String name, int fromIndex) {
225         while(fromIndex < name.length()) {
226             char c = name.charAt(fromIndex);
227             if(c == '.' || c == '#')
228                 return fromIndex;
229             ++fromIndex;
230         }
231         return -1;
232     }
233     
234     public Expression resolveExpression(long location, String name) {
235         int p = findSeparator(name, 1 /* Initial # is not a separator */);
236         if(p == -1) {
237             Expression result = resolveLocalVariable(location, name);
238             if(result != null)
239                 return result;
240             
241             if(localEnvironment != null) {
242                 result = localEnvironment.resolve(environment, name);
243                 if(result != null) {
244                     result.setLocationDeep(location);
245                     return result;
246                 }
247             }
248             
249             return resolveIn(location, environment.getLocalNamespace(), name);
250         }
251         else {
252             if(localEnvironment != null) {
253                 Expression result = localEnvironment.resolve(environment, name);
254                 if(result != null) {
255                     result.setLocationDeep(location);
256                     return result;
257                 }
258             }
259             
260             String prefix = name.substring(0, p);
261             Expression result = resolveLocalVariable(location, prefix);
262             if(result != null)
263                 return resolveFieldAccess(result, p, name);
264             
265             Namespace namespace = environment.getLocalNamespace();
266             int pos = 0;
267             while(name.charAt(p)=='.') {
268                 Namespace temp = namespace.getNamespace(prefix);
269                 if(temp == null)
270                     break;
271                 namespace = temp;
272                 pos = p+1;
273                 p = findSeparator(name, pos);
274                 if(p < 0)
275                     return resolveIn(location, namespace, name.substring(pos));
276                 prefix = name.substring(pos, p);
277             }
278             
279             return resolveComplexNameIn(location, namespace, pos, name);
280         }
281     }
282     
283     public Expression resolvePattern(EVar name) {
284         char firstChar = name.name.charAt(0);
285         if(firstChar == '_' && name.name.length()==1) {
286             return new EVariable(new Variable("_"));
287         }
288         else if(!Character.isUpperCase(firstChar)) {
289             if(!frameNameSets.get(frameNameSets.size()-1).add(name.name))
290                 errorLog.log(name.location, "Repeated variable "+name.name+" in pattern.");
291             return new EVariable(name.location, newVariable(name.name));
292         }
293         else 
294             return resolveExpression(name.location, name.name);
295     }
296
297     /**
298      * Starts a new environment frame. New variables defined in this frame shadow
299      * the old variables and when the frame is popped, the old variables are again
300      * visible.
301      */
302     public void pushFrame() {
303         frames.add(variableEntries.size());
304         frameNameSets.add(new THashSet<String>());
305     }
306     
307     /**
308      * Ends an environment frame. See {@link #pushFrame}.
309      */
310     public void popFrame() {
311         int frame = frames.removeAt(frames.size()-1);
312         int i = variableEntries.size();
313         while(i > frame) {
314             --i;
315             Entry entry = variableEntries.remove(i);
316             if(entry.variable == null)
317                 variables.remove(entry.name);
318             else
319                 variables.put(entry.name, entry.variable);
320         }
321         frameNameSets.remove(frameNameSets.size()-1);
322     }
323
324     public void pushRelationFrame() {
325         relationFrames.add(relationEntries.size());
326     }
327     
328     public void popRelationFrame() {
329         int frame = relationFrames.removeAt(relationFrames.size()-1);
330         int i = relationEntries.size();
331         while(i > frame) {
332             --i;
333             RelationEntry entry = relationEntries.remove(i);
334             if(entry.relation == null)
335                 relations.remove(entry.name);
336             else
337                 relations.put(entry.name, entry.relation);
338         }
339     }
340     
341     public void pushExistentialFrame() {
342         pushFrame();
343         existentialFrames.add(new THashSet<String>());
344         blanksInExistentialFrame.add(new ArrayList<Variable>(2));
345     }
346     
347     public Variable[] popExistentialFrame() {
348         popFrame();
349         THashSet<String> set = existentialFrames.remove(existentialFrames.size()-1);
350         ArrayList<Variable> blanks = blanksInExistentialFrame.remove(blanksInExistentialFrame.size()-1);
351         Variable[] result = new Variable[set.size() + blanks.size()];
352         int i=0;
353         for(String name : set)
354             result[i++] = variables.remove(name);
355         for(Variable blank : blanks)
356             result[i++] = blank;
357         return result;
358     }
359     
360     public Variable newVariable(String name) {
361         Variable variable = new Variable(name);
362         Variable oldVariable = variables.put(name, variable);
363         variableEntries.add(new Entry(name, oldVariable));
364         return variable;
365     }
366     
367     public THashMap<String, Variable> getVariables() {
368         return variables;
369     }
370     
371     public void newRelation(String name, SCLRelation relation) {
372         SCLRelation oldRelation = relations.put(name, relation);
373         relationEntries.add(new RelationEntry(name, oldRelation));
374     }
375             
376     public Precedence getPrecedence(Name op) {
377         Precedence prec = environment.getValue(op).getPrecedence();
378         if(prec == null)
379             return new Precedence(1, Associativity.NONASSOC);
380         else
381             return prec;
382     }
383
384     private SCLValue resolveValueIn(long location, Namespace namespace, final String name) throws AmbiguousNameException {
385             SCLValue value = namespace.getValue(name);
386             if(value == null) {
387                 StringBuilder message = new StringBuilder();
388                 message.append("Couldn't resolve variable ").append(name).append(".");
389                 
390                 final THashSet<String> candidateNames = new THashSet<String>(4);
391                 namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE,
392                         new TObjectProcedure<SCLValue>() {
393                             @Override
394                             public boolean execute(SCLValue value) {
395                                 if(value == null) {
396                                     new Exception().printStackTrace();
397                                     return true;
398                                 }
399                                 String valueName = value.getName().name;
400                                 if(name.equalsIgnoreCase(valueName))
401                                     candidateNames.add(valueName);
402                                 return true;
403                             }
404                         });
405                 if(localEnvironment != null)
406                     localEnvironment.forNames(new TObjectProcedure<String>() {
407                         @Override
408                         public boolean execute(String valueName) {
409                             if(name.equalsIgnoreCase(valueName))
410                                 candidateNames.add(valueName);
411                             return true;
412                         }
413                     });
414                     
415                 if(candidateNames.size() > 0) {
416                     message.append(" Did you mean ");
417                     String[] ns = candidateNames.toArray(new String[candidateNames.size()]);
418                     Arrays.sort(ns);
419                     for(int i=0;i<ns.length;++i) {
420                         if(i > 0) {
421                             message.append(", ");
422                             if(i == ns.length-1)
423                                 message.append("or ");
424                         }
425                         message.append(ns[i]);
426                     }
427                     message.append('?');
428                 }
429                 
430                 errorLog.log(location, message.toString());
431                 return null;
432             }
433             return value;
434     }
435     
436     public Case translateCase(Expression lhs, Expression rhs) {        
437         ArrayList<Expression> parameters = new ArrayList<Expression>(4);  
438         lhs.getParameters(this, parameters);
439         Expression[] patterns = new Expression[parameters.size()];
440         pushFrame();
441         for(int i=0;i<patterns.length;++i) {
442             Expression pattern = parameters.get(i);
443             pattern = pattern.resolveAsPattern(this);
444             patterns[i] = pattern;
445         }
446         rhs = rhs.resolve(this);
447         popFrame();
448         Case case_ = new Case(patterns, rhs);
449         case_.setLhs(lhs.location);
450         return case_;
451     }
452     
453     public Expression translateCases2(ArrayList<DValueAst> definitions) {
454         Case[] cases = new Case[definitions.size()];
455         for(int i=0;i<cases.length;++i) {
456             DValueAst def = definitions.get(i);
457             cases[i] = translateCase(def.lhs, def.value);
458         }
459         // check arity consistency
460         int arity = cases[0].patterns.length;
461         for(int i=1;i<cases.length;++i)
462             if(cases[i].patterns.length != arity)
463                 errorLog.log(definitions.get(i).lhs.location, 
464                         "Inconsistent arity. " + 
465                         "This case has arity " + cases[i].patterns.length + 
466                                 " while previous cases had arity " + arity + ".");
467         if(cases.length == 1 && cases[0].patterns.length == 0)
468             return cases[0].value;
469         else
470             return new ELambda(
471                     Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
472                     cases);
473     }
474     
475     public Expression translateCases(ArrayList<LetStatement> definitions) {
476         Case[] cases = new Case[definitions.size()];
477         for(int i=0;i<cases.length;++i) {
478             LetStatement def = definitions.get(i);
479             cases[i] = translateCase(def.pattern, def.value);
480         }
481         // check arity concistency
482         int arity = cases[0].patterns.length;
483         for(int i=1;i<cases.length;++i)
484             if(cases[i].patterns.length != arity)
485                 errorLog.log(definitions.get(i).pattern.location, 
486                         "Inconsistent arity. " + 
487                         "This case has arity " + cases[i].patterns.length + 
488                         " while previous cases had arity " + arity + ".");
489         if(arity == 0) {
490             if(cases.length > 1)
491                 errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function.");
492             return cases[0].value;
493         }
494         return new ELambda(
495                 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
496                 cases);
497     }
498     
499     private static final Name BIND = Name.create("Prelude", ">>=");
500     
501     public SCLValue getBindFunction() {
502         if(bindFunction == null) {
503             bindFunction = getEnvironment().getValue(BIND);
504         }
505         return bindFunction;
506     }
507
508     public SCLRelation resolveRelation(long location, String name) {
509         SCLRelation relation = relations.get(name);
510         if(relation != null)
511             return relation;
512         
513         try {
514             relation = Environments.getRelation(environment, name);
515             /*if(relation == null) {
516                 errorLog.log(location, "Couldn't resolve relation " + name + ".");
517                 return null;
518             }*/
519             return relation;
520         } catch (AmbiguousNameException e) {
521             errorLog.log(location, e.getMessage());
522             return null;
523         }
524     }
525
526     @Override
527     public SCLValue getValue(Name name) {
528         return environment.getValue(name);
529     }
530 }