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