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