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