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.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;
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;
44 public class TranslationContext extends TypeTranslationContext implements EnvironmentalContext {
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;
55 public EEntityTypeAnnotation currentEntityTypeAnnotation;
56 public PreQuery currentPreQuery;
58 THashMap<String, SCLRelation> relations = new THashMap<String, SCLRelation>();
59 TIntArrayList relationFrames = new TIntArrayList();
60 ArrayList<RelationEntry> relationEntries = new ArrayList<RelationEntry>();
62 THashMap<String, CHRConstraint> chrConstraints = new THashMap<String, CHRConstraint>();
63 TIntArrayList chrConstraintFrames = new TIntArrayList();
64 ArrayList<CHRConstraintEntry> chrConstraintEntries = new ArrayList<CHRConstraintEntry>();
69 public Entry(String name, Variable variable) {
71 this.variable = variable;
75 static class RelationEntry {
78 public RelationEntry(String name, SCLRelation relation) {
80 this.relation = relation;
84 static class CHRConstraintEntry {
86 CHRConstraint constraint;
87 public CHRConstraintEntry(String name, CHRConstraint constraint) {
89 this.constraint = constraint;
93 public TranslationContext(CompilationContext compilationContext, LocalEnvironment localEnvironment) {
94 super(compilationContext);
95 this.localEnvironment = localEnvironment;
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);
104 /* Tries to resolve name as a local variable. It is assumed
105 * that name does not contain '.'.
107 private Expression resolveLocalVariable(long location, String name) {
108 Variable variable = variables.get(name);
110 return new EVariable(location, variable);
112 char c = name.charAt(0);
115 if(existentialFrames.isEmpty()) {
116 errorLog.log(location, "Existential variables can be used only in queries.");
117 return new EError(location);
119 variable = new Variable(name);
120 variables.put(name, variable);
121 existentialFrames.get(existentialFrames.size()-1).add(name);
122 return new EVariable(variable);
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);
130 blanksInExistentialFrame.get(blanksInExistentialFrame.size()-1).add(variable);
131 return new EVariable(variable);
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);
140 return currentEntityTypeAnnotation.resolveAttribute(this, location, name.substring(1));
147 private FieldAccessor createFieldAccessor(char accessSeparator, String name) {
148 IdAccessor accessor = new IdAccessor(name);
149 accessor.accessSeparator = accessSeparator;
153 private Expression resolveFieldAccess(Expression base, int pos, String name) {
154 ArrayList<FieldAccessor> accessors = new ArrayList<FieldAccessor>(2);
156 int p = findSeparator(name, pos+1);
157 accessors.add(createFieldAccessor(
159 name.substring(pos+1, p==-1 ? name.length() : p-1)));
162 return new EFieldAccess(base,
163 accessors.toArray(new FieldAccessor[accessors.size()]));
166 private Expression resolveIn(long location, Namespace namespace, String name) {
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() {
180 public Type getType() {
181 return altValue.getType();
185 public Expression realize() {
186 EConstant expression = new EConstant(altValue);
187 expression.location = location;
192 public String toString() {
193 return altValue.getName().toString().replace('/', '.');
197 EAmbiguous expression = new EAmbiguous(alternatives);
198 expression.location = location;
202 errorLog.log(location, e.getMessage());
207 return new EError(location);
208 return new EConstant(location, value);
211 private Expression resolveComplexNameIn(long location, Namespace namespace, int startPos, String name) {
212 int pos = name.length();
214 int hashPos = name.lastIndexOf('#');
218 while(pos > startPos) {
221 value = namespace.getValue(name.substring(startPos, pos));
222 } catch (AmbiguousNameException e) {
223 errorLog.log(location, e.getMessage());
224 return new EError(location);
227 Expression result = new EConstant(location, value);
228 if(pos < name.length())
229 result = resolveFieldAccess(result, pos, name);
232 pos = name.lastIndexOf('.', pos-1);
234 errorLog.log(location, "Couldn't resolve variable " + name + ".");
235 return new EError(location);
238 private static int findSeparator(String name, int fromIndex) {
239 while(fromIndex < name.length()) {
240 char c = name.charAt(fromIndex);
241 if(c == '.' || c == '#')
248 public Expression resolveExpression(long location, String name) {
249 int p = findSeparator(name, 1 /* Initial # is not a separator */);
251 Expression result = resolveLocalVariable(location, name);
255 if(localEnvironment != null) {
256 result = localEnvironment.resolve(environment, name);
258 result.setLocationDeep(location);
263 return resolveIn(location, environment.getLocalNamespace(), name);
266 if(localEnvironment != null) {
267 Expression result = localEnvironment.resolve(environment, name);
269 result.setLocationDeep(location);
274 String prefix = name.substring(0, p);
275 Expression result = resolveLocalVariable(location, prefix);
277 return resolveFieldAccess(result, p, name);
279 Namespace namespace = environment.getLocalNamespace();
281 while(name.charAt(p)=='.') {
282 Namespace temp = namespace.getNamespace(prefix);
287 p = findSeparator(name, pos);
289 return resolveIn(location, namespace, name.substring(pos));
290 prefix = name.substring(pos, p);
293 return resolveComplexNameIn(location, namespace, pos, name);
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("_"));
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));
308 return resolveExpression(name.location, name.name);
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
316 public void pushFrame() {
317 frames.add(variableEntries.size());
318 frameNameSets.add(new THashSet<String>());
322 * Ends an environment frame. See {@link #pushFrame}.
324 public void popFrame() {
325 int frame = frames.removeAt(frames.size()-1);
326 int i = variableEntries.size();
329 Entry entry = variableEntries.remove(i);
330 if(entry.variable == null)
331 variables.remove(entry.name);
333 variables.put(entry.name, entry.variable);
335 frameNameSets.remove(frameNameSets.size()-1);
338 public void pushRelationFrame() {
339 relationFrames.add(relationEntries.size());
342 public void popRelationFrame() {
343 int frame = relationFrames.removeAt(relationFrames.size()-1);
344 int i = relationEntries.size();
347 RelationEntry entry = relationEntries.remove(i);
348 if(entry.relation == null)
349 relations.remove(entry.name);
351 relations.put(entry.name, entry.relation);
355 public void pushCHRConstraintFrame() {
356 chrConstraintFrames.add(chrConstraintEntries.size());
359 public void popCHRConstraintFrame(ArrayList<CHRConstraint> constraints) {
360 int frame = chrConstraintFrames.removeAt(chrConstraintFrames.size()-1);
361 int i = chrConstraintEntries.size();
364 CHRConstraintEntry entry = chrConstraintEntries.remove(i);
365 CHRConstraint newConstraint;
366 if(entry.constraint == null)
367 newConstraint = chrConstraints.remove(entry.name);
369 newConstraint = chrConstraints.put(entry.name, entry.constraint);
370 if(newConstraint.implicitlyDeclared)
371 constraints.add(newConstraint);
375 public void pushExistentialFrame() {
377 existentialFrames.add(new THashSet<String>());
378 blanksInExistentialFrame.add(new ArrayList<Variable>(2));
381 public Variable[] popExistentialFrame() {
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()];
387 for(String name : set)
388 result[i++] = variables.remove(name);
389 for(Variable blank : blanks)
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));
401 public THashMap<String, Variable> getVariables() {
405 public void newRelation(String name, SCLRelation relation) {
406 SCLRelation oldRelation = relations.put(name, relation);
407 relationEntries.add(new RelationEntry(name, oldRelation));
410 public void newCHRConstraint(String name, CHRConstraint constraint) {
411 CHRConstraint oldConstraint = chrConstraints.put(name, constraint);
412 chrConstraintEntries.add(new CHRConstraintEntry(name, oldConstraint));
415 public Precedence getPrecedence(Name op) {
416 Precedence prec = environment.getValue(op).getPrecedence();
418 return new Precedence(1, Associativity.NONASSOC);
423 private SCLValue resolveValueIn(long location, Namespace namespace, final String name) throws AmbiguousNameException {
424 SCLValue value = namespace.getValue(name);
426 StringBuilder message = new StringBuilder();
427 message.append("Couldn't resolve variable ").append(name).append(".");
429 final THashSet<String> candidateNames = new THashSet<String>(4);
430 namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE,
431 new TObjectProcedure<SCLValue>() {
433 public boolean execute(SCLValue value) {
435 new Exception().printStackTrace();
438 String valueName = value.getName().name;
439 if(name.equalsIgnoreCase(valueName))
440 candidateNames.add(valueName);
444 if(localEnvironment != null)
445 localEnvironment.forNames(new TObjectProcedure<String>() {
447 public boolean execute(String valueName) {
448 if(name.equalsIgnoreCase(valueName))
449 candidateNames.add(valueName);
454 if(candidateNames.size() > 0) {
455 message.append(" Did you mean ");
456 String[] ns = candidateNames.toArray(new String[candidateNames.size()]);
458 for(int i=0;i<ns.length;++i) {
460 message.append(", ");
462 message.append("or ");
464 message.append(ns[i]);
469 errorLog.log(location, message.toString());
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()];
480 for(int i=0;i<patterns.length;++i) {
481 Expression pattern = parameters.get(i);
482 pattern = pattern.resolveAsPattern(this);
483 patterns[i] = pattern;
485 rhs = rhs.resolve(this);
487 Case case_ = new Case(patterns, rhs);
488 case_.setLhs(lhs.location);
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);
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;
510 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
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);
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 + ".");
530 errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function.");
531 return cases[0].value;
534 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
538 public SCLValue getBindFunction() {
539 if(bindFunction == null) {
540 bindFunction = getEnvironment().getValue(Names.Prelude_bind);
545 public SCLRelation resolveRelation(long location, String name) {
546 SCLRelation relation = relations.get(name);
551 relation = Environments.getRelation(environment, name);
552 /*if(relation == null) {
553 errorLog.log(location, "Couldn't resolve relation " + name + ".");
557 } catch (AmbiguousNameException e) {
558 errorLog.log(location, e.getMessage());
563 public CHRConstraint resolveCHRConstraint(String name) {
564 return chrConstraints.get(name);
568 public SCLValue getValue(Name name) {
569 return environment.getValue(name);