diff options
author | 2023-12-24 19:42:21 +0100 | |
---|---|---|
committer | 2023-12-25 00:04:37 +0100 | |
commit | 1f9b4652b1f85fc9f2cefcd46b34431ecea5c381 (patch) | |
tree | 84aba7f8ce90ff5aea38c912b3999ec7810f6f44 /subprojects/generator | |
parent | feat: command line model generator (diff) | |
download | refinery-1f9b4652b1f85fc9f2cefcd46b34431ecea5c381.tar.gz refinery-1f9b4652b1f85fc9f2cefcd46b34431ecea5c381.tar.zst refinery-1f9b4652b1f85fc9f2cefcd46b34431ecea5c381.zip |
refactor(generator): scope overrides
Diffstat (limited to 'subprojects/generator')
3 files changed, 191 insertions, 0 deletions
diff --git a/subprojects/generator/build.gradle.kts b/subprojects/generator/build.gradle.kts index d87ce6de..f78fee4d 100644 --- a/subprojects/generator/build.gradle.kts +++ b/subprojects/generator/build.gradle.kts | |||
@@ -12,4 +12,5 @@ dependencies { | |||
12 | api(project(":refinery-language-semantics")) | 12 | api(project(":refinery-language-semantics")) |
13 | api(libs.eclipseCollections.api) | 13 | api(libs.eclipseCollections.api) |
14 | implementation(project(":refinery-store-query-interpreter")) | 14 | implementation(project(":refinery-store-query-interpreter")) |
15 | testImplementation(testFixtures(project(":refinery-language"))) | ||
15 | } | 16 | } |
diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java b/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java index abfbe7e6..20ea8132 100644 --- a/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java +++ b/subprojects/generator/src/main/java/tools/refinery/generator/ProblemLoader.java | |||
@@ -17,11 +17,18 @@ import org.eclipse.xtext.util.LazyStringInputStream; | |||
17 | import org.eclipse.xtext.validation.CheckMode; | 17 | import org.eclipse.xtext.validation.CheckMode; |
18 | import org.eclipse.xtext.validation.IResourceValidator; | 18 | import org.eclipse.xtext.validation.IResourceValidator; |
19 | import tools.refinery.language.model.problem.Problem; | 19 | import tools.refinery.language.model.problem.Problem; |
20 | import tools.refinery.language.model.problem.Relation; | ||
21 | import tools.refinery.language.model.problem.ScopeDeclaration; | ||
20 | import tools.refinery.store.util.CancellationToken; | 22 | import tools.refinery.store.util.CancellationToken; |
21 | 23 | ||
24 | import java.io.ByteArrayOutputStream; | ||
22 | import java.io.File; | 25 | import java.io.File; |
23 | import java.io.IOException; | 26 | import java.io.IOException; |
24 | import java.io.InputStream; | 27 | import java.io.InputStream; |
28 | import java.nio.charset.StandardCharsets; | ||
29 | import java.util.ArrayList; | ||
30 | import java.util.HashSet; | ||
31 | import java.util.List; | ||
25 | import java.util.Map; | 32 | import java.util.Map; |
26 | 33 | ||
27 | public class ProblemLoader { | 34 | public class ProblemLoader { |
@@ -104,4 +111,58 @@ public class ProblemLoader { | |||
104 | } | 111 | } |
105 | return problem; | 112 | return problem; |
106 | } | 113 | } |
114 | |||
115 | public Problem loadScopeConstraints(Problem problem, List<String> extraScopes, | ||
116 | List<String> overrideScopes) throws IOException { | ||
117 | var allScopes = new ArrayList<>(extraScopes); | ||
118 | allScopes.addAll(overrideScopes); | ||
119 | if (allScopes.isEmpty()) { | ||
120 | return problem; | ||
121 | } | ||
122 | int originalStatementCount = problem.getStatements().size(); | ||
123 | var builder = new StringBuilder(); | ||
124 | var problemResource = problem.eResource(); | ||
125 | try (var outputStream = new ByteArrayOutputStream()) { | ||
126 | problemResource.save(outputStream, Map.of()); | ||
127 | builder.append(outputStream.toString(StandardCharsets.UTF_8)); | ||
128 | } | ||
129 | builder.append('\n'); | ||
130 | for (var scope : allScopes) { | ||
131 | builder.append("scope ").append(scope).append(".\n"); | ||
132 | } | ||
133 | var modifiedProblem = loadString(builder.toString(), problemResource.getURI()); | ||
134 | var modifiedStatements = modifiedProblem.getStatements(); | ||
135 | int modifiedStatementCount = modifiedStatements.size(); | ||
136 | if (modifiedStatementCount != originalStatementCount + allScopes.size()) { | ||
137 | throw new IllegalArgumentException("Failed to parse scope constraints"); | ||
138 | } | ||
139 | // Override scopes remove any scope constraint from the original problem with the same target type. | ||
140 | var overriddenScopes = new HashSet<Relation>(); | ||
141 | for (int i = modifiedStatementCount - overrideScopes.size(); i < modifiedStatementCount; i++) { | ||
142 | var statement = modifiedStatements.get(i); | ||
143 | if (!(statement instanceof ScopeDeclaration scopeDeclaration)) { | ||
144 | throw new IllegalStateException("Invalid scope constraint: " + statement); | ||
145 | } | ||
146 | for (var typeScope : scopeDeclaration.getTypeScopes()) { | ||
147 | overriddenScopes.add(typeScope.getTargetType()); | ||
148 | } | ||
149 | } | ||
150 | int statementIndex = 0; | ||
151 | var iterator = modifiedStatements.iterator(); | ||
152 | // Scope overrides only affect type scopes from the original problem and leave type scopes added on the | ||
153 | // command line intact. | ||
154 | while (statementIndex < originalStatementCount && iterator.hasNext()) { | ||
155 | var statement = iterator.next(); | ||
156 | if (statement instanceof ScopeDeclaration scopeDeclaration) { | ||
157 | var typeScopes = scopeDeclaration.getTypeScopes(); | ||
158 | typeScopes.removeIf(typeScope -> overriddenScopes.contains(typeScope.getTargetType())); | ||
159 | // Scope declarations with no type scopes are invalid, so we have to remove them. | ||
160 | if (typeScopes.isEmpty()) { | ||
161 | iterator.remove(); | ||
162 | } | ||
163 | } | ||
164 | statementIndex++; | ||
165 | } | ||
166 | return modifiedProblem; | ||
167 | } | ||
107 | } | 168 | } |
diff --git a/subprojects/generator/src/test/java/tools/refinery/generator/ProblemLoaderTest.java b/subprojects/generator/src/test/java/tools/refinery/generator/ProblemLoaderTest.java new file mode 100644 index 00000000..0c0db105 --- /dev/null +++ b/subprojects/generator/src/test/java/tools/refinery/generator/ProblemLoaderTest.java | |||
@@ -0,0 +1,129 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.generator; | ||
7 | |||
8 | |||
9 | import com.google.inject.Inject; | ||
10 | import org.eclipse.xtext.testing.InjectWith; | ||
11 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
12 | import org.junit.jupiter.api.extension.ExtendWith; | ||
13 | import org.junit.jupiter.params.ParameterizedTest; | ||
14 | import org.junit.jupiter.params.provider.Arguments; | ||
15 | import org.junit.jupiter.params.provider.MethodSource; | ||
16 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
17 | |||
18 | import java.io.ByteArrayOutputStream; | ||
19 | import java.io.IOException; | ||
20 | import java.nio.charset.StandardCharsets; | ||
21 | import java.util.List; | ||
22 | import java.util.Map; | ||
23 | import java.util.stream.Stream; | ||
24 | |||
25 | import static org.hamcrest.MatcherAssert.assertThat; | ||
26 | import static org.hamcrest.Matchers.is; | ||
27 | |||
28 | @ExtendWith(InjectionExtension.class) | ||
29 | @InjectWith(ProblemInjectorProvider.class) | ||
30 | class ProblemLoaderTest { | ||
31 | private static final String PREFIX = """ | ||
32 | class Foo. | ||
33 | class Bar. | ||
34 | """; | ||
35 | |||
36 | @Inject | ||
37 | private ProblemLoader loader; | ||
38 | |||
39 | @ParameterizedTest | ||
40 | @MethodSource | ||
41 | void loadScopeConstraintsTest(String originalScopes, List<String> scopes, List<String> overrideScopes, | ||
42 | String expectedScopes) throws IOException { | ||
43 | var problem = loader.loadString(PREFIX + originalScopes); | ||
44 | var modifiedProblem = loader.loadScopeConstraints(problem, scopes, overrideScopes); | ||
45 | String serializedProblem; | ||
46 | try (var outputStream = new ByteArrayOutputStream()) { | ||
47 | modifiedProblem.eResource().save(outputStream, Map.of()); | ||
48 | serializedProblem = outputStream.toString(StandardCharsets.UTF_8); | ||
49 | } | ||
50 | assertThat(serializedProblem, is(PREFIX + expectedScopes)); | ||
51 | } | ||
52 | |||
53 | static Stream<Arguments> loadScopeConstraintsTest() { | ||
54 | return Stream.of(Arguments.of("", | ||
55 | List.of(), | ||
56 | List.of(), | ||
57 | ""), Arguments.of("", | ||
58 | List.of("node=5..10"), | ||
59 | List.of(), """ | ||
60 | |||
61 | scope node=5..10. | ||
62 | """), Arguments.of("", | ||
63 | List.of("Foo=2", "Bar=3"), | ||
64 | List.of(), """ | ||
65 | |||
66 | scope Foo=2. | ||
67 | scope Bar=3. | ||
68 | """), Arguments.of(""" | ||
69 | scope Foo = 1, Bar = 1. | ||
70 | """, | ||
71 | List.of("node=5..10"), | ||
72 | List.of(), """ | ||
73 | scope Foo = 1, Bar = 1. | ||
74 | |||
75 | scope node=5..10. | ||
76 | """), Arguments.of(""" | ||
77 | scope Foo = 0..10, Bar = 1. | ||
78 | """, | ||
79 | List.of("Foo = 5"), | ||
80 | List.of(), """ | ||
81 | scope Foo = 0..10, Bar = 1. | ||
82 | |||
83 | scope Foo = 5. | ||
84 | """), Arguments.of(""" | ||
85 | scope Foo = 1, Bar = 1. | ||
86 | """, | ||
87 | List.of(), | ||
88 | List.of("node=5..10"), """ | ||
89 | scope Foo = 1, Bar = 1. | ||
90 | |||
91 | scope node=5..10. | ||
92 | """), Arguments.of(""" | ||
93 | scope Foo = 1, Bar = 1. | ||
94 | """, | ||
95 | List.of(), | ||
96 | List.of("Foo=3..4"), """ | ||
97 | scope Bar = 1. | ||
98 | |||
99 | scope Foo=3..4. | ||
100 | """), Arguments.of(""" | ||
101 | scope Foo = 1, Bar = 1. | ||
102 | """, | ||
103 | List.of("Foo=2"), | ||
104 | List.of("Foo=3..4"), """ | ||
105 | scope Bar = 1. | ||
106 | |||
107 | scope Foo=2. | ||
108 | scope Foo=3..4. | ||
109 | """), Arguments.of(""" | ||
110 | scope Foo = 1. | ||
111 | scope Bar = 1. | ||
112 | """, | ||
113 | List.of(), | ||
114 | List.of("Bar=3..4"), """ | ||
115 | scope Foo = 1. | ||
116 | |||
117 | |||
118 | scope Bar=3..4. | ||
119 | """), Arguments.of(""" | ||
120 | scope Foo = 1, Bar = 1. | ||
121 | """, | ||
122 | List.of(), | ||
123 | List.of("Foo=3..4", "Bar=4..5"), """ | ||
124 | |||
125 | scope Foo=3..4. | ||
126 | scope Bar=4..5. | ||
127 | """)); | ||
128 | } | ||
129 | } | ||