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