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.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.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 PreQuery currentPreQuery;
56 THashMap<String, SCLRelation> relations = new THashMap<String, SCLRelation>();
57 TIntArrayList relationFrames = new TIntArrayList();
58 ArrayList<RelationEntry> relationEntries = new ArrayList<RelationEntry>();
60 THashMap<String, CHRConstraint> chrConstraints = new THashMap<String, CHRConstraint>();
61 TIntArrayList chrConstraintFrames = new TIntArrayList();
62 ArrayList<CHRConstraintEntry> chrConstraintEntries = new ArrayList<CHRConstraintEntry>();
67 public Entry(String name, Variable variable) {
69 this.variable = variable;
73 static class RelationEntry {
76 public RelationEntry(String name, SCLRelation relation) {
78 this.relation = relation;
82 static class CHRConstraintEntry {
84 CHRConstraint constraint;
85 public CHRConstraintEntry(String name, CHRConstraint constraint) {
87 this.constraint = constraint;
91 public TranslationContext(CompilationContext compilationContext, LocalEnvironment localEnvironment) {
92 super(compilationContext);
93 this.localEnvironment = localEnvironment;
96 public static boolean isConstructorName(String name) {
97 int p = name.lastIndexOf('.');
98 char firstChar = name.charAt(p<0 ? 0 : p+1);
99 return Character.isUpperCase(firstChar);
102 /* Tries to resolve name as a local variable. It is assumed
103 * that name does not contain '.'.
105 private Expression resolveLocalVariable(long location, String name) {
106 Variable variable = variables.get(name);
108 return new EVariable(location, variable);
110 char c = name.charAt(0);
113 if(existentialFrames.isEmpty()) {
114 errorLog.log(location, "Existential variables can be used only in queries.");
115 return new EError(location);
117 variable = new Variable(name);
118 variables.put(name, variable);
119 existentialFrames.get(existentialFrames.size()-1).add(name);
120 return new EVariable(variable);
122 if(name.length()==1) {
123 variable = new Variable("_");
124 if(blanksInExistentialFrame.isEmpty()) {
125 errorLog.log(location, "Cannot use blank variables in this context.");
126 return new EError(location);
128 blanksInExistentialFrame.get(blanksInExistentialFrame.size()-1).add(variable);
129 return new EVariable(variable);
136 private Expression resolveFieldAccess(Expression base, int pos, String name) {
138 int p = findSeparator(name, pos+1);
139 FieldAccessor accessor = new IdAccessor(
141 name.substring(pos+1, p==-1 ? name.length() : p-1));
142 base = new EFieldAccess(base, accessor);
148 private static int findSeparator(String name, int fromIndex) {
149 ++fromIndex; // the first character (that can be #) is never a separator
150 while(fromIndex < name.length()) {
151 char c = name.charAt(fromIndex);
152 if(c == '.' || c == '#')
159 public Expression resolveValue(long location, Namespace namespace, String name) {
161 SCLValue value = namespace.getValue(name);
164 String deprecatedDescription = value.isDeprecated();
165 if(deprecatedDescription != null)
166 errorLog.logWarning(location, "Deprecated value " + value.getName().name + "." + (deprecatedDescription.isEmpty() ? "" : " " + deprecatedDescription));
167 return new EConstant(location, value);
168 } catch (AmbiguousNameException e) {
169 if(SCLCompilerConfiguration.ALLOW_OVERLOADING)
170 return resolveAmbigious(location, e.conflictingModules, name);
172 errorLog.log(location, e.getMessage());
173 return new EError(location);
178 private Expression resolveAmbigious(long location, String[] conflictingModules, String name) {
179 EAmbiguous.Alternative[] alternatives = new EAmbiguous.Alternative[conflictingModules.length];
180 for(int i=0;i<conflictingModules.length;++i) {
181 Name altName = Name.create(conflictingModules[i], name);
182 SCLValue altValue = environment.getValue(altName);
183 alternatives[i] = new EAmbiguous.Alternative() {
185 public Type getType() {
186 return altValue.getType();
190 public Expression realize() {
191 EConstant expression = new EConstant(altValue);
192 expression.location = location;
197 public String toString() {
198 return altValue.getName().toString().replace('/', '.');
202 EAmbiguous expression = new EAmbiguous(alternatives);
203 expression.location = location;
207 public Expression resolveVariable(long location, Namespace namespace, String name, int begin) {
208 int end = findSeparator(name, begin);
209 String part = end == -1 ? (begin == 0 ? name : name.substring(begin)) : name.substring(begin, end);
212 Expression result = resolveLocalVariable(location, part);
214 return end == -1 ? result : resolveFieldAccess(result, end, name);
216 // FIXME.. support for records
217 if(localEnvironment != null) {
218 result = localEnvironment.resolve(environment, name);
220 result.setLocationDeep(location);
225 if(end != -1 && name.charAt(end) == '.') {
226 Namespace childNamespace = namespace.getNamespace(part);
227 if(childNamespace != null)
228 return resolveVariable(location, childNamespace, name, end+1);
231 Expression result = null;
233 for(int end2 = name.length();end2 > end;end2 = name.lastIndexOf('.', end2-1)) {
234 part = name.substring(begin, end2);
235 result = resolveValue(location, namespace, part);
237 end = end2 == name.length() ? -1 : end2;
243 result = resolveValue(location, namespace, part);
245 return end == -1 ? result : resolveFieldAccess(result, end, name);
247 reportResolveFailure(location, namespace, part);
248 return new EError(location);
251 private void reportResolveFailure(long location, Namespace namespace, String name) {
252 StringBuilder message = new StringBuilder();
253 message.append("Couldn't resolve ").append(name).append(".");
255 final THashSet<String> candidateNames = new THashSet<String>(4);
256 namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE,
257 new TObjectProcedure<SCLValue>() {
259 public boolean execute(SCLValue value) {
261 new Exception().printStackTrace();
264 String valueName = value.getName().name;
265 if(name.equalsIgnoreCase(valueName))
266 candidateNames.add(valueName);
270 if(localEnvironment != null)
271 localEnvironment.forNames(new TObjectProcedure<String>() {
273 public boolean execute(String valueName) {
274 if(name.equalsIgnoreCase(valueName))
275 candidateNames.add(valueName);
280 if(candidateNames.size() > 0) {
281 message.append(" Did you mean ");
282 String[] ns = candidateNames.toArray(new String[candidateNames.size()]);
284 for(int i=0;i<ns.length;++i) {
286 message.append(", ");
288 message.append("or ");
290 message.append(ns[i]);
295 errorLog.log(location, message.toString());
298 public Expression resolveVariable(long location, String name) {
299 return resolveVariable(location, environment.getLocalNamespace(), name, 0);
302 public Expression resolvePattern(EVar name) {
303 char firstChar = name.name.charAt(0);
304 if(firstChar == '_' && name.name.length()==1) {
305 return new EVariable(new Variable("_"));
307 else if(!Character.isUpperCase(firstChar)) {
308 if(!frameNameSets.get(frameNameSets.size()-1).add(name.name))
309 errorLog.log(name.location, "Repeated variable "+name.name+" in pattern.");
310 return new EVariable(name.location, newVariable(name.name));
313 return resolveVariable(name.location, name.name);
317 * Starts a new environment frame. New variables defined in this frame shadow
318 * the old variables and when the frame is popped, the old variables are again
321 public void pushFrame() {
322 frames.add(variableEntries.size());
323 frameNameSets.add(new THashSet<String>());
327 * Ends an environment frame. See {@link #pushFrame}.
329 public void popFrame() {
330 int frame = frames.removeAt(frames.size()-1);
331 int i = variableEntries.size();
334 Entry entry = variableEntries.remove(i);
335 if(entry.variable == null)
336 variables.remove(entry.name);
338 variables.put(entry.name, entry.variable);
340 frameNameSets.remove(frameNameSets.size()-1);
343 public void pushRelationFrame() {
344 relationFrames.add(relationEntries.size());
347 public void popRelationFrame() {
348 int frame = relationFrames.removeAt(relationFrames.size()-1);
349 int i = relationEntries.size();
352 RelationEntry entry = relationEntries.remove(i);
353 if(entry.relation == null)
354 relations.remove(entry.name);
356 relations.put(entry.name, entry.relation);
360 public void pushCHRConstraintFrame() {
361 chrConstraintFrames.add(chrConstraintEntries.size());
364 public void popCHRConstraintFrame(ArrayList<CHRConstraint> constraints) {
365 int frame = chrConstraintFrames.removeAt(chrConstraintFrames.size()-1);
366 int i = chrConstraintEntries.size();
369 CHRConstraintEntry entry = chrConstraintEntries.remove(i);
370 CHRConstraint newConstraint;
371 if(entry.constraint == null)
372 newConstraint = chrConstraints.remove(entry.name);
374 newConstraint = chrConstraints.put(entry.name, entry.constraint);
375 if(newConstraint.implicitlyDeclared)
376 constraints.add(newConstraint);
380 public void pushExistentialFrame() {
382 existentialFrames.add(new THashSet<String>());
383 blanksInExistentialFrame.add(new ArrayList<Variable>(2));
386 public Variable[] popExistentialFrame() {
388 THashSet<String> set = existentialFrames.remove(existentialFrames.size()-1);
389 ArrayList<Variable> blanks = blanksInExistentialFrame.remove(blanksInExistentialFrame.size()-1);
390 Variable[] result = new Variable[set.size() + blanks.size()];
392 for(String name : set)
393 result[i++] = variables.remove(name);
394 for(Variable blank : blanks)
399 public Variable newVariable(String name) {
400 Variable variable = new Variable(name);
401 Variable oldVariable = variables.put(name, variable);
402 variableEntries.add(new Entry(name, oldVariable));
406 public THashMap<String, Variable> getVariables() {
410 public void newRelation(String name, SCLRelation relation) {
411 SCLRelation oldRelation = relations.put(name, relation);
412 relationEntries.add(new RelationEntry(name, oldRelation));
415 public void newCHRConstraint(String name, CHRConstraint constraint) {
416 CHRConstraint oldConstraint = chrConstraints.put(name, constraint);
417 chrConstraintEntries.add(new CHRConstraintEntry(name, oldConstraint));
420 public Precedence getPrecedence(Name op) {
421 Precedence prec = environment.getValue(op).getPrecedence();
423 return new Precedence(1, Associativity.NONASSOC);
428 public Case translateCase(Expression lhs, Expression rhs) {
429 ArrayList<Expression> parameters = new ArrayList<Expression>(4);
430 lhs.getParameters(this, parameters);
431 Expression[] patterns = new Expression[parameters.size()];
433 for(int i=0;i<patterns.length;++i) {
434 Expression pattern = parameters.get(i);
435 pattern = pattern.resolveAsPattern(this);
436 patterns[i] = pattern;
438 rhs = rhs.resolve(this);
440 Case case_ = new Case(patterns, rhs);
441 case_.setLhs(lhs.location);
445 public Expression translateCases2(ArrayList<DValueAst> definitions) {
446 Case[] cases = new Case[definitions.size()];
447 for(int i=0;i<cases.length;++i) {
448 DValueAst def = definitions.get(i);
449 cases[i] = translateCase(def.lhs, def.value);
451 // check arity consistency
452 int arity = cases[0].patterns.length;
453 for(int i=1;i<cases.length;++i)
454 if(cases[i].patterns.length != arity)
455 errorLog.log(definitions.get(i).lhs.location,
456 "Inconsistent arity. " +
457 "This case has arity " + cases[i].patterns.length +
458 " while previous cases had arity " + arity + ".");
459 if(cases.length == 1 && cases[0].patterns.length == 0)
460 return cases[0].value;
463 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
467 public Expression translateCases(ArrayList<LetStatement> definitions) {
468 Case[] cases = new Case[definitions.size()];
469 for(int i=0;i<cases.length;++i) {
470 LetStatement def = definitions.get(i);
471 cases[i] = translateCase(def.pattern, def.value);
473 // check arity concistency
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).pattern.location,
478 "Inconsistent arity. " +
479 "This case has arity " + cases[i].patterns.length +
480 " while previous cases had arity " + arity + ".");
483 errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function.");
484 return cases[0].value;
487 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
491 public SCLValue getBindFunction() {
492 if(bindFunction == null) {
493 bindFunction = getEnvironment().getValue(Names.Prelude_bind);
498 public SCLRelation resolveRelation(long location, String name) {
499 SCLRelation relation = relations.get(name);
504 relation = Environments.getRelation(environment, name);
505 /*if(relation == null) {
506 errorLog.log(location, "Couldn't resolve relation " + name + ".");
510 } catch (AmbiguousNameException e) {
511 errorLog.log(location, e.getMessage());
516 public CHRConstraint resolveCHRConstraint(String name) {
517 return chrConstraints.get(name);
521 public SCLValue getValue(Name name) {
522 return environment.getValue(name);