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