From c3e27396c62f191b4343df151e5a86bfa63a32f3 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Tue, 5 Oct 2021 00:36:47 +0200 Subject: chore: change package name --- language/build.gradle | 6 +- .../viatra/solver/language/GenerateProblem.mwe2 | 65 ----- .../eclipse/viatra/solver/language/Problem.xtext | 162 ----------- .../solver/language/ProblemRuntimeModule.java | 83 ------ .../solver/language/ProblemStandaloneSetup.java | 33 --- .../viatra/solver/language/ProblemUtil.java | 93 ------ .../conversion/ProblemValueConverterService.java | 19 -- .../conversion/UpperBoundValueConverter.java | 35 --- .../viatra/solver/language/naming/NamingUtil.java | 25 -- .../naming/ProblemQualifiedNameConverter.java | 15 - .../language/resource/DerivedVariableComputer.java | 189 ------------ .../language/resource/NodeNameCollector.java | 87 ------ .../resource/ProblemDerivedStateComputer.java | 162 ----------- .../resource/ProblemLocationInFileProvider.java | 32 -- .../ProblemResourceDescriptionStrategy.java | 102 ------- .../scoping/ProblemGlobalScopeProvider.java | 25 -- .../scoping/ProblemLocalScopeProvider.java | 40 --- .../language/scoping/ProblemScopeProvider.java | 92 ------ .../language/validation/ProblemValidator.java | 13 - .../tools/refinery/language/GenerateProblem.mwe2 | 65 +++++ .../java/tools/refinery/language/Problem.xtext | 162 +++++++++++ .../refinery/language/ProblemRuntimeModule.java | 84 ++++++ .../refinery/language/ProblemStandaloneSetup.java | 34 +++ .../java/tools/refinery/language/ProblemUtil.java | 94 ++++++ .../conversion/ProblemValueConverterService.java | 19 ++ .../conversion/UpperBoundValueConverter.java | 35 +++ .../tools/refinery/language/naming/NamingUtil.java | 25 ++ .../naming/ProblemQualifiedNameConverter.java | 15 + .../language/resource/DerivedVariableComputer.java | 190 ++++++++++++ .../language/resource/NodeNameCollector.java | 88 ++++++ .../resource/ProblemDerivedStateComputer.java | 163 +++++++++++ .../resource/ProblemLocationInFileProvider.java | 33 +++ .../ProblemResourceDescriptionStrategy.java | 103 +++++++ .../scoping/ProblemGlobalScopeProvider.java | 25 ++ .../scoping/ProblemLocalScopeProvider.java | 40 +++ .../language/scoping/ProblemScopeProvider.java | 93 ++++++ .../language/validation/ProblemValidator.java | 13 + .../solver/language/tests/ProblemParsingTest.xtend | 63 ---- .../solver/language/tests/ProblemTestUtil.xtend | 114 -------- .../language/tests/scoping/NodeScopingTest.xtend | 322 --------------------- .../language/tests/ProblemParsingTest.xtend | 63 ++++ .../refinery/language/tests/ProblemTestUtil.xtend | 114 ++++++++ .../language/tests/scoping/NodeScopingTest.xtend | 322 +++++++++++++++++++++ 43 files changed, 1783 insertions(+), 1774 deletions(-) delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2 delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/Problem.xtext delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/ProblemRuntimeModule.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/ProblemStandaloneSetup.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/ProblemUtil.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/conversion/ProblemValueConverterService.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/conversion/UpperBoundValueConverter.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/naming/NamingUtil.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/naming/ProblemQualifiedNameConverter.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/resource/DerivedVariableComputer.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/resource/NodeNameCollector.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemDerivedStateComputer.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemLocationInFileProvider.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemResourceDescriptionStrategy.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemGlobalScopeProvider.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemLocalScopeProvider.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemScopeProvider.java delete mode 100644 language/src/main/java/org/eclipse/viatra/solver/language/validation/ProblemValidator.java create mode 100644 language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 create mode 100644 language/src/main/java/tools/refinery/language/Problem.xtext create mode 100644 language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java create mode 100644 language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java create mode 100644 language/src/main/java/tools/refinery/language/ProblemUtil.java create mode 100644 language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java create mode 100644 language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java create mode 100644 language/src/main/java/tools/refinery/language/naming/NamingUtil.java create mode 100644 language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java create mode 100644 language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java create mode 100644 language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java create mode 100644 language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java create mode 100644 language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java create mode 100644 language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java create mode 100644 language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java create mode 100644 language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java create mode 100644 language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java create mode 100644 language/src/main/java/tools/refinery/language/validation/ProblemValidator.java delete mode 100644 language/src/test/java/org/eclipse/viatra/solver/language/tests/ProblemParsingTest.xtend delete mode 100644 language/src/test/java/org/eclipse/viatra/solver/language/tests/ProblemTestUtil.xtend delete mode 100644 language/src/test/java/org/eclipse/viatra/solver/language/tests/scoping/NodeScopingTest.xtend create mode 100644 language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.xtend create mode 100644 language/src/test/java/tools/refinery/language/tests/ProblemTestUtil.xtend create mode 100644 language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.xtend (limited to 'language') diff --git a/language/build.gradle b/language/build.gradle index 0b8492c3..85b0902f 100644 --- a/language/build.gradle +++ b/language/build.gradle @@ -34,14 +34,14 @@ tasks.named('jar') { def generateXtextLanguage = tasks.register('generateXtextLanguage', JavaExec) { mainClass = 'org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher' classpath = configurations.mwe2 - inputs.file 'src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2' - inputs.file 'src/main/java/org/eclipse/viatra/solver/language/Problem.xtext' + inputs.file 'src/main/java/tools/refinery/language/GenerateProblem.mwe2' + inputs.file 'src/main/java/tools/refinery/language/Problem.xtext' outputs.dir 'src/main/xtext-gen' outputs.dir 'src/testFixtures/xtext-gen' outputs.dir '../language-ide/src/main/xtext-gen' outputs.dir '../language-web/src/main/xtext-gen' outputs.dir '../language-web/build/generated/sources/xtext' - args += 'src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2' + args += 'src/main/java/tools/refinery/language/GenerateProblem.mwe2' args += '-p' args += "rootPath=/${projectDir}/.." } diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2 b/language/src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2 deleted file mode 100644 index 4dfbb242..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/GenerateProblem.mwe2 +++ /dev/null @@ -1,65 +0,0 @@ -module org.eclipse.viatra.solver.language.GenerateProblem - -import org.eclipse.xtext.xtext.generator.* -import org.eclipse.xtext.xtext.generator.model.project.* - -import org.eclipse.viatra.solver.language.mwe2.* - -var rootPath = ".." - -Workflow { - component = XtextGenerator { - configuration = { - project = StandardProjectConfig { - baseName = "language" - rootPath = rootPath - runtimeTest = { - enabled = true - srcGen = 'src/testFixtures/xtext-gen' - } - genericIde = { - name = "language-ide" - } - web = { - enabled = true - name = "language-web" - assets = "../language-web/build/generated/sources/xtext" - } - mavenLayout = true - } - code = { - encoding = "UTF-8" - lineDelimiter = "\n" - fileHeader = "/*\n * generated by Xtext \${version}\n */" - preferXtendStubs = false - } - } - - language = StandardLanguage { - name = "org.eclipse.viatra.solver.language.Problem" - fileExtensions = "problem" - referencedResource = "platform:/resource/refinery-language-model/model/problem.genmodel" - serializer = { - generateStub = false - } - validator = { - generateDeprecationValidation = true - } - generator = { - generateStub = false - } - junitSupport = { - generateStub = false - skipXbaseTestingPackage = true - junitVersion = "5" - } - webSupport = ProblemWebIntegrationFragment { - highlightingPath = "js/mode-problem.js" - generateHtmlExample = false - generateJettyLauncher = false - generateServlet = false - generateWebXml = false - } - } - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/Problem.xtext b/language/src/main/java/org/eclipse/viatra/solver/language/Problem.xtext deleted file mode 100644 index d4fa6f35..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/Problem.xtext +++ /dev/null @@ -1,162 +0,0 @@ -grammar org.eclipse.viatra.solver.language.Problem with org.eclipse.xtext.common.Terminals - -import "http://www.eclipse.org/emf/2002/Ecore" as ecore -import "http://www.eclipse.org/viatra/solver/language/model/Problem" - -Problem: - ("problem" name=Identifier ".")? - statements+=Statement*; - -Statement: - ClassDeclaration | EnumDeclaration | PredicateDefinition | Assertion | NodeValueAssertion | ScopeDeclaration | - UniqueDeclaration; - -ClassDeclaration: - abstract?="abstract"? "class" - name=Identifier - ("extends" superTypes+=[Relation|QualifiedName] ("," superTypes+=[Relation|QualifiedName])*)? - ("{" (referenceDeclarations+=ReferenceDeclaration ";"?)* "}" | "."); - -EnumDeclaration: - "enum" - name=Identifier - ("{" (literals+=EnumLiteral ("," literals+=EnumLiteral)* ("," | ";")?)? "}" | "."); - -EnumLiteral returns Node: - name=Identifier; - -ReferenceDeclaration: - (-> (containment?="contains" | "refers"))? - referenceType=[Relation|QualifiedName] - ("[" multiplicity=Multiplicity "]")? - name=Identifier - ("opposite" opposite=[ReferenceDeclaration|QualifiedName])?; - -PredicateDefinition: - (error?="error" "pred"? | "pred") - name=Identifier - "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" - ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)? - "."; - -Parameter: - parameterType=[Relation|QualifiedName]? name=Identifier; - -Conjunction: - literals+=Literal ("," literals+=Literal)*; - -Literal: - Atom | NegativeLiteral; - -NegativeLiteral: - "!" atom=Atom; - -Atom: - relation=[Relation|QualifiedName] - transitiveClosure?="+"? - "(" (arguments+=Argument ("," arguments+=Argument)*)? ")"; - -Argument: - VariableOrNodeArgument | ConstantArgument; - -VariableOrNodeArgument: - variableOrNode=[VariableOrNode|QualifiedName]; - -ConstantArgument: - constant=Constant; - -Assertion: - default?="default"? - (relation=[Relation|QualifiedName] - "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")" - ":" value=LogicValue | - value=ShortLogicValue? - relation=[Relation|QualifiedName] - "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")") - "."; - -AssertionArgument: - NodeAssertionArgument | WildcardAssertionArgument | ConstantAssertionArgument; - -NodeAssertionArgument: - node=[Node|QualifiedName]; - -WildcardAssertionArgument: - {WildcardAssertionArgument} "*"; - -ConstantAssertionArgument: - constant=Constant; - -enum LogicValue: - TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error"; - -enum ShortLogicValue returns LogicValue: - FALSE="!" | UNKNOWN="?"; - -NodeValueAssertion: - node=[Node|QualifiedName] ":" value=Constant "."; - -Constant: - RealConstant | IntConstant | StringConstant; - -IntConstant: - intValue=Integer; - -RealConstant: - realValue=Real; - -StringConstant: - stringValue=STRING; - -ScopeDeclaration: - "scope" typeScopes+=TypeScope ("," typeScopes+=TypeScope)* "."; - -TypeScope: - targetType=[ClassDeclaration] - (increment?="+=" | "=") - multiplicity=DefiniteMultiplicity; - -Multiplicity: - UnboundedMultiplicity | DefiniteMultiplicity; - -DefiniteMultiplicity returns Multiplicity: - RangeMultiplicity | ExactMultiplicity; - -UnboundedMultiplicity: - {UnboundedMultiplicity}; - -RangeMultiplicity: - lowerBound=INT ".." upperBound=UpperBound; - -ExactMultiplicity: - exactValue=INT; - -UniqueDeclaration: - "unique" nodes+=EnumLiteral ("," nodes+=EnumLiteral)* "."; - -UpperBound returns ecore::EInt: - INT | "*"; - -QualifiedName hidden(): - Identifier ("::" Identifier)*; - -Identifier: - ID | "true" | "false" | "unknown" | "error" | "class" | "abstract" | "extends" | "enum" | "pred" | "scope" | - "unique" | "default" | "problem" | "contains" | "refers"; - -Integer returns ecore::EInt hidden(): - "-"? INT; - -Real returns ecore::EDouble: - "-"? (EXPONENTIAL | INT "." (INT | EXPONENTIAL)); - -@Override -terminal ID: - ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*; - -terminal EXPONENTIAL: - INT ("e" | "E") ("+" | "-")? INT; - -@Override -terminal SL_COMMENT: - ('%' | '//') !('\n' | '\r')* ('\r'? '\n')?; diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemRuntimeModule.java b/language/src/main/java/org/eclipse/viatra/solver/language/ProblemRuntimeModule.java deleted file mode 100644 index 9fec7d75..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemRuntimeModule.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * generated by Xtext 2.25.0 - */ -package org.eclipse.viatra.solver.language; - -import org.eclipse.viatra.solver.language.conversion.ProblemValueConverterService; -import org.eclipse.viatra.solver.language.naming.ProblemQualifiedNameConverter; -import org.eclipse.viatra.solver.language.resource.ProblemDerivedStateComputer; -import org.eclipse.viatra.solver.language.resource.ProblemLocationInFileProvider; -import org.eclipse.viatra.solver.language.resource.ProblemResourceDescriptionStrategy; -import org.eclipse.viatra.solver.language.scoping.ProblemGlobalScopeProvider; -import org.eclipse.viatra.solver.language.scoping.ProblemLocalScopeProvider; -import org.eclipse.xtext.conversion.IValueConverterService; -import org.eclipse.xtext.naming.IQualifiedNameConverter; -import org.eclipse.xtext.resource.DerivedStateAwareResource; -import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager; -import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy; -import org.eclipse.xtext.resource.IDerivedStateComputer; -import org.eclipse.xtext.resource.ILocationInFileProvider; -import org.eclipse.xtext.resource.IResourceDescription; -import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.scoping.IGlobalScopeProvider; -import org.eclipse.xtext.scoping.IScopeProvider; -import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; -import org.eclipse.xtext.validation.IResourceValidator; -import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator; - -import com.google.inject.Binder; -import com.google.inject.name.Names; - -/** - * Use this class to register components to be used at runtime / without the - * Equinox extension registry. - */ -public class ProblemRuntimeModule extends AbstractProblemRuntimeModule { - public Class bindIQualifiedNameConverter() { - return ProblemQualifiedNameConverter.class; - } - - public Class bindIDefaultResourceDescriptionStrategy() { - return ProblemResourceDescriptionStrategy.class; - } - - @Override - public Class bindIValueConverterService() { - return ProblemValueConverterService.class; - } - - @Override - public Class bindIGlobalScopeProvider() { - return ProblemGlobalScopeProvider.class; - } - - @Override - public void configureIScopeProviderDelegate(Binder binder) { - binder.bind(IScopeProvider.class).annotatedWith(Names.named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)) - .to(ProblemLocalScopeProvider.class); - } - - @Override - public Class bindXtextResource() { - return DerivedStateAwareResource.class; - } - - // Method name follows Xtext convention. - @SuppressWarnings("squid:S100") - public Class bindIResourceDescription$Manager() { - return DerivedStateAwareResourceDescriptionManager.class; - } - - public Class bindIResourceValidator() { - return DerivedStateAwareResourceValidator.class; - } - - public Class bindIDerivedStateComputer() { - return ProblemDerivedStateComputer.class; - } - - @Override - public Class bindILocationInFileProvider() { - return ProblemLocationInFileProvider.class; - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemStandaloneSetup.java b/language/src/main/java/org/eclipse/viatra/solver/language/ProblemStandaloneSetup.java deleted file mode 100644 index 11e5ad8a..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemStandaloneSetup.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * generated by Xtext 2.25.0 - */ -package org.eclipse.viatra.solver.language; - -import org.eclipse.emf.ecore.EPackage; -import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; - -import com.google.inject.Injector; - -/** - * Initialization support for running Xtext languages without Equinox extension - * registry. - */ -public class ProblemStandaloneSetup extends ProblemStandaloneSetupGenerated { - - public static void doSetup() { - new ProblemStandaloneSetup().createInjectorAndDoEMFRegistration(); - } - - @Override - // Here we can't rely on java.util.HashMap#computeIfAbsent, because - // org.eclipse.emf.ecore.impl.EPackageRegistryImpl#containsKey is overridden - // without also overriding computeIfAbsent. We must make sure to call the - // overridden containsKey implementation. - @SuppressWarnings("squid:S3824") - public Injector createInjectorAndDoEMFRegistration() { - if (!EPackage.Registry.INSTANCE.containsKey(ProblemPackage.eNS_URI)) { - EPackage.Registry.INSTANCE.put(ProblemPackage.eNS_URI, ProblemPackage.eINSTANCE); - } - return super.createInjectorAndDoEMFRegistration(); - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemUtil.java b/language/src/main/java/org/eclipse/viatra/solver/language/ProblemUtil.java deleted file mode 100644 index a2938274..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/ProblemUtil.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.eclipse.viatra.solver.language; - -import java.util.ArrayDeque; -import java.util.Collection; -import java.util.Deque; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.viatra.solver.language.model.problem.ClassDeclaration; -import org.eclipse.viatra.solver.language.model.problem.Node; -import org.eclipse.viatra.solver.language.model.problem.Problem; -import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; -import org.eclipse.viatra.solver.language.model.problem.ReferenceDeclaration; -import org.eclipse.viatra.solver.language.model.problem.Relation; -import org.eclipse.viatra.solver.language.model.problem.Variable; -import org.eclipse.viatra.solver.language.scoping.ProblemGlobalScopeProvider; - -import com.google.common.collect.ImmutableList; - -public final class ProblemUtil { - private ProblemUtil() { - throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); - } - - public static final String NODE_CLASS_NAME = "node"; - - public static boolean isSingletonVariable(Variable variable) { - return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE; - } - - public static boolean isUniqueNode(Node node) { - var containingFeature = node.eContainingFeature(); - return containingFeature == ProblemPackage.Literals.UNIQUE_DECLARATION__NODES - || containingFeature == ProblemPackage.Literals.ENUM_DECLARATION__LITERALS; - } - - public static boolean isNewNode(Node node) { - return node.eContainingFeature() == ProblemPackage.Literals.CLASS_DECLARATION__NEW_NODE; - } - - public static Optional getBuiltInLibrary(EObject context) { - return Optional.ofNullable(context.eResource()).map(Resource::getResourceSet) - .map(resourceSet -> resourceSet.getResource(ProblemGlobalScopeProvider.BULTIN_LIBRARY_URI, true)) - .map(Resource::getContents).filter(contents -> !contents.isEmpty()).map(contents -> contents.get(0)) - .filter(Problem.class::isInstance).map(Problem.class::cast); - } - - public static boolean isBuiltIn(EObject eObject) { - if (eObject != null) { - var eResource = eObject.eResource(); - if (eResource != null) { - return ProblemGlobalScopeProvider.BULTIN_LIBRARY_URI.equals(eResource.getURI()); - } - } - return false; - } - - public static Optional getNodeClassDeclaration(EObject context) { - return getBuiltInLibrary(context).flatMap(problem -> problem.getStatements().stream() - .filter(ClassDeclaration.class::isInstance).map(ClassDeclaration.class::cast) - .filter(declaration -> NODE_CLASS_NAME.equals(declaration.getName())).findFirst()); - } - - public static Collection getSuperclassesAndSelf(ClassDeclaration classDeclaration) { - Set found = new HashSet<>(); - getNodeClassDeclaration(classDeclaration).ifPresent(found::add); - Deque queue = new ArrayDeque<>(); - queue.addLast(classDeclaration); - while (!queue.isEmpty()) { - ClassDeclaration current = queue.removeFirst(); - if (!found.contains(current)) { - found.add(current); - for (Relation superType : current.getSuperTypes()) { - if (superType instanceof ClassDeclaration superDeclaration) { - queue.addLast(superDeclaration); - } - } - } - } - return found; - } - - public static Collection getAllReferenceDeclarations(ClassDeclaration classDeclaration) { - ImmutableList.Builder builder = ImmutableList.builder(); - for (ClassDeclaration superclass : getSuperclassesAndSelf(classDeclaration)) { - builder.addAll(superclass.getReferenceDeclarations()); - } - return builder.build(); - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/conversion/ProblemValueConverterService.java b/language/src/main/java/org/eclipse/viatra/solver/language/conversion/ProblemValueConverterService.java deleted file mode 100644 index 1c665e6f..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/conversion/ProblemValueConverterService.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.eclipse.viatra.solver.language.conversion; - -import org.eclipse.xtext.common.services.DefaultTerminalConverters; -import org.eclipse.xtext.conversion.IValueConverter; -import org.eclipse.xtext.conversion.ValueConverter; - -import com.google.inject.Inject; - -public class ProblemValueConverterService extends DefaultTerminalConverters { - @Inject - private UpperBoundValueConverter upperBoundValueConverter; - - @ValueConverter(rule = "UpperBound") - // Method name follows Xtext convention. - @SuppressWarnings("squid:S100") - public IValueConverter UpperBound() { - return upperBoundValueConverter; - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/conversion/UpperBoundValueConverter.java b/language/src/main/java/org/eclipse/viatra/solver/language/conversion/UpperBoundValueConverter.java deleted file mode 100644 index 3111b69b..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/conversion/UpperBoundValueConverter.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.eclipse.viatra.solver.language.conversion; - -import org.eclipse.xtext.conversion.ValueConverterException; -import org.eclipse.xtext.conversion.impl.AbstractValueConverter; -import org.eclipse.xtext.conversion.impl.INTValueConverter; -import org.eclipse.xtext.nodemodel.INode; - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -@Singleton -public class UpperBoundValueConverter extends AbstractValueConverter { - public static final String INFINITY = "*"; - - @Inject - private INTValueConverter intValueConverter; - - @Override - public Integer toValue(String string, INode node) throws ValueConverterException { - if (INFINITY.equals(string)) { - return -1; - } else { - return intValueConverter.toValue(string, node); - } - } - - @Override - public String toString(Integer value) throws ValueConverterException { - if (value < 0) { - return INFINITY; - } else { - return intValueConverter.toString(value); - } - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/naming/NamingUtil.java b/language/src/main/java/org/eclipse/viatra/solver/language/naming/NamingUtil.java deleted file mode 100644 index edd455bb..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/naming/NamingUtil.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.eclipse.viatra.solver.language.naming; - -import java.util.regex.Pattern; - -public final class NamingUtil { - private static final String SINGLETON_VARIABLE_PREFIX = "_"; - - private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z][_0-9a-zA-Z]*"); - - private NamingUtil() { - throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); - } - - public static boolean isNullOrEmpty(String name) { - return name == null || name.isEmpty(); - } - - public static boolean isSingletonVariableName(String name) { - return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX); - } - - public static boolean isValidId(String name) { - return name != null && ID_REGEX.matcher(name).matches(); - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/naming/ProblemQualifiedNameConverter.java b/language/src/main/java/org/eclipse/viatra/solver/language/naming/ProblemQualifiedNameConverter.java deleted file mode 100644 index e55e5dda..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/naming/ProblemQualifiedNameConverter.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.eclipse.viatra.solver.language.naming; - -import org.eclipse.xtext.naming.IQualifiedNameConverter; - -import com.google.inject.Singleton; - -@Singleton -public class ProblemQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl { - public static final String DELIMITER = "::"; - - @Override - public String getDelimiter() { - return DELIMITER; - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/resource/DerivedVariableComputer.java b/language/src/main/java/org/eclipse/viatra/solver/language/resource/DerivedVariableComputer.java deleted file mode 100644 index 2789f590..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/resource/DerivedVariableComputer.java +++ /dev/null @@ -1,189 +0,0 @@ -package org.eclipse.viatra.solver.language.resource; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.eclipse.viatra.solver.language.model.problem.Argument; -import org.eclipse.viatra.solver.language.model.problem.Atom; -import org.eclipse.viatra.solver.language.model.problem.Conjunction; -import org.eclipse.viatra.solver.language.model.problem.ExistentialQuantifier; -import org.eclipse.viatra.solver.language.model.problem.ImplicitVariable; -import org.eclipse.viatra.solver.language.model.problem.Literal; -import org.eclipse.viatra.solver.language.model.problem.NegativeLiteral; -import org.eclipse.viatra.solver.language.model.problem.Parameter; -import org.eclipse.viatra.solver.language.model.problem.PredicateDefinition; -import org.eclipse.viatra.solver.language.model.problem.Problem; -import org.eclipse.viatra.solver.language.model.problem.ProblemFactory; -import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; -import org.eclipse.viatra.solver.language.model.problem.Statement; -import org.eclipse.viatra.solver.language.model.problem.VariableOrNodeArgument; -import org.eclipse.viatra.solver.language.naming.NamingUtil; -import org.eclipse.xtext.linking.impl.LinkingHelper; -import org.eclipse.xtext.naming.IQualifiedNameConverter; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; -import org.eclipse.xtext.scoping.IScope; -import org.eclipse.xtext.scoping.IScopeProvider; -import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.google.inject.name.Named; - -@Singleton -public class DerivedVariableComputer { - @Inject - private LinkingHelper linkingHelper; - - @Inject - private IQualifiedNameConverter qualifiedNameConverter; - - @Inject - @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) - private IScopeProvider scopeProvider; - - public void installDerivedVariables(Problem problem, Set nodeNames) { - for (Statement statement : problem.getStatements()) { - if (statement instanceof PredicateDefinition definition) { - installDerivedPredicateDefinitionState(definition, nodeNames); - } - } - } - - protected void installDerivedPredicateDefinitionState(PredicateDefinition definition, Set nodeNames) { - Set knownVariables = new HashSet<>(); - knownVariables.addAll(nodeNames); - for (Parameter parameter : definition.getParameters()) { - String name = parameter.getName(); - if (name != null) { - knownVariables.add(name); - } - } - for (Conjunction body : definition.getBodies()) { - installDeriveConjunctionState(body, knownVariables); - } - } - - protected void installDeriveConjunctionState(Conjunction conjunction, Set knownVariables) { - Set newVariables = new HashSet<>(); - for (Literal literal : conjunction.getLiterals()) { - if (literal instanceof Atom atom) { - createSigletonVariablesAndCollectVariables(atom, knownVariables, newVariables); - } - } - createVariables(conjunction, newVariables); - newVariables.addAll(knownVariables); - for (Literal literal : conjunction.getLiterals()) { - if (literal instanceof NegativeLiteral negativeLiteral) { - installDeriveNegativeLiteralState(negativeLiteral, newVariables); - } - } - } - - protected void installDeriveNegativeLiteralState(NegativeLiteral negativeLiteral, Set knownVariables) { - Set newVariables = new HashSet<>(); - createSigletonVariablesAndCollectVariables(negativeLiteral.getAtom(), knownVariables, newVariables); - createVariables(negativeLiteral, newVariables); - } - - protected void createSigletonVariablesAndCollectVariables(Atom atom, Set knownVariables, - Set newVariables) { - for (Argument argument : atom.getArguments()) { - if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) { - IScope scope = scopeProvider.getScope(variableOrNodeArgument, - ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE); - List nodes = NodeModelUtils.findNodesForFeature(variableOrNodeArgument, - ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE); - for (INode node : nodes) { - var variableName = linkingHelper.getCrossRefNodeAsString(node, true); - var created = tryCreateVariableForArgument(variableOrNodeArgument, variableName, scope, - knownVariables, newVariables); - if (created) { - break; - } - } - } - } - } - - protected boolean tryCreateVariableForArgument(VariableOrNodeArgument variableOrNodeArgument, String variableName, - IScope scope, Set knownVariables, Set newVariables) { - if (!NamingUtil.isValidId(variableName)) { - return false; - } - var qualifiedName = qualifiedNameConverter.toQualifiedName(variableName); - if (scope.getSingleElement(qualifiedName) != null) { - return false; - } - if (NamingUtil.isSingletonVariableName(variableName)) { - createSingletonVariable(variableOrNodeArgument, variableName); - return true; - } - if (!knownVariables.contains(variableName)) { - newVariables.add(variableName); - return true; - } - return false; - } - - protected void createVariables(ExistentialQuantifier quantifier, Set newVariables) { - for (String variableName : newVariables) { - createVariable(quantifier, variableName); - } - } - - protected void createVariable(ExistentialQuantifier quantifier, String variableName) { - if (NamingUtil.isValidId(variableName)) { - ImplicitVariable variable = createNamedVariable(variableName); - quantifier.getImplicitVariables().add(variable); - } - } - - protected void createSingletonVariable(VariableOrNodeArgument argument, String variableName) { - if (NamingUtil.isValidId(variableName)) { - ImplicitVariable variable = createNamedVariable(variableName); - argument.setSingletonVariable(variable); - } - } - - protected ImplicitVariable createNamedVariable(String variableName) { - var variable = ProblemFactory.eINSTANCE.createImplicitVariable(); - variable.setName(variableName); - return variable; - } - - public void discardDerivedVariables(Problem problem) { - for (Statement statement : problem.getStatements()) { - if (statement instanceof PredicateDefinition predicateDefinition) { - discardPredicateDefinitionState(predicateDefinition); - } - } - } - - protected void discardPredicateDefinitionState(PredicateDefinition definition) { - for (Conjunction body : definition.getBodies()) { - body.getImplicitVariables().clear(); - for (Literal literal : body.getLiterals()) { - if (literal instanceof Atom atom) { - discardDerivedAtomState(atom); - } - if (literal instanceof NegativeLiteral negativeLiteral) { - negativeLiteral.getImplicitVariables().clear(); - discardDerivedAtomState(negativeLiteral.getAtom()); - } - } - } - } - - protected void discardDerivedAtomState(Atom atom) { - if (atom == null) { - return; - } - for (Argument argument : atom.getArguments()) { - if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) { - variableOrNodeArgument.setSingletonVariable(null); - } - } - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/resource/NodeNameCollector.java b/language/src/main/java/org/eclipse/viatra/solver/language/resource/NodeNameCollector.java deleted file mode 100644 index 415db9a9..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/resource/NodeNameCollector.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.eclipse.viatra.solver.language.resource; - -import java.util.List; -import java.util.Set; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EStructuralFeature; -import org.eclipse.viatra.solver.language.model.problem.Assertion; -import org.eclipse.viatra.solver.language.model.problem.AssertionArgument; -import org.eclipse.viatra.solver.language.model.problem.NodeAssertionArgument; -import org.eclipse.viatra.solver.language.model.problem.NodeValueAssertion; -import org.eclipse.viatra.solver.language.model.problem.Problem; -import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; -import org.eclipse.viatra.solver.language.model.problem.Statement; -import org.eclipse.viatra.solver.language.naming.NamingUtil; -import org.eclipse.xtext.linking.impl.LinkingHelper; -import org.eclipse.xtext.naming.IQualifiedNameConverter; -import org.eclipse.xtext.nodemodel.INode; -import org.eclipse.xtext.nodemodel.util.NodeModelUtils; -import org.eclipse.xtext.scoping.IScope; -import org.eclipse.xtext.scoping.IScopeProvider; -import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; - -import com.google.common.collect.ImmutableSet; -import com.google.inject.Inject; -import com.google.inject.name.Named; - -public class NodeNameCollector { - @Inject - private LinkingHelper linkingHelper; - - @Inject - private IQualifiedNameConverter qualifiedNameConverter; - - @Inject - @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) - private IScopeProvider scopeProvider; - - private final ImmutableSet.Builder nodeNames = ImmutableSet.builder(); - - private IScope nodeScope; - - public Set getNodeNames() { - return nodeNames.build(); - } - - public void collectNodeNames(Problem problem) { - nodeScope = scopeProvider.getScope(problem, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE); - for (Statement statement : problem.getStatements()) { - collectStatementNodeNames(statement); - } - } - - protected void collectStatementNodeNames(Statement statement) { - if (statement instanceof Assertion assertion) { - collectAssertionNodeNames(assertion); - } else if (statement instanceof NodeValueAssertion nodeValueAssertion) { - collectNodeValueAssertionNodeNames(nodeValueAssertion); - } - } - - protected void collectAssertionNodeNames(Assertion assertion) { - for (AssertionArgument argument : assertion.getArguments()) { - if (argument instanceof NodeAssertionArgument) { - collectNodeNames(argument, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE); - } - } - } - - protected void collectNodeValueAssertionNodeNames(NodeValueAssertion nodeValueAssertion) { - collectNodeNames(nodeValueAssertion, ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE); - } - - private void collectNodeNames(EObject eObject, EStructuralFeature feature) { - List nodes = NodeModelUtils.findNodesForFeature(eObject, feature); - for (INode node : nodes) { - var nodeName = linkingHelper.getCrossRefNodeAsString(node, true); - if (!NamingUtil.isValidId(nodeName)) { - continue; - } - var qualifiedName = qualifiedNameConverter.toQualifiedName(nodeName); - if (nodeScope.getSingleElement(qualifiedName) == null) { - nodeNames.add(nodeName); - } - } - } -} \ No newline at end of file diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemDerivedStateComputer.java b/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemDerivedStateComputer.java deleted file mode 100644 index 3c6c8273..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemDerivedStateComputer.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.eclipse.viatra.solver.language.resource; - -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -import org.eclipse.emf.common.notify.impl.AdapterImpl; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.viatra.solver.language.model.problem.ClassDeclaration; -import org.eclipse.viatra.solver.language.model.problem.Node; -import org.eclipse.viatra.solver.language.model.problem.Problem; -import org.eclipse.viatra.solver.language.model.problem.ProblemFactory; -import org.eclipse.viatra.solver.language.model.problem.Statement; -import org.eclipse.xtext.Constants; -import org.eclipse.xtext.resource.DerivedStateAwareResource; -import org.eclipse.xtext.resource.IDerivedStateComputer; -import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.scoping.IScopeProvider; -import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; - -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.Singleton; -import com.google.inject.name.Named; - -@Singleton -public class ProblemDerivedStateComputer implements IDerivedStateComputer { - public static final String NEW_NODE = "new"; - - @Inject - @Named(Constants.LANGUAGE_NAME) - private String languageName; - - @Inject - @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) - private IScopeProvider scopeProvider; - - @Inject - private Provider nodeNameCollectorProvider; - - @Inject - private DerivedVariableComputer derivedVariableComputer; - - @Override - public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) { - var problem = getProblem(resource); - if (problem != null) { - var adapter = getOrInstallAdapter(resource); - installDerivedProblemState(problem, adapter, preLinkingPhase); - } - } - - protected Problem getProblem(Resource resource) { - List contents = resource.getContents(); - if (contents.isEmpty()) { - return null; - } - EObject object = contents.get(0); - if (object instanceof Problem problem) { - return problem; - } - return null; - } - - protected void installDerivedProblemState(Problem problem, Adapter adapter, boolean preLinkingPhase) { - installNewNodes(problem, adapter); - if (preLinkingPhase) { - return; - } - Set nodeNames = installDerivedNodes(problem); - derivedVariableComputer.installDerivedVariables(problem, nodeNames); - } - - protected void installNewNodes(Problem problem, Adapter adapter) { - for (Statement statement : problem.getStatements()) { - if (statement instanceof ClassDeclaration declaration && !declaration.isAbstract() - && declaration.getNewNode() == null) { - var newNode = adapter.createNodeIfAbsent(declaration, key -> createNode(NEW_NODE)); - declaration.setNewNode(newNode); - } - } - } - - protected Set installDerivedNodes(Problem problem) { - var collector = nodeNameCollectorProvider.get(); - collector.collectNodeNames(problem); - Set nodeNames = collector.getNodeNames(); - List grapNodes = problem.getNodes(); - for (String nodeName : nodeNames) { - var graphNode = createNode(nodeName); - grapNodes.add(graphNode); - } - return nodeNames; - } - - protected Node createNode(String name) { - var node = ProblemFactory.eINSTANCE.createNode(); - node.setName(name); - return node; - } - - @Override - public void discardDerivedState(DerivedStateAwareResource resource) { - var problem = getProblem(resource); - if (problem != null) { - var adapter = getOrInstallAdapter(resource); - discardDerivedProblemState(problem, adapter); - } - } - - protected void discardDerivedProblemState(Problem problem, Adapter adapter) { - Set classDeclarations = new HashSet<>(); - problem.getNodes().clear(); - for (Statement statement : problem.getStatements()) { - if (statement instanceof ClassDeclaration classDeclaration) { - classDeclaration.setNewNode(null); - classDeclarations.add(classDeclaration); - } - } - adapter.retainAll(classDeclarations); - derivedVariableComputer.discardDerivedVariables(problem); - } - - protected Adapter getOrInstallAdapter(Resource resource) { - if (!(resource instanceof XtextResource)) { - return new Adapter(); - } - String resourceLanguageName = ((XtextResource) resource).getLanguageName(); - if (!languageName.equals(resourceLanguageName)) { - return new Adapter(); - } - var adapter = (Adapter) EcoreUtil.getAdapter(resource.eAdapters(), Adapter.class); - if (adapter == null) { - adapter = new Adapter(); - resource.eAdapters().add(adapter); - } - return adapter; - } - - protected static class Adapter extends AdapterImpl { - private Map newNodes = new HashMap<>(); - - public Node createNodeIfAbsent(ClassDeclaration classDeclaration, Function createNode) { - return newNodes.computeIfAbsent(classDeclaration, createNode); - } - - public void retainAll(Collection classDeclarations) { - newNodes.keySet().retainAll(classDeclarations); - } - - @Override - public boolean isAdapterForType(Object type) { - return Adapter.class == type; - } - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemLocationInFileProvider.java b/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemLocationInFileProvider.java deleted file mode 100644 index bd05e8cf..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemLocationInFileProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.eclipse.viatra.solver.language.resource; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.viatra.solver.language.ProblemUtil; -import org.eclipse.viatra.solver.language.model.problem.ImplicitVariable; -import org.eclipse.viatra.solver.language.model.problem.Node; -import org.eclipse.xtext.resource.DefaultLocationInFileProvider; -import org.eclipse.xtext.util.ITextRegion; - -public class ProblemLocationInFileProvider extends DefaultLocationInFileProvider { - @Override - protected ITextRegion doGetTextRegion(EObject obj, RegionDescription query) { - if (obj instanceof Node node) { - return getNodeTextRegion(node, query); - } - if (obj instanceof ImplicitVariable) { - return ITextRegion.EMPTY_REGION; - } - return super.doGetTextRegion(obj, query); - } - - protected ITextRegion getNodeTextRegion(Node node, RegionDescription query) { - if (ProblemUtil.isUniqueNode(node)) { - return super.doGetTextRegion(node, query); - } - if (ProblemUtil.isNewNode(node)) { - EObject container = node.eContainer(); - return doGetTextRegion(container, query); - } - return ITextRegion.EMPTY_REGION; - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemResourceDescriptionStrategy.java b/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemResourceDescriptionStrategy.java deleted file mode 100644 index cd5020e7..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/resource/ProblemResourceDescriptionStrategy.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.eclipse.viatra.solver.language.resource; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.viatra.solver.language.ProblemUtil; -import org.eclipse.viatra.solver.language.model.problem.NamedElement; -import org.eclipse.viatra.solver.language.model.problem.Node; -import org.eclipse.viatra.solver.language.model.problem.Problem; -import org.eclipse.viatra.solver.language.model.problem.Variable; -import org.eclipse.viatra.solver.language.naming.NamingUtil; -import org.eclipse.xtext.EcoreUtil2; -import org.eclipse.xtext.naming.IQualifiedNameConverter; -import org.eclipse.xtext.naming.QualifiedName; -import org.eclipse.xtext.resource.EObjectDescription; -import org.eclipse.xtext.resource.IEObjectDescription; -import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy; -import org.eclipse.xtext.util.IAcceptor; - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -@Singleton -public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { - @Inject - private IQualifiedNameConverter qualifiedNameConverter; - - @Override - public boolean createEObjectDescriptions(EObject eObject, IAcceptor acceptor) { - if (!shouldExport(eObject)) { - return false; - } - var qualifiedName = getNameAsQualifiedName(eObject); - if (qualifiedName == null) { - return true; - } - var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class); - var problemQualifiedName = getNameAsQualifiedName(problem); - boolean nameExported; - if (shouldExportSimpleName(eObject)) { - acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor); - nameExported = true; - } else { - nameExported = false; - } - var parent = eObject.eContainer(); - while (parent != null && parent != problem) { - var parentQualifiedName = getNameAsQualifiedName(parent); - if (parentQualifiedName == null) { - parent = parent.eContainer(); - continue; - } - qualifiedName = parentQualifiedName.append(qualifiedName); - if (shouldExportSimpleName(parent)) { - acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor); - nameExported = true; - } else { - nameExported = false; - } - parent = parent.eContainer(); - } - if (!nameExported) { - acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor); - } - return true; - } - - protected QualifiedName getNameAsQualifiedName(EObject eObject) { - if (!(eObject instanceof NamedElement)) { - return null; - } - var namedElement = (NamedElement) eObject; - var name = namedElement.getName(); - if (NamingUtil.isNullOrEmpty(name)) { - return null; - } - return qualifiedNameConverter.toQualifiedName(name); - } - - protected boolean shouldExport(EObject eObject) { - if (eObject instanceof Variable) { - // Variables are always private to the containing predicate definition. - return false; - } - if (eObject instanceof Node node) { - // Only enum literals and new nodes are visible across problem files. - return ProblemUtil.isUniqueNode(node) || ProblemUtil.isNewNode(node); - } - return true; - } - - protected boolean shouldExportSimpleName(EObject eObject) { - if (eObject instanceof Node node) { - return !ProblemUtil.isNewNode(node); - } - return true; - } - - private void acceptEObjectDescription(EObject eObject, QualifiedName prefix, QualifiedName qualifiedName, - IAcceptor acceptor) { - var qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName); - acceptor.accept(EObjectDescription.create(qualifiedNameWithPrefix, eObject)); - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemGlobalScopeProvider.java b/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemGlobalScopeProvider.java deleted file mode 100644 index 05da277a..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemGlobalScopeProvider.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.eclipse.viatra.solver.language.scoping; - -import java.util.LinkedHashSet; - -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; - -public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider { - public static final String BUILTIN_LIBRARY_NAME = "builtin"; - - public static final URI BULTIN_LIBRARY_URI = getLibraryUri(BUILTIN_LIBRARY_NAME); - - @Override - protected LinkedHashSet getImportedUris(Resource resource) { - LinkedHashSet importedUris = new LinkedHashSet<>(); - importedUris.add(BULTIN_LIBRARY_URI); - return importedUris; - } - - private static URI getLibraryUri(String libraryName) { - return URI.createURI(ProblemGlobalScopeProvider.class.getClassLoader() - .getResource("org/eclipse/viatra/solver/language/" + libraryName + ".problem").toString()); - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemLocalScopeProvider.java b/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemLocalScopeProvider.java deleted file mode 100644 index e832bb09..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemLocalScopeProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.eclipse.viatra.solver.language.scoping; - -import java.util.List; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.xtext.naming.QualifiedName; -import org.eclipse.xtext.resource.IResourceDescriptions; -import org.eclipse.xtext.resource.IResourceDescriptionsProvider; -import org.eclipse.xtext.resource.ISelectable; -import org.eclipse.xtext.scoping.impl.ImportNormalizer; -import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider; - -import com.google.inject.Inject; - -public class ProblemLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { - private static final QualifiedName BUILTIN_LIBRARY_QUALIFIED_NAME = QualifiedName - .create(ProblemGlobalScopeProvider.BUILTIN_LIBRARY_NAME); - - @Inject - private IResourceDescriptionsProvider resourceDescriptionsProvider; - - @Override - protected List getImplicitImports(boolean ignoreCase) { - return List.of(doCreateImportNormalizer(BUILTIN_LIBRARY_QUALIFIED_NAME, true, ignoreCase)); - } - - @Override - protected List getImportedNamespaceResolvers(EObject context, boolean ignoreCase) { - return List.of(); - } - - @Override - protected ISelectable internalGetAllDescriptions(Resource resource) { - // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects. - IResourceDescriptions resourceDescriptions = resourceDescriptionsProvider - .getResourceDescriptions(resource.getResourceSet()); - return resourceDescriptions.getResourceDescription(resource.getURI()); - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemScopeProvider.java b/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemScopeProvider.java deleted file mode 100644 index 56de6b1b..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/scoping/ProblemScopeProvider.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * generated by Xtext 2.25.0 - */ -package org.eclipse.viatra.solver.language.scoping; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EReference; -import org.eclipse.viatra.solver.language.ProblemUtil; -import org.eclipse.viatra.solver.language.model.problem.ClassDeclaration; -import org.eclipse.viatra.solver.language.model.problem.ExistentialQuantifier; -import org.eclipse.viatra.solver.language.model.problem.PredicateDefinition; -import org.eclipse.viatra.solver.language.model.problem.Problem; -import org.eclipse.viatra.solver.language.model.problem.ProblemPackage; -import org.eclipse.viatra.solver.language.model.problem.ReferenceDeclaration; -import org.eclipse.viatra.solver.language.model.problem.Variable; -import org.eclipse.viatra.solver.language.model.problem.VariableOrNodeArgument; -import org.eclipse.xtext.EcoreUtil2; -import org.eclipse.xtext.scoping.IScope; -import org.eclipse.xtext.scoping.Scopes; - -/** - * This class contains custom scoping description. - * - * See - * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping - * on how and when to use it. - */ -public class ProblemScopeProvider extends AbstractProblemScopeProvider { - - @Override - public IScope getScope(EObject context, EReference reference) { - var scope = super.getScope(context, reference); - if (reference == ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE - || reference == ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE) { - return getNodesScope(context, scope); - } - if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE) { - return getVariableScope(context, scope); - } - if (reference == ProblemPackage.Literals.REFERENCE_DECLARATION__OPPOSITE) { - return getOppositeScope(context, scope); - } - return scope; - } - - protected IScope getNodesScope(EObject context, IScope delegateScope) { - var problem = EcoreUtil2.getContainerOfType(context, Problem.class); - if (problem == null) { - return delegateScope; - } - return Scopes.scopeFor(problem.getNodes(), delegateScope); - } - - protected IScope getVariableScope(EObject context, IScope delegateScope) { - List variables = new ArrayList<>(); - EObject currentContext = context; - if (context instanceof VariableOrNodeArgument argument) { - Variable singletonVariable = argument.getSingletonVariable(); - if (singletonVariable != null) { - variables.add(singletonVariable); - } - } - while (currentContext != null && !(currentContext instanceof PredicateDefinition)) { - if (currentContext instanceof ExistentialQuantifier quantifier) { - variables.addAll(quantifier.getImplicitVariables()); - } - currentContext = currentContext.eContainer(); - } - if (currentContext != null) { - PredicateDefinition definition = (PredicateDefinition) currentContext; - variables.addAll(definition.getParameters()); - } - return Scopes.scopeFor(variables, getNodesScope(context, delegateScope)); - } - - protected IScope getOppositeScope(EObject context, IScope delegateScope) { - var referenceDeclaration = EcoreUtil2.getContainerOfType(context, ReferenceDeclaration.class); - if (referenceDeclaration == null) { - return delegateScope; - } - var relation = referenceDeclaration.getReferenceType(); - if (!(relation instanceof ClassDeclaration)) { - return delegateScope; - } - var classDeclaration = (ClassDeclaration) relation; - var referenceDeclarations = ProblemUtil.getAllReferenceDeclarations(classDeclaration); - return Scopes.scopeFor(referenceDeclarations, delegateScope); - } -} diff --git a/language/src/main/java/org/eclipse/viatra/solver/language/validation/ProblemValidator.java b/language/src/main/java/org/eclipse/viatra/solver/language/validation/ProblemValidator.java deleted file mode 100644 index 96b656a3..00000000 --- a/language/src/main/java/org/eclipse/viatra/solver/language/validation/ProblemValidator.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * generated by Xtext 2.25.0 - */ -package org.eclipse.viatra.solver.language.validation; - -/** - * This class contains custom validation rules. - * - * See - * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation - */ -public class ProblemValidator extends AbstractProblemValidator { -} diff --git a/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 b/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 new file mode 100644 index 00000000..1b177c94 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 @@ -0,0 +1,65 @@ +module tools.refinery.language.GenerateProblem + +import org.eclipse.xtext.xtext.generator.* +import org.eclipse.xtext.xtext.generator.model.project.* + +import tools.refinery.language.mwe2.* + +var rootPath = ".." + +Workflow { + component = XtextGenerator { + configuration = { + project = StandardProjectConfig { + baseName = "language" + rootPath = rootPath + runtimeTest = { + enabled = true + srcGen = 'src/testFixtures/xtext-gen' + } + genericIde = { + name = "language-ide" + } + web = { + enabled = true + name = "language-web" + assets = "../language-web/build/generated/sources/xtext" + } + mavenLayout = true + } + code = { + encoding = "UTF-8" + lineDelimiter = "\n" + fileHeader = "/*\n * generated by Xtext \${version}\n */" + preferXtendStubs = false + } + } + + language = StandardLanguage { + name = "tools.refinery.language.Problem" + fileExtensions = "problem" + referencedResource = "platform:/resource/refinery-language-model/model/problem.genmodel" + serializer = { + generateStub = false + } + validator = { + generateDeprecationValidation = true + } + generator = { + generateStub = false + } + junitSupport = { + generateStub = false + skipXbaseTestingPackage = true + junitVersion = "5" + } + webSupport = ProblemWebIntegrationFragment { + highlightingPath = "js/mode-problem.js" + generateHtmlExample = false + generateJettyLauncher = false + generateServlet = false + generateWebXml = false + } + } + } +} diff --git a/language/src/main/java/tools/refinery/language/Problem.xtext b/language/src/main/java/tools/refinery/language/Problem.xtext new file mode 100644 index 00000000..6b46ee86 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/Problem.xtext @@ -0,0 +1,162 @@ +grammar tools.refinery.language.Problem with org.eclipse.xtext.common.Terminals + +import "http://www.eclipse.org/emf/2002/Ecore" as ecore +import "https://refinery.tools/emf/2021/Problem" + +Problem: + ("problem" name=Identifier ".")? + statements+=Statement*; + +Statement: + ClassDeclaration | EnumDeclaration | PredicateDefinition | Assertion | NodeValueAssertion | ScopeDeclaration | + UniqueDeclaration; + +ClassDeclaration: + abstract?="abstract"? "class" + name=Identifier + ("extends" superTypes+=[Relation|QualifiedName] ("," superTypes+=[Relation|QualifiedName])*)? + ("{" (referenceDeclarations+=ReferenceDeclaration ";"?)* "}" | "."); + +EnumDeclaration: + "enum" + name=Identifier + ("{" (literals+=EnumLiteral ("," literals+=EnumLiteral)* ("," | ";")?)? "}" | "."); + +EnumLiteral returns Node: + name=Identifier; + +ReferenceDeclaration: + (-> (containment?="contains" | "refers"))? + referenceType=[Relation|QualifiedName] + ("[" multiplicity=Multiplicity "]")? + name=Identifier + ("opposite" opposite=[ReferenceDeclaration|QualifiedName])?; + +PredicateDefinition: + (error?="error" "pred"? | "pred") + name=Identifier + "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" + ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)? + "."; + +Parameter: + parameterType=[Relation|QualifiedName]? name=Identifier; + +Conjunction: + literals+=Literal ("," literals+=Literal)*; + +Literal: + Atom | NegativeLiteral; + +NegativeLiteral: + "!" atom=Atom; + +Atom: + relation=[Relation|QualifiedName] + transitiveClosure?="+"? + "(" (arguments+=Argument ("," arguments+=Argument)*)? ")"; + +Argument: + VariableOrNodeArgument | ConstantArgument; + +VariableOrNodeArgument: + variableOrNode=[VariableOrNode|QualifiedName]; + +ConstantArgument: + constant=Constant; + +Assertion: + default?="default"? + (relation=[Relation|QualifiedName] + "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")" + ":" value=LogicValue | + value=ShortLogicValue? + relation=[Relation|QualifiedName] + "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")") + "."; + +AssertionArgument: + NodeAssertionArgument | WildcardAssertionArgument | ConstantAssertionArgument; + +NodeAssertionArgument: + node=[Node|QualifiedName]; + +WildcardAssertionArgument: + {WildcardAssertionArgument} "*"; + +ConstantAssertionArgument: + constant=Constant; + +enum LogicValue: + TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error"; + +enum ShortLogicValue returns LogicValue: + FALSE="!" | UNKNOWN="?"; + +NodeValueAssertion: + node=[Node|QualifiedName] ":" value=Constant "."; + +Constant: + RealConstant | IntConstant | StringConstant; + +IntConstant: + intValue=Integer; + +RealConstant: + realValue=Real; + +StringConstant: + stringValue=STRING; + +ScopeDeclaration: + "scope" typeScopes+=TypeScope ("," typeScopes+=TypeScope)* "."; + +TypeScope: + targetType=[ClassDeclaration] + (increment?="+=" | "=") + multiplicity=DefiniteMultiplicity; + +Multiplicity: + UnboundedMultiplicity | DefiniteMultiplicity; + +DefiniteMultiplicity returns Multiplicity: + RangeMultiplicity | ExactMultiplicity; + +UnboundedMultiplicity: + {UnboundedMultiplicity}; + +RangeMultiplicity: + lowerBound=INT ".." upperBound=UpperBound; + +ExactMultiplicity: + exactValue=INT; + +UniqueDeclaration: + "unique" nodes+=EnumLiteral ("," nodes+=EnumLiteral)* "."; + +UpperBound returns ecore::EInt: + INT | "*"; + +QualifiedName hidden(): + Identifier ("::" Identifier)*; + +Identifier: + ID | "true" | "false" | "unknown" | "error" | "class" | "abstract" | "extends" | "enum" | "pred" | "scope" | + "unique" | "default" | "problem" | "contains" | "refers"; + +Integer returns ecore::EInt hidden(): + "-"? INT; + +Real returns ecore::EDouble: + "-"? (EXPONENTIAL | INT "." (INT | EXPONENTIAL)); + +@Override +terminal ID: + ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*; + +terminal EXPONENTIAL: + INT ("e" | "E") ("+" | "-")? INT; + +@Override +terminal SL_COMMENT: + ('%' | '//') !('\n' | '\r')* ('\r'? '\n')?; diff --git a/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java b/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java new file mode 100644 index 00000000..dd7731b4 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java @@ -0,0 +1,84 @@ +/* + * generated by Xtext 2.25.0 + */ +package tools.refinery.language; + +import org.eclipse.xtext.conversion.IValueConverterService; +import org.eclipse.xtext.naming.IQualifiedNameConverter; +import org.eclipse.xtext.resource.DerivedStateAwareResource; +import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager; +import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy; +import org.eclipse.xtext.resource.IDerivedStateComputer; +import org.eclipse.xtext.resource.ILocationInFileProvider; +import org.eclipse.xtext.resource.IResourceDescription; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; +import org.eclipse.xtext.scoping.IScopeProvider; +import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; +import org.eclipse.xtext.validation.IResourceValidator; +import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator; + +import com.google.inject.Binder; +import com.google.inject.name.Names; + +import tools.refinery.language.conversion.ProblemValueConverterService; +import tools.refinery.language.naming.ProblemQualifiedNameConverter; +import tools.refinery.language.resource.ProblemDerivedStateComputer; +import tools.refinery.language.resource.ProblemLocationInFileProvider; +import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; +import tools.refinery.language.scoping.ProblemGlobalScopeProvider; +import tools.refinery.language.scoping.ProblemLocalScopeProvider; + +/** + * Use this class to register components to be used at runtime / without the + * Equinox extension registry. + */ +public class ProblemRuntimeModule extends AbstractProblemRuntimeModule { + public Class bindIQualifiedNameConverter() { + return ProblemQualifiedNameConverter.class; + } + + public Class bindIDefaultResourceDescriptionStrategy() { + return ProblemResourceDescriptionStrategy.class; + } + + @Override + public Class bindIValueConverterService() { + return ProblemValueConverterService.class; + } + + @Override + public Class bindIGlobalScopeProvider() { + return ProblemGlobalScopeProvider.class; + } + + @Override + public void configureIScopeProviderDelegate(Binder binder) { + binder.bind(IScopeProvider.class).annotatedWith(Names.named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)) + .to(ProblemLocalScopeProvider.class); + } + + @Override + public Class bindXtextResource() { + return DerivedStateAwareResource.class; + } + + // Method name follows Xtext convention. + @SuppressWarnings("squid:S100") + public Class bindIResourceDescription$Manager() { + return DerivedStateAwareResourceDescriptionManager.class; + } + + public Class bindIResourceValidator() { + return DerivedStateAwareResourceValidator.class; + } + + public Class bindIDerivedStateComputer() { + return ProblemDerivedStateComputer.class; + } + + @Override + public Class bindILocationInFileProvider() { + return ProblemLocationInFileProvider.class; + } +} diff --git a/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java b/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java new file mode 100644 index 00000000..f49069a5 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java @@ -0,0 +1,34 @@ +/* + * generated by Xtext 2.25.0 + */ +package tools.refinery.language; + +import org.eclipse.emf.ecore.EPackage; + +import com.google.inject.Injector; + +import tools.refinery.language.model.problem.ProblemPackage; + +/** + * Initialization support for running Xtext languages without Equinox extension + * registry. + */ +public class ProblemStandaloneSetup extends ProblemStandaloneSetupGenerated { + + public static void doSetup() { + new ProblemStandaloneSetup().createInjectorAndDoEMFRegistration(); + } + + @Override + // Here we can't rely on java.util.HashMap#computeIfAbsent, because + // org.eclipse.emf.ecore.impl.EPackageRegistryImpl#containsKey is overridden + // without also overriding computeIfAbsent. We must make sure to call the + // overridden containsKey implementation. + @SuppressWarnings("squid:S3824") + public Injector createInjectorAndDoEMFRegistration() { + if (!EPackage.Registry.INSTANCE.containsKey(ProblemPackage.eNS_URI)) { + EPackage.Registry.INSTANCE.put(ProblemPackage.eNS_URI, ProblemPackage.eINSTANCE); + } + return super.createInjectorAndDoEMFRegistration(); + } +} diff --git a/language/src/main/java/tools/refinery/language/ProblemUtil.java b/language/src/main/java/tools/refinery/language/ProblemUtil.java new file mode 100644 index 00000000..ae2efc3d --- /dev/null +++ b/language/src/main/java/tools/refinery/language/ProblemUtil.java @@ -0,0 +1,94 @@ +package tools.refinery.language; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; + +import com.google.common.collect.ImmutableList; + +import tools.refinery.language.model.problem.ClassDeclaration; +import tools.refinery.language.model.problem.Node; +import tools.refinery.language.model.problem.Problem; +import tools.refinery.language.model.problem.ProblemPackage; +import tools.refinery.language.model.problem.ReferenceDeclaration; +import tools.refinery.language.model.problem.Relation; +import tools.refinery.language.model.problem.Variable; +import tools.refinery.language.scoping.ProblemGlobalScopeProvider; + +public final class ProblemUtil { + private ProblemUtil() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static final String NODE_CLASS_NAME = "node"; + + public static boolean isSingletonVariable(Variable variable) { + return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE; + } + + public static boolean isUniqueNode(Node node) { + var containingFeature = node.eContainingFeature(); + return containingFeature == ProblemPackage.Literals.UNIQUE_DECLARATION__NODES + || containingFeature == ProblemPackage.Literals.ENUM_DECLARATION__LITERALS; + } + + public static boolean isNewNode(Node node) { + return node.eContainingFeature() == ProblemPackage.Literals.CLASS_DECLARATION__NEW_NODE; + } + + public static Optional getBuiltInLibrary(EObject context) { + return Optional.ofNullable(context.eResource()).map(Resource::getResourceSet) + .map(resourceSet -> resourceSet.getResource(ProblemGlobalScopeProvider.BULTIN_LIBRARY_URI, true)) + .map(Resource::getContents).filter(contents -> !contents.isEmpty()).map(contents -> contents.get(0)) + .filter(Problem.class::isInstance).map(Problem.class::cast); + } + + public static boolean isBuiltIn(EObject eObject) { + if (eObject != null) { + var eResource = eObject.eResource(); + if (eResource != null) { + return ProblemGlobalScopeProvider.BULTIN_LIBRARY_URI.equals(eResource.getURI()); + } + } + return false; + } + + public static Optional getNodeClassDeclaration(EObject context) { + return getBuiltInLibrary(context).flatMap(problem -> problem.getStatements().stream() + .filter(ClassDeclaration.class::isInstance).map(ClassDeclaration.class::cast) + .filter(declaration -> NODE_CLASS_NAME.equals(declaration.getName())).findFirst()); + } + + public static Collection getSuperclassesAndSelf(ClassDeclaration classDeclaration) { + Set found = new HashSet<>(); + getNodeClassDeclaration(classDeclaration).ifPresent(found::add); + Deque queue = new ArrayDeque<>(); + queue.addLast(classDeclaration); + while (!queue.isEmpty()) { + ClassDeclaration current = queue.removeFirst(); + if (!found.contains(current)) { + found.add(current); + for (Relation superType : current.getSuperTypes()) { + if (superType instanceof ClassDeclaration superDeclaration) { + queue.addLast(superDeclaration); + } + } + } + } + return found; + } + + public static Collection getAllReferenceDeclarations(ClassDeclaration classDeclaration) { + ImmutableList.Builder builder = ImmutableList.builder(); + for (ClassDeclaration superclass : getSuperclassesAndSelf(classDeclaration)) { + builder.addAll(superclass.getReferenceDeclarations()); + } + return builder.build(); + } +} diff --git a/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java b/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java new file mode 100644 index 00000000..508688ed --- /dev/null +++ b/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java @@ -0,0 +1,19 @@ +package tools.refinery.language.conversion; + +import org.eclipse.xtext.common.services.DefaultTerminalConverters; +import org.eclipse.xtext.conversion.IValueConverter; +import org.eclipse.xtext.conversion.ValueConverter; + +import com.google.inject.Inject; + +public class ProblemValueConverterService extends DefaultTerminalConverters { + @Inject + private UpperBoundValueConverter upperBoundValueConverter; + + @ValueConverter(rule = "UpperBound") + // Method name follows Xtext convention. + @SuppressWarnings("squid:S100") + public IValueConverter UpperBound() { + return upperBoundValueConverter; + } +} diff --git a/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java b/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java new file mode 100644 index 00000000..be0d15ad --- /dev/null +++ b/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java @@ -0,0 +1,35 @@ +package tools.refinery.language.conversion; + +import org.eclipse.xtext.conversion.ValueConverterException; +import org.eclipse.xtext.conversion.impl.AbstractValueConverter; +import org.eclipse.xtext.conversion.impl.INTValueConverter; +import org.eclipse.xtext.nodemodel.INode; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +@Singleton +public class UpperBoundValueConverter extends AbstractValueConverter { + public static final String INFINITY = "*"; + + @Inject + private INTValueConverter intValueConverter; + + @Override + public Integer toValue(String string, INode node) throws ValueConverterException { + if (INFINITY.equals(string)) { + return -1; + } else { + return intValueConverter.toValue(string, node); + } + } + + @Override + public String toString(Integer value) throws ValueConverterException { + if (value < 0) { + return INFINITY; + } else { + return intValueConverter.toString(value); + } + } +} diff --git a/language/src/main/java/tools/refinery/language/naming/NamingUtil.java b/language/src/main/java/tools/refinery/language/naming/NamingUtil.java new file mode 100644 index 00000000..e959be74 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/naming/NamingUtil.java @@ -0,0 +1,25 @@ +package tools.refinery.language.naming; + +import java.util.regex.Pattern; + +public final class NamingUtil { + private static final String SINGLETON_VARIABLE_PREFIX = "_"; + + private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z][_0-9a-zA-Z]*"); + + private NamingUtil() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static boolean isNullOrEmpty(String name) { + return name == null || name.isEmpty(); + } + + public static boolean isSingletonVariableName(String name) { + return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX); + } + + public static boolean isValidId(String name) { + return name != null && ID_REGEX.matcher(name).matches(); + } +} diff --git a/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java b/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java new file mode 100644 index 00000000..5453906f --- /dev/null +++ b/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java @@ -0,0 +1,15 @@ +package tools.refinery.language.naming; + +import org.eclipse.xtext.naming.IQualifiedNameConverter; + +import com.google.inject.Singleton; + +@Singleton +public class ProblemQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl { + public static final String DELIMITER = "::"; + + @Override + public String getDelimiter() { + return DELIMITER; + } +} diff --git a/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java b/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java new file mode 100644 index 00000000..2061c30e --- /dev/null +++ b/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java @@ -0,0 +1,190 @@ +package tools.refinery.language.resource; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.xtext.linking.impl.LinkingHelper; +import org.eclipse.xtext.naming.IQualifiedNameConverter; +import org.eclipse.xtext.nodemodel.INode; +import org.eclipse.xtext.nodemodel.util.NodeModelUtils; +import org.eclipse.xtext.scoping.IScope; +import org.eclipse.xtext.scoping.IScopeProvider; +import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; + +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.inject.name.Named; + +import tools.refinery.language.model.problem.Argument; +import tools.refinery.language.model.problem.Atom; +import tools.refinery.language.model.problem.Conjunction; +import tools.refinery.language.model.problem.ExistentialQuantifier; +import tools.refinery.language.model.problem.ImplicitVariable; +import tools.refinery.language.model.problem.Literal; +import tools.refinery.language.model.problem.NegativeLiteral; +import tools.refinery.language.model.problem.Parameter; +import tools.refinery.language.model.problem.PredicateDefinition; +import tools.refinery.language.model.problem.Problem; +import tools.refinery.language.model.problem.ProblemFactory; +import tools.refinery.language.model.problem.ProblemPackage; +import tools.refinery.language.model.problem.Statement; +import tools.refinery.language.model.problem.VariableOrNodeArgument; +import tools.refinery.language.naming.NamingUtil; + +@Singleton +public class DerivedVariableComputer { + @Inject + private LinkingHelper linkingHelper; + + @Inject + private IQualifiedNameConverter qualifiedNameConverter; + + @Inject + @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) + private IScopeProvider scopeProvider; + + public void installDerivedVariables(Problem problem, Set nodeNames) { + for (Statement statement : problem.getStatements()) { + if (statement instanceof PredicateDefinition definition) { + installDerivedPredicateDefinitionState(definition, nodeNames); + } + } + } + + protected void installDerivedPredicateDefinitionState(PredicateDefinition definition, Set nodeNames) { + Set knownVariables = new HashSet<>(); + knownVariables.addAll(nodeNames); + for (Parameter parameter : definition.getParameters()) { + String name = parameter.getName(); + if (name != null) { + knownVariables.add(name); + } + } + for (Conjunction body : definition.getBodies()) { + installDeriveConjunctionState(body, knownVariables); + } + } + + protected void installDeriveConjunctionState(Conjunction conjunction, Set knownVariables) { + Set newVariables = new HashSet<>(); + for (Literal literal : conjunction.getLiterals()) { + if (literal instanceof Atom atom) { + createSigletonVariablesAndCollectVariables(atom, knownVariables, newVariables); + } + } + createVariables(conjunction, newVariables); + newVariables.addAll(knownVariables); + for (Literal literal : conjunction.getLiterals()) { + if (literal instanceof NegativeLiteral negativeLiteral) { + installDeriveNegativeLiteralState(negativeLiteral, newVariables); + } + } + } + + protected void installDeriveNegativeLiteralState(NegativeLiteral negativeLiteral, Set knownVariables) { + Set newVariables = new HashSet<>(); + createSigletonVariablesAndCollectVariables(negativeLiteral.getAtom(), knownVariables, newVariables); + createVariables(negativeLiteral, newVariables); + } + + protected void createSigletonVariablesAndCollectVariables(Atom atom, Set knownVariables, + Set newVariables) { + for (Argument argument : atom.getArguments()) { + if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) { + IScope scope = scopeProvider.getScope(variableOrNodeArgument, + ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE); + List nodes = NodeModelUtils.findNodesForFeature(variableOrNodeArgument, + ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE); + for (INode node : nodes) { + var variableName = linkingHelper.getCrossRefNodeAsString(node, true); + var created = tryCreateVariableForArgument(variableOrNodeArgument, variableName, scope, + knownVariables, newVariables); + if (created) { + break; + } + } + } + } + } + + protected boolean tryCreateVariableForArgument(VariableOrNodeArgument variableOrNodeArgument, String variableName, + IScope scope, Set knownVariables, Set newVariables) { + if (!NamingUtil.isValidId(variableName)) { + return false; + } + var qualifiedName = qualifiedNameConverter.toQualifiedName(variableName); + if (scope.getSingleElement(qualifiedName) != null) { + return false; + } + if (NamingUtil.isSingletonVariableName(variableName)) { + createSingletonVariable(variableOrNodeArgument, variableName); + return true; + } + if (!knownVariables.contains(variableName)) { + newVariables.add(variableName); + return true; + } + return false; + } + + protected void createVariables(ExistentialQuantifier quantifier, Set newVariables) { + for (String variableName : newVariables) { + createVariable(quantifier, variableName); + } + } + + protected void createVariable(ExistentialQuantifier quantifier, String variableName) { + if (NamingUtil.isValidId(variableName)) { + ImplicitVariable variable = createNamedVariable(variableName); + quantifier.getImplicitVariables().add(variable); + } + } + + protected void createSingletonVariable(VariableOrNodeArgument argument, String variableName) { + if (NamingUtil.isValidId(variableName)) { + ImplicitVariable variable = createNamedVariable(variableName); + argument.setSingletonVariable(variable); + } + } + + protected ImplicitVariable createNamedVariable(String variableName) { + var variable = ProblemFactory.eINSTANCE.createImplicitVariable(); + variable.setName(variableName); + return variable; + } + + public void discardDerivedVariables(Problem problem) { + for (Statement statement : problem.getStatements()) { + if (statement instanceof PredicateDefinition predicateDefinition) { + discardPredicateDefinitionState(predicateDefinition); + } + } + } + + protected void discardPredicateDefinitionState(PredicateDefinition definition) { + for (Conjunction body : definition.getBodies()) { + body.getImplicitVariables().clear(); + for (Literal literal : body.getLiterals()) { + if (literal instanceof Atom atom) { + discardDerivedAtomState(atom); + } + if (literal instanceof NegativeLiteral negativeLiteral) { + negativeLiteral.getImplicitVariables().clear(); + discardDerivedAtomState(negativeLiteral.getAtom()); + } + } + } + } + + protected void discardDerivedAtomState(Atom atom) { + if (atom == null) { + return; + } + for (Argument argument : atom.getArguments()) { + if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) { + variableOrNodeArgument.setSingletonVariable(null); + } + } + } +} diff --git a/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java b/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java new file mode 100644 index 00000000..99bf9b64 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java @@ -0,0 +1,88 @@ +package tools.refinery.language.resource; + +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.xtext.linking.impl.LinkingHelper; +import org.eclipse.xtext.naming.IQualifiedNameConverter; +import org.eclipse.xtext.nodemodel.INode; +import org.eclipse.xtext.nodemodel.util.NodeModelUtils; +import org.eclipse.xtext.scoping.IScope; +import org.eclipse.xtext.scoping.IScopeProvider; +import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; + +import com.google.common.collect.ImmutableSet; +import com.google.inject.Inject; +import com.google.inject.name.Named; + +import tools.refinery.language.model.problem.Assertion; +import tools.refinery.language.model.problem.AssertionArgument; +import tools.refinery.language.model.problem.NodeAssertionArgument; +import tools.refinery.language.model.problem.NodeValueAssertion; +import tools.refinery.language.model.problem.Problem; +import tools.refinery.language.model.problem.ProblemPackage; +import tools.refinery.language.model.problem.Statement; +import tools.refinery.language.naming.NamingUtil; + +public class NodeNameCollector { + @Inject + private LinkingHelper linkingHelper; + + @Inject + private IQualifiedNameConverter qualifiedNameConverter; + + @Inject + @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) + private IScopeProvider scopeProvider; + + private final ImmutableSet.Builder nodeNames = ImmutableSet.builder(); + + private IScope nodeScope; + + public Set getNodeNames() { + return nodeNames.build(); + } + + public void collectNodeNames(Problem problem) { + nodeScope = scopeProvider.getScope(problem, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE); + for (Statement statement : problem.getStatements()) { + collectStatementNodeNames(statement); + } + } + + protected void collectStatementNodeNames(Statement statement) { + if (statement instanceof Assertion assertion) { + collectAssertionNodeNames(assertion); + } else if (statement instanceof NodeValueAssertion nodeValueAssertion) { + collectNodeValueAssertionNodeNames(nodeValueAssertion); + } + } + + protected void collectAssertionNodeNames(Assertion assertion) { + for (AssertionArgument argument : assertion.getArguments()) { + if (argument instanceof NodeAssertionArgument) { + collectNodeNames(argument, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE); + } + } + } + + protected void collectNodeValueAssertionNodeNames(NodeValueAssertion nodeValueAssertion) { + collectNodeNames(nodeValueAssertion, ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE); + } + + private void collectNodeNames(EObject eObject, EStructuralFeature feature) { + List nodes = NodeModelUtils.findNodesForFeature(eObject, feature); + for (INode node : nodes) { + var nodeName = linkingHelper.getCrossRefNodeAsString(node, true); + if (!NamingUtil.isValidId(nodeName)) { + continue; + } + var qualifiedName = qualifiedNameConverter.toQualifiedName(nodeName); + if (nodeScope.getSingleElement(qualifiedName) == null) { + nodeNames.add(nodeName); + } + } + } +} \ No newline at end of file diff --git a/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java b/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java new file mode 100644 index 00000000..275feca3 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java @@ -0,0 +1,163 @@ +package tools.refinery.language.resource; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.Constants; +import org.eclipse.xtext.resource.DerivedStateAwareResource; +import org.eclipse.xtext.resource.IDerivedStateComputer; +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.scoping.IScopeProvider; +import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.name.Named; + +import tools.refinery.language.model.problem.ClassDeclaration; +import tools.refinery.language.model.problem.Node; +import tools.refinery.language.model.problem.Problem; +import tools.refinery.language.model.problem.ProblemFactory; +import tools.refinery.language.model.problem.Statement; + +@Singleton +public class ProblemDerivedStateComputer implements IDerivedStateComputer { + public static final String NEW_NODE = "new"; + + @Inject + @Named(Constants.LANGUAGE_NAME) + private String languageName; + + @Inject + @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE) + private IScopeProvider scopeProvider; + + @Inject + private Provider nodeNameCollectorProvider; + + @Inject + private DerivedVariableComputer derivedVariableComputer; + + @Override + public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) { + var problem = getProblem(resource); + if (problem != null) { + var adapter = getOrInstallAdapter(resource); + installDerivedProblemState(problem, adapter, preLinkingPhase); + } + } + + protected Problem getProblem(Resource resource) { + List contents = resource.getContents(); + if (contents.isEmpty()) { + return null; + } + EObject object = contents.get(0); + if (object instanceof Problem problem) { + return problem; + } + return null; + } + + protected void installDerivedProblemState(Problem problem, Adapter adapter, boolean preLinkingPhase) { + installNewNodes(problem, adapter); + if (preLinkingPhase) { + return; + } + Set nodeNames = installDerivedNodes(problem); + derivedVariableComputer.installDerivedVariables(problem, nodeNames); + } + + protected void installNewNodes(Problem problem, Adapter adapter) { + for (Statement statement : problem.getStatements()) { + if (statement instanceof ClassDeclaration declaration && !declaration.isAbstract() + && declaration.getNewNode() == null) { + var newNode = adapter.createNodeIfAbsent(declaration, key -> createNode(NEW_NODE)); + declaration.setNewNode(newNode); + } + } + } + + protected Set installDerivedNodes(Problem problem) { + var collector = nodeNameCollectorProvider.get(); + collector.collectNodeNames(problem); + Set nodeNames = collector.getNodeNames(); + List grapNodes = problem.getNodes(); + for (String nodeName : nodeNames) { + var graphNode = createNode(nodeName); + grapNodes.add(graphNode); + } + return nodeNames; + } + + protected Node createNode(String name) { + var node = ProblemFactory.eINSTANCE.createNode(); + node.setName(name); + return node; + } + + @Override + public void discardDerivedState(DerivedStateAwareResource resource) { + var problem = getProblem(resource); + if (problem != null) { + var adapter = getOrInstallAdapter(resource); + discardDerivedProblemState(problem, adapter); + } + } + + protected void discardDerivedProblemState(Problem problem, Adapter adapter) { + Set classDeclarations = new HashSet<>(); + problem.getNodes().clear(); + for (Statement statement : problem.getStatements()) { + if (statement instanceof ClassDeclaration classDeclaration) { + classDeclaration.setNewNode(null); + classDeclarations.add(classDeclaration); + } + } + adapter.retainAll(classDeclarations); + derivedVariableComputer.discardDerivedVariables(problem); + } + + protected Adapter getOrInstallAdapter(Resource resource) { + if (!(resource instanceof XtextResource)) { + return new Adapter(); + } + String resourceLanguageName = ((XtextResource) resource).getLanguageName(); + if (!languageName.equals(resourceLanguageName)) { + return new Adapter(); + } + var adapter = (Adapter) EcoreUtil.getAdapter(resource.eAdapters(), Adapter.class); + if (adapter == null) { + adapter = new Adapter(); + resource.eAdapters().add(adapter); + } + return adapter; + } + + protected static class Adapter extends AdapterImpl { + private Map newNodes = new HashMap<>(); + + public Node createNodeIfAbsent(ClassDeclaration classDeclaration, Function createNode) { + return newNodes.computeIfAbsent(classDeclaration, createNode); + } + + public void retainAll(Collection classDeclarations) { + newNodes.keySet().retainAll(classDeclarations); + } + + @Override + public boolean isAdapterForType(Object type) { + return Adapter.class == type; + } + } +} diff --git a/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java b/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java new file mode 100644 index 00000000..f6a3ec75 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java @@ -0,0 +1,33 @@ +package tools.refinery.language.resource; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.resource.DefaultLocationInFileProvider; +import org.eclipse.xtext.util.ITextRegion; + +import tools.refinery.language.ProblemUtil; +import tools.refinery.language.model.problem.ImplicitVariable; +import tools.refinery.language.model.problem.Node; + +public class ProblemLocationInFileProvider extends DefaultLocationInFileProvider { + @Override + protected ITextRegion doGetTextRegion(EObject obj, RegionDescription query) { + if (obj instanceof Node node) { + return getNodeTextRegion(node, query); + } + if (obj instanceof ImplicitVariable) { + return ITextRegion.EMPTY_REGION; + } + return super.doGetTextRegion(obj, query); + } + + protected ITextRegion getNodeTextRegion(Node node, RegionDescription query) { + if (ProblemUtil.isUniqueNode(node)) { + return super.doGetTextRegion(node, query); + } + if (ProblemUtil.isNewNode(node)) { + EObject container = node.eContainer(); + return doGetTextRegion(container, query); + } + return ITextRegion.EMPTY_REGION; + } +} diff --git a/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java b/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java new file mode 100644 index 00000000..c5dea671 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java @@ -0,0 +1,103 @@ +package tools.refinery.language.resource; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.EcoreUtil2; +import org.eclipse.xtext.naming.IQualifiedNameConverter; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.resource.EObjectDescription; +import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy; +import org.eclipse.xtext.util.IAcceptor; + +import com.google.inject.Inject; +import com.google.inject.Singleton; + +import tools.refinery.language.ProblemUtil; +import tools.refinery.language.model.problem.NamedElement; +import tools.refinery.language.model.problem.Node; +import tools.refinery.language.model.problem.Problem; +import tools.refinery.language.model.problem.Variable; +import tools.refinery.language.naming.NamingUtil; + +@Singleton +public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { + @Inject + private IQualifiedNameConverter qualifiedNameConverter; + + @Override + public boolean createEObjectDescriptions(EObject eObject, IAcceptor acceptor) { + if (!shouldExport(eObject)) { + return false; + } + var qualifiedName = getNameAsQualifiedName(eObject); + if (qualifiedName == null) { + return true; + } + var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class); + var problemQualifiedName = getNameAsQualifiedName(problem); + boolean nameExported; + if (shouldExportSimpleName(eObject)) { + acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor); + nameExported = true; + } else { + nameExported = false; + } + var parent = eObject.eContainer(); + while (parent != null && parent != problem) { + var parentQualifiedName = getNameAsQualifiedName(parent); + if (parentQualifiedName == null) { + parent = parent.eContainer(); + continue; + } + qualifiedName = parentQualifiedName.append(qualifiedName); + if (shouldExportSimpleName(parent)) { + acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor); + nameExported = true; + } else { + nameExported = false; + } + parent = parent.eContainer(); + } + if (!nameExported) { + acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor); + } + return true; + } + + protected QualifiedName getNameAsQualifiedName(EObject eObject) { + if (!(eObject instanceof NamedElement)) { + return null; + } + var namedElement = (NamedElement) eObject; + var name = namedElement.getName(); + if (NamingUtil.isNullOrEmpty(name)) { + return null; + } + return qualifiedNameConverter.toQualifiedName(name); + } + + protected boolean shouldExport(EObject eObject) { + if (eObject instanceof Variable) { + // Variables are always private to the containing predicate definition. + return false; + } + if (eObject instanceof Node node) { + // Only enum literals and new nodes are visible across problem files. + return ProblemUtil.isUniqueNode(node) || ProblemUtil.isNewNode(node); + } + return true; + } + + protected boolean shouldExportSimpleName(EObject eObject) { + if (eObject instanceof Node node) { + return !ProblemUtil.isNewNode(node); + } + return true; + } + + private void acceptEObjectDescription(EObject eObject, QualifiedName prefix, QualifiedName qualifiedName, + IAcceptor acceptor) { + var qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName); + acceptor.accept(EObjectDescription.create(qualifiedNameWithPrefix, eObject)); + } +} diff --git a/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java b/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java new file mode 100644 index 00000000..8f4c9374 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java @@ -0,0 +1,25 @@ +package tools.refinery.language.scoping; + +import java.util.LinkedHashSet; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; + +public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider { + public static final String BUILTIN_LIBRARY_NAME = "builtin"; + + public static final URI BULTIN_LIBRARY_URI = getLibraryUri(BUILTIN_LIBRARY_NAME); + + @Override + protected LinkedHashSet getImportedUris(Resource resource) { + LinkedHashSet importedUris = new LinkedHashSet<>(); + importedUris.add(BULTIN_LIBRARY_URI); + return importedUris; + } + + private static URI getLibraryUri(String libraryName) { + return URI.createURI(ProblemGlobalScopeProvider.class.getClassLoader() + .getResource("org/eclipse/viatra/solver/language/" + libraryName + ".problem").toString()); + } +} diff --git a/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java b/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java new file mode 100644 index 00000000..05a3bcf9 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java @@ -0,0 +1,40 @@ +package tools.refinery.language.scoping; + +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.resource.IResourceDescriptions; +import org.eclipse.xtext.resource.IResourceDescriptionsProvider; +import org.eclipse.xtext.resource.ISelectable; +import org.eclipse.xtext.scoping.impl.ImportNormalizer; +import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider; + +import com.google.inject.Inject; + +public class ProblemLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { + private static final QualifiedName BUILTIN_LIBRARY_QUALIFIED_NAME = QualifiedName + .create(ProblemGlobalScopeProvider.BUILTIN_LIBRARY_NAME); + + @Inject + private IResourceDescriptionsProvider resourceDescriptionsProvider; + + @Override + protected List getImplicitImports(boolean ignoreCase) { + return List.of(doCreateImportNormalizer(BUILTIN_LIBRARY_QUALIFIED_NAME, true, ignoreCase)); + } + + @Override + protected List getImportedNamespaceResolvers(EObject context, boolean ignoreCase) { + return List.of(); + } + + @Override + protected ISelectable internalGetAllDescriptions(Resource resource) { + // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects. + IResourceDescriptions resourceDescriptions = resourceDescriptionsProvider + .getResourceDescriptions(resource.getResourceSet()); + return resourceDescriptions.getResourceDescription(resource.getURI()); + } +} diff --git a/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java b/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java new file mode 100644 index 00000000..61a3c8f9 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java @@ -0,0 +1,93 @@ +/* + * generated by Xtext 2.25.0 + */ +package tools.refinery.language.scoping; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.xtext.EcoreUtil2; +import org.eclipse.xtext.scoping.IScope; +import org.eclipse.xtext.scoping.Scopes; + +import tools.refinery.language.ProblemUtil; +import tools.refinery.language.model.problem.ClassDeclaration; +import tools.refinery.language.model.problem.ExistentialQuantifier; +import tools.refinery.language.model.problem.PredicateDefinition; +import tools.refinery.language.model.problem.Problem; +import tools.refinery.language.model.problem.ProblemPackage; +import tools.refinery.language.model.problem.ReferenceDeclaration; +import tools.refinery.language.model.problem.Variable; +import tools.refinery.language.model.problem.VariableOrNodeArgument; + +/** + * This class contains custom scoping description. + * + * See + * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping + * on how and when to use it. + */ +public class ProblemScopeProvider extends AbstractProblemScopeProvider { + + @Override + public IScope getScope(EObject context, EReference reference) { + var scope = super.getScope(context, reference); + if (reference == ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE + || reference == ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE) { + return getNodesScope(context, scope); + } + if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE) { + return getVariableScope(context, scope); + } + if (reference == ProblemPackage.Literals.REFERENCE_DECLARATION__OPPOSITE) { + return getOppositeScope(context, scope); + } + return scope; + } + + protected IScope getNodesScope(EObject context, IScope delegateScope) { + var problem = EcoreUtil2.getContainerOfType(context, Problem.class); + if (problem == null) { + return delegateScope; + } + return Scopes.scopeFor(problem.getNodes(), delegateScope); + } + + protected IScope getVariableScope(EObject context, IScope delegateScope) { + List variables = new ArrayList<>(); + EObject currentContext = context; + if (context instanceof VariableOrNodeArgument argument) { + Variable singletonVariable = argument.getSingletonVariable(); + if (singletonVariable != null) { + variables.add(singletonVariable); + } + } + while (currentContext != null && !(currentContext instanceof PredicateDefinition)) { + if (currentContext instanceof ExistentialQuantifier quantifier) { + variables.addAll(quantifier.getImplicitVariables()); + } + currentContext = currentContext.eContainer(); + } + if (currentContext != null) { + PredicateDefinition definition = (PredicateDefinition) currentContext; + variables.addAll(definition.getParameters()); + } + return Scopes.scopeFor(variables, getNodesScope(context, delegateScope)); + } + + protected IScope getOppositeScope(EObject context, IScope delegateScope) { + var referenceDeclaration = EcoreUtil2.getContainerOfType(context, ReferenceDeclaration.class); + if (referenceDeclaration == null) { + return delegateScope; + } + var relation = referenceDeclaration.getReferenceType(); + if (!(relation instanceof ClassDeclaration)) { + return delegateScope; + } + var classDeclaration = (ClassDeclaration) relation; + var referenceDeclarations = ProblemUtil.getAllReferenceDeclarations(classDeclaration); + return Scopes.scopeFor(referenceDeclarations, delegateScope); + } +} diff --git a/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java new file mode 100644 index 00000000..f2378df6 --- /dev/null +++ b/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java @@ -0,0 +1,13 @@ +/* + * generated by Xtext 2.25.0 + */ +package tools.refinery.language.validation; + +/** + * This class contains custom validation rules. + * + * See + * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation + */ +public class ProblemValidator extends AbstractProblemValidator { +} diff --git a/language/src/test/java/org/eclipse/viatra/solver/language/tests/ProblemParsingTest.xtend b/language/src/test/java/org/eclipse/viatra/solver/language/tests/ProblemParsingTest.xtend deleted file mode 100644 index 11bfc6d9..00000000 --- a/language/src/test/java/org/eclipse/viatra/solver/language/tests/ProblemParsingTest.xtend +++ /dev/null @@ -1,63 +0,0 @@ -/* - * generated by Xtext 2.26.0.M1 - */ -package org.eclipse.viatra.solver.language.tests - -import com.google.inject.Inject -import org.eclipse.viatra.solver.language.model.problem.Problem -import org.eclipse.xtext.testing.InjectWith -import org.eclipse.xtext.testing.extensions.InjectionExtension -import org.eclipse.xtext.testing.util.ParseHelper -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.^extension.ExtendWith - -import static org.hamcrest.MatcherAssert.assertThat -import static org.hamcrest.Matchers.* - -@ExtendWith(InjectionExtension) -@InjectWith(ProblemInjectorProvider) -class ProblemParsingTest { - @Inject - ParseHelper parseHelper - - @Inject - extension ProblemTestUtil - - @Test - def void exampleTest() { - val it = parseHelper.parse(''' - class Family { - contains Person[] members - } - - class Person { - Person[0..*] children opposite parent - Person[0..1] parent opposite children - int age - TaxStatus taxStatus - } - - enum TaxStatus { - child, student, adult, retired - } - - % A child cannot have any dependents. - error invalidTaxStatus(Person p) <-> - taxStatus(p, child), children(p, _q). - - unique family. - Family(family). - members(family, anne): true. - members(family, bob). - members(family, ciri). - children(anne, ciri). - ?children(bob, ciri). - taxStatus(anne, adult). - age(anne, 35). - bobAge: 27. - age(bob, bobAge). - !age(ciri, bobAge). - ''') - assertThat(errors, empty) - } -} diff --git a/language/src/test/java/org/eclipse/viatra/solver/language/tests/ProblemTestUtil.xtend b/language/src/test/java/org/eclipse/viatra/solver/language/tests/ProblemTestUtil.xtend deleted file mode 100644 index b3175659..00000000 --- a/language/src/test/java/org/eclipse/viatra/solver/language/tests/ProblemTestUtil.xtend +++ /dev/null @@ -1,114 +0,0 @@ -package org.eclipse.viatra.solver.language.tests - -import org.eclipse.viatra.solver.language.ProblemUtil -import org.eclipse.viatra.solver.language.model.problem.Argument -import org.eclipse.viatra.solver.language.model.problem.Assertion -import org.eclipse.viatra.solver.language.model.problem.AssertionArgument -import org.eclipse.viatra.solver.language.model.problem.Atom -import org.eclipse.viatra.solver.language.model.problem.ClassDeclaration -import org.eclipse.viatra.solver.language.model.problem.Conjunction -import org.eclipse.viatra.solver.language.model.problem.EnumDeclaration -import org.eclipse.viatra.solver.language.model.problem.Literal -import org.eclipse.viatra.solver.language.model.problem.NegativeLiteral -import org.eclipse.viatra.solver.language.model.problem.Node -import org.eclipse.viatra.solver.language.model.problem.NodeAssertionArgument -import org.eclipse.viatra.solver.language.model.problem.NodeValueAssertion -import org.eclipse.viatra.solver.language.model.problem.PredicateDefinition -import org.eclipse.viatra.solver.language.model.problem.Problem -import org.eclipse.viatra.solver.language.model.problem.Variable -import org.eclipse.viatra.solver.language.model.problem.VariableOrNodeArgument -import org.eclipse.viatra.solver.language.model.problem.UniqueDeclaration - -class ProblemTestUtil { - def builtin(Problem it) { - ProblemUtil.getBuiltInLibrary(it).get - } - - def errors(Problem it) { - eResource.errors - } - - def nodeNames(Problem it) { - nodes.map[name] - } - - def pred(Problem it, String name) { - statements.filter(PredicateDefinition).findFirst[it.name == name] - } - - def param(PredicateDefinition it, int i) { - parameters.get(i) - } - - def conj(PredicateDefinition it, int i) { - bodies.get(i) - } - - def lit(Conjunction it, int i) { - literals.get(i) - } - - def negated(Literal it) { - (it as NegativeLiteral).atom - } - - def relation(Literal it) { - (it as Atom).relation - } - - def arg(Atom it, int i) { - it.arguments.get(i) - } - - def arg(Literal it, int i) { - (it as Atom).arg(i) - } - - def variable(Argument it) { - (it as VariableOrNodeArgument).variableOrNode as Variable - } - - def node(Argument it) { - (it as VariableOrNodeArgument).variableOrNode as Node - } - - def assertion(Problem it, int i) { - statements.filter(Assertion).get(i) - } - - def nodeValueAssertion(Problem it, int i) { - statements.filter(NodeValueAssertion).get(i) - } - - def arg(Assertion it, int i) { - arguments.get(i) - } - - def node(AssertionArgument it) { - (it as NodeAssertionArgument).node - } - - def node(Problem it, String name) { - nodes.findFirst[it.name == name] - } - - def uniqueNode(Problem it, String name) { - statements.filter(UniqueDeclaration).flatMap[nodes].findFirst[it.name == name] - } - - def findClass(Problem it, String name) { - statements.filter(ClassDeclaration).findFirst[it.name == name] - } - - def reference(ClassDeclaration it, String name) { - it.referenceDeclarations.findFirst[it.name == name] - } - - def findEnum(Problem it, String name) { - statements.filter(EnumDeclaration).findFirst[it.name == name] - } - - def literal(EnumDeclaration it, String name) { - literals.findFirst[it.name == name] - } -} diff --git a/language/src/test/java/org/eclipse/viatra/solver/language/tests/scoping/NodeScopingTest.xtend b/language/src/test/java/org/eclipse/viatra/solver/language/tests/scoping/NodeScopingTest.xtend deleted file mode 100644 index db588e5d..00000000 --- a/language/src/test/java/org/eclipse/viatra/solver/language/tests/scoping/NodeScopingTest.xtend +++ /dev/null @@ -1,322 +0,0 @@ -package org.eclipse.viatra.solver.language.tests.scoping - -import com.google.inject.Inject -import java.util.stream.Stream -import org.eclipse.viatra.solver.language.model.problem.Problem -import org.eclipse.viatra.solver.language.tests.ProblemInjectorProvider -import org.eclipse.viatra.solver.language.tests.ProblemTestUtil -import org.eclipse.xtext.testing.InjectWith -import org.eclipse.xtext.testing.extensions.InjectionExtension -import org.eclipse.xtext.testing.util.ParseHelper -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.^extension.ExtendWith -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.MethodSource -import org.junit.jupiter.params.provider.ValueSource - -import static org.hamcrest.MatcherAssert.assertThat -import static org.hamcrest.Matchers.* - -@ExtendWith(InjectionExtension) -@InjectWith(ProblemInjectorProvider) -class NodeScopingTest { - @Inject - ParseHelper parseHelper - - @Inject - extension ProblemTestUtil - - @ParameterizedTest - @ValueSource(strings=#["", "builtin::"]) - def void builtInArgumentTypeTest(String prefix) { - val it = parseHelper.parse(''' - pred predicate(«prefix»node a, «prefix»data b, «prefix»int c). - ''') - assertThat(errors, empty) - assertThat(pred('predicate').param(0).parameterType, equalTo(builtin.findClass('node'))) - assertThat(pred('predicate').param(1).parameterType, equalTo(builtin.findClass('data'))) - assertThat(pred('predicate').param(2).parameterType, equalTo(builtin.findClass('int'))) - } - - @Test - def void implicitNodeInAssertionTest() { - val it = parseHelper.parse(''' - pred predicate(node x, node y) <-> node(x). - predicate(a, a). - ?predicate(a, b). - ''') - assertThat(errors, empty) - assertThat(nodeNames, hasItems('a', 'b')) - assertThat(assertion(0).arg(0).node, equalTo(node('a'))) - assertThat(assertion(0).arg(1).node, equalTo(node('a'))) - assertThat(assertion(1).arg(0).node, equalTo(node('a'))) - assertThat(assertion(1).arg(1).node, equalTo(node('b'))) - } - - @Test - def void implicitNodeInNodeValueAssertionTest() { - val it = parseHelper.parse(''' - a: 16. - ''') - assertThat(errors, empty) - assertThat(nodeNames, hasItems('a')) - assertThat(nodeValueAssertion(0).node, equalTo(node('a'))) - } - - @Test - def void implicitNodeInPredicateTest() { - val it = parseHelper.parse(''' - pred predicate(node a) <-> node(b). - predicate(b). - ''') - assertThat(errors, empty) - assertThat(nodeNames, hasItem("b")) - assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(node("b"))) - assertThat(assertion(0).arg(0).node, equalTo(node("b"))) - } - - @ParameterizedTest - @MethodSource("uniqueNodeReferenceSource") - def void uniqueNodeInAssertionTest(String qualifiedNamePrefix, boolean namedProblem) { - val it = parseHelper.parse(''' - «IF namedProblem»problem test.«ENDIF» - unique a, b. - pred predicate(node x, node y) <-> node(x). - predicate(«qualifiedNamePrefix»a, «qualifiedNamePrefix»a). - ?predicate(«qualifiedNamePrefix»a, «qualifiedNamePrefix»b). - ''') - assertThat(errors, empty) - assertThat(nodeNames, empty) - assertThat(assertion(0).arg(0).node, equalTo(uniqueNode('a'))) - assertThat(assertion(0).arg(1).node, equalTo(uniqueNode('a'))) - assertThat(assertion(1).arg(0).node, equalTo(uniqueNode('a'))) - assertThat(assertion(1).arg(1).node, equalTo(uniqueNode('b'))) - } - - @ParameterizedTest - @MethodSource("uniqueNodeReferenceSource") - def void uniqueNodeInNodeValueAssertionTest(String qualifiedNamePrefix, boolean namedProblem) { - val it = parseHelper.parse(''' - «IF namedProblem»problem test.«ENDIF» - unique a. - «qualifiedNamePrefix»a: 16. - ''') - assertThat(errors, empty) - assertThat(nodeNames, empty) - assertThat(nodeValueAssertion(0).node, equalTo(uniqueNode('a'))) - } - - @ParameterizedTest - @MethodSource("uniqueNodeReferenceSource") - def void uniqueNodeInPredicateTest(String qualifiedNamePrefix, boolean namedProblem) { - val it = parseHelper.parse(''' - «IF namedProblem»problem test.«ENDIF» - unique b. - pred predicate(node a) <-> node(«qualifiedNamePrefix»b). - ''') - assertThat(errors, empty) - assertThat(nodeNames, empty) - assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(uniqueNode("b"))) - } - - static def uniqueNodeReferenceSource() { - Stream.of( - Arguments.of("", false), - Arguments.of("", true), - Arguments.of("test::", true) - ) - } - - @ParameterizedTest - @MethodSource("builtInNodeReferencesSource") - def void builtInNodeTest(String qualifiedName) { - val it = parseHelper.parse(''' - pred predicate(node x) <-> node(x). - predicate(«qualifiedName»). - ''') - assertThat(errors, empty) - assertThat(nodes, empty) - assertThat(assertion(0).arg(0).node, equalTo(builtin.findClass('int').newNode)) - } - - @ParameterizedTest - @MethodSource("builtInNodeReferencesSource") - def void builtInNodeInNodeValueAssertionTest(String qualifiedName) { - val it = parseHelper.parse(''' - «qualifiedName»: 16. - ''') - assertThat(errors, empty) - assertThat(nodes, empty) - assertThat(nodeValueAssertion(0).node, equalTo(builtin.findClass('int').newNode)) - } - - @ParameterizedTest - @MethodSource("builtInNodeReferencesSource") - def void builtInNodeInPredicateTest(String qualifiedName) { - val it = parseHelper.parse(''' - pred predicate(node x) <-> node(«qualifiedName»). - ''') - assertThat(errors, empty) - assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(builtin.findClass('int').newNode)) - } - - static def builtInNodeReferencesSource() { - Stream.of( - Arguments.of("int::new"), - Arguments.of("builtin::int::new") - ) - } - - @ParameterizedTest(name="{0}, namedProblem={1}") - @MethodSource("classNewNodeReferencesSource") - def void classNewNodeTest(String qualifiedName, boolean namedProblem) { - val it = parseHelper.parse(''' - «IF namedProblem»problem test.«ENDIF» - class Foo. - pred predicate(node x) <-> node(x). - predicate(«qualifiedName»). - ''') - assertThat(errors, empty) - assertThat(nodes, empty) - assertThat(assertion(0).arg(0).node, equalTo(findClass('Foo').newNode)) - } - - @ParameterizedTest(name="{0}, namedProblem={1}") - @MethodSource("classNewNodeReferencesSource") - def void classNewNodeInNodeValueAssertionTest(String qualifiedName, boolean namedProblem) { - val it = parseHelper.parse(''' - «IF namedProblem»problem test.«ENDIF» - class Foo. - «qualifiedName»: 16. - ''') - assertThat(errors, empty) - assertThat(nodes, empty) - assertThat(nodeValueAssertion(0).node, equalTo(findClass('Foo').newNode)) - } - - @ParameterizedTest(name="{0}, namedProblem={1}") - @MethodSource("classNewNodeReferencesSource") - def void classNewNodeInPredicateTest(String qualifiedName, boolean namedProblem) { - val it = parseHelper.parse(''' - «IF namedProblem»problem test.«ENDIF» - class Foo. - pred predicate(node x) <-> node(«qualifiedName»). - ''') - assertThat(errors, empty) - assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(findClass('Foo').newNode)) - } - - static def classNewNodeReferencesSource() { - Stream.of( - Arguments.of("Foo::new", false), - Arguments.of("Foo::new", true), - Arguments.of("test::Foo::new", true) - ) - } - - @Test - def void newNodeIsNotSpecial() { - val it = parseHelper.parse(''' - class Foo. - pred predicate(node x) <-> node(x). - predicate(new). - ''') - assertThat(errors, empty) - assertThat(nodeNames, hasItem('new')) - assertThat(assertion(0).arg(0).node, not(equalTo(findClass('Foo').newNode))) - } - - @ParameterizedTest(name="{0}, namedProblem={1}") - @MethodSource("enumLiteralReferencesSource") - def void enumLiteralTest(String qualifiedName, boolean namedProblem) { - val it = parseHelper.parse(''' - «IF namedProblem»problem test.«ENDIF» - enum Foo { alpha, beta } - pred predicate(Foo a) <-> node(a). - predicate(«qualifiedName»). - ''') - assertThat(errors, empty) - assertThat(nodes, empty) - assertThat(assertion(0).arg(0).node, equalTo(findEnum("Foo").literal("alpha"))) - } - - @ParameterizedTest(name="{0}, namedProblem={1}") - @MethodSource("enumLiteralReferencesSource") - def void enumLiteralInNodeValueAssertionTest(String qualifiedName, boolean namedProblem) { - val it = parseHelper.parse(''' - «IF namedProblem»problem test.«ENDIF» - enum Foo { alpha, beta } - «qualifiedName»: 16. - ''') - assertThat(errors, empty) - assertThat(nodes, empty) - assertThat(nodeValueAssertion(0).node, equalTo(findEnum("Foo").literal("alpha"))) - } - - @ParameterizedTest(name="{0}, namedProblem={1}") - @MethodSource("enumLiteralReferencesSource") - def void enumLiteralInPredicateTest(String qualifiedName, boolean namedProblem) { - val it = parseHelper.parse(''' - «IF namedProblem»problem test.«ENDIF» - enum Foo { alpha, beta } - pred predicate(Foo a) <-> node(«qualifiedName»). - ''') - assertThat(errors, empty) - assertThat(nodes, empty) - assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(findEnum("Foo").literal("alpha"))) - } - - static def enumLiteralReferencesSource() { - Stream.of( - Arguments.of("alpha", false), - Arguments.of("alpha", true), - Arguments.of("Foo::alpha", false), - Arguments.of("Foo::alpha", true), - Arguments.of("test::alpha", true), - Arguments.of("test::Foo::alpha", true) - ) - } - - @ParameterizedTest - @MethodSource("builtInEnumLiteralReferencesSource") - def void builtInEnumLiteralTest(String qualifiedName) { - val it = parseHelper.parse(''' - pred predicate(node a) <-> node(a). - predicate(«qualifiedName»). - ''') - assertThat(errors, empty) - assertThat(nodes, empty) - assertThat(assertion(0).arg(0).node, equalTo(builtin.findEnum("bool").literal("true"))) - } - - @ParameterizedTest - @MethodSource("builtInEnumLiteralReferencesSource") - def void builtInEnumLiteralInNodeValueAssertionTest(String qualifiedName) { - val it = parseHelper.parse(''' - «qualifiedName»: 16. - ''') - assertThat(errors, empty) - assertThat(nodes, empty) - assertThat(nodeValueAssertion(0).node, equalTo(builtin.findEnum("bool").literal("true"))) - } - - @ParameterizedTest - @MethodSource("builtInEnumLiteralReferencesSource") - def void bultInEnumLiteralInPredicateTest(String qualifiedName) { - val it = parseHelper.parse(''' - pred predicate() <-> node(«qualifiedName»). - ''') - assertThat(errors, empty) - assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(builtin.findEnum("bool").literal("true"))) - } - - static def builtInEnumLiteralReferencesSource() { - Stream.of( - Arguments.of("true"), - Arguments.of("bool::true"), - Arguments.of("builtin::true"), - Arguments.of("builtin::bool::true") - ) - } -} diff --git a/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.xtend b/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.xtend new file mode 100644 index 00000000..9bd728d6 --- /dev/null +++ b/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.xtend @@ -0,0 +1,63 @@ +/* + * generated by Xtext 2.26.0.M1 + */ +package tools.refinery.language.tests + +import com.google.inject.Inject +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.util.ParseHelper +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith +import tools.refinery.language.model.problem.Problem + +import static org.hamcrest.MatcherAssert.assertThat +import static org.hamcrest.Matchers.* + +@ExtendWith(InjectionExtension) +@InjectWith(ProblemInjectorProvider) +class ProblemParsingTest { + @Inject + ParseHelper parseHelper + + @Inject + extension ProblemTestUtil + + @Test + def void exampleTest() { + val it = parseHelper.parse(''' + class Family { + contains Person[] members + } + + class Person { + Person[0..*] children opposite parent + Person[0..1] parent opposite children + int age + TaxStatus taxStatus + } + + enum TaxStatus { + child, student, adult, retired + } + + % A child cannot have any dependents. + error invalidTaxStatus(Person p) <-> + taxStatus(p, child), children(p, _q). + + unique family. + Family(family). + members(family, anne): true. + members(family, bob). + members(family, ciri). + children(anne, ciri). + ?children(bob, ciri). + taxStatus(anne, adult). + age(anne, 35). + bobAge: 27. + age(bob, bobAge). + !age(ciri, bobAge). + ''') + assertThat(errors, empty) + } +} diff --git a/language/src/test/java/tools/refinery/language/tests/ProblemTestUtil.xtend b/language/src/test/java/tools/refinery/language/tests/ProblemTestUtil.xtend new file mode 100644 index 00000000..46418892 --- /dev/null +++ b/language/src/test/java/tools/refinery/language/tests/ProblemTestUtil.xtend @@ -0,0 +1,114 @@ +package tools.refinery.language.tests + +import tools.refinery.language.ProblemUtil +import tools.refinery.language.model.problem.Argument +import tools.refinery.language.model.problem.Assertion +import tools.refinery.language.model.problem.AssertionArgument +import tools.refinery.language.model.problem.Atom +import tools.refinery.language.model.problem.ClassDeclaration +import tools.refinery.language.model.problem.Conjunction +import tools.refinery.language.model.problem.EnumDeclaration +import tools.refinery.language.model.problem.Literal +import tools.refinery.language.model.problem.NegativeLiteral +import tools.refinery.language.model.problem.Node +import tools.refinery.language.model.problem.NodeAssertionArgument +import tools.refinery.language.model.problem.NodeValueAssertion +import tools.refinery.language.model.problem.PredicateDefinition +import tools.refinery.language.model.problem.Problem +import tools.refinery.language.model.problem.UniqueDeclaration +import tools.refinery.language.model.problem.Variable +import tools.refinery.language.model.problem.VariableOrNodeArgument + +class ProblemTestUtil { + def builtin(Problem it) { + ProblemUtil.getBuiltInLibrary(it).get + } + + def errors(Problem it) { + eResource.errors + } + + def nodeNames(Problem it) { + nodes.map[name] + } + + def pred(Problem it, String name) { + statements.filter(PredicateDefinition).findFirst[it.name == name] + } + + def param(PredicateDefinition it, int i) { + parameters.get(i) + } + + def conj(PredicateDefinition it, int i) { + bodies.get(i) + } + + def lit(Conjunction it, int i) { + literals.get(i) + } + + def negated(Literal it) { + (it as NegativeLiteral).atom + } + + def relation(Literal it) { + (it as Atom).relation + } + + def arg(Atom it, int i) { + it.arguments.get(i) + } + + def arg(Literal it, int i) { + (it as Atom).arg(i) + } + + def variable(Argument it) { + (it as VariableOrNodeArgument).variableOrNode as Variable + } + + def node(Argument it) { + (it as VariableOrNodeArgument).variableOrNode as Node + } + + def assertion(Problem it, int i) { + statements.filter(Assertion).get(i) + } + + def nodeValueAssertion(Problem it, int i) { + statements.filter(NodeValueAssertion).get(i) + } + + def arg(Assertion it, int i) { + arguments.get(i) + } + + def node(AssertionArgument it) { + (it as NodeAssertionArgument).node + } + + def node(Problem it, String name) { + nodes.findFirst[it.name == name] + } + + def uniqueNode(Problem it, String name) { + statements.filter(UniqueDeclaration).flatMap[nodes].findFirst[it.name == name] + } + + def findClass(Problem it, String name) { + statements.filter(ClassDeclaration).findFirst[it.name == name] + } + + def reference(ClassDeclaration it, String name) { + it.referenceDeclarations.findFirst[it.name == name] + } + + def findEnum(Problem it, String name) { + statements.filter(EnumDeclaration).findFirst[it.name == name] + } + + def literal(EnumDeclaration it, String name) { + literals.findFirst[it.name == name] + } +} diff --git a/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.xtend b/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.xtend new file mode 100644 index 00000000..ab3e325f --- /dev/null +++ b/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.xtend @@ -0,0 +1,322 @@ +package tools.refinery.language.tests.scoping + +import com.google.inject.Inject +import java.util.stream.Stream +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.util.ParseHelper +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource +import tools.refinery.language.model.problem.Problem +import tools.refinery.language.tests.ProblemInjectorProvider +import tools.refinery.language.tests.ProblemTestUtil + +import static org.hamcrest.MatcherAssert.assertThat +import static org.hamcrest.Matchers.* + +@ExtendWith(InjectionExtension) +@InjectWith(ProblemInjectorProvider) +class NodeScopingTest { + @Inject + ParseHelper parseHelper + + @Inject + extension ProblemTestUtil + + @ParameterizedTest + @ValueSource(strings=#["", "builtin::"]) + def void builtInArgumentTypeTest(String prefix) { + val it = parseHelper.parse(''' + pred predicate(«prefix»node a, «prefix»data b, «prefix»int c). + ''') + assertThat(errors, empty) + assertThat(pred('predicate').param(0).parameterType, equalTo(builtin.findClass('node'))) + assertThat(pred('predicate').param(1).parameterType, equalTo(builtin.findClass('data'))) + assertThat(pred('predicate').param(2).parameterType, equalTo(builtin.findClass('int'))) + } + + @Test + def void implicitNodeInAssertionTest() { + val it = parseHelper.parse(''' + pred predicate(node x, node y) <-> node(x). + predicate(a, a). + ?predicate(a, b). + ''') + assertThat(errors, empty) + assertThat(nodeNames, hasItems('a', 'b')) + assertThat(assertion(0).arg(0).node, equalTo(node('a'))) + assertThat(assertion(0).arg(1).node, equalTo(node('a'))) + assertThat(assertion(1).arg(0).node, equalTo(node('a'))) + assertThat(assertion(1).arg(1).node, equalTo(node('b'))) + } + + @Test + def void implicitNodeInNodeValueAssertionTest() { + val it = parseHelper.parse(''' + a: 16. + ''') + assertThat(errors, empty) + assertThat(nodeNames, hasItems('a')) + assertThat(nodeValueAssertion(0).node, equalTo(node('a'))) + } + + @Test + def void implicitNodeInPredicateTest() { + val it = parseHelper.parse(''' + pred predicate(node a) <-> node(b). + predicate(b). + ''') + assertThat(errors, empty) + assertThat(nodeNames, hasItem("b")) + assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(node("b"))) + assertThat(assertion(0).arg(0).node, equalTo(node("b"))) + } + + @ParameterizedTest + @MethodSource("uniqueNodeReferenceSource") + def void uniqueNodeInAssertionTest(String qualifiedNamePrefix, boolean namedProblem) { + val it = parseHelper.parse(''' + «IF namedProblem»problem test.«ENDIF» + unique a, b. + pred predicate(node x, node y) <-> node(x). + predicate(«qualifiedNamePrefix»a, «qualifiedNamePrefix»a). + ?predicate(«qualifiedNamePrefix»a, «qualifiedNamePrefix»b). + ''') + assertThat(errors, empty) + assertThat(nodeNames, empty) + assertThat(assertion(0).arg(0).node, equalTo(uniqueNode('a'))) + assertThat(assertion(0).arg(1).node, equalTo(uniqueNode('a'))) + assertThat(assertion(1).arg(0).node, equalTo(uniqueNode('a'))) + assertThat(assertion(1).arg(1).node, equalTo(uniqueNode('b'))) + } + + @ParameterizedTest + @MethodSource("uniqueNodeReferenceSource") + def void uniqueNodeInNodeValueAssertionTest(String qualifiedNamePrefix, boolean namedProblem) { + val it = parseHelper.parse(''' + «IF namedProblem»problem test.«ENDIF» + unique a. + «qualifiedNamePrefix»a: 16. + ''') + assertThat(errors, empty) + assertThat(nodeNames, empty) + assertThat(nodeValueAssertion(0).node, equalTo(uniqueNode('a'))) + } + + @ParameterizedTest + @MethodSource("uniqueNodeReferenceSource") + def void uniqueNodeInPredicateTest(String qualifiedNamePrefix, boolean namedProblem) { + val it = parseHelper.parse(''' + «IF namedProblem»problem test.«ENDIF» + unique b. + pred predicate(node a) <-> node(«qualifiedNamePrefix»b). + ''') + assertThat(errors, empty) + assertThat(nodeNames, empty) + assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(uniqueNode("b"))) + } + + static def uniqueNodeReferenceSource() { + Stream.of( + Arguments.of("", false), + Arguments.of("", true), + Arguments.of("test::", true) + ) + } + + @ParameterizedTest + @MethodSource("builtInNodeReferencesSource") + def void builtInNodeTest(String qualifiedName) { + val it = parseHelper.parse(''' + pred predicate(node x) <-> node(x). + predicate(«qualifiedName»). + ''') + assertThat(errors, empty) + assertThat(nodes, empty) + assertThat(assertion(0).arg(0).node, equalTo(builtin.findClass('int').newNode)) + } + + @ParameterizedTest + @MethodSource("builtInNodeReferencesSource") + def void builtInNodeInNodeValueAssertionTest(String qualifiedName) { + val it = parseHelper.parse(''' + «qualifiedName»: 16. + ''') + assertThat(errors, empty) + assertThat(nodes, empty) + assertThat(nodeValueAssertion(0).node, equalTo(builtin.findClass('int').newNode)) + } + + @ParameterizedTest + @MethodSource("builtInNodeReferencesSource") + def void builtInNodeInPredicateTest(String qualifiedName) { + val it = parseHelper.parse(''' + pred predicate(node x) <-> node(«qualifiedName»). + ''') + assertThat(errors, empty) + assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(builtin.findClass('int').newNode)) + } + + static def builtInNodeReferencesSource() { + Stream.of( + Arguments.of("int::new"), + Arguments.of("builtin::int::new") + ) + } + + @ParameterizedTest(name="{0}, namedProblem={1}") + @MethodSource("classNewNodeReferencesSource") + def void classNewNodeTest(String qualifiedName, boolean namedProblem) { + val it = parseHelper.parse(''' + «IF namedProblem»problem test.«ENDIF» + class Foo. + pred predicate(node x) <-> node(x). + predicate(«qualifiedName»). + ''') + assertThat(errors, empty) + assertThat(nodes, empty) + assertThat(assertion(0).arg(0).node, equalTo(findClass('Foo').newNode)) + } + + @ParameterizedTest(name="{0}, namedProblem={1}") + @MethodSource("classNewNodeReferencesSource") + def void classNewNodeInNodeValueAssertionTest(String qualifiedName, boolean namedProblem) { + val it = parseHelper.parse(''' + «IF namedProblem»problem test.«ENDIF» + class Foo. + «qualifiedName»: 16. + ''') + assertThat(errors, empty) + assertThat(nodes, empty) + assertThat(nodeValueAssertion(0).node, equalTo(findClass('Foo').newNode)) + } + + @ParameterizedTest(name="{0}, namedProblem={1}") + @MethodSource("classNewNodeReferencesSource") + def void classNewNodeInPredicateTest(String qualifiedName, boolean namedProblem) { + val it = parseHelper.parse(''' + «IF namedProblem»problem test.«ENDIF» + class Foo. + pred predicate(node x) <-> node(«qualifiedName»). + ''') + assertThat(errors, empty) + assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(findClass('Foo').newNode)) + } + + static def classNewNodeReferencesSource() { + Stream.of( + Arguments.of("Foo::new", false), + Arguments.of("Foo::new", true), + Arguments.of("test::Foo::new", true) + ) + } + + @Test + def void newNodeIsNotSpecial() { + val it = parseHelper.parse(''' + class Foo. + pred predicate(node x) <-> node(x). + predicate(new). + ''') + assertThat(errors, empty) + assertThat(nodeNames, hasItem('new')) + assertThat(assertion(0).arg(0).node, not(equalTo(findClass('Foo').newNode))) + } + + @ParameterizedTest(name="{0}, namedProblem={1}") + @MethodSource("enumLiteralReferencesSource") + def void enumLiteralTest(String qualifiedName, boolean namedProblem) { + val it = parseHelper.parse(''' + «IF namedProblem»problem test.«ENDIF» + enum Foo { alpha, beta } + pred predicate(Foo a) <-> node(a). + predicate(«qualifiedName»). + ''') + assertThat(errors, empty) + assertThat(nodes, empty) + assertThat(assertion(0).arg(0).node, equalTo(findEnum("Foo").literal("alpha"))) + } + + @ParameterizedTest(name="{0}, namedProblem={1}") + @MethodSource("enumLiteralReferencesSource") + def void enumLiteralInNodeValueAssertionTest(String qualifiedName, boolean namedProblem) { + val it = parseHelper.parse(''' + «IF namedProblem»problem test.«ENDIF» + enum Foo { alpha, beta } + «qualifiedName»: 16. + ''') + assertThat(errors, empty) + assertThat(nodes, empty) + assertThat(nodeValueAssertion(0).node, equalTo(findEnum("Foo").literal("alpha"))) + } + + @ParameterizedTest(name="{0}, namedProblem={1}") + @MethodSource("enumLiteralReferencesSource") + def void enumLiteralInPredicateTest(String qualifiedName, boolean namedProblem) { + val it = parseHelper.parse(''' + «IF namedProblem»problem test.«ENDIF» + enum Foo { alpha, beta } + pred predicate(Foo a) <-> node(«qualifiedName»). + ''') + assertThat(errors, empty) + assertThat(nodes, empty) + assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(findEnum("Foo").literal("alpha"))) + } + + static def enumLiteralReferencesSource() { + Stream.of( + Arguments.of("alpha", false), + Arguments.of("alpha", true), + Arguments.of("Foo::alpha", false), + Arguments.of("Foo::alpha", true), + Arguments.of("test::alpha", true), + Arguments.of("test::Foo::alpha", true) + ) + } + + @ParameterizedTest + @MethodSource("builtInEnumLiteralReferencesSource") + def void builtInEnumLiteralTest(String qualifiedName) { + val it = parseHelper.parse(''' + pred predicate(node a) <-> node(a). + predicate(«qualifiedName»). + ''') + assertThat(errors, empty) + assertThat(nodes, empty) + assertThat(assertion(0).arg(0).node, equalTo(builtin.findEnum("bool").literal("true"))) + } + + @ParameterizedTest + @MethodSource("builtInEnumLiteralReferencesSource") + def void builtInEnumLiteralInNodeValueAssertionTest(String qualifiedName) { + val it = parseHelper.parse(''' + «qualifiedName»: 16. + ''') + assertThat(errors, empty) + assertThat(nodes, empty) + assertThat(nodeValueAssertion(0).node, equalTo(builtin.findEnum("bool").literal("true"))) + } + + @ParameterizedTest + @MethodSource("builtInEnumLiteralReferencesSource") + def void bultInEnumLiteralInPredicateTest(String qualifiedName) { + val it = parseHelper.parse(''' + pred predicate() <-> node(«qualifiedName»). + ''') + assertThat(errors, empty) + assertThat(pred("predicate").conj(0).lit(0).arg(0).node, equalTo(builtin.findEnum("bool").literal("true"))) + } + + static def builtInEnumLiteralReferencesSource() { + Stream.of( + Arguments.of("true"), + Arguments.of("bool::true"), + Arguments.of("builtin::true"), + Arguments.of("builtin::bool::true") + ) + } +} -- cgit v1.2.3-54-g00ecf