From c63126d2f1ce5f571c316b37e00fb43d2da7c7d3 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Wed, 31 Jan 2024 02:00:09 +0100 Subject: refactor(language): module and node declarations * New default file extension: .refinery (.problem is also supported). * Add module keyword for self-contained modules. * Rename indiv declarations to atom declaration. * Add node and multi declarations for explicitly declared nodes and multi-objects, respectively. --- .../language/tests/ProblemParsingTest.java | 2 +- .../tests/formatting2/ProblemFormatterTest.java | 8 +++---- .../language/tests/scoping/NodeScopingTest.java | 26 +++++++++++----------- .../tests/serializer/ProblemSerializerTest.java | 18 ++++++++------- 4 files changed, 28 insertions(+), 26 deletions(-) (limited to 'subprojects/language/src/test') diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.java index 72d57f54..17ae5fbb 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/ProblemParsingTest.java @@ -42,7 +42,7 @@ class ProblemParsingTest { error invalidTaxStatus(Person p) <-> taxStatus(p, CHILD), children(p, _q). - indiv family. + atom family. Family(family). members(family, anne): true. members(family, bob). diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java index f688d970..4a15f9de 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/formatting2/ProblemFormatterTest.java @@ -183,8 +183,8 @@ class ProblemFormatterTest { } @Test - void individualDeclarationTest() { - testFormatter(" indiv a , b . ", "indiv a, b.\n"); + void atomDeclarationTest() { + testFormatter(" atom a , b . ", "atom a, b.\n"); } @Test @@ -194,7 +194,7 @@ class ProblemFormatterTest { pred foo(node a). class Foo. foo(n1, n2). - indiv i1. + atom i1. !foo(i1, n1). pred bar(node a, node b). pred quux(). @@ -207,7 +207,7 @@ class ProblemFormatterTest { class Foo. foo(n1, n2). - indiv i1. + atom i1. !foo(i1, n1). pred bar(node a, node b). diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java index e76d2993..bc0320a6 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java @@ -69,35 +69,35 @@ class NodeScopingTest { } @ParameterizedTest - @MethodSource("individualNodeReferenceSource") - void individualNodeInAssertionTest(String qualifiedNamePrefix, boolean namedProblem) { + @MethodSource("atomNodeReferenceSource") + void atomNodeInAssertionTest(String qualifiedNamePrefix, boolean namedProblem) { var problem = parse(""" - indiv a, b. + atom a, b. pred predicate(node x, node y) <-> node(x). predicate({PARAM}a, {PARAM}a). ?predicate({PARAM}a, {PARAM}b). """, qualifiedNamePrefix, namedProblem); assertThat(problem.getResourceErrors(), empty()); assertThat(problem.nodeNames(), empty()); - assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.individualNode("a"))); - assertThat(problem.assertion(0).arg(1).node(), equalTo(problem.individualNode("a"))); - assertThat(problem.assertion(1).arg(0).node(), equalTo(problem.individualNode("a"))); - assertThat(problem.assertion(1).arg(1).node(), equalTo(problem.individualNode("b"))); + assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.atomNode("a"))); + assertThat(problem.assertion(0).arg(1).node(), equalTo(problem.atomNode("a"))); + assertThat(problem.assertion(1).arg(0).node(), equalTo(problem.atomNode("a"))); + assertThat(problem.assertion(1).arg(1).node(), equalTo(problem.atomNode("b"))); } @ParameterizedTest - @MethodSource("individualNodeReferenceSource") - void individualNodeInPredicateTest(String qualifiedNamePrefix, boolean namedProblem) { + @MethodSource("atomNodeReferenceSource") + void atomNodeInPredicateTest(String qualifiedNamePrefix, boolean namedProblem) { var problem = parse(""" - indiv b. + atom b. pred predicate(node a) <-> node({PARAM}b). - """); + """, qualifiedNamePrefix, namedProblem); assertThat(problem.getResourceErrors(), empty()); assertThat(problem.nodeNames(), empty()); - assertThat(problem.pred("predicate").conj(0).lit(0).arg(0).node(), equalTo(problem.individualNode("b"))); + assertThat(problem.pred("predicate").conj(0).lit(0).arg(0).node(), equalTo(problem.atomNode("b"))); } - static Stream individualNodeReferenceSource() { + static Stream atomNodeReferenceSource() { return Stream.of(Arguments.of("", false), Arguments.of("", true), Arguments.of("test::", true)); } diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java index 65675b6b..ad583f8e 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/serializer/ProblemSerializerTest.java @@ -56,15 +56,16 @@ class ProblemSerializerTest { var pred = createPred(); var node = ProblemFactory.eINSTANCE.createNode(); node.setName("a"); - var individualDeclaration = ProblemFactory.eINSTANCE.createIndividualDeclaration(); - individualDeclaration.getNodes().add(node); - problem.getStatements().add(individualDeclaration); + var atomDeclaration = ProblemFactory.eINSTANCE.createNodeDeclaration(); + atomDeclaration.setKind(NodeKind.ATOM); + atomDeclaration.getNodes().add(node); + problem.getStatements().add(atomDeclaration); createAssertion(pred, node, value); assertSerializedResult(""" pred foo(node p). - indiv a. + atom a. """ + serializedAssertion + "\n"); } @@ -79,15 +80,16 @@ class ProblemSerializerTest { var pred = createPred(); var node = ProblemFactory.eINSTANCE.createNode(); node.setName("a"); - var individualDeclaration = ProblemFactory.eINSTANCE.createIndividualDeclaration(); - individualDeclaration.getNodes().add(node); - problem.getStatements().add(individualDeclaration); + var atomDeclaration = ProblemFactory.eINSTANCE.createNodeDeclaration(); + atomDeclaration.setKind(NodeKind.ATOM); + atomDeclaration.getNodes().add(node); + problem.getStatements().add(atomDeclaration); createAssertion(pred, node, value, true); assertSerializedResult(""" pred foo(node p). - indiv a. + atom a. default\040""" + serializedAssertion + "\n"); } -- cgit v1.2.3-54-g00ecf From a2a92561f7b612b472b5b7e49a2d5ca86802092f Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Wed, 31 Jan 2024 17:10:28 +0100 Subject: feat(language): validate module isolation --- subprojects/frontend/src/language/problem.grammar | 4 +- .../src/language/problemLanguageSupport.ts | 2 +- .../java/tools/refinery/language/Problem.xtext | 7 +- .../language/formatting2/ProblemFormatter.java | 3 +- .../tools/refinery/language/utils/ProblemUtil.java | 6 + .../language/validation/ProblemValidator.java | 46 +++++-- .../tests/validation/AssertionValidationTest.java | 1 - .../tests/validation/ModuleValidationTest.java | 145 +++++++++++++++++++++ 8 files changed, 198 insertions(+), 16 deletions(-) create mode 100644 subprojects/language/src/test/java/tools/refinery/language/tests/validation/ModuleValidationTest.java (limited to 'subprojects/language/src/test') diff --git a/subprojects/frontend/src/language/problem.grammar b/subprojects/frontend/src/language/problem.grammar index b08a9c36..ac451e7a 100644 --- a/subprojects/frontend/src/language/problem.grammar +++ b/subprojects/frontend/src/language/problem.grammar @@ -57,10 +57,10 @@ statement { // RuleBody { ":" sep "==>" sep "." } //} | AtomDeclaration { - ckw<"atom"> sep<",", AtomNodeName> "." + kw<"declare">? ckw<"atom"> sep<",", AtomNodeName> "." } | NodeDeclaration { - (ckw<"node"> | ckw<"multi">) sep<",", NodeName> "." + (kw<"declare"> | kw<"declare">? ckw<"multi">) sep<",", NodeName> "." } | ScopeDeclaration { kw<"scope"> sep<",", ScopeElement> "." diff --git a/subprojects/frontend/src/language/problemLanguageSupport.ts b/subprojects/frontend/src/language/problemLanguageSupport.ts index 3847fdd8..2483c2e3 100644 --- a/subprojects/frontend/src/language/problemLanguageSupport.ts +++ b/subprojects/frontend/src/language/problemLanguageSupport.ts @@ -28,7 +28,7 @@ const parserWithMetadata = parser.configure({ LineComment: t.lineComment, BlockComment: t.blockComment, 'module problem class enum pred fn scope': t.definitionKeyword, - 'node atom multi': t.definitionKeyword, + 'declare atom multi': t.definitionKeyword, 'abstract extends refers contains container opposite': t.modifier, 'default error contained containment': t.modifier, 'true false unknown error': t.keyword, diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext index c5c42ebe..4ce3fae1 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext +++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext @@ -256,10 +256,11 @@ ExactMultiplicity: exactValue=INT; NodeDeclaration: - kind=NodeKind nodes+=EnumLiteral ("," nodes+=EnumLiteral)* "."; + ("declare" | "declare"? kind=NodeKind) + nodes+=EnumLiteral ("," nodes+=EnumLiteral)* "."; enum NodeKind: - NODE="node" | ATOM="atom" | MULTI="multi"; + ATOM="atom" | MULTI="multi"; UpperBound returns ecore::EInt: INT | "*"; @@ -274,7 +275,7 @@ Identifier: NonContainmentIdentifier | "contains" | "container"; NonContainmentIdentifier: - ID | "node" | "atom" | "multi" | "contained" | + ID | "atom" | "multi" | "contained" | "sum" | "prod" | "min" | "max" | "problem" | "module"; Real returns ecore::EDouble: diff --git a/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java b/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java index 0b87e8bb..d6ece1ea 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java +++ b/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java @@ -23,7 +23,7 @@ public class ProblemFormatter extends AbstractJavaFormatter { protected void format(Problem problem, IFormattableDocument doc) { doc.prepend(problem, this::noSpace); var region = regionFor(problem); - doc.append(region.feature(ProblemPackage.Literals.PROBLEM__KIND), this::oneSpace); + doc.prepend(region.feature(ProblemPackage.Literals.NAMED_ELEMENT__NAME), this::oneSpace); doc.prepend(region.keyword("."), this::noSpace); appendNewLines(doc, region.keyword("."), this::twoNewLines); for (var statement : problem.getStatements()) { @@ -135,6 +135,7 @@ public class ProblemFormatter extends AbstractJavaFormatter { protected void format(NodeDeclaration nodeDeclaration, IFormattableDocument doc) { surroundNewLines(doc, nodeDeclaration, this::singleNewLine); var region = regionFor(nodeDeclaration); + doc.append(region.keyword("declare"), this::oneSpace); doc.append(region.feature(ProblemPackage.Literals.NODE_DECLARATION__KIND), this::oneSpace); formatList(region, ",", doc); doc.prepend(region.keyword("."), this::noSpace); diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java index ee0e141d..0f87c04b 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java @@ -8,6 +8,7 @@ package tools.refinery.language.utils; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.EcoreUtil2; import tools.refinery.language.model.problem.*; public final class ProblemUtil { @@ -126,6 +127,11 @@ public final class ProblemUtil { }; } + public static boolean isInModule(EObject eObject) { + var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class); + return problem != null && problem.getKind() == ModuleKind.MODULE; + } + private static URI getLibraryUri() { var libraryResource = ProblemUtil.class.getClassLoader() .getResource("tools/refinery/language/%s.problem".formatted(BUILTIN_LIBRARY_NAME)); diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java index a95e8b3c..4cbb02c2 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java +++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java @@ -86,8 +86,8 @@ public class ProblemValidator extends AbstractProblemValidator { var variableOrNode = expr.getVariableOrNode(); if (variableOrNode instanceof Node node && !ProblemUtil.isAtomNode(node)) { var name = node.getName(); - var message = ("Only individuals can be referenced in predicates. " + - "Mark '%s' as individual with the declaration 'indiv %s.'").formatted(name, name); + var message = ("Only atoms can be referenced in predicates. " + + "Mark '%s' as an atom with the declaration 'atom %s.'").formatted(name, name); error(message, expr, ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE, INSIGNIFICANT_INDEX, NODE_CONSTANT_ISSUE); } @@ -302,6 +302,10 @@ public class ProblemValidator extends AbstractProblemValidator { @Check public void checkTypeScope(TypeScope typeScope) { checkArity(typeScope, ProblemPackage.Literals.TYPE_SCOPE__TARGET_TYPE, 1); + if (typeScope.isIncrement() && ProblemUtil.isInModule(typeScope)) { + acceptError("Incremental type scopes are not supported in modules", typeScope, null, 0, + INVALID_MULTIPLICITY_ISSUE); + } } private void checkArity(EObject eObject, EReference reference, int expectedArity) { @@ -351,9 +355,9 @@ public class ProblemValidator extends AbstractProblemValidator { } private void checkExistsAssertion(Assertion assertion, LogicValue value) { - if (value == LogicValue.TRUE || value == LogicValue.UNKNOWN) { - // {@code true} is always a valid value for {@code exists}, while {@code unknown} values may always be - // refined to {@code true} if necessary (e.g., for individual nodes). + if (value == LogicValue.UNKNOWN) { + // {@code unknown} values may always be refined to {@code true} of {@code false} if necessary (e.g., for + // atom nodes or removed multi-objects). return; } var arguments = assertion.getArguments(); @@ -361,9 +365,16 @@ public class ProblemValidator extends AbstractProblemValidator { // We already report an error on invalid arity. return; } - var node = getNodeArgumentForMultiObjectAssertion(arguments.get(0)); - if (node != null && !node.eIsProxy() && ProblemUtil.isAtomNode(node)) { - acceptError("Individual nodes must exist.", assertion, null, 0, UNSUPPORTED_ASSERTION_ISSUE); + var node = getNodeArgumentForMultiObjectAssertion(arguments.getFirst()); + if (node == null || node.eIsProxy()) { + return; + } + if (ProblemUtil.isAtomNode(node) && value != LogicValue.TRUE) { + acceptError("Atom nodes must exist.", assertion, null, 0, UNSUPPORTED_ASSERTION_ISSUE); + } + if (ProblemUtil.isMultiNode(node) && value != LogicValue.FALSE && ProblemUtil.isInModule(node)) { + acceptError("Multi-objects in modules cannot be required to exist.", assertion, null, 0, + UNSUPPORTED_ASSERTION_ISSUE); } } @@ -403,4 +414,23 @@ public class ProblemValidator extends AbstractProblemValidator { } throw new IllegalArgumentException("Unknown assertion argument: " + argument); } + + @Check + private void checkImplicitNodeInModule(Assertion assertion) { + if (!ProblemUtil.isInModule(assertion)) { + return; + } + for (var argument : assertion.getArguments()) { + if (argument instanceof NodeAssertionArgument nodeAssertionArgument) { + var node = nodeAssertionArgument.getNode(); + if (node != null && !node.eIsProxy() && ProblemUtil.isImplicitNode(node)) { + var name = node.getName(); + var message = ("Implicit nodes are not allowed in modules. Explicitly declare '%s' as a node " + + "with the declaration 'declare %s.'").formatted(name, name); + acceptError(message, nodeAssertionArgument, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE, + 0, UNSUPPORTED_ASSERTION_ISSUE); + } + } + } + } } diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java index 82dea31b..1fb08845 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java @@ -82,7 +82,6 @@ class AssertionValidationTest { "exists(n).", "?exists(n).", "!exists(n).", - "exists(*).", "?exists(*).", "exists(Foo::new).", "?exists(Foo::new).", diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ModuleValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ModuleValidationTest.java new file mode 100644 index 00000000..00ad051b --- /dev/null +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/ModuleValidationTest.java @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: 2024 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.language.tests.validation; + +import com.google.inject.Inject; +import org.eclipse.xtext.testing.InjectWith; +import org.eclipse.xtext.testing.extensions.InjectionExtension; +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 tools.refinery.language.model.tests.utils.ProblemParseHelper; +import tools.refinery.language.tests.ProblemInjectorProvider; +import tools.refinery.language.validation.ProblemValidator; + +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +@ExtendWith(InjectionExtension.class) +@InjectWith(ProblemInjectorProvider.class) +class ModuleValidationTest { + @Inject + private ProblemParseHelper parseHelper; + + @ParameterizedTest + @MethodSource + void invalidMultiObjectExistsTest(String invalidDeclaration) { + var problem = parseHelper.parse(""" + module. + + %s + """.formatted(invalidDeclaration)); + var issues = problem.validate(); + assertThat(issues, hasItem(hasProperty("issueCode", + is(ProblemValidator.UNSUPPORTED_ASSERTION_ISSUE)))); + } + + static Stream invalidMultiObjectExistsTest() { + return Stream.of( + Arguments.of(""" + class Foo. + exists(Foo::new). + """), + Arguments.of(""" + multi m. + exists(m). + """)); + } + + @Test + void invalidScopeTest() { + var problem = parseHelper.parse(""" + module. + + class Foo. + scope Foo += 1. + """); + var issues = problem.validate(); + assertThat(issues, hasItem(hasProperty("issueCode", + is(ProblemValidator.INVALID_MULTIPLICITY_ISSUE)))); + } + + @Test + void invalidAssertionArgumentTest() { + var problem = parseHelper.parse(""" + module. + + class Foo. + Foo(foo1). + """); + var issues = problem.validate(); + assertThat(issues, hasItem(allOf( + hasProperty("issueCode", is(ProblemValidator.UNSUPPORTED_ASSERTION_ISSUE)), + hasProperty("message", containsString("foo1"))))); + } + + @ParameterizedTest + @MethodSource + void validDeclarationTest(String validDeclaration) { + var problem = parseHelper.parse(validDeclaration); + var issues = problem.validate(); + assertThat(issues, hasSize(0)); + } + + static Stream validDeclarationTest() { + return Stream.concat( + invalidMultiObjectExistsTest(), + Stream.of( + Arguments.of(""" + class Foo. + scope Foo += 1. + """), + Arguments.of(""" + module. + + class Foo. + scope Foo = 1. + """), + Arguments.of(""" + class Foo. + Foo(foo1). + """), + Arguments.of(""" + module. + + class Foo. + multi foo1. + Foo(foo1). + """), + Arguments.of(""" + module. + + class Foo. + atom foo1. + Foo(foo1). + """), + Arguments.of(""" + module. + + class Foo. + declare foo1. + Foo(foo1). + """), + Arguments.of(""" + module. + + enum Foo { foo1 } + Foo(foo1). + """), + Arguments.of(""" + module. + + class Foo. + Foo(Foo::new). + """) + ) + ); + } +} -- cgit v1.2.3-54-g00ecf From 6c4d81ba085d24035ad1f2e45d4e731d3b33e6db Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sun, 18 Feb 2024 20:44:00 +0100 Subject: refactor(language): no fully qualified self import Make sure it is impossible to create clashing fully qualified names when renaming a module by forbidding modules from referring to their own elements with fully qualified names. Therefore, serializing a solution will not create clashing fully qualified names (which would prevent serialization from succeeding). --- .../language/semantics/SemanticsUtils.java | 41 +++++++- .../language/semantics/SolutionSerializer.java | 106 +++++++++++++-------- .../scoping/NoFullyQualifiedNamesSelectable.java | 62 ++++++++++++ .../scoping/ProblemLocalScopeProvider.java | 29 ++---- .../language/scoping/imports/ImportAdapter.java | 25 ----- .../language/tests/scoping/NodeScopingTest.java | 70 +++++--------- 6 files changed, 202 insertions(+), 131 deletions(-) create mode 100644 subprojects/language/src/main/java/tools/refinery/language/scoping/NoFullyQualifiedNamesSelectable.java (limited to 'subprojects/language/src/test') diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SemanticsUtils.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SemanticsUtils.java index 110295b2..9c40e6df 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SemanticsUtils.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SemanticsUtils.java @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 The Refinery Authors + * SPDX-FileCopyrightText: 2023-2024 The Refinery Authors * * SPDX-License-Identifier: EPL-2.0 */ @@ -8,11 +8,14 @@ package tools.refinery.language.semantics; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.name.Named; +import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.IQualifiedNameProvider; import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.resource.IResourceDescriptionsProvider; import org.eclipse.xtext.scoping.IScope; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,6 +36,9 @@ public class SemanticsUtils { @Inject private IQualifiedNameConverter qualifiedNameConverter; + @Inject + private IResourceDescriptionsProvider resourceDescriptionsProvider; + public Optional getNameWithoutRootPrefix(EObject eObject) { var qualifiedName = delegateQualifiedNameProvider.getFullyQualifiedName(eObject); if (qualifiedName == null) { @@ -41,12 +47,32 @@ public class SemanticsUtils { return Optional.of(qualifiedNameConverter.toString(qualifiedName)); } + @Nullable + public T maybeGetLocalElement(Problem problem, QualifiedName qualifiedName, Class type, EClass eClass) { + var resource = problem.eResource(); + var resourceSet = resource.getResourceSet(); + var resourceDescriptions = resourceDescriptionsProvider.getResourceDescriptions(resourceSet); + var resourceDescription = resourceDescriptions.getResourceDescription(resource.getURI()); + if (resourceDescription == null) { + return null; + } + var eObjectDescriptions = resourceDescription.getExportedObjects(eClass, qualifiedName, false); + return maybeGet(problem, eObjectDescriptions, qualifiedName, type); + } + @Nullable public T maybeGetElement(Problem problem, IScope scope, QualifiedName qualifiedName, Class type) { if (qualifiedName == null) { throw new IllegalArgumentException("Element name must not be null"); } - var iterator = scope.getElements(qualifiedName).iterator(); + var eObjectDescriptions = scope.getElements(qualifiedName); + return maybeGet(problem, eObjectDescriptions, qualifiedName, type); + } + + @Nullable + private T maybeGet(Problem problem, Iterable eObjectDescriptions, + QualifiedName qualifiedName, Class type) { + var iterator = eObjectDescriptions.iterator(); if (!iterator.hasNext()) { return null; } @@ -65,9 +91,20 @@ public class SemanticsUtils { return type.cast(eObject); } + @NotNull + public T getLocalElement(Problem problem, QualifiedName qualifiedName, Class type, EClass eClass) { + var element = maybeGetLocalElement(problem, qualifiedName, type, eClass); + return getOrThrow(element, qualifiedName, type); + } + @NotNull public T getElement(Problem problem, IScope scope, QualifiedName qualifiedName, Class type) { var element = maybeGetElement(problem, scope, qualifiedName, type); + return getOrThrow(element, qualifiedName, type); + } + + @NotNull + private T getOrThrow(@Nullable T element, QualifiedName qualifiedName, Class type) { if (element == null) { var qualifiedNameString = qualifiedNameConverter.toString(qualifiedName); throw new IllegalArgumentException("No such %s: %s" diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java index 2fb0a49d..377a66f3 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java @@ -10,6 +10,8 @@ import com.google.inject.Provider; import org.eclipse.collections.api.factory.primitive.IntObjectMaps; import org.eclipse.collections.api.map.primitive.MutableIntObjectMap; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.xtext.naming.IQualifiedNameProvider; import org.eclipse.xtext.naming.QualifiedName; @@ -18,7 +20,7 @@ import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.resource.XtextResourceSet; import org.eclipse.xtext.scoping.IScopeProvider; import tools.refinery.language.model.problem.*; -import tools.refinery.language.scoping.imports.ImportAdapter; +import tools.refinery.language.naming.NamingUtil; import tools.refinery.language.utils.ProblemDesugarer; import tools.refinery.language.utils.ProblemUtil; import tools.refinery.store.model.Model; @@ -34,9 +36,7 @@ import tools.refinery.store.tuple.Tuple; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.Map; -import java.util.TreeMap; -import java.util.TreeSet; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -66,13 +66,17 @@ public class SolutionSerializer { private Model model; private ReasoningAdapter reasoningAdapter; private PartialInterpretation existsInterpretation; + private Resource originalResource; private Problem originalProblem; + private QualifiedName originalProblemName; private Problem problem; + private QualifiedName newProblemName; private NodeDeclaration nodeDeclaration; private final MutableIntObjectMap nodes = IntObjectMaps.mutable.empty(); public Problem serializeSolution(ProblemTrace trace, Model model) { - var uri = URI.createURI("__synthetic." + ProblemUtil.MODULE_EXTENSION); + var uri = URI.createURI("__solution_%s.%s".formatted(UUID.randomUUID().toString().replace('-', '_'), + ProblemUtil.MODULE_EXTENSION)); return serializeSolution(trace, model, uri); } @@ -83,10 +87,14 @@ public class SolutionSerializer { existsInterpretation = reasoningAdapter.getPartialInterpretation(Concreteness.CANDIDATE, ReasoningAdapter.EXISTS_SYMBOL); originalProblem = trace.getProblem(); + originalProblemName = qualifiedNameProvider.getFullyQualifiedName(originalProblem); problem = copyProblem(originalProblem, uri); - problem.setKind(ModuleKind.MODULE); + problem.setKind(ProblemUtil.getDefaultModuleKind(uri)); + problem.setExplicitKind(false); + problem.setName(null); + newProblemName = qualifiedNameProvider.getFullyQualifiedName(originalProblem); problem.getStatements().removeIf(SolutionSerializer::shouldRemoveStatement); - problem.getNodes().removeIf(this::shouldRemoveNode); + removeNonExistentImplicitNodes(); nodeDeclaration = ProblemFactory.eINSTANCE.createNodeDeclaration(); nodeDeclaration.setKind(NodeKind.NODE); nodeDeclaration.getNodes().addAll(problem.getNodes()); @@ -105,15 +113,28 @@ public class SolutionSerializer { return statement instanceof Assertion || statement instanceof ScopeDeclaration; } - private boolean shouldRemoveNode(Node newNode) { - var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(newNode); - var scope = scopeProvider.getScope(originalProblem, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE); - var originalNode = semanticsUtils.maybeGetElement(originalProblem, scope, qualifiedName, Node.class); - if (originalNode == null) { - return false; + private void removeNonExistentImplicitNodes() { + var originalImplicitNodes = originalProblem.getNodes(); + var newImplicitNodes = problem.getNodes(); + if (newImplicitNodes.size() != originalImplicitNodes.size()) { + throw new IllegalStateException("Expected %d implicit nodes in problem, but got %d after copying" + .formatted(originalImplicitNodes.size(), newImplicitNodes.size())); + } + var iterator = newImplicitNodes.iterator(); + for (var originalNode : originalImplicitNodes) { + if (!iterator.hasNext()) { + throw new AssertionError("Unexpected end of copied implicit node list"); + } + var newNode = iterator.next(); + if (!Objects.equals(originalNode.getName(), newNode.getName())) { + throw new IllegalStateException("Expected copy of '%s' to have the same name, got '%s' instead" + .formatted(originalNode.getName(), newNode.getName())); + } + int nodeId = trace.getNodeId(originalNode); + if (!isExistingNode(nodeId)) { + iterator.remove(); + } } - int nodeId = trace.getNodeId(originalNode); - return !isExistingNode(nodeId); } private boolean isExistingNode(int nodeId) { @@ -125,14 +146,10 @@ public class SolutionSerializer { } private Problem copyProblem(Problem originalProblem, URI uri) { - var newResourceSet = resourceSetProvider.get(); - ImportAdapter.copySettings(originalProblem, newResourceSet); - if (!ProblemUtil.MODULE_EXTENSION.equals(uri.fileExtension())) { - uri = uri.appendFileExtension(ProblemUtil.MODULE_EXTENSION); - } + originalResource = originalProblem.eResource(); + var resourceSet = originalResource.getResourceSet(); var newResource = resourceFactory.createResource(uri); - newResourceSet.getResources().add(newResource); - var originalResource = originalProblem.eResource(); + resourceSet.getResources().add(newResource); if (originalResource instanceof XtextResource) { byte[] bytes; try { @@ -147,7 +164,7 @@ public class SolutionSerializer { throw new IllegalStateException("Failed to copy problem", e); } var contents = newResource.getContents(); - EcoreUtil.resolveAll(newResourceSet); + EcoreUtil.resolveAll(newResource); if (!contents.isEmpty() && contents.getFirst() instanceof Problem newProblem) { return newProblem; } @@ -159,10 +176,24 @@ public class SolutionSerializer { } } + private QualifiedName getConvertedName(EObject original) { + var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(original); + if (originalProblemName != null && qualifiedName.startsWith(originalProblemName)) { + qualifiedName = qualifiedName.skipFirst(originalProblemName.getSegmentCount()); + } + if (newProblemName != null) { + qualifiedName = newProblemName.append(qualifiedName); + } + return NamingUtil.addRootPrefix(qualifiedName); + } + private Relation findRelation(Relation originalRelation) { - var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(originalRelation); - var scope = scopeProvider.getScope(problem, ProblemPackage.Literals.ASSERTION__RELATION); - return semanticsUtils.getElement(problem, scope, qualifiedName, Relation.class); + if (originalRelation.eResource() != originalResource) { + return originalRelation; + } + var qualifiedName = getConvertedName(originalRelation); + return semanticsUtils.getLocalElement(problem, qualifiedName, Relation.class, + ProblemPackage.Literals.RELATION); } private Relation findPartialRelation(PartialRelation partialRelation) { @@ -170,13 +201,11 @@ public class SolutionSerializer { } private Node findNode(Node originalNode) { - var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(originalNode); - return findNode(qualifiedName); - } - - private Node findNode(QualifiedName qualifiedName) { - var scope = scopeProvider.getScope(problem, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE); - return semanticsUtils.maybeGetElement(problem, scope, qualifiedName, Node.class); + if (originalNode.eResource() != originalResource) { + return originalNode; + } + var qualifiedName = getConvertedName(originalNode); + return semanticsUtils.maybeGetLocalElement(problem, qualifiedName, Node.class, ProblemPackage.Literals.NODE); } private void addAssertion(Relation relation, LogicValue value, Node... arguments) { @@ -194,8 +223,8 @@ public class SolutionSerializer { } private void addExistsAssertions() { - var builtinSymbols = desugarer.getBuiltinSymbols(problem) - .orElseThrow(() -> new IllegalStateException("No builtin library in copied problem")); + var builtinSymbols = desugarer.getBuiltinSymbols(problem).orElseThrow(() -> new IllegalStateException("No " + + "builtin library in copied problem")); // Make sure to output exists assertions in a deterministic order. var sortedNewNodes = new TreeMap(); for (var pair : trace.getNodeTrace().keyValuesView()) { @@ -204,8 +233,7 @@ public class SolutionSerializer { var newNode = findNode(originalNode); // Since all implicit nodes that do not exist has already been removed in serializeSolution, // we only need to add !exists assertions to ::new nodes and explicitly declared nodes that do not exist. - if (ProblemUtil.isMultiNode(originalNode) || - (ProblemUtil.isDeclaredNode(originalNode) && !isExistingNode(nodeId))) { + if (ProblemUtil.isMultiNode(originalNode) || (ProblemUtil.isDeclaredNode(originalNode) && !isExistingNode(nodeId))) { sortedNewNodes.put(nodeId, newNode); } else { nodes.put(nodeId, newNode); @@ -218,8 +246,8 @@ public class SolutionSerializer { } private void addClassAssertions() { - var types = trace.getMetamodel().typeHierarchy().getPreservedTypes().keySet().stream() - .collect(Collectors.toMap(Function.identity(), this::findPartialRelation)); + var types = + trace.getMetamodel().typeHierarchy().getPreservedTypes().keySet().stream().collect(Collectors.toMap(Function.identity(), this::findPartialRelation)); var cursor = model.getInterpretation(TypeHierarchyTranslator.TYPE_SYMBOL).getAll(); while (cursor.move()) { var key = cursor.getKey(); diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/NoFullyQualifiedNamesSelectable.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/NoFullyQualifiedNamesSelectable.java new file mode 100644 index 00000000..f9405fc1 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/NoFullyQualifiedNamesSelectable.java @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2024 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.language.scoping; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.resource.ISelectable; +import tools.refinery.language.naming.NamingUtil; + +import java.util.List; + +public class NoFullyQualifiedNamesSelectable implements ISelectable { + private final ISelectable delegateSelectable; + + // {@link com.google.common.base.Predicate} required by Xtext API. + @SuppressWarnings("squid:S4738") + private final Predicate filter = + eObjectDescription -> !NamingUtil.isFullyQualified(eObjectDescription.getName()); + + public NoFullyQualifiedNamesSelectable(ISelectable delegateSelectable) { + this.delegateSelectable = delegateSelectable; + } + + @Override + public boolean isEmpty() { + return delegateSelectable.isEmpty(); + } + + @Override + public Iterable getExportedObjects() { + return filter(delegateSelectable.getExportedObjects()); + } + + @Override + public Iterable getExportedObjects(EClass type, QualifiedName name, boolean ignoreCase) { + if (NamingUtil.isFullyQualified(name)) { + return List.of(); + } + return delegateSelectable.getExportedObjects(type, name, ignoreCase); + } + + @Override + public Iterable getExportedObjectsByType(EClass type) { + return filter(delegateSelectable.getExportedObjectsByType(type)); + } + + @Override + public Iterable getExportedObjectsByObject(EObject object) { + return filter(delegateSelectable.getExportedObjectsByObject(object)); + } + + private Iterable filter(Iterable eObjectDescriptions) { + return Iterables.filter(eObjectDescriptions, filter); + } +} diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java index 1c0c1d86..0067bf94 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java @@ -12,7 +12,6 @@ import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtext.naming.IQualifiedNameProvider; import org.eclipse.xtext.naming.QualifiedName; -import org.eclipse.xtext.resource.IResourceDescription; import org.eclipse.xtext.resource.IResourceDescriptionsProvider; import org.eclipse.xtext.resource.ISelectable; import org.eclipse.xtext.scoping.IScope; @@ -39,41 +38,31 @@ public class ProblemLocalScopeProvider extends AbstractGlobalScopeDelegatingScop if (resource == null) { return IScope.NULLSCOPE; } + var globalScope = getGlobalScope(resource, reference); var localImports = cache.get(CACHE_KEY, resource, () -> computeLocalImports(resource)); - if (localImports.resourceDescription() == null) { - return IScope.NULLSCOPE; + if (localImports == null) { + return globalScope; } - var globalScope = getGlobalScope(resource, reference); var type = reference.getEReferenceType(); boolean ignoreCase = isIgnoreCase(reference); - var scope = ShadowingKeyAwareSelectableBasedScope.createScope(globalScope, localImports.resourceDescription(), - type, ignoreCase); - if (localImports.normalizedSelectable() == null) { - return scope; - } - return ShadowingKeyAwareSelectableBasedScope.createScope(scope, localImports.normalizedSelectable(), type, - ignoreCase); + return ShadowingKeyAwareSelectableBasedScope.createScope(globalScope, localImports, type, ignoreCase); } - protected LocalImports computeLocalImports(Resource resource) { + protected ISelectable computeLocalImports(Resource resource) { // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects. var resourceDescriptions = resourceDescriptionsProvider.getResourceDescriptions(resource.getResourceSet()); var resourceDescription = resourceDescriptions.getResourceDescription(resource.getURI()); if (resourceDescription == null) { - return new LocalImports(null, null); + return null; } var rootElement = resource.getContents().getFirst(); if (rootElement == null) { - return new LocalImports(resourceDescription, null); + return new NoFullyQualifiedNamesSelectable(resourceDescription); } var rootName = delegateQualifiedNameProvider.getFullyQualifiedName(rootElement); if (rootName == null) { - return new LocalImports(resourceDescription, null); + return new NoFullyQualifiedNamesSelectable(resourceDescription); } - var normalizedSelectable = new NormalizedSelectable(resourceDescription, rootName, QualifiedName.EMPTY); - return new LocalImports(resourceDescription, normalizedSelectable); - } - - protected record LocalImports(IResourceDescription resourceDescription, ISelectable normalizedSelectable) { + return new NormalizedSelectable(resourceDescription, rootName, QualifiedName.EMPTY); } } diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java index 5a8f7fd7..d7a5304f 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java @@ -12,7 +12,6 @@ import org.apache.log4j.Logger; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; @@ -203,28 +202,4 @@ public class ImportAdapter extends AdapterImpl { private static ImportAdapter getAdapter(ResourceSet resourceSet) { return (ImportAdapter) EcoreUtil.getAdapter(resourceSet.eAdapters(), ImportAdapter.class); } - - public static void copySettings(EObject context, ResourceSet newResourceSet) { - var resource = context.eResource(); - if (resource == null) { - return; - } - var originalResourceSet = resource.getResourceSet(); - if (originalResourceSet == null) { - return; - } - copySettings(originalResourceSet, newResourceSet); - } - - public static void copySettings(ResourceSet originalResourceSet, ResourceSet newResourceSet) { - var originalAdapter = getAdapter(originalResourceSet); - if (originalAdapter == null) { - return; - } - var newAdapter = getOrInstall(newResourceSet); - newAdapter.libraries.clear(); - newAdapter.libraries.addAll(originalAdapter.libraries); - newAdapter.libraryPaths.clear(); - newAdapter.libraryPaths.addAll(originalAdapter.libraryPaths); - } } diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java index bc0320a6..0704e026 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/scoping/NodeScopingTest.java @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors * * SPDX-License-Identifier: EPL-2.0 */ @@ -68,15 +68,14 @@ class NodeScopingTest { assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.node("b"))); } - @ParameterizedTest - @MethodSource("atomNodeReferenceSource") - void atomNodeInAssertionTest(String qualifiedNamePrefix, boolean namedProblem) { + @Test + void atomNodeInAssertionTest() { var problem = parse(""" atom a, b. pred predicate(node x, node y) <-> node(x). - predicate({PARAM}a, {PARAM}a). - ?predicate({PARAM}a, {PARAM}b). - """, qualifiedNamePrefix, namedProblem); + predicate(a, a). + ?predicate(a, b). + """); assertThat(problem.getResourceErrors(), empty()); assertThat(problem.nodeNames(), empty()); assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.atomNode("a"))); @@ -85,22 +84,17 @@ class NodeScopingTest { assertThat(problem.assertion(1).arg(1).node(), equalTo(problem.atomNode("b"))); } - @ParameterizedTest - @MethodSource("atomNodeReferenceSource") - void atomNodeInPredicateTest(String qualifiedNamePrefix, boolean namedProblem) { + @Test + void atomNodeInPredicateTest() { var problem = parse(""" atom b. - pred predicate(node a) <-> node({PARAM}b). - """, qualifiedNamePrefix, namedProblem); + pred predicate(node a) <-> node(b). + """); assertThat(problem.getResourceErrors(), empty()); assertThat(problem.nodeNames(), empty()); assertThat(problem.pred("predicate").conj(0).lit(0).arg(0).node(), equalTo(problem.atomNode("b"))); } - static Stream atomNodeReferenceSource() { - return Stream.of(Arguments.of("", false), Arguments.of("", true), Arguments.of("test::", true)); - } - @Disabled("No nodes are present in builtin.problem currently") @ParameterizedTest @MethodSource("builtInNodeReferencesSource") @@ -131,37 +125,30 @@ class NodeScopingTest { return Stream.of(Arguments.of("int::new"), Arguments.of("builtin::int::new")); } - @ParameterizedTest - @MethodSource("classNewNodeReferencesSource") - void classNewNodeTest(String qualifiedName, boolean namedProblem) { + @Test + void classNewNodeTest() { var problem = parse(""" class Foo. pred predicate(node x) <-> node(x). - predicate({PARAM}). - """, qualifiedName, namedProblem); + predicate(Foo::new). + """); assertThat(problem.getResourceErrors(), empty()); assertThat(problem.nodeNames(), empty()); assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.findClass("Foo").get().getNewNode())); } - @ParameterizedTest - @MethodSource("classNewNodeReferencesSource") - void classNewNodeInPredicateTest(String qualifiedName, boolean namedProblem) { + @Test + void classNewNodeInPredicateTest() { var problem = parse(""" class Foo. - pred predicate(node x) <-> node({PARAM}). - """, qualifiedName, namedProblem); + pred predicate(node x) <-> node(Foo::new). + """); assertThat(problem.getResourceErrors(), empty()); assertThat(problem.nodeNames(), empty()); assertThat(problem.pred("predicate").conj(0).lit(0).arg(0).node(), equalTo(problem.findClass("Foo").get().getNewNode())); } - static Stream classNewNodeReferencesSource() { - return Stream.of(Arguments.of("Foo::new", false), Arguments.of("Foo::new", true), - Arguments.of("test::Foo::new", true)); - } - @Test void newNodeIsNotSpecial() { var problem = parse(""" @@ -176,12 +163,12 @@ class NodeScopingTest { @ParameterizedTest @MethodSource("enumLiteralReferencesSource") - void enumLiteralTest(String qualifiedName, boolean namedProblem) { + void enumLiteralTest(String qualifiedName) { var problem = parse(""" enum Foo { alpha, beta } pred predicate(Foo a) <-> node(a). predicate({PARAM}). - """, qualifiedName, namedProblem); + """, qualifiedName); assertThat(problem.getResourceErrors(), empty()); assertThat(problem.nodeNames(), empty()); assertThat(problem.assertion(0).arg(0).node(), equalTo(problem.findEnum("Foo").literal("alpha"))); @@ -189,11 +176,11 @@ class NodeScopingTest { @ParameterizedTest @MethodSource("enumLiteralReferencesSource") - void enumLiteralInPredicateTest(String qualifiedName, boolean namedProblem) { + void enumLiteralInPredicateTest(String qualifiedName) { var problem = parse(""" enum Foo { alpha, beta } pred predicate(Foo a) <-> node({PARAM}). - """, qualifiedName, namedProblem); + """, qualifiedName); assertThat(problem.getResourceErrors(), empty()); assertThat(problem.nodeNames(), empty()); assertThat(problem.pred("predicate").conj(0).lit(0).arg(0).node(), @@ -201,9 +188,7 @@ class NodeScopingTest { } static Stream enumLiteralReferencesSource() { - return 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)); + return Stream.of(Arguments.of("alpha"), Arguments.of("Foo::alpha")); } @Disabled("No enum literals are present in builtin.problem currently") @@ -222,7 +207,7 @@ class NodeScopingTest { @Disabled("No enum literals are present in builtin.problem currently") @ParameterizedTest @MethodSource("builtInEnumLiteralReferencesSource") - void bultInEnumLiteralInPredicateTest(String qualifiedName) { + void builtInEnumLiteralInPredicateTest(String qualifiedName) { var problem = parse(""" pred predicate() <-> node({PARAM}). """, qualifiedName); @@ -237,13 +222,8 @@ class NodeScopingTest { Arguments.of("builtin::bool::true")); } - private WrappedProblem parse(String text, String parameter, boolean namedProblem) { - var problemName = namedProblem ? "problem test.\n" : ""; - return parseHelper.parse(problemName + text.replace("{PARAM}", parameter)); - } - private WrappedProblem parse(String text, String parameter) { - return parse(text, parameter, false); + return parseHelper.parse(text.replace("{PARAM}", parameter)); } private WrappedProblem parse(String text) { -- cgit v1.2.3-54-g00ecf