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