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