package org.simantics.scl.compiler.elaboration.contexts; import java.util.ArrayList; import java.util.Arrays; import org.simantics.scl.compiler.common.names.Name; import org.simantics.scl.compiler.common.precedence.Associativity; import org.simantics.scl.compiler.common.precedence.Precedence; import org.simantics.scl.compiler.compilation.CompilationContext; import org.simantics.scl.compiler.elaboration.chr.CHRRuleset; import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint; import org.simantics.scl.compiler.elaboration.expressions.Case; import org.simantics.scl.compiler.elaboration.expressions.EAmbiguous; import org.simantics.scl.compiler.elaboration.expressions.EConstant; import org.simantics.scl.compiler.elaboration.expressions.EError; import org.simantics.scl.compiler.elaboration.expressions.EFieldAccess; import org.simantics.scl.compiler.elaboration.expressions.ELambda; import org.simantics.scl.compiler.elaboration.expressions.EVar; import org.simantics.scl.compiler.elaboration.expressions.EVariable; import org.simantics.scl.compiler.elaboration.expressions.Expression; import org.simantics.scl.compiler.elaboration.expressions.Variable; import org.simantics.scl.compiler.elaboration.expressions.accessor.FieldAccessor; import org.simantics.scl.compiler.elaboration.expressions.accessor.IdAccessor; import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement; import org.simantics.scl.compiler.elaboration.modules.SCLValue; import org.simantics.scl.compiler.elaboration.query.pre.PreQuery; import org.simantics.scl.compiler.elaboration.relations.SCLRelation; import org.simantics.scl.compiler.environment.AmbiguousNameException; import org.simantics.scl.compiler.environment.Environments; import org.simantics.scl.compiler.environment.LocalEnvironment; import org.simantics.scl.compiler.environment.Namespace; import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter; import org.simantics.scl.compiler.errors.Locations; import org.simantics.scl.compiler.internal.parsing.declarations.DValueAst; import org.simantics.scl.compiler.module.debug.ModuleDebugInfo; import org.simantics.scl.compiler.module.debug.SymbolReference; import org.simantics.scl.compiler.top.SCLCompilerConfiguration; import org.simantics.scl.compiler.types.Type; import gnu.trove.list.array.TIntArrayList; import gnu.trove.map.hash.THashMap; import gnu.trove.procedure.TObjectProcedure; import gnu.trove.set.hash.THashSet; public class TranslationContext extends TypeTranslationContext implements EnvironmentalContext { public static class ExistentialFrame { THashSet variables = new THashSet(4); ArrayList blanks = new ArrayList(2); public boolean disallowNewExistentials; public EVariable createBlank(long location) { Variable variable = new Variable("_"); blanks.add(variable); EVariable result = new EVariable(variable); result.location = location; return result; } } THashMap variables = new THashMap(); ArrayList variableEntries = new ArrayList(); LocalEnvironment localEnvironment; TIntArrayList frames = new TIntArrayList(); ArrayList> frameNameSets = new ArrayList>(); ArrayList existentialFrames = new ArrayList(2); SCLValue bindFunction; public PreQuery currentPreQuery; THashMap relations = new THashMap(); TIntArrayList relationFrames = new TIntArrayList(); ArrayList relationEntries = new ArrayList(); THashMap chrConstraints = new THashMap(); TIntArrayList chrConstraintFrames = new TIntArrayList(); ArrayList chrConstraintEntries = new ArrayList(); public CHRRuleset currentRuleset; public ModuleDebugInfo moduleDebugInfo; private String definitionName; static class Entry { String name; Variable variable; public Entry(String name, Variable variable) { this.name = name; this.variable = variable; } } static class RelationEntry { String name; SCLRelation relation; public RelationEntry(String name, SCLRelation relation) { this.name = name; this.relation = relation; } } static class CHRConstraintEntry { String name; CHRConstraint constraint; public CHRConstraintEntry(String name, CHRConstraint constraint) { this.name = name; this.constraint = constraint; } } public TranslationContext(CompilationContext compilationContext, LocalEnvironment localEnvironment, String definitionName) { super(compilationContext); this.localEnvironment = localEnvironment; this.moduleDebugInfo = compilationContext.moduleDebugInfo; this.definitionName = definitionName; } public static boolean isConstructorName(String name) { int p = name.lastIndexOf('.'); char firstChar = name.charAt(p<0 ? 0 : p+1); return Character.isUpperCase(firstChar) || firstChar == '('; } /* Tries to resolve name as a local variable. It is assumed * that name does not contain '.'. */ private Expression resolveLocalVariable(long location, String name) { Variable variable = variables.get(name); if(variable != null) return new EVariable(location, variable); char c = name.charAt(0); switch(c) { case '?': { ExistentialFrame existentialFrame = getCurrentExistentialFrame(); if(existentialFrame == null || existentialFrame.disallowNewExistentials) { errorLog.log(location, "New existential variables can be defined only in queries."); return new EError(location); } variable = new Variable(name); variables.put(name, variable); existentialFrame.variables.add(name); return new EVariable(variable); } case '_': { if(name.length()==1) { ExistentialFrame existentialFrame = getCurrentExistentialFrame(); if(existentialFrame == null || existentialFrame.disallowNewExistentials) { errorLog.log(location, "Blank variables can be used only in queries."); return new EError(location); } return existentialFrame.createBlank(location); } break; } } return null; } public ExistentialFrame getCurrentExistentialFrame() { int size = existentialFrames.size(); if(size == 0) return null; else return existentialFrames.get(size-1); } private Expression resolveFieldAccess(long location, Expression base, int pos, String name) { while(pos != -1) { int p = findSeparator(name, pos+1); int endPos = p==-1 ? name.length() : p; FieldAccessor accessor = new IdAccessor( name.charAt(pos), name.substring(pos+1, endPos)); accessor.location = Locations.sublocation(location, pos+1, endPos); base = new EFieldAccess(base, accessor); base.location = Locations.sublocation(location, 0, endPos); pos = p; } return base; } private static int findSeparator(String name, int fromIndex) { ++fromIndex; // the first character (that can be #) is never a separator while(fromIndex < name.length()) { char c = name.charAt(fromIndex); if(c == '.' || c == '#') return fromIndex; ++fromIndex; } return -1; } public SCLValue resolveSCLValue(long location, Namespace namespace, String name) throws AmbiguousNameException { SCLValue value = namespace.getValue(name); if(value == null) return null; String deprecatedDescription = value.isDeprecated(); if(deprecatedDescription != null) errorLog.logWarning(location, "Deprecated value " + value.getName().name + "." + (deprecatedDescription.isEmpty() ? "" : " " + deprecatedDescription)); if(moduleDebugInfo != null) moduleDebugInfo.symbolReferences.add(new SymbolReference(value.getName(), Name.create(compilationContext.module.getName(), definitionName), location)); return value; } public Expression resolveValue(long location, Namespace namespace, String name) { try { SCLValue value = resolveSCLValue(location, namespace, name); if(value == null) return null; return new EConstant(location, value); } catch (AmbiguousNameException e) { if(SCLCompilerConfiguration.ALLOW_OVERLOADING) return resolveAmbigious(location, e.conflictingModules, name); else { errorLog.log(location, e.getMessage()); return new EError(location); } } } private Expression resolveAmbigious(long location, String[] conflictingModules, String name) { EAmbiguous.Alternative[] alternatives = new EAmbiguous.Alternative[conflictingModules.length]; for(int i=0;i end;end2 = name.lastIndexOf('.', end2-1)) { part = name.substring(begin, end2); result = resolveValue(location, namespace, part); if(result != null) { end = end2 == name.length() ? -1 : end2; break; } } } if(result == null) result = resolveValue(location, namespace, part); if(result != null) return end == -1 ? result : resolveFieldAccess(location, result, end, name); } reportResolveFailure(location, namespace, part); return new EError(location); } public SCLValue resolveRecordConstructor(long location, String name) throws AmbiguousNameException { return resolveRecordConstructor(location, getEnvironment().getLocalNamespace(), name, 0); } public SCLValue resolveRecordConstructor(long location, Namespace namespace, String name, int begin) throws AmbiguousNameException { int end = findSeparator(name, begin); String part = end == -1 ? (begin == 0 ? name : name.substring(begin)) : name.substring(begin, end); if(end != -1 && name.charAt(end) == '.') { Namespace childNamespace = namespace.getNamespace(part); if(childNamespace == null) return null; else return resolveRecordConstructor(location, childNamespace, name, end+1); } else return resolveSCLValue(location, namespace, part); } private void reportResolveFailure(long location, Namespace namespace, String name) { StringBuilder message = new StringBuilder(); message.append("Couldn't resolve ").append(name).append("."); final THashSet candidateNames = new THashSet(4); namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE, new TObjectProcedure() { @Override public boolean execute(SCLValue value) { if(value == null) { new Exception().printStackTrace(); return true; } String valueName = value.getName().name; if(name.equalsIgnoreCase(valueName)) candidateNames.add(valueName); return true; } }); if(localEnvironment != null) localEnvironment.forNames(new TObjectProcedure() { @Override public boolean execute(String valueName) { if(name.equalsIgnoreCase(valueName)) candidateNames.add(valueName); return true; } }); if(candidateNames.size() > 0) { message.append(" Did you mean "); String[] ns = candidateNames.toArray(new String[candidateNames.size()]); Arrays.sort(ns); for(int i=0;i 0) { message.append(", "); if(i == ns.length-1) message.append("or "); } message.append(ns[i]); } message.append('?'); } errorLog.log(location, message.toString()); } public Expression resolveVariable(long location, String name) { return resolveVariable(location, environment.getLocalNamespace(), name, 0); } public Expression resolvePattern(EVar name) { char firstChar = name.name.charAt(0); if(firstChar == '_' && name.name.length()==1) { return new EVariable(new Variable("_")); } else if(!Character.isUpperCase(firstChar)) { if(!frameNameSets.get(frameNameSets.size()-1).add(name.name)) errorLog.log(name.location, "Repeated variable "+name.name+" in pattern."); return new EVariable(name.location, newVariable(name.name)); } else return resolveVariable(name.location, name.name); } /** * Starts a new environment frame. New variables defined in this frame shadow * the old variables and when the frame is popped, the old variables are again * visible. */ public void pushFrame() { frames.add(variableEntries.size()); frameNameSets.add(new THashSet()); } /** * Ends an environment frame. See {@link #pushFrame}. */ public void popFrame() { int frame = frames.removeAt(frames.size()-1); int i = variableEntries.size(); while(i > frame) { --i; Entry entry = variableEntries.remove(i); if(entry.variable == null) variables.remove(entry.name); else variables.put(entry.name, entry.variable); } frameNameSets.remove(frameNameSets.size()-1); } public void pushRelationFrame() { relationFrames.add(relationEntries.size()); } public void popRelationFrame() { int frame = relationFrames.removeAt(relationFrames.size()-1); int i = relationEntries.size(); while(i > frame) { --i; RelationEntry entry = relationEntries.remove(i); if(entry.relation == null) relations.remove(entry.name); else relations.put(entry.name, entry.relation); } } public void pushCHRConstraintFrame() { chrConstraintFrames.add(chrConstraintEntries.size()); } public void popCHRConstraintFrame(CHRRuleset ruleset) { int frame = chrConstraintFrames.removeAt(chrConstraintFrames.size()-1); int i = chrConstraintEntries.size(); while(i > frame) { --i; CHRConstraintEntry entry = chrConstraintEntries.remove(i); CHRConstraint newConstraint; if(entry.constraint == null) newConstraint = chrConstraints.remove(entry.name); else newConstraint = chrConstraints.put(entry.name, entry.constraint); if(newConstraint.implicitlyDeclared) ruleset.addConstraint(newConstraint); } } public void pushExistentialFrame() { pushFrame(); existentialFrames.add(new ExistentialFrame()); } public Variable[] popExistentialFrame() { popFrame(); ExistentialFrame frame = existentialFrames.remove(existentialFrames.size()-1); Variable[] result = new Variable[frame.variables.size() + frame.blanks.size()]; int i=0; for(String name : frame.variables) result[i++] = variables.remove(name); for(Variable blank : frame.blanks) result[i++] = blank; return result; } public Variable newVariable(String name) { Variable variable = new Variable(name); Variable oldVariable = variables.put(name, variable); variableEntries.add(new Entry(name, oldVariable)); return variable; } public THashMap getVariables() { return variables; } public void newRelation(String name, SCLRelation relation) { SCLRelation oldRelation = relations.put(name, relation); relationEntries.add(new RelationEntry(name, oldRelation)); } public void newCHRConstraint(String name, CHRConstraint constraint) { CHRConstraint oldConstraint = chrConstraints.put(name, constraint); chrConstraintEntries.add(new CHRConstraintEntry(name, oldConstraint)); } public Precedence getPrecedence(Name op) { Precedence prec = environment.getValue(op).getPrecedence(); if(prec == null) return new Precedence(1, Associativity.NONASSOC); else return prec; } public Case translateCase(Expression lhs, Expression rhs) { ArrayList parameters = new ArrayList(4); lhs.getParameters(this, parameters); Expression[] patterns = new Expression[parameters.size()]; pushFrame(); for(int i=0;i definitions) { Case[] cases = new Case[definitions.size()]; for(int i=0;i definitions) { Case[] cases = new Case[definitions.size()]; for(int i=0;i 1) errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function."); return cases[0].value; } return new ELambda( Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location), cases); } public SCLRelation resolveRelation(long location, String name) { SCLRelation relation = relations.get(name); if(relation != null) return relation; try { relation = Environments.getRelation(environment, name); /*if(relation == null) { errorLog.log(location, "Couldn't resolve relation " + name + "."); return null; }*/ return relation; } catch (AmbiguousNameException e) { errorLog.log(location, e.getMessage()); return null; } } public CHRConstraint resolveCHRConstraint(String name) { return chrConstraints.get(name); } @Override public SCLValue getValue(Name name) { return environment.getValue(name); } public CHRRuleset resolveRuleset(String name) throws AmbiguousNameException { return Environments.getRuleset(environment, name); } public void disallowNewExistentials() { getCurrentExistentialFrame().disallowNewExistentials = true; } }