1 package org.simantics.scl.compiler.elaboration.contexts;
3 import gnu.trove.list.array.TIntArrayList;
4 import gnu.trove.map.hash.THashMap;
5 import gnu.trove.procedure.TObjectProcedure;
6 import gnu.trove.set.hash.THashSet;
8 import java.util.ArrayList;
9 import java.util.Arrays;
11 import org.simantics.scl.compiler.common.names.Name;
12 import org.simantics.scl.compiler.common.precedence.Associativity;
13 import org.simantics.scl.compiler.common.precedence.Precedence;
14 import org.simantics.scl.compiler.elaboration.expressions.Case;
15 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
16 import org.simantics.scl.compiler.elaboration.expressions.EEntityTypeAnnotation;
17 import org.simantics.scl.compiler.elaboration.expressions.EError;
18 import org.simantics.scl.compiler.elaboration.expressions.EFieldAccess;
19 import org.simantics.scl.compiler.elaboration.expressions.ELambda;
20 import org.simantics.scl.compiler.elaboration.expressions.EVar;
21 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
22 import org.simantics.scl.compiler.elaboration.expressions.Expression;
23 import org.simantics.scl.compiler.elaboration.expressions.Variable;
24 import org.simantics.scl.compiler.elaboration.expressions.accessor.FieldAccessor;
25 import org.simantics.scl.compiler.elaboration.expressions.accessor.IdAccessor;
26 import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement;
27 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
28 import org.simantics.scl.compiler.elaboration.query.pre.PreQuery;
29 import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
30 import org.simantics.scl.compiler.environment.AmbiguousNameException;
31 import org.simantics.scl.compiler.environment.Environment;
32 import org.simantics.scl.compiler.environment.Environments;
33 import org.simantics.scl.compiler.environment.LocalEnvironment;
34 import org.simantics.scl.compiler.environment.Namespace;
35 import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter;
36 import org.simantics.scl.compiler.errors.ErrorLog;
37 import org.simantics.scl.compiler.errors.Locations;
38 import org.simantics.scl.compiler.internal.parsing.declarations.DValueAst;
40 public class TranslationContext extends TypeTranslationContext implements EnvironmentalContext {
42 THashMap<String, Variable> variables = new THashMap<String, Variable>();
43 ArrayList<Entry> variableEntries = new ArrayList<Entry>();
44 LocalEnvironment localEnvironment;
45 TIntArrayList frames = new TIntArrayList();
46 ArrayList<THashSet<String>> frameNameSets = new ArrayList<THashSet<String>>();
47 ArrayList<THashSet<String>> existentialFrames = new ArrayList<THashSet<String>>();
48 ArrayList<ArrayList<Variable>> blanksInExistentialFrame = new ArrayList<ArrayList<Variable>>();
49 SCLValue bindFunction;
51 public EEntityTypeAnnotation currentEntityTypeAnnotation;
52 public PreQuery currentPreQuery;
54 THashMap<String, SCLRelation> relations = new THashMap<String, SCLRelation>();
55 TIntArrayList relationFrames = new TIntArrayList();
56 ArrayList<RelationEntry> relationEntries = new ArrayList<RelationEntry>();
61 public Entry(String name, Variable variable) {
63 this.variable = variable;
67 static class RelationEntry {
70 public RelationEntry(String name, SCLRelation relation) {
72 this.relation = relation;
76 public TranslationContext(ErrorLog errorLog,
77 Environment environment, LocalEnvironment localEnvironment) {
78 super(errorLog, environment);
79 this.localEnvironment = localEnvironment;
82 public static boolean isConstructorName(String name) {
83 char firstChar = name.charAt(0);
84 return Character.isUpperCase(firstChar);
87 /* Tries to resolve name as a local variable. It is assumed
88 * that name does not contain '.'.
90 private Expression resolveLocalVariable(long location, String name) {
91 Variable variable = variables.get(name);
93 return new EVariable(location, variable);
95 char c = name.charAt(0);
98 if(existentialFrames.isEmpty()) {
99 errorLog.log(location, "Existential variables can be used only in queries.");
100 return new EError(location);
102 variable = new Variable(name);
103 variables.put(name, variable);
104 existentialFrames.get(existentialFrames.size()-1).add(name);
105 return new EVariable(variable);
107 if(name.length()==1) {
108 variable = new Variable("_");
109 if(blanksInExistentialFrame.isEmpty()) {
110 errorLog.log(location, "Cannot use blank variables in this context.");
111 return new EError(location);
113 blanksInExistentialFrame.get(blanksInExistentialFrame.size()-1).add(variable);
114 return new EVariable(variable);
118 if(name.length() > 1 && Character.isLetter(name.charAt(1))) {
119 if(currentEntityTypeAnnotation == null) {
120 errorLog.log(location, "Attribute references cannot be made in this context.");
121 return new EError(location);
123 return currentEntityTypeAnnotation.resolveAttribute(this, location, name.substring(1));
130 private FieldAccessor createFieldAccessor(char accessSeparator, String name) {
131 IdAccessor accessor = new IdAccessor(name);
132 accessor.accessSeparator = accessSeparator;
136 private Expression resolveFieldAccess(Expression base, int pos, String name) {
137 ArrayList<FieldAccessor> accessors = new ArrayList<FieldAccessor>(2);
139 int p = findSeparator(name, pos+1);
140 accessors.add(createFieldAccessor(
142 name.substring(pos+1, p==-1 ? name.length() : p-1)));
145 return new EFieldAccess(base,
146 accessors.toArray(new FieldAccessor[accessors.size()]));
149 private Expression resolveIn(long location, Namespace namespace, String name) {
150 SCLValue value = resolveValueIn(location, namespace, name);
152 return new EError(location);
153 return new EConstant(location, value);
156 private Expression resolveComplexNameIn(long location, Namespace namespace, int startPos, String name) {
157 int pos = name.length();
159 int hashPos = name.lastIndexOf('#');
163 while(pos > startPos) {
166 value = namespace.getValue(name.substring(startPos, pos));
167 } catch (AmbiguousNameException e) {
168 errorLog.log(location, e.getMessage());
169 return new EError(location);
172 Expression result = new EConstant(location, value);
173 if(pos < name.length())
174 result = resolveFieldAccess(result, pos, name);
177 pos = name.lastIndexOf('.', pos-1);
179 errorLog.log(location, "Couldn't resolve variable " + name + ".");
180 return new EError(location);
183 private static int findSeparator(String name, int fromIndex) {
184 while(fromIndex < name.length()) {
185 char c = name.charAt(fromIndex);
186 if(c == '.' || c == '#')
193 public Expression resolveExpression(long location, String name) {
194 int p = findSeparator(name, 1 /* Initial # is not a separator */);
196 Expression result = resolveLocalVariable(location, name);
200 if(localEnvironment != null) {
201 result = localEnvironment.resolve(environment, name);
203 result.setLocationDeep(location);
208 return resolveIn(location, environment.getLocalNamespace(), name);
211 if(localEnvironment != null) {
212 Expression result = localEnvironment.resolve(environment, name);
214 result.setLocationDeep(location);
219 String prefix = name.substring(0, p);
220 Expression result = resolveLocalVariable(location, prefix);
222 return resolveFieldAccess(result, p, name);
224 Namespace namespace = environment.getLocalNamespace();
226 while(name.charAt(p)=='.') {
227 Namespace temp = namespace.getNamespace(prefix);
232 p = findSeparator(name, pos);
234 return resolveIn(location, namespace, name.substring(pos));
235 prefix = name.substring(pos, p);
238 return resolveComplexNameIn(location, namespace, pos, name);
242 public Expression resolvePattern(EVar name) {
243 char firstChar = name.name.charAt(0);
244 if(firstChar == '_' && name.name.length()==1) {
245 return new EVariable(new Variable("_"));
247 else if(!Character.isUpperCase(firstChar)) {
248 if(!frameNameSets.get(frameNameSets.size()-1).add(name.name))
249 errorLog.log(name.location, "Repeated variable "+name.name+" in pattern.");
250 return new EVariable(name.location, newVariable(name.name));
253 return resolveExpression(name.location, name.name);
257 * Starts a new environment frame. New variables defined in this frame shadow
258 * the old variables and when the frame is popped, the old variables are again
261 public void pushFrame() {
262 frames.add(variableEntries.size());
263 frameNameSets.add(new THashSet<String>());
267 * Ends an environment frame. See {@link #pushFrame}.
269 public void popFrame() {
270 int frame = frames.removeAt(frames.size()-1);
271 int i = variableEntries.size();
274 Entry entry = variableEntries.remove(i);
275 if(entry.variable == null)
276 variables.remove(entry.name);
278 variables.put(entry.name, entry.variable);
280 frameNameSets.remove(frameNameSets.size()-1);
283 public void pushRelationFrame() {
284 relationFrames.add(relationEntries.size());
287 public void popRelationFrame() {
288 int frame = relationFrames.removeAt(relationFrames.size()-1);
289 int i = relationEntries.size();
292 RelationEntry entry = relationEntries.remove(i);
293 if(entry.relation == null)
294 relations.remove(entry.name);
296 relations.put(entry.name, entry.relation);
300 public void pushExistentialFrame() {
302 existentialFrames.add(new THashSet<String>());
303 blanksInExistentialFrame.add(new ArrayList<Variable>(2));
306 public Variable[] popExistentialFrame() {
308 THashSet<String> set = existentialFrames.remove(existentialFrames.size()-1);
309 ArrayList<Variable> blanks = blanksInExistentialFrame.remove(blanksInExistentialFrame.size()-1);
310 Variable[] result = new Variable[set.size() + blanks.size()];
312 for(String name : set)
313 result[i++] = variables.remove(name);
314 for(Variable blank : blanks)
319 public Variable newVariable(String name) {
320 Variable variable = new Variable(name);
321 Variable oldVariable = variables.put(name, variable);
322 variableEntries.add(new Entry(name, oldVariable));
326 public THashMap<String, Variable> getVariables() {
330 public void newRelation(String name, SCLRelation relation) {
331 SCLRelation oldRelation = relations.put(name, relation);
332 relationEntries.add(new RelationEntry(name, oldRelation));
335 public Precedence getPrecedence(Name op) {
336 Precedence prec = environment.getValue(op).getPrecedence();
338 return new Precedence(1, Associativity.NONASSOC);
343 private SCLValue resolveValueIn(long location, Namespace namespace, final String name) {
345 SCLValue value = namespace.getValue(name);
347 StringBuilder message = new StringBuilder();
348 message.append("Couldn't resolve variable ").append(name).append(".");
350 final THashSet<String> candidateNames = new THashSet<String>(4);
351 namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE,
352 new TObjectProcedure<SCLValue>() {
354 public boolean execute(SCLValue value) {
356 new Exception().printStackTrace();
359 String valueName = value.getName().name;
360 if(name.equalsIgnoreCase(valueName))
361 candidateNames.add(valueName);
365 if(localEnvironment != null)
366 localEnvironment.forNames(new TObjectProcedure<String>() {
368 public boolean execute(String valueName) {
369 if(name.equalsIgnoreCase(valueName))
370 candidateNames.add(valueName);
375 if(candidateNames.size() > 0) {
376 message.append(" Did you mean ");
377 String[] ns = candidateNames.toArray(new String[candidateNames.size()]);
379 for(int i=0;i<ns.length;++i) {
381 message.append(", ");
383 message.append("or ");
385 message.append(ns[i]);
390 errorLog.log(location, message.toString());
394 } catch (AmbiguousNameException e) {
395 errorLog.log(location, e.getMessage());
400 public Case translateCase(Expression lhs, Expression rhs) {
401 ArrayList<Expression> parameters = new ArrayList<Expression>(4);
402 lhs.getParameters(this, parameters);
403 Expression[] patterns = new Expression[parameters.size()];
405 for(int i=0;i<patterns.length;++i) {
406 Expression pattern = parameters.get(i);
407 pattern = pattern.resolveAsPattern(this);
408 patterns[i] = pattern;
410 rhs = rhs.resolve(this);
412 Case case_ = new Case(patterns, rhs);
413 case_.setLhs(lhs.location);
417 public Expression translateCases2(ArrayList<DValueAst> definitions) {
418 Case[] cases = new Case[definitions.size()];
419 for(int i=0;i<cases.length;++i) {
420 DValueAst def = definitions.get(i);
421 cases[i] = translateCase(def.lhs, def.value);
423 // check arity consistency
424 int arity = cases[0].patterns.length;
425 for(int i=1;i<cases.length;++i)
426 if(cases[i].patterns.length != arity)
427 errorLog.log(definitions.get(i).lhs.location,
428 "Inconsistent arity. " +
429 "This case has arity " + cases[i].patterns.length +
430 " while previous cases had arity " + arity + ".");
431 if(cases.length == 1 && cases[0].patterns.length == 0)
432 return cases[0].value;
435 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
439 public Expression translateCases(ArrayList<LetStatement> definitions) {
440 Case[] cases = new Case[definitions.size()];
441 for(int i=0;i<cases.length;++i) {
442 LetStatement def = definitions.get(i);
443 cases[i] = translateCase(def.pattern, def.value);
445 // check arity concistency
446 int arity = cases[0].patterns.length;
447 for(int i=1;i<cases.length;++i)
448 if(cases[i].patterns.length != arity)
449 errorLog.log(definitions.get(i).pattern.location,
450 "Inconsistent arity. " +
451 "This case has arity " + cases[i].patterns.length +
452 " while previous cases had arity " + arity + ".");
455 errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function.");
456 return cases[0].value;
459 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
463 private static final Name BIND = Name.create("Prelude", ">>=");
465 public SCLValue getBindFunction() {
466 if(bindFunction == null) {
467 bindFunction = getEnvironment().getValue(BIND);
472 public SCLRelation resolveRelation(long location, String name) {
473 SCLRelation relation = relations.get(name);
478 relation = Environments.getRelation(environment, name);
479 /*if(relation == null) {
480 errorLog.log(location, "Couldn't resolve relation " + name + ".");
484 } catch (AmbiguousNameException e) {
485 errorLog.log(location, e.getMessage());
491 public SCLValue getValue(Name name) {
492 return environment.getValue(name);