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.compilation.CompilationContext;
10 import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
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.module.debug.ModuleDebugInfo;
36 import org.simantics.scl.compiler.module.debug.SymbolReference;
37 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
38 import org.simantics.scl.compiler.types.Type;
40 import gnu.trove.list.array.TIntArrayList;
41 import gnu.trove.map.hash.THashMap;
42 import gnu.trove.procedure.TObjectProcedure;
43 import gnu.trove.set.hash.THashSet;
45 public class TranslationContext extends TypeTranslationContext implements EnvironmentalContext {
47 public static class ExistentialFrame {
48 THashSet<String> variables = new THashSet<String>(4);
49 ArrayList<Variable> blanks = new ArrayList<Variable>(2);
50 public boolean disallowNewExistentials;
52 public EVariable createBlank(long location) {
53 Variable variable = new Variable("_");
55 EVariable result = new EVariable(variable);
56 result.location = location;
61 THashMap<String, Variable> variables = new THashMap<String, Variable>();
62 ArrayList<Entry> variableEntries = new ArrayList<Entry>();
63 LocalEnvironment localEnvironment;
64 TIntArrayList frames = new TIntArrayList();
65 ArrayList<THashSet<String>> frameNameSets = new ArrayList<THashSet<String>>();
66 ArrayList<ExistentialFrame> existentialFrames = new ArrayList<ExistentialFrame>(2);
67 SCLValue bindFunction;
69 public PreQuery currentPreQuery;
71 THashMap<String, SCLRelation> relations = new THashMap<String, SCLRelation>();
72 TIntArrayList relationFrames = new TIntArrayList();
73 ArrayList<RelationEntry> relationEntries = new ArrayList<RelationEntry>();
75 THashMap<String, CHRConstraint> chrConstraints = new THashMap<String, CHRConstraint>();
76 TIntArrayList chrConstraintFrames = new TIntArrayList();
77 ArrayList<CHRConstraintEntry> chrConstraintEntries = new ArrayList<CHRConstraintEntry>();
79 private THashSet<Expression> expandedFromWildcard;
81 public CHRRuleset currentRuleset;
83 public ModuleDebugInfo moduleDebugInfo;
85 private String definitionName;
90 public Entry(String name, Variable variable) {
92 this.variable = variable;
96 static class RelationEntry {
99 public RelationEntry(String name, SCLRelation relation) {
101 this.relation = relation;
105 static class CHRConstraintEntry {
107 CHRConstraint constraint;
108 public CHRConstraintEntry(String name, CHRConstraint constraint) {
110 this.constraint = constraint;
114 public TranslationContext(CompilationContext compilationContext, LocalEnvironment localEnvironment, String definitionName) {
115 super(compilationContext);
116 this.localEnvironment = localEnvironment;
117 this.moduleDebugInfo = compilationContext.moduleDebugInfo;
118 this.definitionName = definitionName;
121 public static boolean isConstructorName(String name) {
122 int p = name.lastIndexOf('.');
123 char firstChar = name.charAt(p<0 ? 0 : p+1);
124 return Character.isUpperCase(firstChar) || firstChar == '(';
127 /* Tries to resolve name as a local variable. It is assumed
128 * that name does not contain '.'.
130 private Expression resolveLocalVariable(long location, String name) {
131 Variable variable = variables.get(name);
133 return new EVariable(location, variable);
135 char c = name.charAt(0);
138 ExistentialFrame existentialFrame = getCurrentExistentialFrame();
139 if(existentialFrame == null || existentialFrame.disallowNewExistentials) {
140 errorLog.log(location, "New existential variables can be defined only in queries.");
141 return new EError(location);
143 variable = new Variable(name);
144 variables.put(name, variable);
145 existentialFrame.variables.add(name);
146 return new EVariable(location, variable);
149 if(name.length()==1) {
150 ExistentialFrame existentialFrame = getCurrentExistentialFrame();
151 if(existentialFrame == null || existentialFrame.disallowNewExistentials) {
152 errorLog.log(location, "Blank variables can be used only in queries.");
153 return new EError(location);
155 return existentialFrame.createBlank(location);
163 public ExistentialFrame getCurrentExistentialFrame() {
164 int size = existentialFrames.size();
168 return existentialFrames.get(size-1);
171 private Expression resolveFieldAccess(long location, Expression base, int pos, String name) {
173 int p = findSeparator(name, pos+1);
174 int endPos = p==-1 ? name.length() : p;
175 FieldAccessor accessor = new IdAccessor(
177 name.substring(pos+1, endPos));
178 accessor.location = Locations.sublocation(location, pos+1, endPos);
179 base = new EFieldAccess(base, accessor);
180 base.location = Locations.sublocation(location, 0, endPos);
186 private static int findSeparator(String name, int fromIndex) {
187 ++fromIndex; // the first character (that can be #) is never a separator
188 while(fromIndex < name.length()) {
189 char c = name.charAt(fromIndex);
190 if(c == '.' || c == '#')
197 public SCLValue resolveSCLValue(long location, Namespace namespace, String name) throws AmbiguousNameException {
198 SCLValue value = namespace.getValue(name);
201 String deprecatedDescription = value.isDeprecated();
202 if(deprecatedDescription != null)
203 errorLog.logWarning(location, "Deprecated value " + value.getName().name + "." + (deprecatedDescription.isEmpty() ? "" : " " + deprecatedDescription));
204 if(moduleDebugInfo != null)
205 moduleDebugInfo.symbolReferences.add(new SymbolReference(value.getName(), Name.create(compilationContext.module.getName(), definitionName), location));
209 public Expression resolveValue(long location, Namespace namespace, String name) {
211 SCLValue value = resolveSCLValue(location, namespace, name);
214 return new EConstant(location, value);
215 } catch (AmbiguousNameException e) {
216 if(SCLCompilerConfiguration.ALLOW_OVERLOADING)
217 return resolveAmbigious(location, e.conflictingModules, name);
219 errorLog.log(location, e.getMessage());
220 return new EError(location);
225 private Expression resolveAmbigious(long location, String[] conflictingModules, String name) {
226 EAmbiguous.Alternative[] alternatives = new EAmbiguous.Alternative[conflictingModules.length];
227 for(int i=0;i<conflictingModules.length;++i) {
228 Name altName = Name.create(conflictingModules[i], name);
229 SCLValue altValue = environment.getValue(altName);
230 alternatives[i] = new EAmbiguous.Alternative() {
232 public Type getType() {
233 return altValue.getType();
237 public Expression realize() {
238 EConstant expression = new EConstant(altValue);
239 expression.location = location;
240 if(moduleDebugInfo != null)
241 moduleDebugInfo.symbolReferences.add(new SymbolReference(altValue.getName(), Name.create(compilationContext.module.getName(), definitionName), location));
246 public String toString() {
247 return altValue.getName().toString().replace('/', '.');
251 EAmbiguous expression = new EAmbiguous(alternatives);
252 expression.location = location;
256 public Expression resolveVariable(long location, Namespace namespace, String name, int begin) {
257 int end = findSeparator(name, begin);
258 String part = end == -1 ? (begin == 0 ? name : name.substring(begin)) : name.substring(begin, end);
261 Expression result = resolveLocalVariable(location, part);
263 return end == -1 ? result : resolveFieldAccess(location, result, end, name);
265 // FIXME.. support for records
266 if(localEnvironment != null) {
267 result = localEnvironment.resolve(environment, name);
269 result.setLocationDeep(location);
274 if(end != -1 && name.charAt(end) == '.') {
275 Namespace childNamespace = namespace.getNamespace(part);
276 if(childNamespace != null)
277 return resolveVariable(location, childNamespace, name, end+1);
280 Expression result = null;
282 for(int end2 = name.length();end2 > end;end2 = name.lastIndexOf('.', end2-1)) {
283 part = name.substring(begin, end2);
284 result = resolveValue(location, namespace, part);
286 end = end2 == name.length() ? -1 : end2;
292 result = resolveValue(location, namespace, part);
294 return end == -1 ? result : resolveFieldAccess(location, result, end, name);
296 reportResolveFailure(location, namespace, part);
297 return new EError(location);
300 public SCLValue resolveRecordConstructor(long location, String name) throws AmbiguousNameException {
301 return resolveRecordConstructor(location, getEnvironment().getLocalNamespace(), name, 0);
304 public SCLValue resolveRecordConstructor(long location, Namespace namespace, String name, int begin) throws AmbiguousNameException {
305 int end = findSeparator(name, begin);
306 String part = end == -1 ? (begin == 0 ? name : name.substring(begin)) : name.substring(begin, end);
308 if(end != -1 && name.charAt(end) == '.') {
309 Namespace childNamespace = namespace.getNamespace(part);
310 if(childNamespace == null)
313 return resolveRecordConstructor(location, childNamespace, name, end+1);
316 return resolveSCLValue(location, namespace, part);
319 private void reportResolveFailure(long location, Namespace namespace, String name) {
320 StringBuilder message = new StringBuilder();
321 message.append("Couldn't resolve ").append(name).append(".");
323 final THashSet<String> candidateNames = new THashSet<String>(4);
324 namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE,
325 new TObjectProcedure<SCLValue>() {
327 public boolean execute(SCLValue value) {
329 new Exception().printStackTrace();
332 String valueName = value.getName().name;
333 if(name.equalsIgnoreCase(valueName))
334 candidateNames.add(valueName);
338 if(localEnvironment != null)
339 localEnvironment.forNames(new TObjectProcedure<String>() {
341 public boolean execute(String valueName) {
342 if(name.equalsIgnoreCase(valueName))
343 candidateNames.add(valueName);
348 if(candidateNames.size() > 0) {
349 message.append(" Did you mean ");
350 String[] ns = candidateNames.toArray(new String[candidateNames.size()]);
352 for(int i=0;i<ns.length;++i) {
354 message.append(", ");
356 message.append("or ");
358 message.append(ns[i]);
363 errorLog.log(location, message.toString());
366 public Expression resolveVariable(long location, String name) {
367 return resolveVariable(location, environment.getLocalNamespace(), name, 0);
370 public Expression resolvePattern(EVar name) {
371 char firstChar = name.name.charAt(0);
372 if(firstChar == '_' && name.name.length()==1) {
373 return new EVariable(new Variable("_"));
375 else if(!Character.isUpperCase(firstChar)) {
376 if(!frameNameSets.get(frameNameSets.size()-1).add(name.name))
377 errorLog.log(name.location, "Repeated variable "+name.name+" in pattern.");
378 return new EVariable(name.location, newVariable(name.name));
381 return resolveVariable(name.location, name.name);
385 * Starts a new environment frame. New variables defined in this frame shadow
386 * the old variables and when the frame is popped, the old variables are again
389 public void pushFrame() {
390 frames.add(variableEntries.size());
391 frameNameSets.add(new THashSet<String>());
395 * Ends an environment frame. See {@link #pushFrame}.
397 public void popFrame() {
398 int frame = frames.removeAt(frames.size()-1);
399 int i = variableEntries.size();
402 Entry entry = variableEntries.remove(i);
403 if(entry.variable == null)
404 variables.remove(entry.name);
406 variables.put(entry.name, entry.variable);
408 frameNameSets.remove(frameNameSets.size()-1);
411 public void pushRelationFrame() {
412 relationFrames.add(relationEntries.size());
415 public void popRelationFrame() {
416 int frame = relationFrames.removeAt(relationFrames.size()-1);
417 int i = relationEntries.size();
420 RelationEntry entry = relationEntries.remove(i);
421 if(entry.relation == null)
422 relations.remove(entry.name);
424 relations.put(entry.name, entry.relation);
428 public void pushCHRConstraintFrame() {
429 chrConstraintFrames.add(chrConstraintEntries.size());
432 public void popCHRConstraintFrame(CHRRuleset ruleset) {
433 int frame = chrConstraintFrames.removeAt(chrConstraintFrames.size()-1);
434 int i = chrConstraintEntries.size();
437 CHRConstraintEntry entry = chrConstraintEntries.remove(i);
438 CHRConstraint newConstraint;
439 if(entry.constraint == null)
440 newConstraint = chrConstraints.remove(entry.name);
442 newConstraint = chrConstraints.put(entry.name, entry.constraint);
443 if(newConstraint.implicitlyDeclared)
444 ruleset.addConstraint(newConstraint);
448 public void pushExistentialFrame() {
450 existentialFrames.add(new ExistentialFrame());
453 public Variable[] popExistentialFrame() {
455 ExistentialFrame frame = existentialFrames.remove(existentialFrames.size()-1);
456 Variable[] result = new Variable[frame.variables.size() + frame.blanks.size()];
458 for(String name : frame.variables)
459 result[i++] = variables.remove(name);
460 for(Variable blank : frame.blanks)
465 public Variable newVariable(String name) {
466 Variable variable = new Variable(name);
467 Variable oldVariable = variables.put(name, variable);
468 variableEntries.add(new Entry(name, oldVariable));
472 public THashMap<String, Variable> getVariables() {
476 public void newRelation(String name, SCLRelation relation) {
477 SCLRelation oldRelation = relations.put(name, relation);
478 relationEntries.add(new RelationEntry(name, oldRelation));
481 public void newCHRConstraint(String name, CHRConstraint constraint) {
482 CHRConstraint oldConstraint = chrConstraints.put(name, constraint);
483 chrConstraintEntries.add(new CHRConstraintEntry(name, oldConstraint));
486 public Precedence getPrecedence(Name op) {
487 Precedence prec = environment.getValue(op).getPrecedence();
489 return new Precedence(1, Associativity.NONASSOC);
494 public Case translateCase(Expression lhs, Expression rhs) {
495 ArrayList<Expression> parameters = new ArrayList<Expression>(4);
496 lhs.getParameters(this, parameters);
497 Expression[] patterns = new Expression[parameters.size()];
499 for(int i=0;i<patterns.length;++i) {
500 Expression pattern = parameters.get(i);
501 pattern = pattern.resolveAsPattern(this);
502 patterns[i] = pattern;
504 rhs = rhs.resolve(this);
506 Case case_ = new Case(patterns, rhs);
507 case_.setLhs(lhs.location);
511 public Expression translateCases2(ArrayList<DValueAst> definitions) {
512 Case[] cases = new Case[definitions.size()];
513 for(int i=0;i<cases.length;++i) {
514 DValueAst def = definitions.get(i);
515 cases[i] = translateCase(def.lhs, def.value);
517 // check arity consistency
518 int arity = cases[0].patterns.length;
519 for(int i=1;i<cases.length;++i)
520 if(cases[i].patterns.length != arity)
521 errorLog.log(definitions.get(i).lhs.location,
522 "Inconsistent arity. " +
523 "This case has arity " + cases[i].patterns.length +
524 " while previous cases had arity " + arity + ".");
525 if(cases.length == 1 && cases[0].patterns.length == 0)
526 return cases[0].value;
529 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
533 public Expression translateCases(ArrayList<LetStatement> definitions) {
534 Case[] cases = new Case[definitions.size()];
535 for(int i=0;i<cases.length;++i) {
536 LetStatement def = definitions.get(i);
537 cases[i] = translateCase(def.pattern, def.value);
539 // check arity concistency
540 int arity = cases[0].patterns.length;
541 for(int i=1;i<cases.length;++i)
542 if(cases[i].patterns.length != arity)
543 errorLog.log(definitions.get(i).pattern.location,
544 "Inconsistent arity. " +
545 "This case has arity " + cases[i].patterns.length +
546 " while previous cases had arity " + arity + ".");
549 errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function.");
550 return cases[0].value;
553 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
557 public SCLRelation resolveRelation(long location, String name) {
558 SCLRelation relation = relations.get(name);
563 relation = Environments.getRelation(environment, name);
564 /*if(relation == null) {
565 errorLog.log(location, "Couldn't resolve relation " + name + ".");
569 } catch (AmbiguousNameException e) {
570 errorLog.log(location, e.getMessage());
575 public CHRConstraint resolveCHRConstraint(String name) {
576 return chrConstraints.get(name);
580 public SCLValue getValue(Name name) {
581 return environment.getValue(name);
584 public CHRRuleset resolveRuleset(String name) throws AmbiguousNameException {
585 return Environments.getRuleset(environment, name);
589 * Tells that new existential variables are no longer allowed in this context.
591 public void disallowNewExistentials() {
592 getCurrentExistentialFrame().disallowNewExistentials = true;
596 * Marks that the expression is a result of expanding .. wildcard pattern in records.
598 public void addExpandedFromWildcard(Expression expression) {
599 if(expandedFromWildcard == null)
600 expandedFromWildcard = new THashSet<>();
601 expandedFromWildcard.add(expression);
605 * Asks if the expression is a result of expanding .. wildcard pattern in records.
607 public boolean isExpandedFromWildcard(Expression expression) {
608 if(expandedFromWildcard == null)
611 return expandedFromWildcard.contains(expression);