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

Resolve "Feature: Add context-free connect"

- add context-free-endpoint-target (only for receive for now)
- using this target automatically will generate all context-sensitive connect-methods, and a connect method that dispatches to them
- methods are only generated if not given in spec already
- add error messages and tests
- also marked (commented out) tests in negative tests that are not currently handled
- updated documentation
parent c1cecf6a
No related branches found
No related tags found
3 merge requests!39Version 1.1.0,!35Version 1.0.0,!22Resolve "Feature: Add context-free connect"
Showing
with 386 additions and 96 deletions
...@@ -29,13 +29,53 @@ A breakdown of the parts of that syntax: ...@@ -29,13 +29,53 @@ A breakdown of the parts of that syntax:
As described above, it can be combined with `indexed`. As described above, it can be combined with `indexed`.
If used on its own, the incoming data is interpreted as a complete list and its elements will be appended to the current list. If used on its own, the incoming data is interpreted as a complete list and its elements will be appended to the current list.
- The `<Non-Terminal>[.<Target>["()"]]` notation describes the actual affected node. - The `<Non-Terminal>[.<Target>["()"]]` notation describes the actual affected node.
- If the target is omitted, all nodes of that non-terminal type can be connected, irrespective of their context. - If the target is omitted, all nodes of that non-terminal type can be connected, irrespective of their context. This is a context-free endpoint definition.
- The target can be any child on the right-hand side of a production rule, a role of a relation, or an attribute. - The target can be any child on the right-hand side of a production rule, a role of a relation, or an attribute.
The brackets `()` after the target must be used in case of an attribute, and only then. The brackets `()` after the target must be used in case of an attribute, and only then.
- Optionally, an endpoint can use one or more [mappings](#mappings). - Optionally, an endpoint can use one or more [mappings](#mappings).
They will be applied before sending, or after receiving a message. They will be applied before sending, or after receiving a message.
Mappings will always be applied in the order they are listed after `using`. Mappings will always be applied in the order they are listed after `using`.
### Context-Free Endpoints
!!! attention
Context-Free endpoints are currently only supported for receiving endpoints.
An endpoint with only a non-terminal and without a target is called context-free endpoint.
Specifying such an endpoint has several consequences:
- The given non-terminal can be connected to in all contexts it occurs as if there were endpoints for all those contexts.
- There is a special method available on the given non-terminal to connect itself, which selects the correct connect-method depending on its context.
- Context-sensitive endpoints for this non-terminal can still be specified to modify mappings in this context. If the context is a list, the endpoint must use `indexed` and cannot use `with add`.
**Example**:
```java
// grammar
Root ::= A SingleA:A [OptA:A] ListA:A* ;
A ::= <Value> ;
// connect
receive A;
receive Root.SingleA using MyMapping; // specialized endpoint
```
Implied, additional connect specifications:
```java
receive Root.A;
receive Root.OptA;
receive indexed Root.ListA;
```
Application code:
```java
A a = root.getOptA();
// new method on A:
a.connect("<some-uri-to-connect>");
// equivalent to (implicitly generated):
root.connectOptA("<some-uri-to-connect>");
```
## Mappings ## Mappings
A mapping is a side effect-free function with one argument (the value that will be transformed) and one result (the transformed value), that will be applied on a value to be sent for a sending endpoint, a received value for a receiving endpoint, or the result of another mapping. A mapping is a side effect-free function with one argument (the value that will be transformed) and one result (the transformed value), that will be applied on a value to be sent for a sending endpoint, a received value for a receiving endpoint, or the result of another mapping.
......
...@@ -12,6 +12,11 @@ aspect Analysis { ...@@ -12,6 +12,11 @@ aspect Analysis {
.filter(containingEndpointDefinition()::matchesType) .filter(containingEndpointDefinition()::matchesType)
.count() > 1; .count() > 1;
} }
eq ContextFreeTypeEndpointTarget.isAlreadyDefined() {
return lookupContextFreeTypeEndpointDefinitions(getTypeDecl()).stream()
.filter(containingEndpointDefinition()::matchesType)
.count() > 1;
}
syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this; syn boolean DependencyDefinition.isAlreadyDefined() = lookupDependencyDefinition(getSource().containingTypeDecl(), getID()) != this;
// --- matchesType --- // --- matchesType ---
...@@ -61,6 +66,7 @@ aspect Analysis { ...@@ -61,6 +66,7 @@ aspect Analysis {
syn boolean EndpointTarget.entityIsNormalAttribute(); syn boolean EndpointTarget.entityIsNormalAttribute();
eq TokenEndpointTarget.entityIsNormalAttribute() = !getToken().getNTA(); eq TokenEndpointTarget.entityIsNormalAttribute() = !getToken().getNTA();
eq TypeEndpointTarget.entityIsNormalAttribute() = !getType().getNTA(); eq TypeEndpointTarget.entityIsNormalAttribute() = !getType().getNTA();
eq ContextFreeTypeEndpointTarget.entityIsNormalAttribute() = false;
// --- needProxyToken --- // --- needProxyToken ---
syn boolean TokenComponent.needProxyToken() = !getDependencySourceDefinitionList().isEmpty() || getTokenEndpointTargetList().stream().map(EndpointTarget::containingEndpointDefinition).anyMatch(EndpointDefinition::shouldSendValue); syn boolean TokenComponent.needProxyToken() = !getDependencySourceDefinitionList().isEmpty() || getTokenEndpointTargetList().stream().map(EndpointTarget::containingEndpointDefinition).anyMatch(EndpointDefinition::shouldSendValue);
......
...@@ -27,6 +27,16 @@ aspect Errors { ...@@ -27,6 +27,16 @@ aspect Errors {
token().effectiveJavaTypeUse()) token().effectiveJavaTypeUse())
to RagConnect.errors(); to RagConnect.errors();
ContextFreeTypeEndpointTarget contributes error("Context-Free endpoint not allowed for root node " +
getTypeDecl().getName() + "!")
when getTypeDecl().occurencesInProductionRules().isEmpty()
to RagConnect.errors();
EndpointDefinition contributes error("Clash with implied, indexed endpoint definition of context-free endpoint in line " +
clashingContextFreeEndpointDefinition().getStartLine() + "!")
when !getSend() && clashingContextFreeEndpointDefinition() != null
to RagConnect.errors();
DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID()) DependencyDefinition contributes error("Dependency definition already defined for " + getSource().containingTypeDecl().getName() + " with name " + getID())
when isAlreadyDefined() when isAlreadyDefined()
to RagConnect.errors(); to RagConnect.errors();
...@@ -45,6 +55,18 @@ aspect ErrorHelpers { ...@@ -45,6 +55,18 @@ aspect ErrorHelpers {
} }
return false; return false;
} }
syn EndpointDefinition EndpointDefinition.clashingContextFreeEndpointDefinition() {
if (getSend() || !typeIsList() || getIndexBasedListAccess()) {
return null;
}
List<EndpointDefinition> contextFreeEndpointsWithSameType = lookupContextFreeTypeEndpointDefinitions(
getEndpointTarget().asTypeEndpointTarget().getType().getTypeDecl());
if (!contextFreeEndpointsWithSameType.isEmpty()) {
return contextFreeEndpointsWithSameType.get(0);
}
return null;
}
} }
aspect ErrorMessage { aspect ErrorMessage {
......
...@@ -157,6 +157,14 @@ aspect MustacheMappingApplicationAndDefinition { ...@@ -157,6 +157,14 @@ aspect MustacheMappingApplicationAndDefinition {
eq MTypeSendDefinition.preemptiveExpectedValue() = lastValue(); eq MTypeSendDefinition.preemptiveExpectedValue() = lastValue();
eq MTypeSendDefinition.preemptiveReturn() = "return false;"; eq MTypeSendDefinition.preemptiveReturn() = "return false;";
eq MContextFreeTypeReceiveDefinition.firstInputVarName() = "message";
eq MContextFreeTypeReceiveDefinition.preemptiveExpectedValue() = "this";
eq MContextFreeTypeReceiveDefinition.preemptiveReturn() = "return;";
eq MContextFreeTypeSendDefinition.firstInputVarName() = null;
eq MContextFreeTypeSendDefinition.preemptiveExpectedValue() = null;
eq MContextFreeTypeSendDefinition.preemptiveReturn() = null;
syn String MEndpointDefinition.parentTypeName() = getEndpointDefinition().parentTypeName(); syn String MEndpointDefinition.parentTypeName() = getEndpointDefinition().parentTypeName();
syn String MEndpointDefinition.getterMethodName() = getEndpointDefinition().getterMethodName(); syn String MEndpointDefinition.getterMethodName() = getEndpointDefinition().getterMethodName();
...@@ -174,9 +182,17 @@ aspect MustacheRagConnect { ...@@ -174,9 +182,17 @@ aspect MustacheRagConnect {
syn List<EndpointDefinition> RagConnect.allEndpointDefinitionList() { syn List<EndpointDefinition> RagConnect.allEndpointDefinitionList() {
List<EndpointDefinition> result = new ArrayList<>(); List<EndpointDefinition> result = new ArrayList<>();
for (ConnectSpecification spec : getConnectSpecificationFileList()) { // first gather all user-defined endpoint definitions, that are not context-free
spec.getEndpointDefinitionList().forEach(result::add); for (EndpointDefinition def : givenEndpointDefinitionList()) {
if (!def.hasContextFreeTypeEndpointTarget()) {
result.add(def);
} }
}
// then check for additional endpoints, and add if no conflict with existing definitions exists
for (EndpointDefinition def : givenEndpointDefinitionList()) {
def.getEndpointTarget().impliedEndpointDefinitions().iterator().forEachRemaining(result::add);
}
return result; return result;
} }
...@@ -192,8 +208,54 @@ aspect MustacheRagConnect { ...@@ -192,8 +208,54 @@ aspect MustacheRagConnect {
return result; return result;
} }
syn List<TypeDecl> RagConnect.typeDeclsOfContextFreeEndpointTargets() {
List<TypeDecl> result = new ArrayList<>();
for (EndpointTarget target : givenEndpointTargetList()) {
if (target.isContextFreeTypeEndpointTarget()) {
result.add(target.asContextFreeTypeEndpointTarget().getTypeDecl());
}
}
return result;
}
// === MappingDefinition === // === MappingDefinition ===
syn boolean MappingDefinition.isUsed() = !effectiveUsedAt().isEmpty(); syn boolean MappingDefinition.isUsed() = !effectiveUsedAt().isEmpty();
// === attributes needed for computing above ones ===
syn List<EndpointDefinition> RagConnect.givenEndpointDefinitionList() {
List<EndpointDefinition> result = new ArrayList<>();
for (ConnectSpecification spec : getConnectSpecificationFileList()) {
spec.getEndpointDefinitionList().forEach(result::add);
}
return result;
}
syn nta JastAddList<EndpointDefinition> EndpointTarget.impliedEndpointDefinitions() = new JastAddList<>();
eq ContextFreeTypeEndpointTarget.impliedEndpointDefinitions() {
JastAddList<EndpointDefinition> result = super.impliedEndpointDefinitions();
EndpointDefinition containingDef = containingEndpointDefinition();
for (TypeComponent typeComponent : getTypeDecl().occurencesInProductionRules()) {
List<EndpointDefinition> defsForTypeComponent = lookupTypeEndpointDefinitions(typeComponent);
if (!defsForTypeComponent.stream().anyMatch(containingDef::matchesType)) {
// there is no user-defined endpoint definition for this typeComponent yet
// -> create a new endpoint definition with the same options and mappings as the context-free def
// (except indexed-based for list-types)
EndpointDefinition newDef = new EndpointDefinition();
newDef.setAlwaysApply(containingDef.getAlwaysApply());
newDef.setIndexBasedListAccess(typeComponent.isListComponent());
newDef.setSend(containingDef.getSend());
containingDef.getMappings().forEach(newDef::addMapping);
TypeEndpointTarget target = new TypeEndpointTarget();
target.setType(typeComponent);
newDef.setEndpointTarget(target);
result.add(newDef);
}
}
return result;
}
} }
aspect MustacheReceiveAndSendAndHandleUri { aspect MustacheReceiveAndSendAndHandleUri {
...@@ -234,6 +296,10 @@ aspect MustacheReceiveAndSendAndHandleUri { ...@@ -234,6 +296,10 @@ aspect MustacheReceiveAndSendAndHandleUri {
eq TypeEndpointTarget.parentTypeName() = getType().containingTypeDecl().getName(); eq TypeEndpointTarget.parentTypeName() = getType().containingTypeDecl().getName();
eq TypeEndpointTarget.entityName() = getType().getName() + (typeIsList() && !containingEndpointDefinition().getIndexBasedListAccess() ? "List" : ""); eq TypeEndpointTarget.entityName() = getType().getName() + (typeIsList() && !containingEndpointDefinition().getIndexBasedListAccess() ? "List" : "");
eq ContextFreeTypeEndpointTarget.getterMethodName() = null;
eq ContextFreeTypeEndpointTarget.parentTypeName() = getTypeDecl().getName();
eq ContextFreeTypeEndpointTarget.entityName() = "";
} }
aspect MustacheReceiveDefinition { aspect MustacheReceiveDefinition {
...@@ -241,6 +307,8 @@ aspect MustacheReceiveDefinition { ...@@ -241,6 +307,8 @@ aspect MustacheReceiveDefinition {
syn boolean RagConnect.configLoggingEnabledForReads() = getConfiguration().getLoggingEnabledForReads(); syn boolean RagConnect.configLoggingEnabledForReads() = getConfiguration().getLoggingEnabledForReads();
// === EndpointDefinition === // === EndpointDefinition ===
syn boolean EndpointDefinition.hasContextFreeTypeEndpointTarget() = getEndpointTarget().isContextFreeTypeEndpointTarget();
syn boolean EndpointDefinition.hasTypeEndpointTarget() = getEndpointTarget().isTypeEndpointTarget(); syn boolean EndpointDefinition.hasTypeEndpointTarget() = getEndpointTarget().isTypeEndpointTarget();
syn String EndpointDefinition.idTokenName() = "InternalRagconnectTopicInList"; syn String EndpointDefinition.idTokenName() = "InternalRagconnectTopicInList";
...@@ -281,26 +349,29 @@ aspect MustacheSendDefinition { ...@@ -281,26 +349,29 @@ aspect MustacheSendDefinition {
syn String EndpointTarget.senderName(); syn String EndpointTarget.senderName();
eq TokenEndpointTarget.senderName() = "_sender_" + getToken().getName(); eq TokenEndpointTarget.senderName() = "_sender_" + getToken().getName();
eq TypeEndpointTarget.senderName() = "_sender_" + getType().getName(); eq TypeEndpointTarget.senderName() = "_sender_" + getType().getName();
eq ContextFreeTypeEndpointTarget.senderName() = null;
syn String MEndpointDefinition.updateMethodName(); syn String MEndpointDefinition.updateMethodName();
syn String MEndpointDefinition.writeMethodName(); syn String MEndpointDefinition.writeMethodName();
// MTokenReceiveDefinition
eq MTokenReceiveDefinition.updateMethodName() = null; eq MTokenReceiveDefinition.updateMethodName() = null;
eq MTokenReceiveDefinition.writeMethodName() = null; eq MTokenReceiveDefinition.writeMethodName() = null;
// MTokenSendDefinition
eq MTokenSendDefinition.updateMethodName() = "_update_" + tokenName(); eq MTokenSendDefinition.updateMethodName() = "_update_" + tokenName();
eq MTokenSendDefinition.writeMethodName() = "_writeLastValue_" + tokenName(); eq MTokenSendDefinition.writeMethodName() = "_writeLastValue_" + tokenName();
// MTypeReceiveDefinition
eq MTypeReceiveDefinition.updateMethodName() = null; eq MTypeReceiveDefinition.updateMethodName() = null;
eq MTypeReceiveDefinition.writeMethodName() = null; eq MTypeReceiveDefinition.writeMethodName() = null;
// MTypeSendDefinition
eq MTypeSendDefinition.updateMethodName() = "_update_" + typeName(); eq MTypeSendDefinition.updateMethodName() = "_update_" + typeName();
eq MTypeSendDefinition.writeMethodName() = "_writeLastValue_" + typeName(); eq MTypeSendDefinition.writeMethodName() = "_writeLastValue_" + typeName();
eq MContextFreeTypeReceiveDefinition.updateMethodName() = null;
eq MContextFreeTypeReceiveDefinition.writeMethodName() = null;
eq MContextFreeTypeSendDefinition.updateMethodName() = null;
eq MContextFreeTypeSendDefinition.writeMethodName() = null;
syn String EndpointDefinition.tokenName() = token().getName(); syn String EndpointDefinition.tokenName() = token().getName();
syn String MEndpointDefinition.tokenName() = getEndpointDefinition().tokenName(); syn String MEndpointDefinition.tokenName() = getEndpointDefinition().tokenName();
...@@ -350,6 +421,25 @@ aspect MustacheTokenComponent { ...@@ -350,6 +421,25 @@ aspect MustacheTokenComponent {
// > see MustacheSend for updateMethodName, writeMethodName // > see MustacheSend for updateMethodName, writeMethodName
} }
aspect MustacheTypeDecl {
// === TypeComponent ===
syn String TypeComponent.parentTypeName() = containingTypeDecl().getName();
// === TypeDecl ===
syn List<TypeComponent> TypeDecl.occurencesInProductionRules() {
List<TypeComponent> result = new ArrayList<>();
for (TypeDecl typeDecl : program().typeDecls()) {
for (Component comp : typeDecl.getComponentList()) {
if (comp.isTypeComponent() && comp.asTypeComponent().getTypeDecl().equals(this)) {
result.add(comp.asTypeComponent());
}
}
}
return result;
}
}
aspect AttributesForMustache { aspect AttributesForMustache {
syn String MEndpointDefinition.lastValue() = getEndpointDefinition().lastValue(); syn String MEndpointDefinition.lastValue() = getEndpointDefinition().lastValue();
...@@ -361,22 +451,7 @@ aspect AttributesForMustache { ...@@ -361,22 +451,7 @@ aspect AttributesForMustache {
syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1); syn MInnerMappingDefinition MEndpointDefinition.lastDefinition() = getInnerMappingDefinition(getNumInnerMappingDefinition() - 1);
syn nta MEndpointDefinition EndpointDefinition.toMustache() { syn nta MEndpointDefinition EndpointDefinition.toMustache() {
final MEndpointDefinition result; final MEndpointDefinition result = getEndpointTarget().createMEndpointDefinition(getSend());
if (getEndpointTarget().isTokenEndpointTarget()) {
if (getSend()) {
result = new MTokenSendDefinition();
} else {
result = new MTokenReceiveDefinition();
}
} else if (getEndpointTarget().isTypeEndpointTarget()) {
if (getSend()) {
result = new MTypeSendDefinition();
} else {
result = new MTypeReceiveDefinition();
}
} else {
throw new RuntimeException("Unknown endpoint target type: " + getEndpointTarget());
}
result.setEndpointDefinition(this); result.setEndpointDefinition(this);
for (MappingDefinition def : effectiveMappings()) { for (MappingDefinition def : effectiveMappings()) {
MInnerMappingDefinition inner = new MInnerMappingDefinition(); MInnerMappingDefinition inner = new MInnerMappingDefinition();
...@@ -385,6 +460,20 @@ aspect AttributesForMustache { ...@@ -385,6 +460,20 @@ aspect AttributesForMustache {
} }
return result; return result;
} }
abstract MEndpointDefinition EndpointTarget.createMEndpointDefinition(boolean isSend);
MEndpointDefinition TokenEndpointTarget.createMEndpointDefinition(boolean isSend) {
return isSend ? new MTokenSendDefinition() : new MTokenReceiveDefinition();
}
MEndpointDefinition TypeEndpointTarget.createMEndpointDefinition(boolean isSend) {
return isSend ? new MTypeSendDefinition() : new MTypeReceiveDefinition();
}
MEndpointDefinition ContextFreeTypeEndpointTarget.createMEndpointDefinition(boolean isSend) {
return isSend ? new MContextFreeTypeSendDefinition() : new MContextFreeTypeReceiveDefinition();
}
MEndpointDefinition UntypedEndpointTarget.createMEndpointDefinition(boolean isSend) {
throw new RuntimeException("Untyped endpoint target type, typeName= " +
getTypeName() + ", childName=" + getChildName());
}
} }
aspect GrammarGeneration { aspect GrammarGeneration {
......
...@@ -7,6 +7,9 @@ MTokenSendDefinition : MTokenEndpointDefinition; ...@@ -7,6 +7,9 @@ MTokenSendDefinition : MTokenEndpointDefinition;
abstract MTypeEndpointDefinition : MEndpointDefinition; abstract MTypeEndpointDefinition : MEndpointDefinition;
MTypeReceiveDefinition : MTypeEndpointDefinition; MTypeReceiveDefinition : MTypeEndpointDefinition;
MTypeSendDefinition : MTypeEndpointDefinition; MTypeSendDefinition : MTypeEndpointDefinition;
abstract MContextFreeTypeEndpointDefinition : MEndpointDefinition;
MContextFreeTypeReceiveDefinition : MContextFreeTypeEndpointDefinition;
MContextFreeTypeSendDefinition : MContextFreeTypeEndpointDefinition;
MInnerMappingDefinition; MInnerMappingDefinition;
rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition; rel MInnerMappingDefinition.MappingDefinition -> MappingDefinition;
...@@ -294,6 +294,7 @@ aspect Mappings { ...@@ -294,6 +294,7 @@ aspect Mappings {
syn String EndpointTarget.targetTypeName(); syn String EndpointTarget.targetTypeName();
eq TokenEndpointTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName(); eq TokenEndpointTarget.targetTypeName() = getToken().effectiveJavaTypeUse().getName();
eq TypeEndpointTarget.targetTypeName() = getType().getTypeDecl().getName(); eq TypeEndpointTarget.targetTypeName() = getType().getTypeDecl().getName();
eq ContextFreeTypeEndpointTarget.targetTypeName() = getTypeDecl().getName();
// eq ReceiveFromRestDefinition.suitableDefaultMapping() { // eq ReceiveFromRestDefinition.suitableDefaultMapping() {
// String typeName = getMappingList().isEmpty() ? // String typeName = getMappingList().isEmpty() ?
......
...@@ -5,7 +5,7 @@ aspect RagConnectNameResolution { ...@@ -5,7 +5,7 @@ aspect RagConnectNameResolution {
eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token); eq RagConnect.getConnectSpecificationFile().lookupTokenEndpointDefinitions(TokenComponent token) = lookupTokenEndpointDefinitions(token);
syn java.util.List<EndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) { syn java.util.List<EndpointDefinition> RagConnect.lookupTokenEndpointDefinitions(TokenComponent token) {
java.util.List<EndpointDefinition> result = new java.util.ArrayList<>(); java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
for (EndpointTarget target : allEndpointTargetList()) { for (EndpointTarget target : givenEndpointTargetList()) {
if (target.isTokenEndpointTarget() && target.asTokenEndpointTarget().getToken().equals(token)) { if (target.isTokenEndpointTarget() && target.asTokenEndpointTarget().getToken().equals(token)) {
result.add(target.containingEndpointDefinition()); result.add(target.containingEndpointDefinition());
} }
...@@ -19,7 +19,7 @@ aspect RagConnectNameResolution { ...@@ -19,7 +19,7 @@ aspect RagConnectNameResolution {
eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type); eq RagConnect.getConnectSpecificationFile().lookupTypeEndpointDefinitions(TypeComponent type) = lookupTypeEndpointDefinitions(type);
syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) { syn java.util.List<EndpointDefinition> RagConnect.lookupTypeEndpointDefinitions(TypeComponent type) {
java.util.List<EndpointDefinition> result = new java.util.ArrayList<>(); java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
for (EndpointTarget target : allEndpointTargetList()) { for (EndpointTarget target : givenEndpointTargetList()) {
if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) { if (target.isTypeEndpointTarget() && target.asTypeEndpointTarget().getType().equals(type)) {
result.add(target.containingEndpointDefinition()); result.add(target.containingEndpointDefinition());
} }
...@@ -27,6 +27,20 @@ aspect RagConnectNameResolution { ...@@ -27,6 +27,20 @@ aspect RagConnectNameResolution {
return result; return result;
} }
// --- lookupContextFreeTypeEndpointDefinition ---
inh java.util.List<EndpointDefinition> EndpointDefinition.lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl);
inh java.util.List<EndpointDefinition> EndpointTarget.lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl);
eq RagConnect.getConnectSpecificationFile().lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl) = lookupContextFreeTypeEndpointDefinitions(typeDecl);
syn java.util.List<EndpointDefinition> RagConnect.lookupContextFreeTypeEndpointDefinitions(TypeDecl typeDecl) {
java.util.List<EndpointDefinition> result = new java.util.ArrayList<>();
for (EndpointTarget target : givenEndpointTargetList()) {
if (target.isContextFreeTypeEndpointTarget() && target.asContextFreeTypeEndpointTarget().getTypeDecl().equals(typeDecl)) {
result.add(target.containingEndpointDefinition());
}
}
return result;
}
// --- lookupDependencyDefinition --- // --- lookupDependencyDefinition ---
inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id); inh DependencyDefinition DependencyDefinition.lookupDependencyDefinition(TypeDecl source, String id);
eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) { eq RagConnect.getConnectSpecificationFile().lookupDependencyDefinition(TypeDecl source, String id) {
......
...@@ -12,6 +12,12 @@ aspect NewStuff { ...@@ -12,6 +12,12 @@ aspect NewStuff {
syn boolean EndpointTarget.isTypeEndpointTarget() = false; syn boolean EndpointTarget.isTypeEndpointTarget() = false;
eq TypeEndpointTarget.isTypeEndpointTarget() = true; eq TypeEndpointTarget.isTypeEndpointTarget() = true;
/** Tests if EndpointTarget is a ContextFreeTypeEndpointTarget.
* @return 'true' if this is a ContextFreeTypeEndpointTarget, otherwise 'false'
*/
syn boolean EndpointTarget.isContextFreeTypeEndpointTarget() = false;
eq ContextFreeTypeEndpointTarget.isContextFreeTypeEndpointTarget() = true;
/** Tests if EndpointTarget is a UntypedEndpointTarget. /** Tests if EndpointTarget is a UntypedEndpointTarget.
* @return 'true' if this is a UntypedEndpointTarget, otherwise 'false' * @return 'true' if this is a UntypedEndpointTarget, otherwise 'false'
*/ */
...@@ -32,6 +38,13 @@ aspect NewStuff { ...@@ -32,6 +38,13 @@ aspect NewStuff {
eq EndpointTarget.asTypeEndpointTarget() = null; eq EndpointTarget.asTypeEndpointTarget() = null;
eq TypeEndpointTarget.asTypeEndpointTarget() = this; eq TypeEndpointTarget.asTypeEndpointTarget() = this;
/** casts a EndpointTarget into a ContextFreeTypeEndpointTarget if possible.
* @return 'this' cast to a ContextFreeTypeEndpointTarget or 'null'
*/
syn ContextFreeTypeEndpointTarget EndpointTarget.asContextFreeTypeEndpointTarget();
eq EndpointTarget.asContextFreeTypeEndpointTarget() = null;
eq ContextFreeTypeEndpointTarget.asContextFreeTypeEndpointTarget() = this;
/** casts a EndpointTarget into a UntypedEndpointTarget if possible. /** casts a EndpointTarget into a UntypedEndpointTarget if possible.
* @return 'this' cast to a UntypedEndpointTarget or 'null' * @return 'this' cast to a UntypedEndpointTarget or 'null'
*/ */
...@@ -73,8 +86,8 @@ aspect RagConnectNavigation { ...@@ -73,8 +86,8 @@ aspect RagConnectNavigation {
} }
//--- allEndpointTargetList --- //--- givenEndpointTargetList ---
syn List<EndpointTarget> RagConnect.allEndpointTargetList() { syn List<EndpointTarget> RagConnect.givenEndpointTargetList() {
List<EndpointTarget> result = new ArrayList<>(); List<EndpointTarget> result = new ArrayList<>();
for (ConnectSpecification spec : getConnectSpecificationFileList()) { for (ConnectSpecification spec : getConnectSpecificationFileList()) {
spec.getEndpointDefinitionList().forEach(endpointDef -> result.add(endpointDef.getEndpointTarget())); spec.getEndpointDefinitionList().forEach(endpointDef -> result.add(endpointDef.getEndpointTarget()));
......
...@@ -11,7 +11,9 @@ TokenEndpointTarget : EndpointTarget; ...@@ -11,7 +11,9 @@ TokenEndpointTarget : EndpointTarget;
rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget*; rel TokenEndpointTarget.Token <-> TokenComponent.TokenEndpointTarget*;
TypeEndpointTarget : EndpointTarget; TypeEndpointTarget : EndpointTarget;
rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*; rel TypeEndpointTarget.Type <-> TypeComponent.TypeEndpointTarget*;
UntypedEndpointTarget : EndpointTarget ::= <TokenOrType>; // only used by parser ContextFreeTypeEndpointTarget : EndpointTarget;
rel ContextFreeTypeEndpointTarget.TypeDecl <-> TypeDecl.ContextFreeTypeEndpointTarget?;
UntypedEndpointTarget : EndpointTarget ::= <TypeName> <ChildName>; // only used by parser
// to be integrated: // to be integrated:
//AttributeEndpointTarget : EndpointTarget ::= <Name> ; //AttributeEndpointTarget : EndpointTarget ::= <Name> ;
//RelationEndpointTarget : EndpointTarget ; //RelationEndpointTarget : EndpointTarget ;
......
aspect ParserRewrites { aspect ParserRewrites {
rewrite UntypedEndpointTarget { rewrite UntypedEndpointTarget {
when (tryGloballyResolveTypeComponentByToken(getTokenOrType()) != null) when (getChildName() != null && tryGloballyResolveTypeComponentByToken(combinedName()) != null)
to TypeEndpointTarget { to TypeEndpointTarget {
TypeEndpointTarget result = new TypeEndpointTarget(); TypeEndpointTarget result = new TypeEndpointTarget();
result.setType(TypeComponent.createRef(this.getTokenOrType())); result.copyOtherValuesFrom(this);
result.setType(TypeComponent.createRef(this.combinedName()));
return result; return result;
} }
when (tryGloballyResolveTokenComponentByToken(getTokenOrType()) != null)
when (getChildName() != null && tryGloballyResolveTokenComponentByToken(combinedName()) != null)
to TokenEndpointTarget { to TokenEndpointTarget {
TokenEndpointTarget result = new TokenEndpointTarget(); TokenEndpointTarget result = new TokenEndpointTarget();
result.setToken(TokenComponent.createRef(this.getTokenOrType())); result.copyOtherValuesFrom(this);
result.setToken(TokenComponent.createRef(this.combinedName()));
return result;
}
when (getChildName() == "")
to ContextFreeTypeEndpointTarget {
ContextFreeTypeEndpointTarget result = new ContextFreeTypeEndpointTarget();
result.copyOtherValuesFrom(this);
result.setTypeDecl(TypeDecl.createRef(getTypeName()));
return result; return result;
} }
} }
syn String UntypedEndpointTarget.combinedName() = getTypeName() + "." + getChildName();
protected void EndpointTarget.copyOtherValuesFrom(EndpointTarget source) {
this.setStart(source.getStartLine(), source.getStartColumn());
this.setEnd(source.getEndLine(), source.getEndColumn());
}
eq UntypedEndpointTarget.senderName() = "<untyped.senderName>"; eq UntypedEndpointTarget.senderName() = "<untyped.senderName>";
eq UntypedEndpointTarget.getterMethodName() = "<untyped.getterMethodName>"; eq UntypedEndpointTarget.getterMethodName() = "<untyped.getterMethodName>";
eq UntypedEndpointTarget.parentTypeName() = "<untyped.parentTypeName>"; eq UntypedEndpointTarget.parentTypeName() = "<untyped.parentTypeName>";
......
...@@ -27,13 +27,13 @@ ConnectSpecificationFile connect_specification_file ...@@ -27,13 +27,13 @@ ConnectSpecificationFile connect_specification_file
// return def; // return def;
// } // }
private EndpointDefinition createEndpointDefinition( private EndpointDefinition createEndpointDefinition(
String type_name, String child_name, boolean send, EndpointTarget endpointTarget, boolean send,
boolean indexBasedListAccess, boolean withAdd) { boolean indexBasedListAccess, boolean withAdd) {
EndpointDefinition result = new EndpointDefinition(); EndpointDefinition result = new EndpointDefinition();
result.setSend(send); result.setSend(send);
result.setIndexBasedListAccess(indexBasedListAccess); result.setIndexBasedListAccess(indexBasedListAccess);
result.setWithAdd(withAdd); result.setWithAdd(withAdd);
result.setEndpointTarget(new UntypedEndpointTarget(type_name + "." + child_name)); result.setEndpointTarget(endpointTarget);
return result; return result;
} }
:} ; :} ;
...@@ -53,30 +53,16 @@ EndpointDefinition endpoint_definition ...@@ -53,30 +53,16 @@ EndpointDefinition endpoint_definition
; ;
EndpointDefinition endpoint_definition_type EndpointDefinition endpoint_definition_type
= SEND ID.type_name DOT ID.child_name = SEND endpoint_target.t {: return createEndpointDefinition(t, true, false, false); :}
{: | RECEIVE endpoint_target.t {: return createEndpointDefinition(t, false, false, false); :}
return createEndpointDefinition(type_name, child_name, true, false, false); | RECEIVE INDEXED endpoint_target.t {: return createEndpointDefinition(t, false, true, false); :}
:} | RECEIVE WITH ADD endpoint_target.t {: return createEndpointDefinition(t, false, false, true ); :}
// | SEND INDEXED ID.type_name DOT ID.child_name | RECEIVE INDEXED WITH ADD endpoint_target.t {: return createEndpointDefinition(t, false, true, true ); :}
// {: ;
// return createEndpointDefinition(type_name, child_name, true, true, false);
// :} EndpointTarget endpoint_target
| RECEIVE ID.type_name DOT ID.child_name = ID.type_name DOT ID.child_name {: return new UntypedEndpointTarget(type_name, child_name); :}
{: | ID.type_name {: return new UntypedEndpointTarget(type_name, ""); :}
return createEndpointDefinition(type_name, child_name, false, false, false);
:}
| RECEIVE INDEXED ID.type_name DOT ID.child_name
{:
return createEndpointDefinition(type_name, child_name, false, true, false);
:}
| RECEIVE WITH ADD ID.type_name DOT ID.child_name
{:
return createEndpointDefinition(type_name, child_name, false, false, true);
:}
| RECEIVE INDEXED WITH ADD ID.type_name DOT ID.child_name
{:
return createEndpointDefinition(type_name, child_name, false, true, true);
:}
; ;
ArrayList string_list ArrayList string_list
......
...@@ -28,6 +28,10 @@ aspect RagConnect { ...@@ -28,6 +28,10 @@ aspect RagConnect {
{{> tokenComponent}} {{> tokenComponent}}
{{/tokenComponentsThatNeedProxy}} {{/tokenComponentsThatNeedProxy}}
{{#typeDeclsOfContextFreeEndpointTargets}}
{{> typeDecl}}
{{/typeDeclsOfContextFreeEndpointTargets}}
{{> ListAspect}} {{> ListAspect}}
public void {{rootNodeName}}.ragconnectCheckIncremental() { public void {{rootNodeName}}.ragconnectCheckIncremental() {
......
uncache {{Name}}._ragconnect_myContext();
inh String {{Name}}._ragconnect_myContext();
inh ASTNode {{Name}}._ragconnect_myParent();
uncache {{Name}}._ragconnect_myIndexInList();
inh int {{Name}}._ragconnect_myIndexInList();
{{#occurencesInProductionRules}}
eq {{parentTypeName}}.get{{Name}}()._ragconnect_myContext() = "{{parentTypeName}}.{{Name}}";
eq {{parentTypeName}}.get{{Name}}()._ragconnect_myParent() = this;
{{^isListComponent}}
eq {{parentTypeName}}.get{{Name}}()._ragconnect_myIndexInList() = -1;
{{/isListComponent}}
{{#isListComponent}}
eq {{parentTypeName}}.get{{Name}}(int i)._ragconnect_myIndexInList() = i;
{{/isListComponent}}
{{/occurencesInProductionRules}}
{{#ContextFreeTypeEndpointTarget}}{{#containingEndpointDefinition}}
public boolean {{Name}}.{{connectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
switch (_ragconnect_myContext()) {
{{#occurencesInProductionRules}}
{{!only using "connectMethodName" is not correct, since the actual name might be different, e.g., if both send and receive are defined. need a reference to the actual endpoint-definition here}}
{{^isListComponent}}
case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{connectMethodName}}{{Name}}({{connectParameterName}});
{{/isListComponent}}
{{#isListComponent}}
case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{connectMethodName}}{{Name}}({{connectParameterName}}, _ragconnect_myIndexInList());
{{/isListComponent}}
{{/occurencesInProductionRules}}
default:
System.err.println("No matching context while connecting " + this + " to " + {{connectParameterName}});
return false;
}
}
public boolean {{Name}}.{{disconnectMethodName}}(String {{connectParameterName}}) throws java.io.IOException {
switch (_ragconnect_myContext()) {
{{#occurencesInProductionRules}}
case "{{parentTypeName}}.{{Name}}": return (({{parentTypeName}}) _ragconnect_myParent()).{{disconnectMethodName}}{{Name}}({{connectParameterName}});
{{/occurencesInProductionRules}}
default:
System.err.println("No matching context while disconnecting for " + this + " from " + {{connectParameterName}});
return false;
}
}
{{/containingEndpointDefinition}}{{/ContextFreeTypeEndpointTarget}}
...@@ -112,6 +112,15 @@ task specificTest(type: Test, dependsOn: testClasses) { ...@@ -112,6 +112,15 @@ task specificTest(type: Test, dependsOn: testClasses) {
} }
} }
task newTests(type: Test, dependsOn: testClasses) {
description = 'Run test tagged with tag "New"'
group = 'verification'
useJUnitPlatform {
includeTags 'New'
}
}
preprocessorTesting { preprocessorTesting {
//noinspection GroovyAssignabilityCheck //noinspection GroovyAssignabilityCheck
relastCompilerLocation = '../libs/relast.jar' relastCompilerLocation = '../libs/relast.jar'
...@@ -556,21 +565,27 @@ task compileSingleListVariantIncremental(type: RagConnectTest, dependsOn: ':ragc ...@@ -556,21 +565,27 @@ task compileSingleListVariantIncremental(type: RagConnectTest, dependsOn: ':ragc
} }
} }
task cleanCurrentManualTest(type: Delete) { // --- Test: contextFreeSimple-incremental ---
// delete "src/test/02-after-ragconnect/singleListVariant" task compileContextFreeSimpleIncremental(type: RagConnectTest, dependsOn: ':ragconnect.base:jar') {
// delete "src/test/03-after-relast/singleListVariant" ragconnect {
// delete "src/test/java-gen/singleListVariant/ast" outputDir = file('src/test/02-after-ragconnect/contextFreeSimpleInc')
delete "src/test/02-after-ragconnect/singleList" inputFiles = [file('src/test/01-input/contextFreeSimple/Test.relast'),
delete "src/test/03-after-relast/singleList" file('src/test/01-input/contextFreeSimple/Test.connect')]
delete "src/test/java-gen/singleList/ast" rootNode = 'Root'
extraOptions = ['--experimental-jastadd-329']
}
relast {
useJastAddNames = true
grammarName = 'src/test/03-after-relast/contextFreeSimpleInc/contextFreeSimpleInc'
serializer = 'jackson'
}
jastadd {
jastAddList = 'JastAddList'
packageName = 'contextFreeSimpleInc.ast'
extraOptions = ['--tracing=cache,flush',
'--incremental=param',
'--cache=all',
'--rewrite=cnta',
'--flush=full']
} }
task cleanCurrentIncrementalTest(type: Delete) {
// delete "src/test/02-after-ragconnect/singleListVariantInc"
// delete "src/test/03-after-relast/singleListVariantInc"
// delete "src/test/java-gen/singleListVariantInc/ast"
delete "src/test/02-after-ragconnect/singleListInc"
delete "src/test/03-after-relast/singleListInc"
delete "src/test/java-gen/singleListInc/ast"
} }
compileSingleListManual.dependsOn cleanCurrentManualTest
compileSingleListIncremental.dependsOn cleanCurrentIncrementalTest
# ContextFree-Simple
Idea: Use only context-free context, in simple situation (non-recursive)
receive A;
//"receive Root.A;" is implied
receive Root.SingleA using PrependPrefix;
receive Root.OptA using AddSuffix;
//"receive Root.ListA;" would clash as "receive indexed Root.ListA" is implied
PrependPrefix maps A a to A {:
A result = new A();
result.setValue("pre" + a.getValue());
return result;
:}
AddSuffix maps A a to A {:
A result = new A();
result.setValue(a.getValue() + "post");
return result;
:}
Root ::= A SingleA:A [OptA:A] ListA:A* ;
A ::= <Value> ;
A ::= B C D ; A ::= B C D E* ;
// read definitions // read definitions
B ::= /<ErrorNTA:String>/ <ErrorTypeOfFirstMapping:String> <ErrorTypeOfLastMapping:String> <DoubledValue:int> <ErrorTypeMismatch:String> ; B ::= /<ErrorNTA:String>/ <ErrorTypeOfFirstMapping:String> <ErrorTypeOfLastMapping:String> <DoubledValue:int> <ErrorTypeMismatch:String> ;
// write definitions // write definitions
C ::= <ErrorNotNTA:String> /<ErrorTypeOfFirstMapping:String>/ /<ErrorTypeOfLastMapping1:String>/ /<ErrorTypeOfLastMapping2:List<String>>/ /<ErrorTypeMismatch:String>/ /<DoubledValue:int>/ ; C ::= /<ErrorTypeOfFirstMapping:String>/ /<ErrorTypeOfLastMapping1:String>/ /<ErrorTypeOfLastMapping2:List<String>>/ /<ErrorTypeMismatch:String>/ /<DoubledValue:int>/ ;
// dependency definitions // dependency definitions
D ::= <SourceNonExistingTarget> D ::= <SourceNonExistingTarget>
...@@ -13,3 +13,6 @@ D ::= <SourceNonExistingTarget> ...@@ -13,3 +13,6 @@ D ::= <SourceNonExistingTarget>
<SourceSameAsListNode> /<TargetSameAsListNode>/ <SourceSameAsListNode> /<TargetSameAsListNode>/
<SourceDoubledValue> /<TargetDoubledValue>/ <SourceDoubledValue> /<TargetDoubledValue>/
MyList:D* ; MyList:D* ;
// context-free endpoints
E ::= ;
// --- update receive definitions --- // --- receive definitions ---
// Error: there must not be two receive definitions for the same token // Error: there must not be two receive definitions for the same token
receive B.DoubledValue ; receive B.DoubledValue ;
receive B.DoubledValue using IntToInt ; receive B.DoubledValue using IntToInt ;
...@@ -18,22 +18,26 @@ receive B.ErrorTypeOfLastMapping using StringToList ; ...@@ -18,22 +18,26 @@ receive B.ErrorTypeOfLastMapping using StringToList ;
// Error: types of mappings must match (modulo inheritance) // Error: types of mappings must match (modulo inheritance)
receive B.ErrorTypeMismatch using StringToList, IntToInt ; receive B.ErrorTypeMismatch using StringToList, IntToInt ;
// --- update send definitions --- // Error: Context-Free endpoint not allowed for root nodes
receive A;
// Error: Clash with implied endpoint definition of context-free endpoint
receive E;
receive A.E;
// --- send definitions ---
// NOT HANDLED \\ Error: the token must be resolvable within the parent type // NOT HANDLED \\ Error: the token must be resolvable within the parent type
// NOT HANDLED \\ receive C.NonExisting ; // NOT HANDLED \\ receive C.NonExisting ;
// Error: Token must be a TokenNTA (i.e., check for Token.getNTA()) // NOT HANDLED \\ // Error: from-type of first mapping must be type of Token
send C.ErrorNotNTA ; // NOT HANDLED \\ send C.ErrorTypeOfFirstMapping using IntToInt ;
// Error: from-type of first mapping must be type of Token // NOT HANDLED \\ // Error: to-type of last mapping must be byte[] or a supported primitive type
send C.ErrorTypeOfFirstMapping using IntToInt ; // NOT HANDLED \\ send C.ErrorTypeOfLastMapping1 using StringToList ;
// NOT HANDLED \\ send C.ErrorTypeOfLastMapping2 ;
// Error: to-type of last mapping must be byte[] or a supported primitive type // NOT HANDLED \\ // Error: types of mappings must match (modulo inheritance)
send C.ErrorTypeOfLastMapping1 using StringToList ; // NOT HANDLED \\ send C.ErrorTypeMismatch using StringToList, IntToInt ;
send C.ErrorTypeOfLastMapping2 ;
// Error: types of mappings must match (modulo inheritance)
send C.ErrorTypeMismatch using StringToList, IntToInt ;
// Error: no more than one send mapping for each TokenComponent // Error: no more than one send mapping for each TokenComponent
send C.DoubledValue ; send C.DoubledValue ;
...@@ -44,8 +48,8 @@ send C.DoubledValue using IntToInt ; ...@@ -44,8 +48,8 @@ send C.DoubledValue using IntToInt ;
// NOT HANDLED \\ D.SourceNonExistingTarget canDependOn D.NonExisting as NonExistingTarget ; // NOT HANDLED \\ D.SourceNonExistingTarget canDependOn D.NonExisting as NonExistingTarget ;
// NOT HANDLED \\ D.NonExisting canDependOn D.TargetNonExistingSource as NonExistingSource ; // NOT HANDLED \\ D.NonExisting canDependOn D.TargetNonExistingSource as NonExistingSource ;
// Error: There must be a send update definition for the target token // NOT HANDLED \\ // Error: There must be a send update definition for the target token
D.SourceNoWriteDef canDependOn D.TargetNoWriteDef as NoWriteDef ; // NOT HANDLED \\ D.SourceNoWriteDef canDependOn D.TargetNoWriteDef as NoWriteDef ;
// Error: The name of a dependency definition must not be equal to a list-node on the source // Error: The name of a dependency definition must not be equal to a list-node on the source
D.SourceSameAsListNode canDependOn D.TargetSameAsListNode as MyList ; D.SourceSameAsListNode canDependOn D.TargetSameAsListNode as MyList ;
......
...@@ -5,7 +5,9 @@ Standard.connect Line 13, column 1: No suitable default mapping found for type j ...@@ -5,7 +5,9 @@ Standard.connect Line 13, column 1: No suitable default mapping found for type j
Standard.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the token (String)! Standard.connect Line 13, column 1: to-type of last mapping (java.util.List) not assignable to type of the token (String)!
Standard.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the token (String)! Standard.connect Line 16, column 1: to-type of last mapping (List) not assignable to type of the token (String)!
Standard.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the token (String)! Standard.connect Line 19, column 1: to-type of last mapping (int) not assignable to type of the token (String)!
Standard.connect Line 39, column 1: Endpoint definition already defined for C.DoubledValue Standard.connect Line 22, column 9: Context-Free endpoint not allowed for root node A!
Standard.connect Line 40, column 1: Endpoint definition already defined for C.DoubledValue Standard.connect Line 26, column 1: Clash with implied, indexed endpoint definition of context-free endpoint in line 25!
Standard.connect Line 51, column 1: The name of a dependency definition must not be equal to a list-node on the source Standard.connect Line 43, column 1: Endpoint definition already defined for C.DoubledValue
Standard.connect Line 56, column 1: Dependency definition already defined for D with name DoubledValue Standard.connect Line 44, column 1: Endpoint definition already defined for C.DoubledValue
Standard.connect Line 55, column 1: The name of a dependency definition must not be equal to a list-node on the source
Standard.connect Line 60, column 1: Dependency definition already defined for D with name DoubledValue
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment