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