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.CHRRuleset;
12 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
13 import org.simantics.scl.compiler.elaboration.expressions.Case;
14 import org.simantics.scl.compiler.elaboration.expressions.EAmbiguous;
15 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
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 public static class ExistentialFrame {
47 THashSet<String> variables = new THashSet<String>(4);
48 ArrayList<Variable> blanks = new ArrayList<Variable>(2);
49 public boolean disallowNewExistentials;
51 public EVariable createBlank(long location) {
52 Variable variable = new Variable("_");
54 EVariable result = new EVariable(variable);
55 result.location = location;
60 THashMap<String, Variable> variables = new THashMap<String, Variable>();
61 ArrayList<Entry> variableEntries = new ArrayList<Entry>();
62 LocalEnvironment localEnvironment;
63 TIntArrayList frames = new TIntArrayList();
64 ArrayList<THashSet<String>> frameNameSets = new ArrayList<THashSet<String>>();
65 ArrayList<ExistentialFrame> existentialFrames = new ArrayList<ExistentialFrame>(2);
66 SCLValue bindFunction;
68 public PreQuery currentPreQuery;
70 THashMap<String, SCLRelation> relations = new THashMap<String, SCLRelation>();
71 TIntArrayList relationFrames = new TIntArrayList();
72 ArrayList<RelationEntry> relationEntries = new ArrayList<RelationEntry>();
74 THashMap<String, CHRConstraint> chrConstraints = new THashMap<String, CHRConstraint>();
75 TIntArrayList chrConstraintFrames = new TIntArrayList();
76 ArrayList<CHRConstraintEntry> chrConstraintEntries = new ArrayList<CHRConstraintEntry>();
81 public Entry(String name, Variable variable) {
83 this.variable = variable;
87 static class RelationEntry {
90 public RelationEntry(String name, SCLRelation relation) {
92 this.relation = relation;
96 static class CHRConstraintEntry {
98 CHRConstraint constraint;
99 public CHRConstraintEntry(String name, CHRConstraint constraint) {
101 this.constraint = constraint;
105 public TranslationContext(CompilationContext compilationContext, LocalEnvironment localEnvironment) {
106 super(compilationContext);
107 this.localEnvironment = localEnvironment;
110 public static boolean isConstructorName(String name) {
111 int p = name.lastIndexOf('.');
112 char firstChar = name.charAt(p<0 ? 0 : p+1);
113 return Character.isUpperCase(firstChar);
116 /* Tries to resolve name as a local variable. It is assumed
117 * that name does not contain '.'.
119 private Expression resolveLocalVariable(long location, String name) {
120 Variable variable = variables.get(name);
122 return new EVariable(location, variable);
124 char c = name.charAt(0);
127 ExistentialFrame existentialFrame = getCurrentExistentialFrame();
128 if(existentialFrame == null || existentialFrame.disallowNewExistentials) {
129 errorLog.log(location, "New existential variables can be defined only in queries.");
130 return new EError(location);
132 variable = new Variable(name);
133 variables.put(name, variable);
134 existentialFrame.variables.add(name);
135 return new EVariable(variable);
138 if(name.length()==1) {
139 ExistentialFrame existentialFrame = getCurrentExistentialFrame();
140 if(existentialFrame == null || existentialFrame.disallowNewExistentials) {
141 errorLog.log(location, "Blank variables can be used only in queries.");
142 return new EError(location);
144 return existentialFrame.createBlank(location);
152 public ExistentialFrame getCurrentExistentialFrame() {
153 int size = existentialFrames.size();
157 return existentialFrames.get(size-1);
160 private Expression resolveFieldAccess(long location, Expression base, int pos, String name) {
162 int p = findSeparator(name, pos+1);
163 int endPos = p==-1 ? name.length() : p;
164 FieldAccessor accessor = new IdAccessor(
166 name.substring(pos+1, endPos));
167 accessor.location = Locations.sublocation(location, pos+1, endPos);
168 base = new EFieldAccess(base, accessor);
169 base.location = Locations.sublocation(location, 0, endPos);
175 private static int findSeparator(String name, int fromIndex) {
176 ++fromIndex; // the first character (that can be #) is never a separator
177 while(fromIndex < name.length()) {
178 char c = name.charAt(fromIndex);
179 if(c == '.' || c == '#')
186 public Expression resolveValue(long location, Namespace namespace, String name) {
188 SCLValue value = namespace.getValue(name);
191 String deprecatedDescription = value.isDeprecated();
192 if(deprecatedDescription != null)
193 errorLog.logWarning(location, "Deprecated value " + value.getName().name + "." + (deprecatedDescription.isEmpty() ? "" : " " + deprecatedDescription));
194 return new EConstant(location, value);
195 } catch (AmbiguousNameException e) {
196 if(SCLCompilerConfiguration.ALLOW_OVERLOADING)
197 return resolveAmbigious(location, e.conflictingModules, name);
199 errorLog.log(location, e.getMessage());
200 return new EError(location);
205 private Expression resolveAmbigious(long location, String[] conflictingModules, String name) {
206 EAmbiguous.Alternative[] alternatives = new EAmbiguous.Alternative[conflictingModules.length];
207 for(int i=0;i<conflictingModules.length;++i) {
208 Name altName = Name.create(conflictingModules[i], name);
209 SCLValue altValue = environment.getValue(altName);
210 alternatives[i] = new EAmbiguous.Alternative() {
212 public Type getType() {
213 return altValue.getType();
217 public Expression realize() {
218 EConstant expression = new EConstant(altValue);
219 expression.location = location;
224 public String toString() {
225 return altValue.getName().toString().replace('/', '.');
229 EAmbiguous expression = new EAmbiguous(alternatives);
230 expression.location = location;
234 public Expression resolveVariable(long location, Namespace namespace, String name, int begin) {
235 int end = findSeparator(name, begin);
236 String part = end == -1 ? (begin == 0 ? name : name.substring(begin)) : name.substring(begin, end);
239 Expression result = resolveLocalVariable(location, part);
241 return end == -1 ? result : resolveFieldAccess(location, result, end, name);
243 // FIXME.. support for records
244 if(localEnvironment != null) {
245 result = localEnvironment.resolve(environment, name);
247 result.setLocationDeep(location);
252 if(end != -1 && name.charAt(end) == '.') {
253 Namespace childNamespace = namespace.getNamespace(part);
254 if(childNamespace != null)
255 return resolveVariable(location, childNamespace, name, end+1);
258 Expression result = null;
260 for(int end2 = name.length();end2 > end;end2 = name.lastIndexOf('.', end2-1)) {
261 part = name.substring(begin, end2);
262 result = resolveValue(location, namespace, part);
264 end = end2 == name.length() ? -1 : end2;
270 result = resolveValue(location, namespace, part);
272 return end == -1 ? result : resolveFieldAccess(location, result, end, name);
274 reportResolveFailure(location, namespace, part);
275 return new EError(location);
278 private void reportResolveFailure(long location, Namespace namespace, String name) {
279 StringBuilder message = new StringBuilder();
280 message.append("Couldn't resolve ").append(name).append(".");
282 final THashSet<String> candidateNames = new THashSet<String>(4);
283 namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE,
284 new TObjectProcedure<SCLValue>() {
286 public boolean execute(SCLValue value) {
288 new Exception().printStackTrace();
291 String valueName = value.getName().name;
292 if(name.equalsIgnoreCase(valueName))
293 candidateNames.add(valueName);
297 if(localEnvironment != null)
298 localEnvironment.forNames(new TObjectProcedure<String>() {
300 public boolean execute(String valueName) {
301 if(name.equalsIgnoreCase(valueName))
302 candidateNames.add(valueName);
307 if(candidateNames.size() > 0) {
308 message.append(" Did you mean ");
309 String[] ns = candidateNames.toArray(new String[candidateNames.size()]);
311 for(int i=0;i<ns.length;++i) {
313 message.append(", ");
315 message.append("or ");
317 message.append(ns[i]);
322 errorLog.log(location, message.toString());
325 public Expression resolveVariable(long location, String name) {
326 return resolveVariable(location, environment.getLocalNamespace(), name, 0);
329 public Expression resolvePattern(EVar name) {
330 char firstChar = name.name.charAt(0);
331 if(firstChar == '_' && name.name.length()==1) {
332 return new EVariable(new Variable("_"));
334 else if(!Character.isUpperCase(firstChar)) {
335 if(!frameNameSets.get(frameNameSets.size()-1).add(name.name))
336 errorLog.log(name.location, "Repeated variable "+name.name+" in pattern.");
337 return new EVariable(name.location, newVariable(name.name));
340 return resolveVariable(name.location, name.name);
344 * Starts a new environment frame. New variables defined in this frame shadow
345 * the old variables and when the frame is popped, the old variables are again
348 public void pushFrame() {
349 frames.add(variableEntries.size());
350 frameNameSets.add(new THashSet<String>());
354 * Ends an environment frame. See {@link #pushFrame}.
356 public void popFrame() {
357 int frame = frames.removeAt(frames.size()-1);
358 int i = variableEntries.size();
361 Entry entry = variableEntries.remove(i);
362 if(entry.variable == null)
363 variables.remove(entry.name);
365 variables.put(entry.name, entry.variable);
367 frameNameSets.remove(frameNameSets.size()-1);
370 public void pushRelationFrame() {
371 relationFrames.add(relationEntries.size());
374 public void popRelationFrame() {
375 int frame = relationFrames.removeAt(relationFrames.size()-1);
376 int i = relationEntries.size();
379 RelationEntry entry = relationEntries.remove(i);
380 if(entry.relation == null)
381 relations.remove(entry.name);
383 relations.put(entry.name, entry.relation);
387 public void pushCHRConstraintFrame() {
388 chrConstraintFrames.add(chrConstraintEntries.size());
391 public void popCHRConstraintFrame(ArrayList<CHRConstraint> constraints) {
392 int frame = chrConstraintFrames.removeAt(chrConstraintFrames.size()-1);
393 int i = chrConstraintEntries.size();
396 CHRConstraintEntry entry = chrConstraintEntries.remove(i);
397 CHRConstraint newConstraint;
398 if(entry.constraint == null)
399 newConstraint = chrConstraints.remove(entry.name);
401 newConstraint = chrConstraints.put(entry.name, entry.constraint);
402 if(newConstraint.implicitlyDeclared)
403 constraints.add(newConstraint);
407 public void pushExistentialFrame() {
409 existentialFrames.add(new ExistentialFrame());
412 public Variable[] popExistentialFrame() {
414 ExistentialFrame frame = existentialFrames.remove(existentialFrames.size()-1);
415 Variable[] result = new Variable[frame.variables.size() + frame.blanks.size()];
417 for(String name : frame.variables)
418 result[i++] = variables.remove(name);
419 for(Variable blank : frame.blanks)
424 public Variable newVariable(String name) {
425 Variable variable = new Variable(name);
426 Variable oldVariable = variables.put(name, variable);
427 variableEntries.add(new Entry(name, oldVariable));
431 public THashMap<String, Variable> getVariables() {
435 public void newRelation(String name, SCLRelation relation) {
436 SCLRelation oldRelation = relations.put(name, relation);
437 relationEntries.add(new RelationEntry(name, oldRelation));
440 public void newCHRConstraint(String name, CHRConstraint constraint) {
441 CHRConstraint oldConstraint = chrConstraints.put(name, constraint);
442 chrConstraintEntries.add(new CHRConstraintEntry(name, oldConstraint));
445 public Precedence getPrecedence(Name op) {
446 Precedence prec = environment.getValue(op).getPrecedence();
448 return new Precedence(1, Associativity.NONASSOC);
453 public Case translateCase(Expression lhs, Expression rhs) {
454 ArrayList<Expression> parameters = new ArrayList<Expression>(4);
455 lhs.getParameters(this, parameters);
456 Expression[] patterns = new Expression[parameters.size()];
458 for(int i=0;i<patterns.length;++i) {
459 Expression pattern = parameters.get(i);
460 pattern = pattern.resolveAsPattern(this);
461 patterns[i] = pattern;
463 rhs = rhs.resolve(this);
465 Case case_ = new Case(patterns, rhs);
466 case_.setLhs(lhs.location);
470 public Expression translateCases2(ArrayList<DValueAst> definitions) {
471 Case[] cases = new Case[definitions.size()];
472 for(int i=0;i<cases.length;++i) {
473 DValueAst def = definitions.get(i);
474 cases[i] = translateCase(def.lhs, def.value);
476 // check arity consistency
477 int arity = cases[0].patterns.length;
478 for(int i=1;i<cases.length;++i)
479 if(cases[i].patterns.length != arity)
480 errorLog.log(definitions.get(i).lhs.location,
481 "Inconsistent arity. " +
482 "This case has arity " + cases[i].patterns.length +
483 " while previous cases had arity " + arity + ".");
484 if(cases.length == 1 && cases[0].patterns.length == 0)
485 return cases[0].value;
488 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
492 public Expression translateCases(ArrayList<LetStatement> definitions) {
493 Case[] cases = new Case[definitions.size()];
494 for(int i=0;i<cases.length;++i) {
495 LetStatement def = definitions.get(i);
496 cases[i] = translateCase(def.pattern, def.value);
498 // check arity concistency
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).pattern.location,
503 "Inconsistent arity. " +
504 "This case has arity " + cases[i].patterns.length +
505 " while previous cases had arity " + arity + ".");
508 errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function.");
509 return cases[0].value;
512 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
516 public SCLValue getBindFunction() {
517 if(bindFunction == null) {
518 bindFunction = getEnvironment().getValue(Names.Prelude_bind);
523 public SCLRelation resolveRelation(long location, String name) {
524 SCLRelation relation = relations.get(name);
529 relation = Environments.getRelation(environment, name);
530 /*if(relation == null) {
531 errorLog.log(location, "Couldn't resolve relation " + name + ".");
535 } catch (AmbiguousNameException e) {
536 errorLog.log(location, e.getMessage());
541 public CHRConstraint resolveCHRConstraint(String name) {
542 return chrConstraints.get(name);
546 public SCLValue getValue(Name name) {
547 return environment.getValue(name);
550 public CHRRuleset resolveRuleset(String name) throws AmbiguousNameException {
551 return Environments.getRuleset(environment, name);
554 public void disallowNewExistentials() {
555 getCurrentExistentialFrame().disallowNewExistentials = true;