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 String deprecatedDescription = value.isDeprecated();
209 if(deprecatedDescription != null)
210 errorLog.logWarning(location, "Deprecated value " + value.getName().name + "." + (deprecatedDescription.isEmpty() ? "" : " " + deprecatedDescription));
211 return new EConstant(location, value);
214 private Expression resolveComplexNameIn(long location, Namespace namespace, int startPos, String name) {
215 int pos = name.length();
217 int hashPos = name.lastIndexOf('#');
221 while(pos > startPos) {
224 value = namespace.getValue(name.substring(startPos, pos));
225 } catch (AmbiguousNameException e) {
226 errorLog.log(location, e.getMessage());
227 return new EError(location);
230 Expression result = new EConstant(location, value);
231 if(pos < name.length())
232 result = resolveFieldAccess(result, pos, name);
235 pos = name.lastIndexOf('.', pos-1);
237 errorLog.log(location, "Couldn't resolve variable " + name + ".");
238 return new EError(location);
241 private static int findSeparator(String name, int fromIndex) {
242 while(fromIndex < name.length()) {
243 char c = name.charAt(fromIndex);
244 if(c == '.' || c == '#')
251 public Expression resolveExpression(long location, String name) {
252 int p = findSeparator(name, 1 /* Initial # is not a separator */);
254 Expression result = resolveLocalVariable(location, name);
258 if(localEnvironment != null) {
259 result = localEnvironment.resolve(environment, name);
261 result.setLocationDeep(location);
266 return resolveIn(location, environment.getLocalNamespace(), name);
269 if(localEnvironment != null) {
270 Expression result = localEnvironment.resolve(environment, name);
272 result.setLocationDeep(location);
277 String prefix = name.substring(0, p);
278 Expression result = resolveLocalVariable(location, prefix);
280 return resolveFieldAccess(result, p, name);
282 Namespace namespace = environment.getLocalNamespace();
284 while(name.charAt(p)=='.') {
285 Namespace temp = namespace.getNamespace(prefix);
290 p = findSeparator(name, pos);
292 return resolveIn(location, namespace, name.substring(pos));
293 prefix = name.substring(pos, p);
296 return resolveComplexNameIn(location, namespace, pos, name);
300 public Expression resolvePattern(EVar name) {
301 char firstChar = name.name.charAt(0);
302 if(firstChar == '_' && name.name.length()==1) {
303 return new EVariable(new Variable("_"));
305 else if(!Character.isUpperCase(firstChar)) {
306 if(!frameNameSets.get(frameNameSets.size()-1).add(name.name))
307 errorLog.log(name.location, "Repeated variable "+name.name+" in pattern.");
308 return new EVariable(name.location, newVariable(name.name));
311 return resolveExpression(name.location, name.name);
315 * Starts a new environment frame. New variables defined in this frame shadow
316 * the old variables and when the frame is popped, the old variables are again
319 public void pushFrame() {
320 frames.add(variableEntries.size());
321 frameNameSets.add(new THashSet<String>());
325 * Ends an environment frame. See {@link #pushFrame}.
327 public void popFrame() {
328 int frame = frames.removeAt(frames.size()-1);
329 int i = variableEntries.size();
332 Entry entry = variableEntries.remove(i);
333 if(entry.variable == null)
334 variables.remove(entry.name);
336 variables.put(entry.name, entry.variable);
338 frameNameSets.remove(frameNameSets.size()-1);
341 public void pushRelationFrame() {
342 relationFrames.add(relationEntries.size());
345 public void popRelationFrame() {
346 int frame = relationFrames.removeAt(relationFrames.size()-1);
347 int i = relationEntries.size();
350 RelationEntry entry = relationEntries.remove(i);
351 if(entry.relation == null)
352 relations.remove(entry.name);
354 relations.put(entry.name, entry.relation);
358 public void pushCHRConstraintFrame() {
359 chrConstraintFrames.add(chrConstraintEntries.size());
362 public void popCHRConstraintFrame(ArrayList<CHRConstraint> constraints) {
363 int frame = chrConstraintFrames.removeAt(chrConstraintFrames.size()-1);
364 int i = chrConstraintEntries.size();
367 CHRConstraintEntry entry = chrConstraintEntries.remove(i);
368 CHRConstraint newConstraint;
369 if(entry.constraint == null)
370 newConstraint = chrConstraints.remove(entry.name);
372 newConstraint = chrConstraints.put(entry.name, entry.constraint);
373 if(newConstraint.implicitlyDeclared)
374 constraints.add(newConstraint);
378 public void pushExistentialFrame() {
380 existentialFrames.add(new THashSet<String>());
381 blanksInExistentialFrame.add(new ArrayList<Variable>(2));
384 public Variable[] popExistentialFrame() {
386 THashSet<String> set = existentialFrames.remove(existentialFrames.size()-1);
387 ArrayList<Variable> blanks = blanksInExistentialFrame.remove(blanksInExistentialFrame.size()-1);
388 Variable[] result = new Variable[set.size() + blanks.size()];
390 for(String name : set)
391 result[i++] = variables.remove(name);
392 for(Variable blank : blanks)
397 public Variable newVariable(String name) {
398 Variable variable = new Variable(name);
399 Variable oldVariable = variables.put(name, variable);
400 variableEntries.add(new Entry(name, oldVariable));
404 public THashMap<String, Variable> getVariables() {
408 public void newRelation(String name, SCLRelation relation) {
409 SCLRelation oldRelation = relations.put(name, relation);
410 relationEntries.add(new RelationEntry(name, oldRelation));
413 public void newCHRConstraint(String name, CHRConstraint constraint) {
414 CHRConstraint oldConstraint = chrConstraints.put(name, constraint);
415 chrConstraintEntries.add(new CHRConstraintEntry(name, oldConstraint));
418 public Precedence getPrecedence(Name op) {
419 Precedence prec = environment.getValue(op).getPrecedence();
421 return new Precedence(1, Associativity.NONASSOC);
426 private SCLValue resolveValueIn(long location, Namespace namespace, final String name) throws AmbiguousNameException {
427 SCLValue value = namespace.getValue(name);
429 StringBuilder message = new StringBuilder();
430 message.append("Couldn't resolve variable ").append(name).append(".");
432 final THashSet<String> candidateNames = new THashSet<String>(4);
433 namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE,
434 new TObjectProcedure<SCLValue>() {
436 public boolean execute(SCLValue value) {
438 new Exception().printStackTrace();
441 String valueName = value.getName().name;
442 if(name.equalsIgnoreCase(valueName))
443 candidateNames.add(valueName);
447 if(localEnvironment != null)
448 localEnvironment.forNames(new TObjectProcedure<String>() {
450 public boolean execute(String valueName) {
451 if(name.equalsIgnoreCase(valueName))
452 candidateNames.add(valueName);
457 if(candidateNames.size() > 0) {
458 message.append(" Did you mean ");
459 String[] ns = candidateNames.toArray(new String[candidateNames.size()]);
461 for(int i=0;i<ns.length;++i) {
463 message.append(", ");
465 message.append("or ");
467 message.append(ns[i]);
472 errorLog.log(location, message.toString());
478 public Case translateCase(Expression lhs, Expression rhs) {
479 ArrayList<Expression> parameters = new ArrayList<Expression>(4);
480 lhs.getParameters(this, parameters);
481 Expression[] patterns = new Expression[parameters.size()];
483 for(int i=0;i<patterns.length;++i) {
484 Expression pattern = parameters.get(i);
485 pattern = pattern.resolveAsPattern(this);
486 patterns[i] = pattern;
488 rhs = rhs.resolve(this);
490 Case case_ = new Case(patterns, rhs);
491 case_.setLhs(lhs.location);
495 public Expression translateCases2(ArrayList<DValueAst> definitions) {
496 Case[] cases = new Case[definitions.size()];
497 for(int i=0;i<cases.length;++i) {
498 DValueAst def = definitions.get(i);
499 cases[i] = translateCase(def.lhs, def.value);
501 // check arity consistency
502 int arity = cases[0].patterns.length;
503 for(int i=1;i<cases.length;++i)
504 if(cases[i].patterns.length != arity)
505 errorLog.log(definitions.get(i).lhs.location,
506 "Inconsistent arity. " +
507 "This case has arity " + cases[i].patterns.length +
508 " while previous cases had arity " + arity + ".");
509 if(cases.length == 1 && cases[0].patterns.length == 0)
510 return cases[0].value;
513 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
517 public Expression translateCases(ArrayList<LetStatement> definitions) {
518 Case[] cases = new Case[definitions.size()];
519 for(int i=0;i<cases.length;++i) {
520 LetStatement def = definitions.get(i);
521 cases[i] = translateCase(def.pattern, def.value);
523 // check arity concistency
524 int arity = cases[0].patterns.length;
525 for(int i=1;i<cases.length;++i)
526 if(cases[i].patterns.length != arity)
527 errorLog.log(definitions.get(i).pattern.location,
528 "Inconsistent arity. " +
529 "This case has arity " + cases[i].patterns.length +
530 " while previous cases had arity " + arity + ".");
533 errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function.");
534 return cases[0].value;
537 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
541 public SCLValue getBindFunction() {
542 if(bindFunction == null) {
543 bindFunction = getEnvironment().getValue(Names.Prelude_bind);
548 public SCLRelation resolveRelation(long location, String name) {
549 SCLRelation relation = relations.get(name);
554 relation = Environments.getRelation(environment, name);
555 /*if(relation == null) {
556 errorLog.log(location, "Couldn't resolve relation " + name + ".");
560 } catch (AmbiguousNameException e) {
561 errorLog.log(location, e.getMessage());
566 public CHRConstraint resolveCHRConstraint(String name) {
567 return chrConstraints.get(name);
571 public SCLValue getValue(Name name) {
572 return environment.getValue(name);