Skip to content
Snippets Groups Projects
Commit a3a28e6c authored by René Schöne's avatar René Schöne
Browse files

Change component resolving for inherited children of type-decls.

- also add simple passing tests
parent dcc63fc3
No related branches found
No related tags found
1 merge request!37Resolve "Inherited components of a type can not be chosen as port targets"
Pipeline #15482 failed
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
### Development Changes ### Development Changes
- Bugfix: "error: variable handler is already defined" when using multiple protocols [#58](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/58) - Bugfix: "error: variable handler is already defined" when using multiple protocols [#58](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/58)
- Bugfix: Inherited components of a type can not be chosen as port targets [#59](https://git-st.inf.tu-dresden.de/jastadd/ragconnect/-/issues/59)
## 1.0.0 ## 1.0.0
......
...@@ -91,7 +91,7 @@ aspect RagConnectNameResolution { ...@@ -91,7 +91,7 @@ aspect RagConnectNameResolution {
String childTypeName = id.substring(dotIndex + 1); String childTypeName = id.substring(dotIndex + 1);
TypeDecl type = program().resolveTypeDecl(parentTypeName); TypeDecl type = program().resolveTypeDecl(parentTypeName);
// iterate over components and find the matching typeComponent // iterate over components and find the matching typeComponent
for (Component comp : type.getComponentList()) { for (Component comp : type.allComponentsAsOwnedByMe()) {
if (comp.isTypeComponent() && comp.getName().equals(childTypeName)) { if (comp.isTypeComponent() && comp.getName().equals(childTypeName)) {
return comp.asTypeComponent(); return comp.asTypeComponent();
} }
...@@ -114,7 +114,7 @@ aspect RagConnectNameResolution { ...@@ -114,7 +114,7 @@ aspect RagConnectNameResolution {
String childTypeName = id.substring(dotIndex + 1); String childTypeName = id.substring(dotIndex + 1);
TypeDecl type = program().resolveTypeDecl(parentTypeName); TypeDecl type = program().resolveTypeDecl(parentTypeName);
// iterate over components and find the matching typeComponent // iterate over components and find the matching typeComponent
for (Component comp : type.getComponentList()) { for (Component comp : type.allComponentsAsOwnedByMe()) {
if (comp.getName().equals(childTypeName)) { if (comp.getName().equals(childTypeName)) {
return comp; return comp;
} }
...@@ -138,7 +138,7 @@ aspect RagConnectNameResolution { ...@@ -138,7 +138,7 @@ aspect RagConnectNameResolution {
String tokenName = id.substring(dotIndex + 1); String tokenName = id.substring(dotIndex + 1);
TypeDecl type = program().resolveTypeDecl(typeName); TypeDecl type = program().resolveTypeDecl(typeName);
// iterate over components and find the matching tokenComponent // iterate over components and find the matching tokenComponent
for (Component comp : type.getComponentList()) { for (Component comp : type.allComponentsAsOwnedByMe()) {
if (comp.isTokenComponent() && comp.getName().equals(tokenName)) { if (comp.isTokenComponent() && comp.getName().equals(tokenName)) {
return comp.asTokenComponent(); return comp.asTokenComponent();
} }
...@@ -175,10 +175,60 @@ aspect RagConnectNameResolution { ...@@ -175,10 +175,60 @@ aspect RagConnectNameResolution {
} }
return null; return null;
} }
}
aspect RelastNameResolution {
syn boolean Role.matches(String typeName, String roleName) = false; syn boolean Role.matches(String typeName, String roleName) = false;
eq NavigableRole.matches(String typeName, String roleName) { eq NavigableRole.matches(String typeName, String roleName) {
return getType().getName().equals(typeName) && getName().equals(roleName); return getType().getName().equals(typeName) && getName().equals(roleName);
} }
/** Returns all components including inherited ones */
syn List<Component> TypeDecl.allComponents() {
List<Component> result = new ArrayList<>();
getComponentList().forEach(result::add);
if (hasSuperType()) {
mergeComponentLists(result, getSuperType().allComponents());
}
return result;
}
/** Returns same as allComponents() but for each component the containingTypeDecl will compute to this component */
syn nta JastAddList<Component> TypeDecl.allComponentsAsOwnedByMe() {
JastAddList<Component> result = new JastAddList<>();
for (Component comp : allComponents()) {
result.add(comp.treeCopy());
}
return result;
}
public static void TypeDecl.mergeComponentLists(List<Component> subTypeResult, List<Component> superTypeResult) {
for (int i = superTypeResult.size() - 1; i >= 0; i--) {
Component superTypeComponent = superTypeResult.get(i);
if (subTypeResult.stream().noneMatch(subTypeComponent -> subTypeComponent.matches(superTypeComponent))) {
subTypeResult.add(0, superTypeComponent);
}
}
}
syn boolean Component.matches(Component other) = false;
eq TokenComponent.matches(Component other) {
if (!other.isTokenComponent()) { return false; }
return getName().equals(other.getName()) && getJavaTypeUse().prettyPrint().equals(other.asTokenComponent().getJavaTypeUse().prettyPrint());
}
eq TypeComponent.matches(Component other) {
return getName().equals(other.getName()) && getTypeDecl().equals(other.asTypeComponent().getTypeDecl());
}
eq NormalComponent.matches(Component other) {
if (!other.isTypeComponent() || !other.asTypeComponent().isNormalComponent()) { return false; }
return super.matches(other);
}
eq ListComponent.matches(Component other) {
if (!other.isTypeComponent() || !other.asTypeComponent().isListComponent()) { return false; }
return super.matches(other);
}
eq OptComponent.matches(Component other) {
if (!other.isTypeComponent() || !other.asTypeComponent().isOptComponent()) { return false; }
return super.matches(other);
}
} }
# Passing
This directory contains use case for RagConnect, that just need to pass compiling (no dedicated Java test).
// both directions for SpecialA1
send SpecialA1.Input ;
receive SpecialA1.Input ;
send SpecialA1.Many ;
receive SpecialA1.Many ;
send SpecialA1.Maybe ;
receive SpecialA1.Maybe ;
send SpecialA1.B ;
receive SpecialA1.B ;
// only one direction for SpecialA2
send SpecialA2.Input ;
send SpecialA2.Many ;
send SpecialA2.Maybe ;
send SpecialA2.B ;
Root ::= A ;
A ::= <Input:String> Many:B* [Maybe:B] B ;
SpecialA1 : A ;
SpecialA2 : A ;
B ::= ;
package org.jastadd.ragconnect.tests;
import org.jastadd.ragconnect.tests.utils.TestUtils;
import org.junit.jupiter.api.BeforeEach;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.jastadd.ragconnect.tests.utils.TestUtils.readFile;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* TODO: Add description.
*
* @author rschoene - Initial contribution
*/
public abstract class AbstractCompilerTest extends RagConnectTest {
protected abstract String getDirectoryName();
protected String getOutputDirectory() {
return TestUtils.OUTPUT_DIRECTORY_PREFIX + getDirectoryName();
}
protected abstract String getDefaultGrammarName();
@BeforeEach
public void ensureOutputDirectory() {
File outputDirectory = new File(getOutputDirectory());
assertTrue((outputDirectory.exists() && outputDirectory.isDirectory()) || outputDirectory.mkdir());
}
protected Path test(String inputDirectoryName, int expectedReturnValue, String rootNode, String... connectNames) throws IOException {
String grammarFile = Paths.get(inputDirectoryName, getDefaultGrammarName() + ".relast").toString();
List<String> connectFiles = Arrays.stream(connectNames)
.map(connectName -> Paths.get(inputDirectoryName,connectName + ".connect").toString())
.collect(Collectors.toList());
return TestUtils.runCompiler(grammarFile, connectFiles, rootNode,
getDirectoryName(), expectedReturnValue);
}
protected void testAndCompare(String inputDirectoryName, String expectedName, String rootNode, String... connectNames) throws IOException {
Path outPath = test(inputDirectoryName, 1, rootNode, connectNames);
final String startOfErrorsPattern = "Errors:\n";
String out = readFile(outPath, Charset.defaultCharset());
assertThat(out).contains(startOfErrorsPattern);
out = out.substring(out.indexOf(startOfErrorsPattern) + startOfErrorsPattern.length());
TestUtils.assertLinesMatch(getDirectoryName(), expectedName, out);
logger.info("ragconnect for " + expectedName + " returned:\n{}", out);
}
}
package org.jastadd.ragconnect.tests; package org.jastadd.ragconnect.tests;
import org.jastadd.ragconnect.tests.utils.TestUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.jastadd.ragconnect.tests.utils.TestUtils.readFile;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* Test error messages. * Test error messages.
* *
* @author rschoene - Initial contribution * @author rschoene - Initial contribution
*/ */
public class ErrorsTest extends RagConnectTest { public class ErrorsTest extends AbstractCompilerTest {
private static final String ERROR_DIRECTORY = "errors/";
private static final String OUTPUT_DIRECTORY = TestUtils.OUTPUT_DIRECTORY_PREFIX + ERROR_DIRECTORY;
private static final String DEFAULT_GRAMMAR_NAME = "Errors"; @Override
protected String getDirectoryName() {
return "errors";
}
@BeforeAll @Override
public static void createOutputDirectory() { protected String getDefaultGrammarName() {
File outputDirectory = new File(OUTPUT_DIRECTORY); return "Errors";
assertTrue((outputDirectory.exists() && outputDirectory.isDirectory()) || outputDirectory.mkdir());
} }
@Test @Test
void testStandardErrors() throws IOException { void testStandardErrors() throws IOException {
test("Standard", "A", "Standard"); testAndCompare(getDirectoryName(), "Standard", "A", "Standard");
} }
@Test @Test
void testTwoPartsErrors() throws IOException { void testTwoPartsErrors() throws IOException {
test("Part", "A", "Part1", "Part2"); testAndCompare(getDirectoryName(), "Part", "A", "Part1", "Part2");
} }
@SuppressWarnings("SameParameterValue")
private void test(String expectedName, String rootNode, String... connectNames) throws IOException {
String grammarFile = ERROR_DIRECTORY + DEFAULT_GRAMMAR_NAME + ".relast";
List<String> connectFiles = Arrays.stream(connectNames)
.map(connectName -> ERROR_DIRECTORY + connectName + ".connect")
.collect(Collectors.toList());
Path outPath = TestUtils.runCompiler(grammarFile, connectFiles, rootNode, ERROR_DIRECTORY, 1);
final String startOfErrorsPattern = "Errors:\n";
String out = readFile(outPath, Charset.defaultCharset());
assertThat(out).contains(startOfErrorsPattern);
out = out.substring(out.indexOf(startOfErrorsPattern) + startOfErrorsPattern.length());
TestUtils.assertLinesMatch(ERROR_DIRECTORY, expectedName, out);
logger.info("ragconnect for " + expectedName + " returned:\n{}", out);
}
} }
package org.jastadd.ragconnect.tests;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Paths;
/**
* Use cases that just need to compile.
*
* @author rschoene - Initial contribution
*/
public class PassingTest extends AbstractCompilerTest {
@Override
protected String getDirectoryName() {
return "passing";
}
@Override
protected String getDefaultGrammarName() {
return "Test";
}
protected void run(String inputDirectoryName, String rootNode) throws IOException {
super.test(Paths.get("passing", inputDirectoryName).toString(),
0, rootNode, getDefaultGrammarName());
}
@Test
public void testInheritance() throws IOException {
run("inheritance", "Root");
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment