package org.simantics.scl.compiler.environment; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import org.simantics.scl.compiler.common.names.Name; import org.simantics.scl.compiler.compilation.CompilationContext; import org.simantics.scl.compiler.elaboration.chr.CHRRuleset; import org.simantics.scl.compiler.elaboration.contexts.TypeTranslationContext; import org.simantics.scl.compiler.elaboration.modules.SCLValue; import org.simantics.scl.compiler.elaboration.modules.TypeClass; import org.simantics.scl.compiler.elaboration.modules.TypeDescriptor; import org.simantics.scl.compiler.elaboration.relations.SCLEntityType; import org.simantics.scl.compiler.elaboration.relations.SCLRelation; import org.simantics.scl.compiler.elaboration.rules.MappingRelation; import org.simantics.scl.compiler.elaboration.rules.TransformationRule; import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter; import org.simantics.scl.compiler.internal.codegen.effects.EffectConstructor; import org.simantics.scl.compiler.internal.parsing.exceptions.SCLSyntaxErrorException; import org.simantics.scl.compiler.internal.parsing.parser.SCLParserImpl; import org.simantics.scl.compiler.internal.parsing.types.TypeAst; import org.simantics.scl.compiler.top.SCLExpressionCompilationException; import org.simantics.scl.compiler.types.TCon; import org.simantics.scl.compiler.types.Type; import gnu.trove.procedure.TObjectProcedure; public class Environments { /** * Get the SCLValue object representing an SCL value defined in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return An SCLValue instance, or null if not found. * @throws AmbiguousNameException if the same name is found in multiple imported modules. */ public static SCLValue getValue(Environment environment, String localName) throws AmbiguousNameException { return getEnvironmentEntry(environment, localName, getValue); } /** * Get the SCLRelation object representing an SCL relation defined in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return An SCLRelation instance, or null if not found. * @throws AmbiguousNameException if the same name is found in multiple imported modules. */ public static SCLRelation getRelation(Environment environment, String localName) throws AmbiguousNameException { return getEnvironmentEntry(environment, localName, getRelation); } public static MappingRelation getMappingRelation(Environment environment, String localName) throws AmbiguousNameException { return getEnvironmentEntry(environment, localName, getMappingRelation); } public static TransformationRule getRule(Environment environment, String localName) throws AmbiguousNameException { return getEnvironmentEntry(environment, localName, getRule); } /** * Get the SCLEntityType object representing an entity type defined in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return An SCLEntityType instance, or null if not found. * @throws AmbiguousNameException if the same name is found in multiple imported modules. */ public static SCLEntityType getEntityType(Environment environment, String localName) throws AmbiguousNameException { return getEnvironmentEntry(environment, localName, getEntityType); } /** * Get the TypeConstructor object representing an type defined in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return A TypeConstructor instance, or null if not found. * @throws AmbiguousNameException if the same name is found in multiple imported modules. */ public static TypeDescriptor getTypeDescriptor(Environment environment, String localName) throws AmbiguousNameException { return getEnvironmentEntry(environment, localName, getTypeDescriptor); } /** * Get the EffectConstructor object representing an effect defined in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return An EffectConstructor instance, or null if not found. * @throws AmbiguousNameException if the same name is found in multiple imported modules. */ public static EffectConstructor getEffectConstructor(Environment environment, String localName) throws AmbiguousNameException { return getEnvironmentEntry(environment, localName, getEffectConstructor); } /** * Get the TypeClass object representing a type class defined in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return A TypeClass instance, or null if not found. * @throws AmbiguousNameException if the same name is found in multiple imported modules. */ public static TypeClass getTypeClass(Environment environment, String localName) throws AmbiguousNameException { return getEnvironmentEntry(environment, localName, getTypeClass); } public static CHRRuleset getRuleset(Environment environment, String localName) throws AmbiguousNameException { return getEnvironmentEntry(environment, localName, getRuleset); } /** * Get the Name object representing an SCL value defined in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return A Name instance, or null if not found. * @throws AmbiguousNameException if the same name is used in multiple imported modules. */ public static Name getValueName(Environment environment, String localName) throws AmbiguousNameException { SCLValue value = getValue(environment, localName); if(value == null) return null; else return value.getName(); } /** * Get the TCon object representing a type declared in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return A TCon instance, or null if not found. * @throws AmbiguousNameException if the same name is used in multiple imported modules. */ public static TCon getTypeDescriptorName(Environment environment, String localName) throws AmbiguousNameException { TypeDescriptor typeDescriptor = getTypeDescriptor(environment, localName); if(typeDescriptor == null) return null; else return typeDescriptor.name; } /** * Get the TCon object representing an effect declared in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return A TCon instance, or null if not found. * @throws AmbiguousNameException if the same name is used in multiple imported modules. */ public static TCon getEffectConstructorName(Environment environment, String localName) throws AmbiguousNameException { EffectConstructor effectConstructor = getEffectConstructor(environment, localName); if(effectConstructor == null) return null; else return effectConstructor.name; } /** * Get the TCon object representing a type class declared in a given environment. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment the environment * @param localName the name to be searched for * @return A TCon instance, or null if not found. * @throws AmbiguousNameException if the same name is used in multiple imported modules. */ public static TCon getTypeClassName(Environment environment, String localName) throws AmbiguousNameException { TypeClass typeClass = getTypeClass(environment, localName); if(typeClass == null) return null; else return typeClass.name; } /** * Parse a given SCL type expression into a Type defined in a given environment. * @param environment the environment * @param typeText an SCL language type expression * @return A Type instance * @throws SCLExpressionCompilationException if the expression compilation fails */ public static Type getType(Environment environment, String typeText) throws SCLExpressionCompilationException { SCLParserImpl parser = new SCLParserImpl(new StringReader(typeText)); CompilationContext compilationContext = new CompilationContext(); compilationContext.environment = environment; try { TypeAst typeAst = (TypeAst)parser.parseType(); TypeTranslationContext context = new TypeTranslationContext(compilationContext); Type type = context.toType(typeAst); if(compilationContext.errorLog.hasNoErrors()) return type; } catch(SCLSyntaxErrorException e) { compilationContext.errorLog.log(e.location, e.getMessage()); } catch(Exception e) { compilationContext.errorLog.log(e); } throw new SCLExpressionCompilationException(compilationContext.errorLog.getErrors()); } /** * Find a list of values in an environment that share a common prefix. * The name can be a local name or a fully scoped name with modules separated by periods. * @param environment An environment * @param prefix A name prefix * @param values A collection into which the found values are added */ public static void findValuesForPrefix(Environment environment, String prefix, TObjectProcedure proc) { findValuesForPrefix(environment.getLocalNamespace(), prefix, proc); } public static List findValuesForPrefix(Environment environment, String prefix) { final ArrayList result = new ArrayList(); findValuesForPrefix(environment, prefix, new TObjectProcedure() { @Override public boolean execute(SCLValue value) { result.add(value); return true; } }); return result; } /** * Find a list of values in a namespace that share a common prefix. * The name can be a local name or a fully scoped name with modules separated by periods. * @param namespace An namespace * @param prefix A name prefix * @param values A collection into which the found values are added */ public static void findValuesForPrefix(Namespace namespace, String prefix, TObjectProcedure proc) { int p = prefix.indexOf('.'); if(p > 0) { Namespace childNamespace = namespace.getNamespace(prefix.substring(0, p)); if(childNamespace != null) findValuesForPrefix(childNamespace, prefix.substring(p+1), proc); } else namespace.findValuesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, proc); } public static List findTypesForPrefix(Environment environment, String prefix) { final List results = new ArrayList<>(); findTypesForPrefix(environment.getLocalNamespace(), prefix, new Consumer() { @Override public void accept(TCon tcon) { results.add(tcon); } }); return results; } /** * Find a list of values in a namespace that share a common prefix. * The name can be a local name or a fully scoped name with modules separated by periods. * @param namespace An namespace * @param prefix A name prefix * @param values A collection into which the found values are added */ public static void findTypesForPrefix(Namespace namespace, String prefix, Consumer consumer) { int p = prefix.indexOf('.'); if(p > 0) { Namespace childNamespace = namespace.getNamespace(prefix.substring(0, p)); if(childNamespace != null) findTypesForPrefix(childNamespace, prefix.substring(p+1), consumer); } else namespace.findTypesForPrefix(prefix, AcceptAllNamespaceFilter.INSTANCE, consumer); } /* Accessor objects for retrieving values from a namespace. */ private static interface NamespaceValueAccessor { public T get(Namespace ns, String name) throws AmbiguousNameException; } private static final NamespaceValueAccessor getValue = new NamespaceValueAccessor() { @Override public SCLValue get(Namespace ns, String name) throws AmbiguousNameException { return ns.getValue(name); } }; private static final NamespaceValueAccessor getRelation = new NamespaceValueAccessor() { @Override public SCLRelation get(Namespace ns, String name) throws AmbiguousNameException { return ns.getRelation(name); } }; private static final NamespaceValueAccessor getMappingRelation = new NamespaceValueAccessor() { @Override public MappingRelation get(Namespace ns, String name) throws AmbiguousNameException { return ns.getMappingRelation(name); } }; private static final NamespaceValueAccessor getRule = new NamespaceValueAccessor() { @Override public TransformationRule get(Namespace ns, String name) throws AmbiguousNameException { return ns.getRule(name); } }; private static final NamespaceValueAccessor getEntityType = new NamespaceValueAccessor() { @Override public SCLEntityType get(Namespace ns, String name) throws AmbiguousNameException { return ns.getEntityType(name); } }; private static final NamespaceValueAccessor getTypeDescriptor = new NamespaceValueAccessor() { @Override public TypeDescriptor get(Namespace ns, String name) throws AmbiguousNameException { return ns.getTypeDescriptor(name); } }; private static final NamespaceValueAccessor getEffectConstructor = new NamespaceValueAccessor() { @Override public EffectConstructor get(Namespace ns, String name) throws AmbiguousNameException { return ns.getEffectConstructor(name); } }; private static final NamespaceValueAccessor getTypeClass = new NamespaceValueAccessor() { @Override public TypeClass get(Namespace ns, String name) throws AmbiguousNameException { return ns.getTypeClass(name); } }; private static final NamespaceValueAccessor getRuleset = new NamespaceValueAccessor() { @Override public CHRRuleset get(Namespace ns, String name) throws AmbiguousNameException { return ns.getRuleset(name); } }; private static T getEnvironmentEntry(Environment environment, String localName, NamespaceValueAccessor accessor) throws AmbiguousNameException { Namespace namespace = environment.getLocalNamespace(); int curPos = 0; while(true) { int pos = localName.indexOf('.', curPos); if(pos < 0) return accessor.get(namespace, localName.substring(curPos)); Namespace newNamespace = namespace.getNamespace(localName.substring(curPos, pos)); if(newNamespace == null) return accessor.get(namespace, localName.substring(curPos)); namespace = newNamespace; curPos = pos + 1; } } }