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