1 package org.simantics.scl.compiler.elaboration.contexts;
3 import java.util.ArrayList;
4 import java.util.Arrays;
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.elaboration.expressions.Case;
10 import org.simantics.scl.compiler.elaboration.expressions.EAmbiguous;
11 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
12 import org.simantics.scl.compiler.elaboration.expressions.EEntityTypeAnnotation;
13 import org.simantics.scl.compiler.elaboration.expressions.EError;
14 import org.simantics.scl.compiler.elaboration.expressions.EFieldAccess;
15 import org.simantics.scl.compiler.elaboration.expressions.ELambda;
16 import org.simantics.scl.compiler.elaboration.expressions.EVar;
17 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
18 import org.simantics.scl.compiler.elaboration.expressions.Expression;
19 import org.simantics.scl.compiler.elaboration.expressions.Variable;
20 import org.simantics.scl.compiler.elaboration.expressions.accessor.FieldAccessor;
21 import org.simantics.scl.compiler.elaboration.expressions.accessor.IdAccessor;
22 import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement;
23 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
24 import org.simantics.scl.compiler.elaboration.query.pre.PreQuery;
25 import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
26 import org.simantics.scl.compiler.environment.AmbiguousNameException;
27 import org.simantics.scl.compiler.environment.Environment;
28 import org.simantics.scl.compiler.environment.Environments;
29 import org.simantics.scl.compiler.environment.LocalEnvironment;
30 import org.simantics.scl.compiler.environment.Namespace;
31 import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter;
32 import org.simantics.scl.compiler.errors.ErrorLog;
33 import org.simantics.scl.compiler.errors.Locations;
34 import org.simantics.scl.compiler.internal.parsing.declarations.DValueAst;
35 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
36 import org.simantics.scl.compiler.types.Type;
38 import gnu.trove.list.array.TIntArrayList;
39 import gnu.trove.map.hash.THashMap;
40 import gnu.trove.procedure.TObjectProcedure;
41 import gnu.trove.set.hash.THashSet;
43 public class TranslationContext extends TypeTranslationContext implements EnvironmentalContext {
45 THashMap<String, Variable> variables = new THashMap<String, Variable>();
46 ArrayList<Entry> variableEntries = new ArrayList<Entry>();
47 LocalEnvironment localEnvironment;
48 TIntArrayList frames = new TIntArrayList();
49 ArrayList<THashSet<String>> frameNameSets = new ArrayList<THashSet<String>>();
50 ArrayList<THashSet<String>> existentialFrames = new ArrayList<THashSet<String>>();
51 ArrayList<ArrayList<Variable>> blanksInExistentialFrame = new ArrayList<ArrayList<Variable>>();
52 SCLValue bindFunction;
54 public EEntityTypeAnnotation currentEntityTypeAnnotation;
55 public PreQuery currentPreQuery;
57 THashMap<String, SCLRelation> relations = new THashMap<String, SCLRelation>();
58 TIntArrayList relationFrames = new TIntArrayList();
59 ArrayList<RelationEntry> relationEntries = new ArrayList<RelationEntry>();
64 public Entry(String name, Variable variable) {
66 this.variable = variable;
70 static class RelationEntry {
73 public RelationEntry(String name, SCLRelation relation) {
75 this.relation = relation;
79 public TranslationContext(ErrorLog errorLog,
80 Environment environment, LocalEnvironment localEnvironment) {
81 super(errorLog, environment);
82 this.localEnvironment = localEnvironment;
85 public static boolean isConstructorName(String name) {
86 char firstChar = name.charAt(0);
87 return Character.isUpperCase(firstChar);
90 /* Tries to resolve name as a local variable. It is assumed
91 * that name does not contain '.'.
93 private Expression resolveLocalVariable(long location, String name) {
94 Variable variable = variables.get(name);
96 return new EVariable(location, variable);
98 char c = name.charAt(0);
101 if(existentialFrames.isEmpty()) {
102 errorLog.log(location, "Existential variables can be used only in queries.");
103 return new EError(location);
105 variable = new Variable(name);
106 variables.put(name, variable);
107 existentialFrames.get(existentialFrames.size()-1).add(name);
108 return new EVariable(variable);
110 if(name.length()==1) {
111 variable = new Variable("_");
112 if(blanksInExistentialFrame.isEmpty()) {
113 errorLog.log(location, "Cannot use blank variables in this context.");
114 return new EError(location);
116 blanksInExistentialFrame.get(blanksInExistentialFrame.size()-1).add(variable);
117 return new EVariable(variable);
121 if(name.length() > 1 && Character.isLetter(name.charAt(1))) {
122 if(currentEntityTypeAnnotation == null) {
123 errorLog.log(location, "Attribute references cannot be made in this context.");
124 return new EError(location);
126 return currentEntityTypeAnnotation.resolveAttribute(this, location, name.substring(1));
133 private FieldAccessor createFieldAccessor(char accessSeparator, String name) {
134 IdAccessor accessor = new IdAccessor(name);
135 accessor.accessSeparator = accessSeparator;
139 private Expression resolveFieldAccess(Expression base, int pos, String name) {
140 ArrayList<FieldAccessor> accessors = new ArrayList<FieldAccessor>(2);
142 int p = findSeparator(name, pos+1);
143 accessors.add(createFieldAccessor(
145 name.substring(pos+1, p==-1 ? name.length() : p-1)));
148 return new EFieldAccess(base,
149 accessors.toArray(new FieldAccessor[accessors.size()]));
152 private Expression resolveIn(long location, Namespace namespace, String name) {
155 value = resolveValueIn(location, namespace, name);
156 } catch (AmbiguousNameException e) {
157 if(SCLCompilerConfiguration.ALLOW_OVERLOADING) {
158 EAmbiguous.Alternative[] alternatives = new EAmbiguous.Alternative[e.conflictingModules.length];
159 //System.out.println("Overloading:");
160 for(int i=0;i<e.conflictingModules.length;++i) {
161 Name altName = Name.create(e.conflictingModules[i], e.name);
162 //System.out.println(" " + altName);
163 SCLValue altValue = environment.getValue(altName);
164 alternatives[i] = new EAmbiguous.Alternative() {
166 public Type getType() {
167 return altValue.getType();
171 public Expression realize() {
172 EConstant expression = new EConstant(altValue);
173 expression.location = location;
178 public String toString() {
179 return altValue.getName().toString().replace('/', '.');
183 EAmbiguous expression = new EAmbiguous(alternatives);
184 expression.location = location;
188 errorLog.log(location, e.getMessage());
193 return new EError(location);
194 return new EConstant(location, value);
197 private Expression resolveComplexNameIn(long location, Namespace namespace, int startPos, String name) {
198 int pos = name.length();
200 int hashPos = name.lastIndexOf('#');
204 while(pos > startPos) {
207 value = namespace.getValue(name.substring(startPos, pos));
208 } catch (AmbiguousNameException e) {
209 errorLog.log(location, e.getMessage());
210 return new EError(location);
213 Expression result = new EConstant(location, value);
214 if(pos < name.length())
215 result = resolveFieldAccess(result, pos, name);
218 pos = name.lastIndexOf('.', pos-1);
220 errorLog.log(location, "Couldn't resolve variable " + name + ".");
221 return new EError(location);
224 private static int findSeparator(String name, int fromIndex) {
225 while(fromIndex < name.length()) {
226 char c = name.charAt(fromIndex);
227 if(c == '.' || c == '#')
234 public Expression resolveExpression(long location, String name) {
235 int p = findSeparator(name, 1 /* Initial # is not a separator */);
237 Expression result = resolveLocalVariable(location, name);
241 if(localEnvironment != null) {
242 result = localEnvironment.resolve(environment, name);
244 result.setLocationDeep(location);
249 return resolveIn(location, environment.getLocalNamespace(), name);
252 if(localEnvironment != null) {
253 Expression result = localEnvironment.resolve(environment, name);
255 result.setLocationDeep(location);
260 String prefix = name.substring(0, p);
261 Expression result = resolveLocalVariable(location, prefix);
263 return resolveFieldAccess(result, p, name);
265 Namespace namespace = environment.getLocalNamespace();
267 while(name.charAt(p)=='.') {
268 Namespace temp = namespace.getNamespace(prefix);
273 p = findSeparator(name, pos);
275 return resolveIn(location, namespace, name.substring(pos));
276 prefix = name.substring(pos, p);
279 return resolveComplexNameIn(location, namespace, pos, name);
283 public Expression resolvePattern(EVar name) {
284 char firstChar = name.name.charAt(0);
285 if(firstChar == '_' && name.name.length()==1) {
286 return new EVariable(new Variable("_"));
288 else if(!Character.isUpperCase(firstChar)) {
289 if(!frameNameSets.get(frameNameSets.size()-1).add(name.name))
290 errorLog.log(name.location, "Repeated variable "+name.name+" in pattern.");
291 return new EVariable(name.location, newVariable(name.name));
294 return resolveExpression(name.location, name.name);
298 * Starts a new environment frame. New variables defined in this frame shadow
299 * the old variables and when the frame is popped, the old variables are again
302 public void pushFrame() {
303 frames.add(variableEntries.size());
304 frameNameSets.add(new THashSet<String>());
308 * Ends an environment frame. See {@link #pushFrame}.
310 public void popFrame() {
311 int frame = frames.removeAt(frames.size()-1);
312 int i = variableEntries.size();
315 Entry entry = variableEntries.remove(i);
316 if(entry.variable == null)
317 variables.remove(entry.name);
319 variables.put(entry.name, entry.variable);
321 frameNameSets.remove(frameNameSets.size()-1);
324 public void pushRelationFrame() {
325 relationFrames.add(relationEntries.size());
328 public void popRelationFrame() {
329 int frame = relationFrames.removeAt(relationFrames.size()-1);
330 int i = relationEntries.size();
333 RelationEntry entry = relationEntries.remove(i);
334 if(entry.relation == null)
335 relations.remove(entry.name);
337 relations.put(entry.name, entry.relation);
341 public void pushExistentialFrame() {
343 existentialFrames.add(new THashSet<String>());
344 blanksInExistentialFrame.add(new ArrayList<Variable>(2));
347 public Variable[] popExistentialFrame() {
349 THashSet<String> set = existentialFrames.remove(existentialFrames.size()-1);
350 ArrayList<Variable> blanks = blanksInExistentialFrame.remove(blanksInExistentialFrame.size()-1);
351 Variable[] result = new Variable[set.size() + blanks.size()];
353 for(String name : set)
354 result[i++] = variables.remove(name);
355 for(Variable blank : blanks)
360 public Variable newVariable(String name) {
361 Variable variable = new Variable(name);
362 Variable oldVariable = variables.put(name, variable);
363 variableEntries.add(new Entry(name, oldVariable));
367 public THashMap<String, Variable> getVariables() {
371 public void newRelation(String name, SCLRelation relation) {
372 SCLRelation oldRelation = relations.put(name, relation);
373 relationEntries.add(new RelationEntry(name, oldRelation));
376 public Precedence getPrecedence(Name op) {
377 Precedence prec = environment.getValue(op).getPrecedence();
379 return new Precedence(1, Associativity.NONASSOC);
384 private SCLValue resolveValueIn(long location, Namespace namespace, final String name) throws AmbiguousNameException {
385 SCLValue value = namespace.getValue(name);
387 StringBuilder message = new StringBuilder();
388 message.append("Couldn't resolve variable ").append(name).append(".");
390 final THashSet<String> candidateNames = new THashSet<String>(4);
391 namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE,
392 new TObjectProcedure<SCLValue>() {
394 public boolean execute(SCLValue value) {
396 new Exception().printStackTrace();
399 String valueName = value.getName().name;
400 if(name.equalsIgnoreCase(valueName))
401 candidateNames.add(valueName);
405 if(localEnvironment != null)
406 localEnvironment.forNames(new TObjectProcedure<String>() {
408 public boolean execute(String valueName) {
409 if(name.equalsIgnoreCase(valueName))
410 candidateNames.add(valueName);
415 if(candidateNames.size() > 0) {
416 message.append(" Did you mean ");
417 String[] ns = candidateNames.toArray(new String[candidateNames.size()]);
419 for(int i=0;i<ns.length;++i) {
421 message.append(", ");
423 message.append("or ");
425 message.append(ns[i]);
430 errorLog.log(location, message.toString());
436 public Case translateCase(Expression lhs, Expression rhs) {
437 ArrayList<Expression> parameters = new ArrayList<Expression>(4);
438 lhs.getParameters(this, parameters);
439 Expression[] patterns = new Expression[parameters.size()];
441 for(int i=0;i<patterns.length;++i) {
442 Expression pattern = parameters.get(i);
443 pattern = pattern.resolveAsPattern(this);
444 patterns[i] = pattern;
446 rhs = rhs.resolve(this);
448 Case case_ = new Case(patterns, rhs);
449 case_.setLhs(lhs.location);
453 public Expression translateCases2(ArrayList<DValueAst> definitions) {
454 Case[] cases = new Case[definitions.size()];
455 for(int i=0;i<cases.length;++i) {
456 DValueAst def = definitions.get(i);
457 cases[i] = translateCase(def.lhs, def.value);
459 // check arity consistency
460 int arity = cases[0].patterns.length;
461 for(int i=1;i<cases.length;++i)
462 if(cases[i].patterns.length != arity)
463 errorLog.log(definitions.get(i).lhs.location,
464 "Inconsistent arity. " +
465 "This case has arity " + cases[i].patterns.length +
466 " while previous cases had arity " + arity + ".");
467 if(cases.length == 1 && cases[0].patterns.length == 0)
468 return cases[0].value;
471 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
475 public Expression translateCases(ArrayList<LetStatement> definitions) {
476 Case[] cases = new Case[definitions.size()];
477 for(int i=0;i<cases.length;++i) {
478 LetStatement def = definitions.get(i);
479 cases[i] = translateCase(def.pattern, def.value);
481 // check arity concistency
482 int arity = cases[0].patterns.length;
483 for(int i=1;i<cases.length;++i)
484 if(cases[i].patterns.length != arity)
485 errorLog.log(definitions.get(i).pattern.location,
486 "Inconsistent arity. " +
487 "This case has arity " + cases[i].patterns.length +
488 " while previous cases had arity " + arity + ".");
491 errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function.");
492 return cases[0].value;
495 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
499 private static final Name BIND = Name.create("Prelude", ">>=");
501 public SCLValue getBindFunction() {
502 if(bindFunction == null) {
503 bindFunction = getEnvironment().getValue(BIND);
508 public SCLRelation resolveRelation(long location, String name) {
509 SCLRelation relation = relations.get(name);
514 relation = Environments.getRelation(environment, name);
515 /*if(relation == null) {
516 errorLog.log(location, "Couldn't resolve relation " + name + ".");
520 } catch (AmbiguousNameException e) {
521 errorLog.log(location, e.getMessage());
527 public SCLValue getValue(Name name) {
528 return environment.getValue(name);