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